本文从属于笔者的Web 前端入门与最佳实践。
Installation
Webpack的安装非常简单,直接使用npm i webpack --save-dev
命令安装即可。一般来说,基于Webpack的项目的目录结构如下:
/app
main.js
component.js
/build
bundle.js (自动创建)
index.html
package.json
webpack.config.js
webpack-dev-server
webpack-dev-server --content-base build/
live-reload
<script src="http://localhost:8080/webpack-dev-server.js"></script>
Webpack Config
类似与Gulp中使用gulpfile.js配置Gulp编译项目,Webpack中使用webpack.config.js来进行全局配置,一个基本的配置文件如下所示:
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
本处以简单的HelloWorld为例演示如何通过Webpack将所有的JS、CSS以及资源文件打包到一起。
app/component.js
'use strict';
module.exports = function () {
var element = document.createElement('h1');
element.innerHTML = 'Hello world';
return element;
};
app/main.js*
'use strict';
var component = require('./component.js');
document.body.appendChild(component());
现在在你的命令行运行 webpack
,然后你的应用会开始编译,一个 bundle.js
文件就这样出现在你的 /build
文件夹下,需要在 build/
下的 index.html 去启动项目。
build/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
设置package.json
npm
是一个非常好用的用来编译的指令,通过 npm
你可以不用去担心项目中使用了什么技术,你只要调用这个指令就可以了,只要你在 package.json
中设置 scripts
的值就可以了。
npm i webpack --save
- 如果你想要把 Webpack 作为一个项目的开发依赖,就可以使用--save-dev
,这样就非常方便地让你在开发一个库的时候,不会依赖工具(但不是个好方法!)。把下面的内容添加到
package.json
中。
"scripts": {
"build": "webpack"
}
Code Splitting(代码分割)
上面的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块中,这些块会在使用时被动态加载。
Production
在发布版本中,可能需要一些特殊的配置或者插件,譬如只有在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',
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。