视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
webpack学习笔记之优化缓存、合并、懒加载
2020-11-27 22:31:53 责编:小采
文档


除了的webpack基本配置,还可以进一步添加配置,优化合并文件,加快编译速度。下面是生产环境配置文件webpack.production.js,与wenbpack.config.js相比其不需要一些dev-tools,dev-server和jshint校验等,将与开发相关的东西删掉。下面的介绍均以此代码配置作参考。

/*生成环境配置文件:不需要一些dev-tools,dev-server和jshint校验等。和开发有关的东西删掉*/
var webpack = require('webpack');
var path = require('path');
var node_modules = path.resolve(__dirname, 'node_modules');
var pathToReact = path.resolve(node_modules, 'react/dist/react.min.js');
var pathToReactDOM = path.resolve(node_modules, 'react-dom/dist/react-dom.min.js');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
//具体作用及缺点见plugins中的描述
//var WebpackMd5Hash = require('webpack-md5-hash');
// 该插件是对“webpack-md5-hash”的改进:在主文件中获取到各异步模块的hash值,然后将这些hash值与主文件的代码内容一同作为计算hash的参数,这样就能保证主文件的hash值会跟随异步模块的修改而修改。
var WebpackSplitHash = require('webpack-split-hash');

var config = {
 entry:{
 app:path.resolve(__dirname, 'src/js/entry.js'),
 mobile: path.resolve(__dirname, 'src/js/mobile.js'),
 //添加要打包在vendors.js里面的库
 vendors:['react','react-dom']
 },
 resolve:{
 alias: {
 'react.js': pathToReact, //alias:别名,
 'react-dom.js': pathToReactDOM
 },
 fallback: path.join(__dirname, "node_modules")
 },
 resolveLoader: { 
 fallback: path.join(__dirname, "node_modules") 
 },
 output:{
 path:path.resolve(__dirname, 'dist'),
 publicPath:'../',//生成的html里的引用路径用 publicPath
 //以文件内容的MD5生成Hash名称的script来防止缓存
 filename: 'js/[name].[chunkhash:8].js',
 //异步加载的模块是要以文件形式加载,生成的文件名是以chunkFilename配置的
 chunkFilename: 'js/[name].[chunkhash:8].js'
 },
 module:{
 loaders:[{
 test: /\.jsx?$/,
 //这里(node_modules文件夹)再也不需通过任何第三方来加载
 exclude:path.resolve(__dirname, 'node_modules'),
 loader: 'babel',
 query:{
 presets:['es2015', 'react']
 }
 },
 {
 test:/\.css$/,
 //loader:'style!css'
 loader: ExtractTextPlugin.extract("style", "css")
 },
 {
 test:/\.scss$/,
 loader:'style!css!sass'
 },
 //url-loader:图片、字体图标加载器,是对file-loader的上层封装,支持base编码。传入的size(也有的写limit) 参数是告诉它图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE 字符串。
 {
 test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
 loader: 'url?limit=25000&name=[name].[ext]'
 }]
 },
 plugins:[
 //提取公共代码的插件,用于提取多个入口文件的公共脚本部分,然后生成一个vendors.js。注意HTML代码中要加载这个公共文件
 new webpack.optimize.CommonsChunkPlugin({
 name: 'vendors',
 filename: 'js/vendors.js'
 }),
 //在文件开头插入banner
 new webpack.BannerPlugin("The file is creted by yangmin.--"+ new Date()),
 //压缩js文件
 new webpack.optimize.UglifyJsPlugin({
 compress: {
 warnings: false
 }
 }),
 /*插件:单独使用style标签加载css文件.contenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值*/
 new ExtractTextPlugin("css/[name].[contenthash:8].css"),//设置其路径(路径相对于path)
 /*插件:动态生成html,在webpack完成前端资源打包以后,自动将打包后的资源路径和版本号写入HTML中,达到自动化的效果。*/
 new HtmlWebpackPlugin({
 filename:'view/index.html', //生成的html存放路径,相对于 path
 template:'src/view/index.html', //html模板路径
 inject:true, //允许插件修改哪些内容,true/'head'/'body'/false,
 chunks:['vendors','app'],//加载指定模块中的文件,否则页面会加载所有文件
 hash:false, //为静态资源生成hash值
 minify:{ //压缩HTML文件
 removeComments:false, //移除HTML中的注释
 collapseWhitespace:false //删除空白符与换行符
 } 
 }),
 new HtmlWebpackPlugin({
 filename:'view/mobile.html', //生成的html存放路径,相对于 path
 template:'src/view/mobile.html', //html模板路径
 inject:true, //允许插件修改哪些内容,true/'head'/'body'/false,
 chunks:['vendors','mobile'],//加载指定模块中的文件,否则页面会加载所欲文件
 hash:false, //为静态资源生成hash值
 minify:{ //压缩HTML文件
 removeComments:false, //移除HTML中的注释
 collapseWhitespace:false //删除空白符与换行符
 }
 }),
 /*作用:生成具有hash值的css和js文件,即css和js文件hash值解耦.
 *缺点:webpack-md5-hash插件对chunk-hash钩子进行捕获并重新计算chunkhash,它的计算方法是只计算模块本身的当前内容(包括同步模块)。这种计算方式把异步模块的内容忽略掉了,会造成一个问题:异步模块的修改并未影响主文件的hash值。
 */
 //new WebpackMd5Hash()
 new WebpackSplitHash()
 ]
}
 module.exports = config;

一、在开发环境中使用压缩文件

例如ReactJS项目中为了不让 Webpack 去遍历 React JS 及其所有依赖,你可以在webpack.config.js中重写它的行为。

config.alias: 每当 "react" 在代码中被引入,它会使用压缩后的 React JS 文件。

noParse: 阻止Webpack 去解析那个压缩后的文件。

当加载多个压缩文件时,下述方法更优雅简便,webpack.production.js:

var webpack = require("webpack");
...
var HtmlWebpackPlugin = require('html-webpack-plugin');

var deps = [
 'react/dist/react.min.js',
 'react-dom/dist/react-dom.min.js'
];
var config = {
 ...
 resolve:{
 alias:{},
 fallback:path.join(__dirname, "node_modules")
 },
 ...
 module:{
 ...
 noParse:[] 
 } 
}
/*当加载多个压缩文件时,下述方法更优雅简便*/
deps.forEach(function(dep){ 
 var depPath = path.resolve(node_modules, dep);
 //path.dep是路径分隔符。
 config.resolve.alias[dep.split(path.dep)[0]] = depPath; 
 config.module.noParse.push(depPath);

});

module.exports = config;

二、分离应用和第三方文件

当你的应用依赖其他库尤其是像 React JS 这种大型库的时候,需要考虑把这些依赖分离出去,这样就能够让用户在你更新应用之后不需要再次下载第三方文件。上述配置文件中的entry里添加了第三方包vendors,其值为要分离打包的文件。运行配置后会在dist/js下生成三个文件:app.js、mobile.js、vendors.js。注意在页面中药引入vendors.js

<script src="../dist/vendors.js"></script>
<script src="../dist/app.js"></script>

三、多重入口

当应用有多个页面, 页面之间虽然有共享代码,但是不想在页面中加载所有代码时可以定义多重入口。例如配置文件中的app.js针对pc端页面,mobile.js仅针对移动端页面,output的filename:'js/[name].[chunkhash:8].js',采用了文件名变量,这样在dist/js中可生成与源文件同名的文件。

四、优化缓存及懒加载

在生产环境中,将输出文件名添加hash值,目的是在文件更改时强制客户端重新加载这个文件,而未改变的文件继续使用缓存文件。常用的有hash和chunkhash。配置文件中的[chunkhash:8]即截取8位chunkhash值。  

webpack的编译理念:webpack将style视为js的一部分,所以在计算chunkhash时,会把所有的js代码和style代码混合在一起计算。比如entry.js引用了main.css:

import 'main.css'; 
alert('I am main.js');

webpack计算chunkhash时,以entry.js文件为编译入口,整个chunk的内容会将main.css的内容也计算在内。所以,不论是修改了js代码还是css代码,整个chunk的内容都改变了,计算所得的chunkhash随之改变。但理想情况下是想css或js内容改变时仅影响自身文件的chunkhash,这样客户端只需更新一部分文件。解决此问题首先要将css单独编译输出文件,因为正常情况下webpack会把js文件中引入的css文件编译输出到html页面的<style></style>标签中。

1.使用extract-text-webpack-plugin单独编译输出css文件

安装extract-text-webpack-plugin,

npm install extract-text-webpack-plugin --save-dev

然后在配置文件中引入插件,

//webpack.production.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin');

该插件除了chunkhash还提供了另外一种hash值contenthash。顾名思义,contenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值。此hash是可解决上述问题的关键所在。上述配置文件使用了contenthash:

//webpack.production.config.js
new ExtractTextPlugin("css/[name].[contenthash:8].css"),//设置其路径(路径相对于path)

2.使用使用webpack-md5-hash解耦css和js文件hash值

再考虑以下情况,只修改了main.css文件,未修改entry.js文件,编译输出的js文件hash是否改变?答案是改变了,因为上文提到的webpack的编译理念,webpack将style视为js的一部分,所以在计算chunkhash时,会把所有的js代码和style代码混合在一起计算。解决办法是使用webpack-md5-hash插件:

//webpack.production.config.js

var WebpackMd5Hash = require('webpack-md5-hash');
...
new WebpackMd5Hash();

它的作用是生成具有hash值的css和js文件,即css和js文件hash值解耦。webpack-md5-hash插件对chunk-hash钩子进行捕获并重新计算chunkhash,它的计算方法是只计算模块本身的当前内容(包括同步模块)。

3.主文件使用hash代替chunkhash解决异步加载模块改变时主文件hash不改变

假如文件中引入了异步模块,异步模块修改后会影响编译输出的js文件的chunkhash吗?现在入口文件中引入异步模块a.js,a.js文件又异步引入b.js,b.js同步引入c模块

//entry.js

'use strict';

import './saveCarInfo.js';

window.onload = function(){//懒加载
 require.ensure(['./a.js'],function(require){
 var moduleA = require('./a.js');
 },'a');
};

//a.js

'use strict'

console.log("a");

setTimeout(function(){
 require.ensure([],function(require){
 require('./b.js');
 },'b');
},10000);

module.exports = "moduleA";
//b.js
import fn_c from './c.js';

console.log('b');
module.exports = 'moduleB';
//c.js

console.log("c");
module.exports = "moduleC";

运行npm run deploy,编译输出如下,我们看到除了入口文件、css文件、html文件被输出外,异步加载的模块a.js、b.js也被当做模块输出。

此时修改a.js文件中的代码,经编译后,a.[chunkhash].js的chunkhash会改变,而生成的主文件app.[chunkhash].js的chunkhash值并没有改变。原因是webpack-md5-hash的这种计算方式把异步模块的内容忽略掉了,这会造成一个问题:异步模块的修改并未影响主文件的chunkhash值。解决办法是将输出的主文件采用[hash],而非[chunkhash]

output:{
 path:path.resolve(__dirname, 'dist'),
 publicPath:'../',//生成的html里的引用路径用 publicPath
 filename: 'js/[name].[hash:8].js',
 //异步加载的模块是要以文件形式加载,生成的文件名是以chunkFilename配置的
 chunkFilename: 'js/[name].[chunkhash:8].js'
},

这种做法也存在缺陷,如果项目中存在不止一个主js文件,修改任意js代码会影响所有最终主文件的[hash]值。例如上面的项目配置中会生成两个带[hash]的主文件:app.[hash].js, mobile.[hash].js。无论是修改entry.js代码还是异步模块a.js,或b.js的代码,app.[hash].js和mobile.[hash].js的[hash]都会改变。

补充:npm提供了webpack-split-hash插件代替webpack-md5-hash,该插件可以获取到各异步模块的hash值,然后将这些hash值与主文件的代码内容一同作为计算hash的参数,这样就能保证主文件的hash值会跟随异步模块的修改而修改。但我验证后没有实现。。

4.使用html-webpack-plugin动态生成html

配置文件中的输出文件都带了[chunkhash]作为版本号,在style或js文件改变时,其值都会随之改变。利用html-webpack-plugin在webpack完成前端资源打包以后,自动将打包后的资源路径和版本号写入HTML中,达到自动化的效果。

//webpack.production.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');
...
var config = {
...
plugins:[
... 
 new HtmlWebpackPlugin({
 filename:'view/index.html', //生成的html存放路径,相对于 path
 template:'src/view/index.html', //html模板路径
 inject:true, //允许插件修改哪些内容,true/'head'/'body'/false,
 chunks:['vendors','app'],//加载指定模块中的文件,否则页面会加载所有文件
 hash:false, //为静态资源生成hash值
 minify:{ //压缩HTML文件
 removeComments:false, //移除HTML中的注释
 collapseWhitespace:false //删除空白符与换行符
 } 
 }),
 new HtmlWebpackPlugin({
 filename:'view/mobile.html', //生成的html存放路径,相对于 path
 template:'src/view/mobile.html', //html模板路径
 inject:true, //允许插件修改哪些内容,true/'head'/'body'/false,
 chunks:['vendors','mobile'],//加载指定模块中的文件,否则页面会加载所有文件
 hash:false, //为静态资源生成hash值
 minify:{ //压缩HTML文件
 removeComments:false, //移除HTML中的注释
 collapseWhitespace:false //删除空白符与换行符
 } 
 })
]}

下载本文
显示全文
专题