写在前面:css
文章翻译自petehunt大神的petehunt/webpack-howto,做为学习webpack的开始。fork了一份,翻译后的在这里https://github.com/zjzhome/webpack-howto/blob/master/README-cn.md,有些地方翻译的很差,也但愿在阅读后对于很差的地方对出评论或者在github上提个issue。html
本文讲述如何使用webpack将事情作好,包括了咱们在Instagram作的大多数事情,咱们没作的固然没有:)webpack
个人建议:以此文做为学习webpack的开始,而后再阅读官方的文档说明。git
和browserify相似,但能够将你的app划分为不一样的文件。 若是你的单页应用你有多个页面,用户只需下载当前页的代码,若是他们离开此页浏览其余页面,不用再次下载那些通用的代码。github
一般状况下能够替代grunt和gulp,由于webpack能够构建和打包CSS,预处理CSS、能够编译为JS的语言和图片等等。web
webpack支持AMD和CommonJS以及其余的模块系统(Angular、ES6)。若是你不知道用哪一种,使用CommonJS。npm
下面的两个命令是等价的:json
browserify main.js > bundle.js
webpack main.js bundle.js
不过,webpack比browserify更加给力,通常状况下,咱们会建立webpack.config.js
来保证条理性。gulp
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' } };
这彻底是咱们熟悉的JS,因此随意将挥洒你的代码。bootstrap
切换到包含webpack.config.js
的目录, 运行:
webpack中和browserify转换器以及RequireJS中的插件等价的是loader
。下面展现了如何让webpack支持加载CoffeeScript和Facebook JSX+ES6(你须要首先 npm install babel-loader coffee-loader )):
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'babel-loader' } ] } };
为了在加载文件的时候没必要生命文件扩展名,你必须加上 resolve.extensions 这个参数来告诉webpack该寻找哪些文件.
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'babel-loader' } ] }, resolve: { // you can now require('file') instead of require('file.coffee') extensions: ['', '.js', '.json', '.coffee'] } };
首先,在你的代码中使用require()
来引入你的静态资源。
require('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
当你引入CSS(或者less等),webpack会将CSS做为字符串打包到JS中,而后 require() 会在页面中插入 <style> 标签。当引入图片资源的时候,webpack会将图片地址打包进JS, require() 返回图片地址。
可是你要告诉webpack怎么作:
// webpack.config.js module.exports = { entry: './main.js', output: { path: './build', // This is where images AND js will go publicPath: 'http://mycdn.com/', // This is used to generate URLs to e.g. images filename: 'bundle.js' }, module: { loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders { test: /\.css$/, loader: 'style-loader!css-loader' }, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // inline base64 URLs for <=8k images, direct URLs for the rest ] } };
有些代码咱们只想用在开发环境(好比日志)或者内部的服务器(好比处于内测的未发布产品特性)。在你的代码里,使用如下几个变量:
if (__DEV__) { console.warn('Extra logging'); } // ... if (__PRERELEASE__) { showSecretFeature(); }
而后告诉webpack这些全局变量:
// webpack.config.js // definePlugin takes raw strings and inserts them, so you can put strings of JS if you want. 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
命令会去清除无用的代码,全部抱在这些代码块里的代码都会被删除,因此不要泄露私密特性或字符串。
假设你有一个用户资料页面和一个订阅页面,用户在浏览资料页面的你不想用户下载订阅页面的代码。因此要打多个包,为每个页面建立一个“主要模块”(也叫做入口):
// webpack.config.js module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' // Template based on keys in entry above } };
对于资料页,在页面插入 <script src="build/Profile.js"></script> ,订阅页面也是同样。
订阅页面和资料页面共用许多代码(像React以及其余的样式和组件等)。webpack能够抽出他们共用的代码,而后打一个通用包在各个页面缓存。
// 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' // Template based on keys in entry above }, plugins: [commonsPlugin] };
在上一步的script标签以前加上 <script src="build/common.js"></script> ,而后尽情享受免费的高速缓存吧。
CommonJS是同步的,可是Webpack提供一个方法来异步的指定依赖。这在客户端路由中颇有用,每一个页面都须要路由,你不想直到你真正须要他们的时候才下载相关功能。
指定你想异步加载的分割点,好比:
if (window.location.pathname === '/feed') { showLoadingState(); require.ensure([], function() { // this syntax is weird but it works hideLoadingState(); require('./feed').show(); // when this function is called, the module is guaranteed to be synchronously available. }); } else if (window.location.pathname === '/profile') { showLoadingState(); require.ensure([], function() { hideLoadingState(); require('./profile').show(); }); }
webpack会作接下来的工做,而且产生额外的块文件,而后加载他们。
当你加载这些文件到html的script标签,webpack假定他们在根目录,你能够经过 output.publicPath 来定义:
// webpack.config.js output: { path: "/home/proj/public/assets", //path to where webpack will build your stuff publicPath: "/assets/" //path that will be considered when requiring your files }
看一下真实的案例:一个成功的团队是如何使用webpack的。这是Pete Hunt在全球开源大会上关于Intagram使用的webpack的讨论:http://youtu.be/VkTCL6Nqm6Y。
webpack是严格模块化的。和browserify和requirejs等替代选择工具相比,webpack的伟大之处在于在构建过程当中他让插件将本身注入到尽量多的地方。webpack不少看似内建的功能只是默认加载的插件,他们是能够重写的(好比CommonJS的require()语法分析器)。