webpack-03 - webpack性能优化

381 阅读6分钟

缩小文件的搜索范围

优化loader配置

有三个配置项来缩⼩loader的处理范围:

  • include:引入符合以下任何条件的模块,也就是说去哪些个模块搜索精准搜索(官方推荐使用)
  • exclude:排除所有符合条件的模块

include和exclude可以是字符串也可以是数组,甚至还可以是函数、正则及对象。

示例:

include: path.resolve(__dirname, "./src"),

include: [
    path.resolve(__dirname, 'app/styles'),
    path.resolve(__dirname, 'vendor/styles'),
],

resolve

resolve.alias

resolve.alias配置通过别名来将原导⼊路径映射成⼀个新的导⼊路径。

resolve: {
    alias: {
        "@": path.resolve(__dirname, "./src/"),
    }
}

注意: 在html、css中使用的时候,需要加上~

background: url(~@/imgs/1.jpg); // 注意不要加引号

js中:

import '@/styles/index.less';

resolve.modules

resolve.modules⽤于配置webpack去哪些⽬录下去寻找第三⽅模块,默认是在当前项⽬⽬录下的node_modules⾥⾯去找,如果没有找到,就会去上⼀级⽬录的node_modules找,再没有会去上上级目录的node_modules中找,以此类推,和Node.js的模块寻找机制很类似。

如果我们的第三⽅模块都安装在了项⽬根⽬录下,就可以直接指明这个路径。

module.exports={
 xxxxx,
 resolve:{
 	// 定义第三方依赖的位置,支持单个和多个
 	// modules: path.resolve(__dirname, "./node_modules") 
 	modules: [path.resolve(__dirname, "./node_modules")]
 } 
}

resolve.extensions

extensions在导⼊语句但没带后缀时,webpack会⾃动带上后缀后,去尝试查找⽂件是否存在。默认有.js.json

extensions: ['.js', '.json', '.ts', '.jsx']

注意:

  • 这个列表的值越多,需要匹配的时间就越久,因为会为每一个省略后缀的文件都挨个加上后缀,然后再去查找是否存在,所以尽量都带上后缀。
  • 加入同一个目录下同时有a.js和a.ts文件,当省略了后缀的时候,系统会从左往右依次找寻,找到了满足条件的a.js就结束了不再继续往后匹配,即使a.ts也满足。

压缩html

new htmlWebpackPlugin({
     template: "./src/index.html",
     filename: "index.html",
     minify: { // 压缩
         removeComments: true, // 去掉注释
         collapseWhitespace: true, // 删除换⾏符与空⽩符
         minifyCSS: true // 压缩内联的css
     }
 }),

分离css文件

mini-css-extract-plugin

之前我们说到了,style-loader是通过js动态生成css直接打包进js⾥的,但是我们希望能单独生成css文件,因为这样就可以和js并行下载提高页面加载效率,缺点就是会增加请求。

下载

npm i mini-css-extract-plugin -D

引入

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

配置使用

module.exports = {
    module: {
        rules: [
            // 打包 css
            {
                // test 用于配置当前打包规则要匹配的文件名
                test: /\.less$/,
                // use 用于配置当前打包规则所使用的 loader
                // 使用MiniCssExtractPlugin.loader来替代style-loader
                // 但是需要注意的是,抽离了css成文件夹之后,引用层级问题,需要使用../
                // use: [ MiniCssExtractPlugin.loader, 'css-loader', "postcss-loader", 'less-loader'],
                use: [ {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        publicPath: '../'
                    }
                }, 'css-loader', "postcss-loader", 'less-loader'],
                // include用于引入符合以下任何条件的模块
                include: path.resolve(__dirname, './src')
            },
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
             filename: "css/[name]_[contenthash:4].css",
             chunkFilename: "[id].css"
        })
    ]
}

压缩css

  • cssnano:是一个基于node开发的缩减器,也 是一个 postcss插件,可以添加到构建流程中,用于确保最终生成的用于生产环境的 CSS 样式表文件尽可能的小。
  • optimize-css-assets-webpack-plugin

安装:

// webpack4.x版本就需要使用这两个的4.0版本,否则cssnano.process会报错
npm i optimize-css-assets-webpack-plugin@4.0.0 cssnano@4.0.0 -D

引入:

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

使用:

new OptimizeCSSAssetsPlugin({
	assetNameRegExp: /\.css$/g, // 此插件压缩的对象是由mini-css-extract-plugin插件输出的css文件,而不是css源文件
	cssProcessor: require("cssnano"), // 压缩css的处理器,返回promise对象
	cssProcessorOptions: { // 传递给cssProcessor的配置对象
		// 移除所有的空格和引号
 		discardComments: { removeAll: true }
	},
	canPrint: true // 配置插件是否可以将消息打印到控制台
})

tree Shaking清除无用css、js

Tree Shaking 通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code),依赖于 ES2015 模块语法的静态解构特性,如import和export。这个术语和概念实际上是由 ES2015 模块打包工具 rollup 普及起来的。webpack 4 ,通过 package.json"sideEffects" 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯正的ES2015 模块)",由此可以安全地删除文件中未使用的部分。

官方地址

注意:只有mode是production才会⽣效,develpoment的tree shaking是不⽣效的,因为webpack为了⽅便我们调试。

css tree shaking

安装:

npm i purify-css purifycss-webpack glob-all -D

使用:

const PurifyCSS = require('purifycss-webpack');
const glob = require('glob-all');

    plugins: [
    	// xxxxx
        new PurifyCSS({ // 清除⽆⽤ css
            paths: glob.sync([
                // 匹配要清除的路径⽂件
                path.resolve(__dirname, './src/*.html'), // 我们也需要对html⽂件进⾏清除
                path.resolve(__dirname, './src/*.js')
            ])
        })
    ],

JS tree shaking

注意:只⽀持import⽅式引⼊,不⽀持commonjs的⽅式引⼊,所以需要确保没有编译器将ES6模块语法转换为 CommonJS 。

//webpack.config.js
optimization: {
 usedExports: true // 哪些导出的模块被使⽤了,再做打包
}

sideEffects

如果所有代码都不包含 side effect,可以简单地将该属性标记为 false,来告知 webpack,它可以安全地删除未用到的 export。

// package.json中:
"sideEffects":false //对所有模块进⾏tree shaking , 仅⽣产模式有效,需要配合usedExports

官方sideEffects和usedExports的解释:

但是这样所有导入文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader 并 import 一个 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除。也就是说,如果代码确实有一些副作用,可以改为提供一个数组:

// package.json中:
"sideEffects": [
  "**/*.css",
  "**/*.scss",
  "./esnext/index.js",
  "./esnext/configure.js"
],

作⽤域提升

作⽤域提升Scope Hoisting是webpack通过ES6语法的静态分析,分析出模块之间的依赖关系,尽可能地把模块放到同⼀个函数中。从而可以让打包出来的代码⽂件更⼩运⾏更快。

用法:

optimization: {
    concatenateModules: true // 开启Scope Hoisting
}

作用:

例如:

// a.js
export default '嘿嘿嘿';
// index.js
import a from './a.js';
console.log(a);

配置了Scope Hoisting之后,打包之后我们可以发现a.js和index.js的内容合并在一起了。

代码切割

split-chunks-plugin

当我们打包完成后,只生成一个bundleJs,这个时候就可能代码体积过大,下载时间需要也长。webpack中提供了⼀种⽅便的⽅法供我们实现代码分割:

官方地址:

配置项说明:

 optimization: {
      splitChunks: {
            chunks: 'all',// async异步(默认) initial同步 all所有的模块有效
            minSize: 20000,//最⼩尺⼨,当模块⼤于20kb
            maxSize: 0,//对模块进⾏⼆次分割时使⽤,不推荐使⽤
            minChunks: 1,//打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
            maxAsyncRequests: 30,//最⼤异步请求数,默认5
            maxInitialRequests: 30,//最⼤初始化请求数,默认3
            automaticNameDelimiter: '-',//打包分割符号
            name: true,//打包后的名称
            cacheGroups: {//缓存组
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "defaultVendors", // 要缓存的、分隔出来的chunk名称
                    priority: -10,//缓存组优先级 数字越⼤,优先级越⾼
                    reuseExistingChunk: true//是否重⽤该chunk
                },
                other: {
                    chunks: "initial", // initial| all | async(默认)
                    test: /react|lodash/, // 正则规则验证,如果符合就提取chunk,
                    name: "other",// 要缓存的、分隔出来的chunk名称
                    minSize: 20000,//最⼩尺⼨,当模块⼤于20kb
                    minChunks: 1,//打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
                },
                default: {
                    minChunks: 2,//打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
                    priority: -20,//缓存组优先级 数字越⼤,优先级越⾼
                    reuseExistingChunk: true//是否重⽤该chunk
                }
            }
        }
    }

帮我们⾃动做代码分割的简洁方式:

optimization:{
     splitChunks:{
     	chunks:"all"
     }
 }

压缩图片

安装:(推荐使用cnpm安装,使用yarn和npm安装依赖下载不完全)

cnpm install image-webpack-loader -D

使用:

rules: [{
  test: /\.(gif|png|jpe?g|svg)$/i,
  use: [
    'file-loader',
    {
      loader: 'image-webpack-loader',
      options: {
        mozjpeg: {
          progressive: true,
        },
        // optipng.enabled: false will disable optipng
        optipng: {
          enabled: false,
        },
        pngquant: {
          quality: [0.65, 0.90],
          speed: 4
        },
        gifsicle: {
          interlaced: false,
        },
        // the webp option will enable WEBP
        webp: {
          quality: 75
        }
      }
    },
  ],
}]

官方地址

注意事项:

  • 一些OSX系统可能会报错:

    Module build failed: Error: dyld: Library not loaded: /usr/local/opt/libpng/lib/libpng16.16.dylib
    

    这个时候,需要翻墙安装libpng

    brew install libpng
    
  • 如果报:Cannot find module 'mozjpeg'Cannot find module gifsicle等模块,是下载依赖不完全,删除node_modules,使用cnpm i重新下载依赖。

代码地址

github代码地址: