趁着国庆假期,在项目的前端搭上了 webpack,以便更好地模块化管理前端代码。这里和大家分享一下,期待更多交流。(参考了这篇业界良心的 tutorial:http://clarkdave.net/2015/01/how-to-use-webpack-with-rails)
在项目的目录下创建 package.json 来管理需要的外部 module:
{
"name": "app",
"description": "the app",
"version": "1.0.0",
"dependencies": {
"coffee-loader": "^0.7.2",
"coffee-script": "^1.10.0",
"exports-loader": "~0.6.2",
"expose-loader": "~0.6.0",
"imports-loader": "~0.6.3",
"css-loader": "~0.6.3",
"underscore": "~1.8.3",
"webpack": "~1.4.13",
"jquery": "~2.1.4"
}
}
安装需要的 module(墙内可以使用淘宝镜像 https://npm.taobao.org/ )
npm install
安装 webpack
npm install -g webpack
由于 webpack 本身功能的强大,配置文件也可以很复杂,这里简单配置一下,添加一个叫 webpack.config.js 的文件
var path = require('path');
var webpack = require('webpack');
var config = module.exports = {
context: __dirname,
// 告诉 webpack 去哪里找 entry 文件
// entry 文件最终会被 compile 出来
// 我们将新的 js 代码都统一放在 app/frontend/javascritps 文件夹下面
entry: './app/frontend/javascripts/entry.js'
};
config.output = {
// 告诉 webpack 根据 entry 文件编译出来的文件叫 bundle.js
// 并把文件生成到 app/assets/javascripts 目录下,这样 Rails 的 Asset Pipeline 便可以直接使用
path: path.join(__dirname, 'app', 'assets', 'javascripts'),
filename: 'bundle.js',
publicPath: '/assets'
};
config.resolve = {
// 告诉 webpack 可引用文件的后缀
// 比如我们添加了 '.js',所以可以用 require('app') 来代替 require('app.js')
extensions: ['', '.js', '.coffee']
};
config.module = {
loaders: [
// 用来 load CoffeeScript
{ test: /\.coffee$/, loader: 'coffee-loader' },
],
};
entry.js 可以是任何 js 文件,里面可以通过 require 来引用其他的 module,举个例子:
_ = require('underscore')
console.log(_.keys({"foo": "bar"}))
运行以下命令,webpack 会根据上一步的配置文件生成一个 bundle.js
webpack -d --display-reasons --display-chunks --progress
之后只需要在 view 里引用生成的 js 文件(bundle.js)
<%= javascript_include_tag 'bundle' %>
因为有些 module 只有 bower 有,所以我们可以在使用 npm 的同时也使用 bower 安装 bower
npm install -g bower
添加 bower.json
{
"name": "app",
"version": "1.0.0",
"description": "the app",
"dependencies": {
"jquery": "~1.11.0"
}
}
在 webpack 配置里添加 bower_components
config.resolve = {
...
modulesDirectories: [ 'node_modules', 'bower_components' ],
...
};
再添加一个 plugin(这里感叹一下 webpack 的 plugin 相当之丰富)
config.plugins = [
// 告诉 webpack 除了 package.json,还可以去哪里寻找 module 的描述
new webpack.ResolverPlugin([
new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
])
];
配置好之后就可以同时使用 npm 和 bower 的 package 啦
bower install
以上的配置都只是一个 entry 文件(entry.js),有时候需要多个 entry 文件来更好分离不同页面的 js,那么可以使用以下配置:
var config = module.exports = {
...
entry: {
entry1: './app/frontend/javascripts/entry1.js',
entry2: './app/frontend/javascripts/entry2.js
}
...
};
config.output = {
...
// 生成的文件直接使用 entry 的名称来命名
filename: '[name]_bundle.js',
...
};
之后我们在 view 里需要的地方便可使用 entry1_bundle.js 和 entry2_bundle.js
开发环境里可以使用以下的命令让 webpack 保持自动编译前端代码:
webpack --watch --colors
当然也可以使用 foreman 统一设置,比如我们项目的 Procfile:
sidekiq: bundle exec sidekiq
web: rails server
webpack: webpack --watch --colors
团队目前有两名 Rubyist,本来是想等招到前端再来好好做一下模块化,但苦于厦门靠谱前端难寻,趁着国庆假期终于下决心自己撸。个人感觉 webpack 这货非常之强大,CommomJS、AMD、ES6 语法任君选择,但配置相对复杂些。这里特别感谢老同学 Strikingly CTO 郭达峰@dfguo 的推荐,也很期待下周 RubyConf 上 Strikingly 团队关于 webpack 的分享。