配合上一篇文章作的翻译: https://github.com/petehunt/webpack-howtocss
这是 Webpack 怎么作事情的烹饪书. 其中包含了咱们在 Instagram 作的大部分功能, 并且都是在用的功能.webpack
个人建议是: 把这个当成是 Webpack 的文档开始学习, 而后看官方文档的具体说明.git
他像 Browserify, 可是将你的应用打包为多个文件. 若是你的单页面应用有多个页面, 那么用户只从下载对应页面的代码. 当他么访问到另外一个页面, 他们不须要从新下载通用的代码.github
他在不少地方能替代 Grunt 跟 Gulp 由于他可以编译打包 CSS, 作 CSS 预处理, 编译 JS 方言, 打包图片, 还有其余一些.web
它支持 AMD 跟 CommonJS, 以及其余一些模块系统, (Angular, ES6). 若是你不知道用什么, 就用 CommonJS.npm
对应地:json
jsbrowserify main.js > bundle.js
jswebpack main.js bundle.js
Webpack 比 Browserify 更强大, 你通常会用 webpack.config.js
来组织各个过程:bootstrap
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' } };
这仅仅是 JavaScript, 能够随意添加要运行的代码.缓存
切换到有 webpack.config.js
的目录而后运行:服务器
webpack
来执行一次开发的编译webpack -p
for building once for production (minification)webpack -p
来针对发布环境编译(压缩代码)webpack --watch
来进行开发过程持续的增量编译(飞快地!)webpack -d
来生成 SourceMapsWebpack 对应 Browsserify transform 和 RequireJS 插件的工具称为 loader
. 下边是 Webpack 加载 CoffeeScript 和 Facebook JSX-ES6 的配置(你须要 npm install jsx-loader coffee-loader
):
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' } // loaders 能够接受 querystring 格式的参数 ] } };
要开启后缀名的自动补全, 你须要设置 resolve.extensions
参数指明那些文件 Webpack 是要搜索的:
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' } ] }, resolve: { // 如今能够写 require('file') 代替 require('file.coffee') extensions: ['', '.js', '.json', '.coffee'] } };
首先更新你的代码用 require()
加载静态资源(就像在 Node 里使用 require()
):
jsrequire('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
当你引用 CSS(或者 LESS 吧), Webpack 会将 CSS 内联到 JavaScript 包当中, require()
会在页面当中插入一个 `<style>标签. 当你引入图片, Webpack 在包当中插入对应图片的 URL, 这个 URL 是由
require()` 返回的.
你须要配置 Webpack(添加 loader):
js// webpack.config.js module.exports = { entry: './main.js', output: { path: './build', // 图片和 JS 会到这里来 publicPath: 'http://mycdn.com/', // 这个用来成成好比图片的 URL filename: 'bundle.js' }, module: { loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // 用 ! 来链接多我的 loader { test: /\.css$/, loader: 'style-loader!css-loader' }, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // 内联 base64 URLs, 限定 <=8k 的图片, 其余的用 URL ] } };
有些代码咱们只想在开发环境使用(好比 log), 或者 dogfooging 的服务器里边(好比内部员工正在测试的功能). 在你的代码中, 引用全局变量吧:
jsif (__DEV__) { console.warn('Extra logging'); } // ... if (__PRERELEASE__) { showSecretFeature(); }
而后配置 Webpack 当中的对应全局变量:
js// webpack.config.js // definePlugin 接收字符串插入到代码当中, 因此你须要的话能够写上 JS 的字符串 var definePlugin = new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')), __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false')) }); module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [definePlugin] };
而后你在控制台里用 BUILD_DEV=1 BUILD_PRERELEASE=1 webpack
编译. 注意一下由于 webpack -p
会执行 uglify dead-code elimination, 任何这种代码都会被剔除, 因此你不用担忧秘密功能泄漏.
好比你用 profile 页面跟 feed 页面. 当用户访问 profile, 你不想让他们下载 feed 页面的代码. 所以你建立多个包: 每一个页面一个 "main module":
js// webpack.config.js module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' // 模版基于上边 entry 的 key } };
针对 profile, 在页面当中插入 <script src="build/Profile.js"></script>
. feed 页面也是同样.
feed 页面跟 profile 页面共用不要代码(好比 React 还有通用的样式和 component). Webpack 能够分析出来他们有多少共用模块, 而后生成一个共享的包用于代码的缓存.
js// webpack.config.js var webpack = require('webpack'); var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' }, plugins: [commonsPlugin] };
在上一个步骤的 script 标签前面加上 <script src="build/common.js"></script>
你就能获得廉价的缓存了.
CommonJS 是同步的, 可是 Webpack 提供了异步指定依赖的方案. 这对于客户端的路由颇有用, 你想要在每一个页面都有路由, 但你又不像在真的用到功能以前就下载某个功能的代码.
声明你想要异步加载的那个"分界点". 好比:
jsif (window.location.pathname === '/feed') { showLoadingState(); require.ensure([], function() { // 语法奇葩, 可是有用 hideLoadingState(); require('./feed').show(); // 函数调用后, 模块保证在同步请求下可用 }); } else if (window.location.pathname === '/profile') { showLoadingState(); require.ensure([], function() { hideLoadingState(); require('./profile').show(); }); }
Webpack 会完成其他的工做, 生成额外的 chunk 文件帮你加载好.
Webpack 在 HTML script 标签中加载他们时会假设这些文件是怎你的根路径下. 你能够用 output.publicPath
来配置.
js// webpack.config.js output: { path: "/home/proj/public/assets", // path 指向 Webpack 编译能的资源位置 publicPath: "/assets/" // 引用你的文件时考虑使用的地址 }
看一看真实环境当中一个成功的团队怎么使用 Webpack: http://www.tudou.com/programs/view/6KB6lNbVzhs/ .
这是 Peter Hunt 在 OSCon 大会关于 Instagram 如何使用 Webpack 的演讲.
Webpack 是很是地模块化. Webpack 的优秀之处在于相比 Browserify 跟 RequireJS 之类方案, 能让插件将自身集成到编译过程中的更多地方. 不少看起来像是编写在核心代码的功能, 其实就是默认加载的插件, 他们能够被覆盖.(好比, CommonJS 的 require()
parser)