最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Webpack 性能优化

    正文概述 掘金(小丶k)   2021-01-16   432

    一、 开发优化(development)

    1. 文件监听

    缺点: 需要手动刷新

    启动方式:

    1. 方式1

    启动 webpack 命令式 带上--watch 参数,启动监听

    // package.json
    "scripts": {
      "watch":"webpack --watch"
    }
    
    npm run watch
    
    npx webpack --watch
    
    1. 方法式2

    在配置⽂件⾥设置 watch:true

    // webpack.config.js
    module.exports = {
        watch: true, //默认false,不开启
            //配合watch,只有开启才有作⽤
        watchOptions: {
            //默认为空,不监听的⽂件或者⽬录,⽀持正则
            ignored: /node_modules/,
            //监听到⽂件变化后,等300ms再去执⾏,默认300ms,
            aggregateTimeout: 300,
            //判断⽂件是否发⽣变化是通过不停的询问系统指定⽂件有没有变化,默认每秒问1次
            poll: 1000 //ms
        }
        // 轮询 1s查看1次
    }
    

    2. webpack-dev-server 自动打开浏览器,保存自动刷新

    2.1 安装

    注意: webpack-dev-server 和 webpack-cli 4.x.x有兼容性问题,会报错, webpack-cli 需要安装 3.x.x,

    npm install webpack-dev-server -D
    

    启动

    // package.json
    "scripts": {
       "server": "webpack-dev-server"
    }
    
    npm run server
    

    2.2 配置

    // webpack.config.js
    var path = require('path');
    
    module.exports = { 
        //...
        devServer: {
            contentBase: path.join(__dirname, 'dist'),
            compress: true,
            port: 9000 
        }
    };
    
    1. devServer: 打包的模块是放在内存中的,从而提升打包速度.

    2. contentBase:告诉服务器内容的来源。仅在需要提供静态文件时才进行配置.

    3. port: 端口

    4. open: 是否打开浏览器

    5. proxy: 解决开发环境的跨域问题

    // webpack.config.js
    devserver: {
    	proxy: {
        	
      		'/api': { // 请求
         	target: 'http://localhost:9092'
      		}
    	}
    }
    
    // index.js
    axios.get("/api/info").then(res => {
       console.log(res);
    });
    
    1. mock server

    before 和 after 是中间件 提供的两个钩子, 一个在中间件启动之前,一个在中间件启动之后

    before(app, server) {
      app.get('/api/mock', (req, res) => {
          res.json({
              hello: 'express'
          })
      })
    }
    

    3. Hot Module Replacement (HMR:热模块替换)

    3.1 热更新配置

    热更新: 新代码生效, 网页不刷新, 状态不丢失

    // 引入 webpack
    const webpack = require('webpack');
    
    module.exports = {
        plugins: [
        // 使用 webpack 插件
            new webpack.HotModuleReplacementPlugin(),
        ],
        devServer: {
          hot: true,
          //即便HMR不⽣效,浏览器也不要⾃动刷新,就开启hotOnly
          hotOnly: true,
        }
    } 
    

    注意:CSS 热更新 不⽀持抽离出的 CSS, 不建议开发环境使用 mini-css-extract-plugin,还有不⽀持contenthash,chunkhash

    3.2 JS 热模块替换

    需要使⽤ module.hot.accept 来观察模块更新 从⽽更新

    //number.js
    function number() {
    	var div = document.createElement("div");
    	div.setAttribute("id", "number");
    	div.innerHTML = 13000;
    	document.body.appendChild(div);
    }
    export default number;
    
    import number from "./number";
    
    number();
    
    // 是否开启热更新
    if (module.hot) {
        // 模块是否发生变化
        module.hot.accept("./number", function () {
            // 移除该元素
            document.body.removeChild(document.getElementById("number"));
            // 重新添加该元素
            number();
        });
    }
    

    3.3 React

    React Hot Loader: 实时调整 react 组件。

    3.4 Vue

    Vue Loader: 此 loader 支持 vue 组件的 HMR,提供开箱即用体验

    4. 缩小 Loader 处理范围

    4.1 include: 只处理那

    module.exports = {
    	module: {
        	rules: [{
    	    	test: /\.css$/,
                	// 只处理src 目录下的文件
        		include: path.resolve(__dirname, './src'),
    		}]
        }
    }
    

    4.2 exclude: 不处理那

    module.exports = {
        module: {
            rules: [{
                test: /\.css$/,
                // 不去处理那
                exclude: path.resolve(__dirname, './node_modules')
            }]
        }
    }
    

    include 和 exclude 二选一,推荐使用 include

    5. 优化 resolve 配置

    5.1 resolve.modules

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

    // webpack.config.js
    module.exports={
        resolve: {
            // 指定路径只去这里查找,没有就是没有了
            modules: [
                path.resolve(__dirname, "./node_modules")
            ]
        }
    }
    

    5.2 resolve.alias

    默认情况下,webpack会从⼊⼝⽂件./node_modules/bin/react/index开始递归解析和处理依赖的⽂件。我们可以直接指定⽂件,避免这处的耗时。

    // webpack.config.js
    module.exports={
        resolve: {
            alias: {
        		"@": path.join(__dirname, "./pages"),
        		// 指定 react 文件路径,节约查找时间
                react: path.resolve(__dirname, "./node_modules/react/umd/react.production.min.js"),
        		// 指定 react-dom 文件路径,节约查找时间
        		"react-dom": path.resolve(__dirname,"./node_modules/react-dom/umd/react-dom.production.min.js")
        }
    }
    

    5.3 resolve.extensions

    • 后缀尝试列表尽量的⼩
    • 导⼊语句尽量的带上后缀。
    // webpack.config.js
    module.exports={
        resolve: {
        	// 默认配置
        	extensions:['.js','.json','.jsx','.ts']
        }
    }
    

    6. 使⽤⼯具量化

    6.1 speed-measure-webpack-plugin:可以测量各个插件和 loader 所花费的时间

    安装:

    npm i speed-measure-webpack-plugin -D
    
    //webpack.config.js
    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
    
    const smp = new SpeedMeasurePlugin();
    
    const config = {
    	//...webpack配置
    }
    
    module.exports = smp.wrap(config)
    

    6.2 webpack-bundle-analyzer:分析webpack打包后的模块依赖关系

    安装:

    npm install webpack-bundle-analyzer -D
    

    启动webpack 构建,会默认打开⼀个窗⼝

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    
    module.exports = {
       //....
     	plugins: [
     	//...
     	new BundleAnalyzerPlugin(),
     	]
    }
    

    7. DllPlugin插件打包第三⽅类库 优化构建性能

    Dll动态链接库 其实就是做缓存

    webpack已经内置了对动态链接库的⽀持

    • DllPlugin: ⽤于打包出⼀个个单独的动态链接库⽂件
    • DllReferencePlugin:⽤于在主要的配置⽂件中引⼊DllPlugin插件打包好的动态链接库⽂件

    新建webpack.dll.config.js⽂件,打包基础模块

    我们在 index.js 中使⽤了第三⽅库 react 、 react-dom ,接下来,我们先对这两个库先进⾏打包。

    7.1 打包出动态链接库

    // webpack.dll.config.js
    const path = require("path");
    const { DllPlugin } = require("webpack");
    
    module.exports = {
        mode: "development",
        entry: {
            react: ["react", "react-dom"] //! node_modules?
        },
        output: {
            path: path.resolve(__dirname, "./dll"),
            filename: "[name].dll.js",
            library: "react"
        },
        plugins: [
            new DllPlugin({
                // manifest.json⽂件的输出位置
                path: path.join(__dirname, "./dll", "[name]-manifest.json"),
                // 定义打包的公共vendor⽂件对外暴露的函数名
                name: "react"
            })
        ]
    };
    

    运行

    npx webpack --config webpack.dll.config.js
    

    会生成⼀个dll⽂件夹,⾥边有dll.js⽂件,这样我们就把我们的React这些已经单独打包了

    • dll⽂件包含了⼤量模块的代码,这些模块被存放在⼀个数组⾥。⽤数组的索引号为ID,通过变量讲⾃⼰暴露在全局中,就可以在 window.xxx访问到其中的模块
    • Manifest.json 描述了与其对应的dll.js包含了哪些模块,以及ID和路径。

    7.2 引入动态连接库

    • 将⽹⻚依赖的基础模块抽离,打包到单独的动态链接库,⼀个动态链接库是可以包含多个模块的。
    • 当需要导⼊的模块存在于某个动态链接库中时,不要再次打包,直接使⽤构建好的动态链接库即可。
    module.exports = {
    	new DllReferencePlugin({
     	manifest: path.resolve(__dirname,"./dll/react-manifest.json")
    	})
    }
    
    • ⻚⾯依赖的所有动态链接库都需要被加载。
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initialscale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>webpack</title>
        <link href="css/main_e2bf39.css" rel="stylesheet" />
      </head>
      <body>
        <div id="app"></div>
        <!-- 引入动态连接库 -->
        <script type="text/javascript" src="react.dll.js"></script>
        <script type="text/javascript" src="js/main_142e6c.js"></script>
      </body>
    </html>
    

    使用 add-asset-html-webpack-plugin 插件,它会将我们打包后的 dll.js ⽂件注⼊到我们⽣成的 index.html 中.在webpack.base.config.js ⽂件中进⾏更改

    安装: npm i add-asset-html-webpack-plugin

    new AddAssetHtmlWebpackPlugin({
    	filepath: path.resolve(__dirname, '../dll/react.dll.js') // 对应的 dll ⽂件路径
    }),
    

    8. HardSourceWebpackPlugin, DllPlugin 的替换方案

    DllPlugin 这个理解起来不费劲,操作起来很费劲。所幸,在Webpack5中已经不⽤它了,⽽是⽤ HardSourceWebpackPlugin ,⼀样的优化效果,但是使⽤却及其简单

    • 提供中间缓存的作⽤
    • ⾸次构建没有太⼤的变化
    • 第⼆次构建时间就会有较⼤的节省

    安装:

    npm i hard-source-webpack-plugin -D
    
    // webpack.config.js
    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
    
    module.exports = {
    	plugins: [
        		new HardSourceWebpackPlugin()
        	]
    }
    

    9. IgnorePlugin 从 bundle 中排除某些模块

    webpack 中文网

    var webpack = require('webpack');
    
    module.exports = {
      plugins: {
       new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
      }
    }
    

    10. 使⽤ happypack 并发执⾏任务

    happypack 启动也需要一定时间,按需使用。

    安装:npm i -D happypack

    const path = require('path');
    const HappyPack = require('happypack');
    const os = require('os');
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
    
    module.exports = {
        mode: 'development',
        module: {
            rules: [{
                test: /\.css$/,
                include: path.resolve(__dirname, './src'),
                use: [
                    {
                        // 一个 loader 对应一个 id
                        loader: 'happypack/loader?id=css'
                    }
                ]
            }]
        },
        //在plugins中增加
        plugins: [
            new HappyPack({
                 // ⽤唯⼀的标识符id,来代表当前的HappyPack是⽤来处理⼀类特定的⽂件
                id: "css",
                // 处理的 loader
                loaders: ['style-loader', 'css-loader'],
                threadPool: happyThreadPool
            })
        ]
    };
    

    11. ParallelUglifyPlugin

    • Webpack 内置 Uglify 工具压缩 JS 是单线程的
    • ParallelUglifyPlugin插件则会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS去压缩代码

    安装:

    npm i -D webpack-parallel-uglify-plugin
    

    配置:

    const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
    
    module.exports = {
      plugins: {
        // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
        new ParallelUglifyPlugin({
          // 传递给 UglifyJS 的参数
          // (还是使用 UglifyJS 压缩, 只不过帮助开启多进程)
          uglifyJS: {
            output: {
              beautify: false, // 最紧凑的输出
              comments: false, // 删除所有的注释
            },
            compress: {
              // 删除所有的 `console.log`语句, 可以兼容 ie 浏览器
              drop_console: true,
              // 内嵌定义了但是只用到一次的变量
              collapse_vars: true,
              // 提取出出现多次但是没有定义成变量的静态值
              reduce_vars: true
            }
          }
        })
      }
    }
    

    二、生产优化(production)

    1. 使⽤ externals 优化 cdn 静态资源

    <body>
     	<div id="root">root</div>
     	<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    </body>
    

    我们可以将⼀些JS⽂件存储在 CDN 上(减少 Webpack 打包出来的 js 体积),在 index.html 中通过标签引⼊

    我们希望在使⽤时,仍然可以通过 import 的⽅式去引⽤(如 import $ from 'jquery' ),并且希望 webpack 不会对其进⾏打包,此时就可以配置 externals 。

    //webpack.config.js
    module.exports = {
        //...
        externals: {
            //jquery通过script引⼊之后,全局中即有了 jQuery 变量
            'jquery': 'jQuery'
        }
    }
    

    2. 使⽤静态资源路径 publicPath (CDN)

    // webpack.config.js
    output:{
        publicPath: '//cdnURL.com', //指定存放JS⽂件的CDN地址
    }
    

    确保静态资源⽂件的上传与否

    3. css⽂件的处理

    • 使⽤postcss为样式⾃动补⻬浏览器前缀 caniuse
    • 借助 MiniCssExtractPlugin 抽离 css
    • 压缩 css

    3.1 压缩 css

    借助 ptimize-css-assets-webpack-plugin 和 cssnano

    安装:

    npm i optimize-css-assets-webpack-plugin -D
    npm i cssnano -D
    

    配置:

    const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
    
    module.exports = {
        plugins: [
            new OptimizeCSSAssetsPlugin({
                cssProcessor: require("cssnano"), //引⼊cssnano配置压缩选项
                cssProcessorOptions: {
                    discardComments: { removeAll: true }
                }
            })
        ]
    }
    

    4. 压缩 HTML

    借助 htmlWebpackPlugin 插件

    安装:

    npm i html-webpack-plugin -D
    

    配置:

    new htmlWebpackPlugin({
        title: "京东商城",
        template: "./index.html",
        filename: "index.html",
        minify: {
            // 压缩HTML⽂件
            removeComments: true, // 移除HTML中的注释
            collapseWhitespace: true, // 删除空⽩符与换⾏符
            minifyCSS: true // 压缩内联css
        }
    })
    

    5. tree Shaking 清除⽆⽤ css, js (Dead Code)

    Dead Code 特征:

    • 代码不会被执⾏,不可到达
    • 代码执⾏的结果不会被⽤到
    • 代码只会影响死变量(只写不读)

    5.1 Css tree shaking

    借助 purgecss-webpack-plugin 插件清除无用的 css

    借助 glob-all 处理路径

    安装:

    npm i purgecss-webpack-plugin -D
    npm i glob-all -D
    

    配置:

    const PurgeCSSPlugin = require('purgecss-webpack-plugin')
    const globAll = require("glob-all");
    
    module.exports = {
      plugins: [
          // 清除无用 css
          new PurgeCSSPlugin({
            paths: globAll.sync([
                // 要做 CSS Tree Shaking 的路径文件
                path.resolve(__dirname, './src/*.html'),
                // 请注意,我们同样需要对 html 文件进行tree shaking
                path.resolve(__dirname, './src/*.js')
            ])
          })
      ]
    }
    

    5.2 JavaScript tree shaking

    生产模式自动开启,只⽀持 import ⽅式引⼊,不⽀持 commonjs 的⽅式引⼊

    ES6 和 commonjs 的区别

    commonjs 动态引入, 执行时引入,模块输出的是值的浅拷贝,模块输出后被改变,其他引用模块不会改变

    ES6 静态引入, 编译时引入,模块输出的是值的引用,模块输出后被改变, 引用模块会改变

    module.exports = {
        mode: 'production'
    }
    
    • 自动开启代码压缩
    • Vue React 等会删除掉调式代码(如开发环境的 warning)
    • 自动启动 Tree-Shaking

    6. 代码分割 code Splitting

    单⻚⾯应⽤spa: 打包完后,所有⻚⾯只⽣成了⼀个bundle.js,代码体积变⼤,不利于下载,没有合理利⽤浏览器资源

    多⻚⾯应⽤mpa: 如果多个⻚⾯引⼊了⼀些公共模块,那么可以把这些公共的模块抽离出来,单独打包。公共代码只需要下载⼀次就缓存起来了,避免了重复下载。

    • 代码体积更小
    • 创建函数作用域更少

    code Splitting 概念与 webpack 并没有直接的关系,只不过 webpack 中提供了⼀种更加⽅便的⽅法供我们实现代码分割基于split-chunks-plugin

    optimization: {
        splitChunks: {
            chunks: 'async',//对同步 initial,异步 async,所有的模块有效 all
            minSize: 30000,//最⼩尺⼨,当模块⼤于30kb
            maxSize: 0,//对模块进⾏⼆次分割时使⽤,不推荐使⽤
            minChunks: 1,//打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
            maxAsyncRequests: 5,//最⼤异步请求数,默认5
            maxInitialRequests: 3,//最⼤初始化请求书,⼊⼝⽂件同步请求,默认3
            automaticNameDelimiter: '-',//打包分割符号
            name: true,//打包后的名称,除了布尔值,还可以接收⼀个函数function
            cacheGroups: {//缓存组
                vendors: {
                test: /[\\/]node_modules[\\/]/,
                name:"vendor", // 要缓存的 分隔出来的 chunk 名称
                priority: -10//缓存组优先级 数字越⼤,优先级越⾼
            },
            other:{
                chunks: "initial", // 必须三选⼀: "initial" | "all" | "async"(默认就是async)
                test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk,
                name:"other",
                minSize: 30000,
                minChunks: 1,
            },
            default: {
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true//可设置是否重⽤该chunk
            }
            }
        }
    }
    
    optimization: {
     	//帮我们⾃动做代码分割
     	splitChunks:{
     		chunks:"all",//默认是⽀持异步,我们使⽤all
     	}
    }
    

    7. 作⽤域提升(Scope Hoisting)

    示例:

    // hello.js
    export default "hello webpack";
    
    import str from "./hello.js";
    console.log(str);
    

    Webpack 性能优化

    通过配置 optimization.concatenateModules=true 开启 Scope Hoisting

    // webpack.config.js
    module.exports = {
       optimization: {
       		concatenateModules: true
       }
    }
    

    Webpack 性能优化

    三、development vs Production 模式区分打包

    借助 npm install webpack-merge -D 合并公共配置

    安装:

    npm install webpack-merge -D
    
    // webpack.dev.js
    const merge = require("webpack-merge");
    // 引入 Webpack 公共配置
    const commonConfig = require("./webpack.common.js");
    const devConfig = {
     ...
    }
    
    // 合并配置并导出
    module.exports = merge(commonConfig,devConfig);
    
    // package.json
    
    "scripts":{
     	"dev":"webpack-dev-server --config webpack.dev.js",
     	"build":"webpack --config webpack.prod.js"
    }
    

    四、总结

    1. 可用于生产环境

    • 优化 loader 处理范围
    • 优化 resolve 配置
    • IgnorePlugin
    • happyPack
    • tree Shaking
    • ParallelUglifyPlugin
    • 代码分割 code Splitting
    • 作⽤域提升(Scope Hoisting)
    • 使用 CDN 优化

    2. 不可用于生产环境

    • Hot Module Replacement (HMR:热模块替换)
    • 使⽤⼯具量化
    • DllPlugin、HardSourceWebpackPlugin

    起源地下载网 » Webpack 性能优化

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元