本文从属于笔者的Web 前端入门与最佳实践。javascript
Webpack的安装很是简单,直接使用npm i webpack --save-dev
命令安装便可。通常来讲,基于Webpack的项目的目录结构以下:css
/app main.js component.js /build bundle.js (自动建立) index.html package.json webpack.config.js
webpack-dev-server --content-base build/
<script src="http://localhost:8080/webpack-dev-server.js"></script>
相似与Gulp中使用gulpfile.js配置Gulp编译项目,Webpack中使用webpack.config.js来进行全局配置,一个基本的配置文件以下所示:html
var path = require('path'); module.exports = { entry: path.resolve(__dirname, 'assets/js/main.js'), output: { path: path.resolve(__dirname, 'dist'), publicPath: 'dist/', filename: 'game_bundle.js', }, module: { loaders: [ {test: /\.jsx$/,exclude:/libs/, loader: 'babel?stage=0'}, {test: /\.js$/, exclude:/libs/,loader: 'babel?stage=0'}, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ,// inline base64 URLs for <=8k images, direct URLs for the rest { test: /\.css$/, loader: 'style-loader!css-loader!autoprefixer-loader?browsers=last 2 versions' } ], }, externals: { jquery: "jQuery", pageResponse:'pageResponse' }, resolve : { alias: { libs : path.resolve(__dirname, 'libs') } } };
本处以简单的HelloWorld为例演示如何经过Webpack将全部的JS、CSS以及资源文件打包到一块儿。前端
app/component.jsjava
'use strict'; module.exports = function () { var element = document.createElement('h1'); element.innerHTML = 'Hello world'; return element; };
app/main.js*node
'use strict'; var component = require('./component.js'); document.body.appendChild(component());
如今在你的命令行运行 webpack
,而后你的应用会开始编译,一个 bundle.js
文件就这样出如今你的 /build
文件夹下,须要在 build/
下的 index.html 去启动项目。jquery
build/index.htmlwebpack
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> </head> <body> <script src="bundle.js"></script> </body> </html>
package.json
npm
是一个很是好用的用来编译的指令,经过 npm
你能够不用去担忧项目中使用了什么技术,你只要调用这个指令就能够了,只要你在 package.json
中设置 scripts
的值就能够了。git
npm i webpack --save
- 若是你想要把 Webpack 做为一个项目的开发依赖,就可使用 --save-dev
,这样就很是方便地让你在开发一个库的时候,不会依赖工具(但不是个好方法!)。github
把下面的内容添加到 package.json
中。
"scripts": { "build": "webpack" }
上面的HelloWorld示例演示了Webpack最基本的用法,可是若是咱们有一个相对复杂的Button组件定义:
src/Components/Button.scss
.button { background: tomato; color: white; }
src/Components/Button.html
<a class="button" href="{{link}}">{{text}}</a>
src/Components/Button.js
import $ from 'jquery'; import template from './Button.html'; import Mustache from 'mustache'; import './Button.scss'; export default class Button { constructor(link) { this.link = link; } onClick(event) { event.preventDefault(); alert(this.link); } render(node) { const text = $(node).text(); // Render our button $(node).html( Mustache.render(template, {text}) ); // Attach our listeners $('.button').click(this.onClick.bind(this)); } }
按钮最终呈现的样式以下所示:
在主模块中,这个Button不必定在初始加载时就用到,可能在部分页面中须要用到该按钮,可是在其余页面中则根本不须要它们。在这种状况下,若是在一开始的时候就将全部Button的代码以及样式导入,无疑会加深初始时候的消耗。而Webpack的代码分割功能的提出正是为了解决这个问题,也就是所谓的"总体导入"与"无维持的手动导入"之间取得一个平衡。若是须要引入代码分割的功能,便是须要在代码中引入所谓的切割点,即动态地将整个代码切分为多个文件而且动态地按需加载。基本语法以下所示:
import $ from 'jquery'; // This is a split point require.ensure([], () => { // All the code in here, and everything that is imported // will be in a separate file const library = require('some-big-library'); $('foo').click(() => library.doSomething()); });
全部在require.ensure
中定义的文件会被切分为多个大的独立分块,这些独立的分块会在须要被调用时被使用Ajax进行动态导入。使用Code Splitting以后整个文件目录的布局方式以下所示:
bundle.js |- jquery.js |- index.js // our main file chunk1.js |- some-big-libray.js |- index-chunk.js // the code in the callback
固然,开发者并不须要手动导入chunk1.js文件,Webpack打包的模块加载器会按需自动加载。这就意味着对于开发者而言并不须要使用复杂的逻辑去手动控制按需加载,而只须要使用require.ensure方法便可。
src/index.js
if (document.querySelectorAll('a').length) { require.ensure([], () => { const Button = require('./Components/Button'); const button = new Button('google.com'); button.render('a'); }); }
若是在编译时候使用以下参数:--display-chunks
,那么能够查看具体的被打包的状况:
$ webpack --display-modules --display-chunks Hash: 432341dc518c06c9d8da Version: webpack 1.12.2 Time: 952ms Asset Size Chunks Chunk Names bundle.js 3.88 kB 0 [emitted] main 1.bundle.js 287 kB 1 [emitted] chunk {0} bundle.js (main) 294 bytes [rendered] [0] ./src/index.js 294 bytes {0} [built] chunk {1} 1.bundle.js 278 kB {0} [rendered] [1] ./src/Components/Button.js 2.02 kB {1} [built] [2] ./~/jquery/dist/jquery.js 248 kB {1} [built] [3] ./src/Components/Button.html 72 bytes {1} [built] [4] ./~/mustache/mustache.js 19.3 kB {1} [built] [5] ./src/Components/Button.scss 1.05 kB {1} [built] [6] ./~/css-loader!./~/sass-loader!./src/Components/Button.scss 212 bytes {1} [built] [7] ./~/css-loader/lib/css-base.js 1.51 kB {1} [built] [8] ./~/style-loader/addStyles.js 6.09 kB {1} [built]
如上所述,入口文件bundle.js
中只会包含部分Webpack的逻辑,其余的譬如jQuery、Mustache、Button这样的部分会被包含在1.bundle.js块中,这些块会在使用时被动态加载。
在发布版本中,可能须要一些特殊的配置或者插件,譬如只有在NODE_ENV
环境变量等于production
的状况下才会有逻辑配置须要添加在配置文件中,那么在Webpack的配置文件中可使用以下定义:
var webpack = require('webpack'); var production = process.env.NODE_ENV === 'production'; var plugins = [ new webpack.optimize.CommonsChunkPlugin({ name: 'main', // Move dependencies to our main file children: true, // Look for common dependencies in all children, minChunks: 2, // How many times a dependency must come up before being extracted }), ]; if (production) { plugins = plugins.concat([ // Production plugins go here ]); } module.exports = { entry: './src', output: { path: 'builds', filename: 'bundle.js', publicPath: 'builds/', }, plugins: plugins, // ... };
在发布版本中,Webpack的一些配置能够被关闭,譬如:
module.exports = { debug: !production, devtool: production ? false : 'eval',