这篇文章是「模块化」系列的最后一篇,聊一聊前端模块加载器。上一篇文章《模块化(二)》中提到的实现 AMD 规范的 RequireJS 就是一种比较常用的模块加载器,除此之外比较常用的有 Browserify 和 Webpack,接下来逐一介绍。
Browserify
npm 的模块非常丰富,但是其模块符合 CommonJS 规范,对浏览器不友好。Browserify 就是为了解决这个问题而生,递归分析应用中 require()
的调用顺序,将所有依赖的模块打包成一个 js 文件,浏览器通过一个 标签即可加载。
接下来再以 browserify 实现前两篇文章中的示例。
前提:node 和 npm 环境准备就绪,jQuery 已安装。
安装 browserify
1 |
npm install -g browserify |
www/scripts 目录
math.js
1 2 3 |
exports.add = function(x, y) { return x + y; }; |
increment.js
1 2 3 4 |
var math = require('./math'); exports.increment = function(val) { return math.add(val, 1); } |
mian.js
1 2 3 4 5 6 7 8 9 10 |
var $ = require('jquery'); var inc = require('./increment').increment; $(function() { $(':button').click(function() { var val = parseInt($('#val').val()); var res = inc(isNaN(val) ? 0 : val); $('#val').val(res); }) }); |
browserify 打包
在 www 目录下,使用 browserify 打包
1 |
browserify ./scripts/main.js -o bundle.js |
index.html
www目录下的index.html文件
1 2 3 4 5 6 7 8 9 10 11 12 |
<!doctype html> <html> <head> </head> <body> <div> Input Number: <input type="text" id="val"> <input type="button" value="Plus One"> </div> <script src="./bundle.js"></script> </body> </html> |
Webpack
Webpack 是一种功能强大的模块加载器,与 Browserify 相比,可以灵活按配置将 js 打成多个包按需加载,提高首次访问页面的速读,同时也支持 css 、图片等静态文件的管理。
同样,以 webpack 实现 incrememnt 功能。
前提:node 和 npm 环境准备就绪。
安装 webpack
1 |
npm install webpack -g |
示例中还演示了加载 css 的功能,需要在 www 目录安装 css-loader
和 style-loader
,不带 -g
参数。
1 |
npm install css-loader style-loader |
www/styles 目录
style.css
1 2 3 |
body { background: #98887B; } |
www/scripts 目录
math.js 和 increment.js 同 Browserify,lib子目录有 jquery-2.2.0.js 文件。
main.js 文件略微不同,还加载了 css 文件。
1 2 3 4 5 6 7 8 9 10 11 12 |
require('../styles/style.css'); var $ = require('./lib/jquery-2.2.0'); var inc = require('./increment').increment; $(function() { $(':button').click(function() { var val = parseInt($('#val').val()); var res = inc(isNaN(val) ? 0 : val); $('#val').val(res); }) }); |
www 目录
www 目录增加 webpack 配置文件 webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 |
module.exports = { entry: "./scripts/main.js", output: { path: __dirname, filename: "bundle.js" }, module: { loaders: [ { test: /\.css$/, loader: "style!css" } ] } }; |
webpack 打包
在 www 目录下,使用 webpack 打包,由于存在配置文件,直接运行命令 webpack 即可
1 |
webpack |
index.html
www 目录下的 index.html 同 Browserify 中的例子,由于加载了 css 底色会有不同。
模块加载器比较
这个表格是 Webpack 官网上所列的 RequireJS、Browserify、Webpack 支持特性的比较:
Feature | webpack/webpack | jrburke/requirejs | substack/node-browserify |
---|---|---|---|
CommonJs require |
yes | only wrapping in define |
yes |
CommonJs require.resolve |
yes | no | no |
CommonJs exports |
yes | only wrapping in define |
yes |
AMD define |
yes | yes | deamdify |
AMD require |
yes | yes | no |
AMD require loads on demand |
yes | with manual configuration | no |
ES2015 import /export |
no | no | no |
Generate a single bundle | yes | yes♦ | yes |
Load each file separate | no | yes | no |
Multiple bundles | yes | with manual configuration | with manual configuration |
Additional chunks are loaded on demand | yes | yes | no |
Multi pages build with common bundle | with manual configuration | yes | |
Concat in require require("./fi" + "le") |
yes | no♦ | no |
Indirect require var r = require; r("./file") |
yes | no♦ | no |
Expressions in require (guided) require("./templates/" + template) |
yes (all files matching included) | no♦ | no |
Expressions in require (free) require(moduleName) |
with manual configuration | no♦ | no |
Requirable files | file system | web | file system |
Plugins | yes | yes | yes |
Preprocessing | loaders, transforms | loaders | transforms |
Watch mode | yes | not required | yes |
Debugging support | SourceUrl, SourceMaps | not required | SourceMaps |
Node.js built-in libs require("path") |
yes | no | yes |
Other Node.js stuff | process, __dir/filename, global | – | process, __dir/filename, global |
Replacement for browser | web_modules , .web.js , package.json field, alias config option |
alias option | package.json field, alias option |
Minimizing | uglify | uglify, closure compiler | uglifyify |
Mangle path names | yes | no | partial |
Runtime overhead | 243B + 20B per module + 4B per dependency | 14.7kB + 0B per module + (3B + X) per dependency | 415B + 25B per module + (6B + 2X) per dependency |
Dependencies | 19MB / 127 packages | 11MB / 118 packages | 1.2MB / 1 package |
♦ in production mode (opposite in development mode)
X is the length of the path string
HACKHAT 也有一个第三方的模块加载器的对比文章 Module loader comparision: Webpack vs Require.js vs Browserify 可供参考。
结论
「后端程序员的 JavaScript 之旅 – 模块化」系列文章介绍了:模块模式,解决在天生模块化缺失的情况下如何根据 JS 自身的语言特性构建模块化的基本方法;流行的模块化规范 CommonJS 、AMD 和 ES 6 模块标准等,了解到模块化规范对生态圈的重要性;介绍了生态圈中常用的三种常用的模块加载器及其特点。