[url=https://github.com/webpack/extract-text-webpack-plugin/blob/master/README.md]extract text plugin for webpack 2[/url]
[b]拆分业务代码与框架代码[/b]
通常一个 web 应用都会引用若干第三方库,这些第三方库通常比较稳定不会经常变动,但是如果将业务代码和框架代码打包在了一起,这样业务代码每次变动打包得到的结果都会变动,及时只改变了一个字符,浏览器也无法利用缓存,必须全部重新加载。因此,何不将第三方库单独打包在一起呢?
这里举个案例,一个 react 项目中使用了 [code]react[/code] 和[code] react-dom [/code]这两个包,我希望将他们打包在一起,将业务代码打包在一起。
下面一步一步来:
1. 安装 react 和 react-dom:
npm i react react-dom --save
2. 配置 entry,output 和 loader
先使用单入口,让代码工作起来。另外因为使用了 react 所以要使用 [code]babel-loader [/code]来加载 js
// webpack.config.js
module.exports = {
entry: 'index.js',
output: {
path: 'build/',
filname: '[name]@[chunkhash].js'
},
module:{
loaders:[{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
}]
}
}
3. 编写业务代码
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
var Hello = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<Hello name={'world'} />, document.getElementById('app'));
index.html:
<div id="app"></div>
<!--entry 为一个字符串,这个 chunk 的名字会是 main, 因此这里引入 main.js -->
<script src="build/main.js"></script>
启动 [code]webpack-dev-server[/code],打开浏览器这个时候应该能在页面上看到 hello world,这说明工作正常。
4. 拆分框架代码
为了拆分框架代码,我们需要增加一个入口,在这个入口中要包含[code] react [/code]和[code] react-dom[/code]
module.exports = {
entry: {
main: 'index.js',
vendor: ['react', 'react-dom']
}
//...
}
单单像上面这样配置,打包后会得到 main.js 和 vendor.js,但会发现在 main.js 中依然包含了 react 和 react-dom 的代码,这是因为指定了入口后,webpack 就会从入口文件开始讲整个依赖打包进来,index.js 中引用了 react 和 react-dom 自然会被打包进去。要想达到之前所说的那个效果,还需要借助一个插件 —— CommonsChunkPlugin
5. 使用 CommonsChunkPlugin
这个插件的功能是将多个打包结果中公共的部分抽取出来,作为一个单独的文件。它符合目前的场景,因为[code] main.js [/code]和 [code]vendor.js [/code]中存在一份公共的代码,那就是 [code]vendor.js [/code]中的内容。(这个说法并不准确,这里只是指 react 和 react-dom 都被打包进了这两个文件)
let webpack = require('webpack');
module.exports = {
entry: {
main: 'index.js',
vendor: ['react', 'react-dom']
},
//...
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor' // 指定一个希望作为公共包的入口
})
]
}
再进行打包,这个时候会发现[code] main.js [/code]中的代码不在包含 react 的代码了。看似实现了我们的需求,但真实应用下并没有这么简单,在实际项目中 js 脚本通常都会给添加一个 MD5 的 hash 在后面,形如[code] app@709d9850745a4c8ba1d4[/code].js 这样每次打包后,如果文件内容变了,后面的 hash 也会变动。就以上场景,会发现当我们修改了业务代码后,得到的 hash 是不同的,因此每次都会得到两个不同的打包结果。业务代码改变了,拆分出来的框架包也变了,这显然不符合初衷 —— 利用浏览器缓存。
这是因为 webpack 在打包的时候会产生一些运行时代码,比如 [code]__webpack_require__ [/code]、[code]webpackJsonp [/code]等等,这些函数是用来帮助 webpack 完成模块加载等功能的,业务代码的改变会导致业务代码打包后的 hash 值改变,而在 webpack 的运行时代码中实际上是保存了打包后的结果的文件名的,因为它在异步加载模块的时候需要用到。因此,下面需要做的是将 webpack 的运行时代码拆分出来。
修改 plugins 如下,将 name 修改为 names,并增加一个 init 的包名,执行打包,会发现 webpack 的运行时代码都被入该包内。
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'init']
})
]
这样以来,修改了业务代码后,vendor 因为只引用了 react 和 react-dom 因此,业务代码的改变不会改变 vendor 这个包的内容,hash 也保持不变。但,也仅仅如此 如果你引用了其他模块,webpack 收集依赖的时候会给每个模块编一个号,引入其他模块会导致模块数改变,也就会导致编号改变,这个时候打包出来的 vendor 还是会改变。
那么到底该如何解决这个问题呢?在官方文档上没有找到解决方案。后面我会继续探索这一问题,找到解决方案后会及时更新到这里,如果你有解决方案,还请不吝赐教,谢谢。
希望本文所述对你有所帮助,webpack代码拆分内容就给大家介绍到这里了。希望大家继续关注我们的网站!