npm」タグアーカイブ

WebPack + Vueで、「Element」導入時にハマった点

前提

以下環境にて確認

要素 バージョン
debian 8.6
node 8.2.1
npm 5.3.0

概要

Webpackを用いてVueアプリを開発している中で、Vue2.0用のUIライブラリであるElementを導入しようとしたら、Webpack周りでハマったので導入手順と解決手順を備忘録に残す。

備忘録が主な目的なので、Webpack,Vue,Elementなどに関する説明は割愛

Elementの導入

npmでインストールする

npm install element-ui -s

エントリポイントでElement関連をimport

import ElementUI  from 'element-ui'
import locale     from 'element-ui/lib/locale/lang/ja'
import 'element-ui/lib/theme-default/index.css'

Elementの利用を宣言。localeを指定することで日本語が有効になるらしい

Vue.use(ElementUI, {locale});

エラー① Element内のCSSファイルが解決できない

この状態でwebpackを回すと、以下のエラーが出る

ERROR in ./node_modules/element-ui/lib/theme-default/index.css
Module parse failed: /root/degulog/node_modules/element-ui/lib/theme-default/index.css Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type.

どうやらElementに付随するCSSファイルをバンドルできないらしい。

原因は単純に、これまでscssだけ使ってたのでcssに対するローダーをwebpackで設定していなかったため。
なのでwebpack.config.jsのloadersに以下を追加すれば解決

{ test: /\.css$/, loader: 'style-loader!css-loader' },

エラー② Element内のwoffファイルが解決できない

続けて以下のエラーが発生した

ERROR in ./node_modules/element-ui/lib/theme-default/fonts/element-icons.woff?t=1472440741
Module parse failed: /root/degulog/node_modules/element-ui/lib/theme-default/fonts/element-icons.woff?t=1472440741 Unexpected character '' (1:4)
You may need an appropriate loader to handle this file type.

問題の本筋は前項と同様。woffを解決するローダが設定されていなかった。

url-loaderを適用すれば良いが、まずそれが入っていなかたのでインストールする。

$ npm install url-loader

で、webpack.config.jsonのloadersに以下を追加。woff意外にも対応できるように汎用的に記述。

{
  test: /\.(otf|eot|svg|ttf|woff|woff2)(\?.+)?$/,
  loader: 'url-loader'
},

動作確認

依存解決も上手く行って、以下のようにElementが利用できた。
例はDatetimePickerコンポーネント

<el-date-picker
  v-model="value1"
  type="datetime"
  placeholder="Select date and time">
</el-date-picker>

ES6のファイルを自動で結合してミニファイする

前提

要素 バージョン 関連記事
node/npm 7.4.0/4.1.2 Debian8.7にMEAN開発環境を構築する
gulp 3.9.1 Babel+Gulpで始めるES6

概要

JavaScriptの次世代仕様ES6で記述された複数のJavaScirptファイルを、一つのファイルに結合し、ミニファイルする作業を自動化する。

ES6及びタスクを自動化させるgulpについては関連記事で触れているので割愛する。

JavaScriptファイルの結合について

HTTPでは基本的に1リクエスト1ファイルとなるため、クライント側で利用するJavaScriptファイルがN個あればN回余分にリクエストを発行することになってしまう。これは些細な違いとは言え、オーバーヘッドが積もりパフォーマンスの低下に繋がることもある。

そこで、複数のJavaScriptファイルを1ファイルに結合することで、JavaScriptファイルのリクエスト回数を1回に抑えることができる。

しかし、ファイルを一つにすることで、全てのファイルのスクリプトが集約するため、そのファイルの容量が増大してしまうデメリットがある。これについては、後述するミニファイの技術を用いることで緩和することができる。

JavaScriptファイルのミニファイについて

通常、プログラマがスクリプトを記述する際は、可読性などから、以下のように冗長的なコーディングを行う
– 適切な空白や改行
– 理解しやすい識別子
– コメント

しかし、これらは処理系にとっては無意味な要素であり、処理系の負担やファイルの容量を無駄に増やしてしまう。

そこで、以下の例のように、処理系にとって不要の情報を極限まで削る(ミニファイ)ことで、ファイルの容量を削減し、パフォーマンスを向上させることができる。

ミニファイ前

/* 2数の和を戻す関数 */
var getSum = function(number1 , number2) {
  return number1 + number2; // 計算結果を戻す
};

ミニファイ後

var getSum=function(b,a){return b+a};

ファイル結合とミニファイの自動化

ここでは、複数ファイルに分割して記述されたES6のコードを、タスクランナーツールgulpを用いて、以下の手順でファイルを変換する

  1. gulp-concatで一つのES6ファイルに結合する
  2. gulp-babelでES5ファイルに変換する
  3. gulp-uglifyでミニファイする

必要なnpmモジュールをインストール

node/npm/gulpインストール後、追加で下記のモジュールをインストールする

$ npm install --save-dev gulp-concat
$ npm install --save-dev gulp-uglify
$ npm install --save-dev gulp-rename

gulp-renameについては後述

gulpによる自動変換

gulpfile.js

var gulp = require('gulp'),
    concat = require("gulp-concat"),
    uglify = require("gulp-uglify"),
    babel = require('gulp-babel'),
    rename = require('gulp-rename');

gulp.task('js' , function() {
  gulp.src('scripts/*.js')
    .pipe(concat('main.js'))
    .pipe(babel({presets: ['es2015']}))
    .pipe(uglify())
    .pipe(rename('main.min.js'))
    .pipe(gulp.dest('build/'));
});

gulp.task('watch' , function() {
  gulp.watch('scripts/*.js' , ['js']);
});

gulp.task('default' , ['js' , 'watch']);

上記gulpfileは、gulp実行時及びscriptsディレクトリ内のjsファイルに変更があった場合に、jsタスクを実行する。

jsタスクは、scriptsディレクトリ内のjsファイルをconcatモジュールで結合し、babelモジュールでES5に変換後、uglifyモジュールでミニファイしている。

uglifyモジュールは、JavaScriptファイルをミニファイする機能を持っているが、それを別名で保存する機能を持っていない。そこで。renameモジュールを前項で別途インストールしておいたので、それを用いてファイル名を変換する。

実行例を以下に示す

実行前

hoge.js

/* カウンタを備えたモジュール */
let myModule = (function() {
  let n = 0;
  return {
    add: () => n++,
    get: () => n,
  };
})();

fuga.js

/* 2数の和を求める関数 */
let myFunction = (numberA , numberB) => numberA + numberB;

実行後

main.min.js

"use strict";var myModule=function(){var c=0;return{add:function b(){return c++},get:function a(){return c}}}();var myFunction=function myFunction(b,a){return b+a};

処理系にとっては等価であるが、グット軽量化できたことがわかる

所感

  • AngularJSなどのJavaScriptフレームワークを用いていると、MVC分離のためにファイル数がどんどん増えていってしまうので、やはりこういった結合・ミニファイの技術は必須だ
  • ミニファイされたコードを用いて動作確認を行うと、バグが発生した際にブラウザでコードを確認しづらい問題がある
    — これについては、ミニファイ前のファイルも生成することで、開発中はミニファイ前を使用する運用にすれば解決する
  • ファイルが増えてもスクリプトをロードするタグを追加する必要がないので楽になる
  • JavaScriptファイル間で複雑な依存関係がある場合は、別途依存対策を取る必要がありそう
  • 当たり前だが、この技術はJavaScriptだけじゃなくCSSでも適用できるはず
    — 複数のLESSファイルを一つに結合し、CSSに変換してミニファイ

参考