タグ「JavaScript」の 記事 10 件中 1 ~ 10 件を表示しています。

何だかこのブログでは久々にプログラミングの話をするようだ。 若い頃は毎日のようにプログラミングの記事を書いていた (その片鱗が Qiita に残っている) のに、最近ではほとんど書かない。 若い時より学習意欲が減退してしまったというのもあるが、いくら勉強しても自分のやりたいような仕事ができるとは限らないので直接的に役に立つことが少ないというのがある (あれだけ勉強した Python / Django や Laravel も業務では一度も使えていない)。 それを言い出すとじゃあ転職したほうがいいんじゃないかとかなるかもしれないが、今更そんな気にもならない。

そんなこんなで最近になってようやく Vue.js 3 を試して使い始めた。 Vue 3 の目玉は Composition API らしく Vue 2 の Options API の書き方に比べて結構簡潔になるし何よりネストが浅くなるのがいい。 <script setup> 直下に書いた constfunction<template> 側でそのまま認識したのは感動した。 再利用性が高まるというメリットも書いてあったがそれはまだ感じていない。 Vue 2 で書かれたコードを Vue 3 の Composition API に移行するのはかなり大変そうだが、少なくとも一から書くぶんにはそんなに違和感もない。 強いて言えばググった時にバージョンによって記述が微妙に違うので記事の最新性を確認してから参考にするように務めるといったところか。 まあそれは Vue に限ったことではないのだが。

あと Vuex についてだが使ったプロジェクトで軒並み Vue と Vuex の役割分担がしっかりできていなくてコードがぐちゃぐちゃになるという問題があったので今回は使わずにやってみようかと思って試してみたが、とても無理だった。 Vue はコンポーネント間のデータのやり取りがかなり面倒な印象で、それは Vue 3 でも変わらなかった。 やっぱり Vuex はよっぽど小さいプロダクトでもない限り使わないというわけにはいかないようだ。 であればせめて Vuex は state と mutation のみ使用するとか、機能限定で使うとゴチャゴチャにならなそうな気がする。 今回のプロジェクトではそうしてみようと思った。

JavaScript で長押し時に実行するイベントを定義する機能というのはいわゆる言語標準としては実装されていない。 つまりクリックイベントであれば HTMLElement.onclick に設定すれば良い (若しくは HTMLElement.addEventListener('click', function)) が HTMLElement.onlongpressHTMLElement.addEventListener('longpress', function) 的なものは用意されていないということだ。 そのために長押し時に何かするライブラリが Vue.js や jQuery 向けに公開されているが、ピュア JavaScript 向けには見た感じ存在しない。 そしてそういったコードは検索するといくつか出てくるが、どれも何だか複雑なので今回わかりやすい形で自分で実装してみた。

定義

本当は HTMLElement.addEventListener('longpress', function) みたいな感じでコールできるようにしたかったが、それは addEventListener メソッドをオーバーライドしないといけなそうな感じに見えたので止めて HTMLElement.setOnLongPressListener() とコールバックを設定して実行させる形にした:

HTMLElement.prototype.setOnLongPressListener = function(listener, interval = 800) {
    let isInProgress = false;  // 長押し中かどうか
    this.addEventListener('mousedown', () => {
        isInProgress = true;
        setTimeout(() => {
            if (isInProgress) {
                listener();
            }
        }, interval);
    });
    this.addEventListener('mouseup', () => {
        isInProgress = false;
    });
};

ギミックは至って単純で mousedown した時にタイマーを起動して interval 経過後に mouseup されていなかったらコールバックを実行するというものだ。 コードを読めばすぐ分かると思う。 長押しを検出するミリ秒はデフォルト 0.8 秒とした。

使用例

document.querySelectorAll('table tbody td').forEach(element => {
    element.setOnLongPressListener(() => element.style.borderColor = 'blue');
});
document.querySelectorAll('table tbody td').forEach(element => {
    element.setOnLongPressListener(() => element.style.backgroundColor = 'red', 1500);
});

このコードを実行するとすべてのテーブルの tbody 内のセルを長押しすると 0.8 秒後に枠線が青くなり 1.5 秒後に背景が赤くなる。

昨日書いた Javascript.infoJavaScript の基礎の部分の一人読書会を実践した。

2.1 Hello, world!

<script>...</script> だけで良い。 今どきは type 属性を使わない。

<script type="text/javascript"><!--
    ...
//--></script>

のような JavaScript をサポートしないブラウザのためのコメントアウトも非常にレガシーなので使わない。

2.2 コード構造

下記のようなコードは想定通り機能しないので避ける:

alert("There will be an error")
[1, 2].forEach(alert)

JavaScript は角括弧 [...] の前にはセミコロンを想定しないため alert() の戻り値への配列添字アクセスのように解釈されてしまう。 こういった不測の事態を避けるためセミコロンを省略せずに常に付与するのが望ましい。

ショートカットキー Ctrl + / で 1 行コメントアウト、Ctrl + Shift + / で複数行コメントアウトができる。

2.3 モダンなモード, "use strict"

IDE が警告出してくれていたので使っていなかった、というより忘れていたが、使おう。

2.4 変数

定数 const は小文字キャメルケースのもの (いわゆる不変な参照) と大文字スネークケースの従来の定数を混在させて良い。 定数か不変な参照かどうかは下記のように実行時に変化するかをみるのがよい:

const BIRTHDAY = '18.04.1982';  // 定数
const age = someCode(BIRTHDAY);  // 不変な参照

2.5 データ型

Infinity (無限大) という定数が定義されているのは初めて知った。 BigInt も初めて知ったが、おそらく使う機会は無いだろう。

2.8 演算子

以下のように数値変換に + 演算子が使える:

let apples = "2";
let oranges = "3";

// 二項演算子プラスの処理の前に両方の値が数値に変換されます
alert(+apples + +oranges);  // 5

似たような考え方だが - 演算子でゼロを引いてもいいので、いつも apples - 0 + (oranges - 0) のようにしていた。

カンマ演算子の存在は改めて書かれるとなるほどと思った。 確かに for 文で使われる:

// 1 行に 3 つの演算子
for (a = 1, b = 3, c = a * b; a < 10; a++) {
 ...
}

昨日新しく出たオライリーの JavaScript 第 7 版が網羅的な学習に良いのでは、と書いたが今日ネットでいろいろ探していたら Javascript.info というサイトを見つけた。

Javascript.info は、ロシアで最大級のJavaScriptチュートリアルと学習プラットフォームである learn.javascript.ru のフォークとして作成されました。このチュートリアルの内容はオープンソースであり、誰もが貢献できます。

とのこと。 ちょっと見てみたが網羅的で詳しくてかなり良い。 今の時代はこんなコンテンツが無料で得られるのか、と感動する。 ということで高いお金を出してオライリーの本を買う必要はなさそうだ。 勿論本で勉強したほうが好みという人はいるだろうが、私はこれで十分だ。

久々に技術の話を書く。 オライリーの JavaScript 第 7 版が出ていた。 その存在によって ECMAScript 2015 (ES6) のモダンな記法で書くのを阻害されていた忌まわしき IE11 が今年 6 月 15 日にサポート終了されるとのことで、ようやく ES6 をトランスパイラなしで書けるようになりそうだ。 どちらにしろモジュールのインポートや SASS のコンパイルで Laravel Mix にはお世話になりそうだが、それにしても直に ES6 記法で書けるのは大きい。 そのために今一度 JavaScript を一から網羅的に学習するために、オライリーの JavaScript 本は必読だ。 値段がかなり高いが、それだけの価値はあると思う。

JavaScript 開発に必須

昨今の JavaScript 開発において ECMAScript 6 で記述してビルド時にブラウザで解釈できるレガシーな JavaScript に変換するために Babel などのトランスパイラの設定は必須になっていると思うが、私は以前より Laravel Mix という Laravel に付属している簡単に使えるトランスパイラを愛用している。 以前書いた記事だと Laravel プロジェクトからファイルを引っ張ってくる方法だったが、今回試した方法だともっとシンプルにいけたのでメモ。 尚、これを試すのに arms inc. Engineers' Blog 様の記事が大変参考になった。 前提条件として Node.js は既にインストールされているものとする。

1. 新規 Node.js プロジェクトを作成しインストール

まず PhpStorm で新規プロジェクトを作成する時に Node.js プロジェクトにする。 こうすると package.json のみが含まれたシンプルな Node.js プロジェクトが生成される。 ここで以下を叩き Laravel Mix と cross-env をインストールする:

npm install laravel-mix cross-env --save-dev

そして package.json に Laravel Mix のビルド用のスクリプトを配置する (各コマンドの説明は省略する):

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --watch",
    "prod": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  }
}

ちなみに私の環境だと --hide-modules オプションがついているとスクリプト実行時にエラーになった。 この場合 --hide-modules を削除すると実行できた。

2. webpack.mix.js ファイルの配置と動作確認

webpack.mix.js はビルド用の設定ファイルとなっている。 今回は JavaScript と SASS で書くことにする。 TypeScript や LESS を使いたかったりする場合は別途書き換える。 プロジェクト配下に webpack.mix.js というファイルを以下のように作る:

const mix = require('laravel-mix');
mix.js('src/app.js', 'dist/')
    .sass('src/app.scss', 'dist/')

この例の場合 src/app.js に置かれた JavaScript が dist/app.js にビルドされ src/app.scss に書かれた SCSS が dist/app.css にビルドされることとなる。 ということで srcdist ディレクトリを作成し src/app.jssrc/app.scss に適当な記述をしたファイルを作成し npm run dev を叩く。 webpack compiled successfully が表示され、正しく dist/app.jsdist/app.css が作成されることを確認する。 更に npm run watch を叩いて適当に src/app.jssrc/app.scss を書き換えて保存するとそれぞれビルドされて dist/app.jsdist/app.css が書き換わることを確認する。

Vue.js のコンポーネントファイル (いわゆる Hoge.vue) 内で最初から <style> タグが用意されているので疑いもせずに CSS で書いていたのだが、これが普通に SCSS (SASS) で書けるのを今日知った……。 <style lang="scss"> と書き換えると SCSS で記述できる。 この程度のことはまず疑問をもって調べるのが良いと身を持って感じた。

最近 PHP のフレームワークとして Laravel をよく使用している。 Laravel にバンドルされている ES6 や SASS を JavaScript や CSS にトランスパイルする仕組みがよくできていると感心していた。 この Laravel Mix という仕組みが実は Laravel を使用していないプロジェクトでも簡単に導入できたのでここに書き記しておく。

導入

  1. Laravel 公式を参考に Laravel の新規プロジェクトを適当に作る
  2. そのプロジェクト直下の package.jsonwebpack.mix.js を取り出して適用したいプロジェクトにコピー
  3. そのプロジェクト直下の resources/js/app.jsresources/js/bootstrap.jsresources/sass/app.scss を取り出して同じく適用したいプロジェクトにコピー
  4. package.json があるディレクトリで npm install を叩く

npm run dev を叩いて public/js/app.jspublic/css/app.css にビルドされるのを確認する。 ビルド先を変えたい場合は webpack.mix.js を適当に書き換える。

使い方

Laravel 公式参照。 基本的に npm run watch を叩くと変更を監視してくれるのでその状態のまま開発を進めると楽。

恥ずかしながら今まで jQuery ばかり使用していたので document.querySelector の存在を知らなかったのでメモ。 Laravel の Blade テンプレート内でデフォルトで head 要素内に以下のように meta タグで CSRF トークンが出力されている:

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    ...
</head>

上記は Blade 内なので {{ csrf_token }} という Blade の記法で取得できるが、この値を Vue.js のコンポーネント内で取得するのに meta タグから取り出す必要があった。 そこで私は最初「jQuery なら一発で取れるのに」と思いながら以下のように書いてしまった:

/**
 * メタ要素から CSRF トークンを取得する.
 *
 * @returns {string} CSRF トークン
 */
csrfToken() {
    let children = document.head.children;
    for (const child of children) {
        if (child.getAttribute('name') === 'csrf-token') {
            return child.getAttribute('content');
        }
    }
    return null;
}

しかしこれを書き終えた後で「Laravel で使われている Axios でヘッダに CSRF トークンを付与しているはずだがそこはどうやっているのか」と思い bootstrap.js を参照してみたら以下のように書かれていた:

let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

この querySelector というのを知らなかったのだが、どうも CSS の記法でセレクタを記述することで jQuery のように DOM 要素を取得できる DOM のメソッドらしい。 最近できたのかと思いきや IE8 から利用可能なようで意外とレガシーなものだった……。 最初の 1 件を取得したい場合 querySelector を使用し、複数件数取得したい場合は querySelectorAll を使用する。

これから Vue.js のようなモダンな JS フレームワークをメインとし脱 jQuery としていくにあたって必須知識だろう。 覚えておきたい。

Vue.js を使用していて Vuex を使用すべきか否かというのがよく分からなかった。 公式にも以下のように書いてある:

もし、あなたが大規模な SPA を構築することなく、Vuex を導入した場合、冗長で恐ろしいと感じるかもしれません。そう感じることは全く普通です。あなたのアプリがシンプルであれば、Vuex なしで問題ないでしょう。

シンプルとはどのような状態を指すのか、というところだがこの場合のシンプルというのは「単一のコンポーネントのみで構成されるようなもの」だということが作っているうちに分かってきた。 Vue.js は子コンポーネント同士、若しくは子コンポーネントから親コンポーネントに伝達させるのに this.$emit などというよく分からないものを使用し、これがバケツリレーのように冗長だし記法も分かりにくい。

今回後から Vuex を導入したが、後から変更すると既存をそのように修正しなければならず面倒だ。 コンポーネントで分割することが最初から分かっているならばはじめから Vuex を導入してしまったほうがいいと思う。 「大規模向け」とかそういうワードに惑わされると損だ。