LoginSignup
39
43

More than 5 years have passed since last update.

webpackでモジュール解決しながらディレクトリ構成を保ったまま書き出す

Posted at

webpackって超便利ですよね。
違うファイルで作ったモジュールを1つにまとめてることができて、bundle.jsみたいなファイルにまとめることが出来て。。。

でも。。。。

僕みたいなまだまだレガシーエンジニアには1つのファイルにまとめてSPAライクにすることなんて出来ないわけです。

ぶっちゃけ共通のApp.jsと各ページ1ファイル読み込むパターンで十分なんです。
初期ロードで数10MBのjsを読み込むのって我慢できないんです。

なので今回はwebpackを使って複数のディレクトリにまたがったファイルを依存解決も行いながら別ディレクトリに同じ構成で書き出すのをやってみたいと思います。

目指す姿

applicationのルートディレクトリにfrontendsディレクトリを作成し、PC用の各ページのjsファイル、mobile各ページ用のjsファイル、共通で使えそうなmodulesファイルに分けました。

assets
frontends
├── App.es6
├── App.mobile.es6
├── mobile
│   └── users
|       ├── setting.es6
|       └── show.es6
├── modules
│   ├── _Dropdown.es6
│   ├── _Follow.es6
│   ├── _Tab.es6
│   └── _XhrMethods.es6
├── pc
│   └── users
|       ├── setting.es6
|       └── show.es6
└── validations
    ├── _Contact.es6
    ├── _Password.edit.es6
    ├── _Password.edit.from.email.es6
    └── _Send.email.es6

もちろん各ファイルの中ではimportしまくってます!

// pc/users/show.es6
import Tab from '../../modules/_Tab.es6'
import DropDown from '../../modules/_DropDown.es6'

const tab = new Tab({active: 'hogehoge',...})
const dropDown = new DropDown({el: 'jsDropdown'})

// more

実際に使ってるのはApp.es6, App.mobile.es6, pc/, mobile/なので、そのファイルをそのままdist/javascriptsに書き出したいと思います。

dist/javascripts
├── App.es6.js
├── App.mobile.es6.js
├── mobile
│   └── users
|       ├── setting.es6.js
|       └── show.es6.js
└── pc
    └── users
        ├── setting.es6.js
        └── show.es6.js

よくあるwebpackで複数ファイルを出力する方法

実際によくあるのが、webpack.config.jsentriesにhashを指定すること複数のファイルの出力に対応するということです。

例: 複数ファイルを指定

module.exports = {
  entry: {
    pc_users_show: 'frontends/users/show.es6',
    pc_users_setting: 'frontends/users/setting.es6',
    // more
  },
  output: {
    path: '/dist/javascripts',
    filename: '[name].js'
  },
  // その他の設定
}

こうしてwebpackwを実行すると/distentryで指定したkeyの名前でファイルが主力されます。

dist/javascripts
├── pc_users_show.es6.js
└── pc_users_setting.es6.js

ここで問題なのがoutput.pathが1つのみなのでディレクトリごとに書き出すなどの処理ができないということです。

webpackで階層ごとにファイルを書き出す

output.pathではディレクトリが1つしか指定できませんが、entryのkeyをいじることで階層ごとに書き出すことが可能です。

module.exports = {
  entry: {
    pc/users/show: 'frontends/users/show.es6', //keyを階層化して指定する
    pc/users/setting: 'frontends/users/setting.es6',
    // more
  },
  output: {
    path: '/dist/javascripts',
    filename: '[name].js'
  },
  // その他の設定
}

dist/javascripts
└── pc
    └── users
        ├── setting.es6.js
        └── show.es6.js

こうすることで階層化しつつ、各モジュール依存を解決した状態で書き出すことは出来ました(๑•̀ㅂ•́)و✧

ただ、ファイルが増えるたびに全てのファイルをentryのkeyの追加していくのは面倒くさいです。
なので、globを使用してファイル名を取得してkeyも一緒に作るようにしちゃいましょう!

globを使用してwebpackでいい感じに階層化してモジュール依存解決して書き出す

const path    = require('path'),
      glob    = require('glob'),
      _       = require('lodash');


const jsBasePath = path.resolve(__dirname, 'frontends'),
      jsDistPath = path.resolve(__dirname, 'dists/javascripts')

// filenameを取得するメソッドを追記
// 後から使う
String.prototype.filename = function(){
  return this.match(".+/(.+?)([\?#;].*)?$")[1];
}

// jsBasePath配下の.es6のpathも含めたファイルを取得する
// _から始まるファイルはmoduleなので書き出す必要はないのでいらない
var targets = _.filter(glob.sync(`${jsBasePath}/**/*.es6`), (item) => {
  return !item.filename().match(/^_/)
});

// entryに入れるhash
var entries = {};

// pathも含めたfilenameからpathとfilenameでhashを作る
targets.forEach(value => {
  var re = new RegExp(`${jsBasePath}/`);
  var key = value.replace(re, '');

  // 確認用に取得したファイル名を出す
  console.log('--------------------------')
  console.log(key) 
  console.log(value.filename()) 
  entries[key] = value;
});

module.exports = {
  entry: entries, //作成したhashをset
  output: {
    path: jsDistPath,
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.es6$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015']
        }
      }
    ]
  }
}

こうすることで階層構造を保ったままファイルを書き出すことが出来ました。

Hash: a94765d83d77487ff60d
Version: webpack 1.13.3
Time: 18715ms
                              Asset     Size  Chunks             Chunk Names
                         App.es6.js   596 kB       0  [emitted]  App.es6
        mobile/users/setting.es6.js  7.86 kB       1  [emitted]  mobile/users/setting.es6
           mobile/users/show.es6.js   592 kB       2  [emitted]  mobile/users/show.es6
                  App.mobile.es6.js   583 kB       3  [emitted]  App.mobile.es6
            pc/users/setting.es6.js  7.86 kB       4  [emitted]  pc/users/setting.es6
               pc/users/show.es6.js   593 kB       5  [emitted]  pc/users/show.es6
    + 386 hidden modules
[BS] Reloading Browsers...

終わりに

みなさんお気付きの通り、こちら超ゴリゴリの実装な気がしてたまりません(´;ω;`)
何かいい方法があればご教授ください。。。

39
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
43