【 web高级 02webpack 】webpack04

webpack性能优化

  • 优化开发体验
  • 优化输出质量量

优化开发体验

  • 提升效率

  • 优化构建速度

  • 优化使⽤体验

优化输出质量

  • 优化要发布到线上的代码,减少⽤户能感知到的加载时间
  • 提升代码性能,性能好,执⾏就快

一、缩小文件查找范围

webpack 在启动后会从配置的 Entry 出发,解析出文件中的导入语句,再递归解析,在遇到导入语句时,webpack 会做一下操作

  • 寻找对应的导入文件
  • 使用配置中的 Loader 去处理文件,例如解析 js 文件用的 babel-loader

1.1-优化loader配置

loader是一个消耗性能的大户

include  推荐使用
exclude  
include:path.resolve(__dirname, "./src"),

构建时间对比

loader              1971 ms
loader + include    824ms

1.2-优化reslove.modules配置

webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,resolve 配置 webpack 如何寻找模块对应的文件。resolve.modules 的默认值是 [‘node_modules’], 指先去当前目录的./node_modules 目录下找对应的模块,如果没有找到,就去上一级目录…/node_modules, 层层往上找。一般我们安装的第三方目录都会放在项目的根目录 ./node_modules 目录下,可以指明第三方模块的绝对路径,减少查找路径

告诉 webpack 解析模块时应该搜索的目录。

const path = require('path');

module.exports = {
	//...
	resolve: {
		//查找第三方依赖
		modules: [path.resolve(__dirname, 'src'), 'node_modules'],
	},
};
不加                    824 ms
+ reslove.modules   849 ms

1.3-优化reslove.alias配置

创建 import 或 require 的别名,来确保模块引入变得更简单

resolve: {
	//查找第三方依赖
	modules: [path.resolve(__dirname, 'src'), 'node_modules'],
	//减少查找过程
	//起别名
	alias: {
		"@":path.resolve(__dirname, 'src'),
		"_lib":path.resolve(__dirname, './src/common/js'),
		"_css":path.resolve(__dirname, './src/common/css'),
		"_com":path.resolve(__dirname, './src/components'),
		"_img":path.resolve(__dirname, './src/assets'),            
	}
},
不加           2016 ms
+alias        2004 ms

1.4-优化resolve.extensions配置

resolve.extensions在导入语句没带文件后缀时,webpack会自动带上后缀后,去尝试查找文件是否存在。

默认值:

extensions:['.wasm', '.mjs', '.js', '.json']

建议:

  • 后缀尝试列表尽量小,不要滥用extensions
  • 导入语句尽量的带上后缀

二、使用CDN

2.1-使用externals优化cdn静态资源

前提:
公司有cdn
静态资源有部署到cdn 有链接了了
我想使用cdn!!!!!!!!
我的bundle文件里,就不用打包进去这个依赖了,体积会小

CDN 引入 lodash,而不是把它打包:

安装lodash

npm install lodash -S
index.js

const _ = require('lodash'); 
console.log(_.head([1, 2, 3]));

CDN引入

index.html
<script src="https://www.jsdelivr.com/package/npm/lodash"></script>


index.js
import _ from 'lodash';
console.log(_.head([1, 2, 3])); 
//但是我试了lodash官网的其他方法会报错,head方法是webpack官网的案例


webpack.config.js
externals: {              
	'lodash': '_'
},

总得来说,
externals配置就是为了使import _ from 'lodash'这句代码,
在本身不引入lodash的情况下,能够在各个环境都能解释执行。

require         2098 ms
cdn             1760 ms 

2.2-使用静态资源路径publicPath(CDN)

CDN通过将资源部署到世界各地,使得用户可以就近访问资源,加快访问速度。要接入CDN,需要把网页的静态资源上传到CDN服务上,在访问这些资源时,使用CDN服务提供的URL。

webpack.config.js
output: {
	publicPath: 'https://cdn.example.com/assets/',
},
  • 咱们公司得有CDN服务器地址
  • 确保静态资源文件的上传与否
    在这里插入图片描述

三、css文件的处理

3.1-使用less或者sass当做css技术栈

 npm install less less-loader --save-dev
{  
	test: /\.less$/, 
	use: ["style-loader", "css-loader", "less-loader"]
}

3.2-使用postcss为样式自动补齐浏览器前缀

npm i  postcss-loader autoprefixer -D
## 新建 postcss.config.js
const autoprefixer = require("autoprefixer");


module.exports = {
    plugins: [
        // postcss 使用 autoprefixer 添加前缀的标准

        /**
         * last 2 versions  兼容最近的两个版本
         * ios 14 13
         * 
         * 
         * >1%
         * 全球浏览器的市场份额  大于1%的浏览器
         */
    
        autoprefixer({
            overrideBrowserslist: ["last 2 versions", ">1%"],
        })

    ],
}

------

index.less
html{
    body{
        background: blue;
    }
}

------
webpack.config.js
{
	test: /\.less$/i,
	include: path.resolve(__dirname, "./src"),
	use: [
	
		"style-loader",
			{
				loader: "css-loader",
				options: {
				modules: true,//启用/禁用CSS模块及其配置,默认不开启
			},
		},
		{
			loader: "postcss-loader",
		},
		"less-loader"
	],
},

如果不做抽取设置,我们的css是直接打包进js里面的,我们希望能单独生成css文件。
因为单独成css,css可以和js并行下载,提高页面加载效率。

3.3-借助MiniCSSExtractPlugin完成css抽离

npm install mini-css-extract-plugin -D
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

{
    test: /\.less$/i,
    include: path.resolve(__dirname, "./src"),
    use: [

        // "style-loader",
        MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                modules: true,
            },

        },
        {
            loader: "postcss-loader",
        },
        "less-loader"
   ],
},


plugins: [
	new MiniCssExtractPlugin({
		// filename: "[name][chunkhash:8].css" 
		filename: "css/[name]-[contenthash:8].css"
	}),
],

3.4-压缩css

webpack4 : optimize-css-assets-webpack-plugin

  • 借助optimize-css-assets-webpack-plugin
  • 借助cssnano
npm install cssnano -D 
npm i optimize-css-assets-webpack-plugin -D

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

new OptimizeCSSAssetsPlugin({  
	cssProcessor: require("cssnano"), //引⼊入cssnano配置压缩选项  
	cssProcessorOptions: {    
		discardComments: { removeAll: true } 
	}
})

**webpack5 : css-minimizer-webpack-plugin **

  • 借助css-minimizer-webpack-plugin
npm install css-minimizer-webpack-plugin --save-dev

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new CssMinimizerPlugin(),
    ],
  },
};

在这里插入图片描述

3.5-压缩HTML

webpack4 : html-webpack-plugin

  • 借助html-webpack-plugin
new htmlWebpackPlugin({
	title: "My App",
	filename: "index.html",
	template: "./src/index.html",
	minify: {
		collapseWhitespace: true, //删除空白符与换行符
		removeComments: true, //移除html中的注释
		minifyCSS: true //压缩内联css
	}
}),

webpack5 : html-minimizer-webpack-plugin

npm install html-minimizer-webpack-plugin --save-dev

const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");


optimization: {
	minimize: true,
	minimizer: [
		new HtmlMinimizerPlugin(),
	],
},

在这里插入图片描述


四、development vs production 模式区分打包

官网描述

npm install --save-dev webpack-merge

在这里插入图片描述

TerserWebpackPlugin
压缩 JavaScript

npm install terser-webpack-plugin --save-dev
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
};

webpack.common.js

const path = require("path");


module.exports = {
    entry: "./src/index.js",
    resolve: {
        modules: [path.resolve(__dirname, 'src'), 'node_modules'],
        alias: {
            "@": path.resolve(__dirname, 'src'),
            "_lib": path.resolve(__dirname, './src/common/js'),
            "_css": path.resolve(__dirname, './src/common/css'),
            "_com": path.resolve(__dirname, './src/components'),
            "_img": path.resolve(__dirname, './src/assets'),
        },
        extensions: ['.js', '.json',],
    },
}



webpack.dev.js

//开发配置

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");


const devConfig = {
    mode: "development",
    output: {
        clean: true,
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].js",
        clean:true
    },
    devtool:"inline-cheap-source-map",
    module: {
        rules: [
            {
                test: /\.less$/i,
                include: path.resolve(__dirname, "./src/common/css/"),
                use: [
                    "style-loader",
                    {
                        loader: "css-loader",
                        options: {
                            modules: true,
                        },

                    },
                    {
                        loader: "postcss-loader",
                    },
                    "less-loader"],
            },
            {
                test: /\.(png|jpe?g|gif)$/i,
                include: path.resolve(__dirname, "./src"),
                loader: 'url-loader', 
                options: {
                    name: "[name]_[hash:6].[ext]",
                    outputPath: "images/",
                    limit: 210 * 1024, 

                },

            },
            {
                test: /\.js$/,
                include: path.resolve(__dirname, "./src"),
                use: {
                    loader: 'babel-loader',
                }
            }
        ]
    },
    devServer: {
        contentBase: "./dist",
        compress: true,
        port: 8080,
        proxy: {
            "/api": {
                target: "http://localhost:9092"
            }
        },
        before: function (app, server, compiler) {
            app.get('/api/mock.json', function (req, res) {
                res.json({
                    hello: 'express'
                });
            });
        },

        hot: true,
        // hotOnly:true,

    },
    plugins: [
        new htmlWebpackPlugin({
            title: "My App",
            filename: "index.html",
            template: "./src/index.html",
        }),
    ],
};


module.exports = merge(common, devConfig);

webpack.prod.js

//生产配置

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

const path = require("path");

const htmlWebpackPlugin = require("html-webpack-plugin");

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");


const prodConfig = {
    mode: "production",
    output: {
        clean: true,
        path: path.resolve(__dirname, "./build"),
        filename: "[name].js"
    },
    module: {
        rules: [

            {
                test: /\.less$/i,
                include: path.resolve(__dirname, "./src"),
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: "css-loader",
                        options: {
                            modules: true,
                        },

                    },
                    {
                        loader: "postcss-loader",
                    },
                    "less-loader"],
            },
            {
                test: /\.(png|jpe?g|gif)$/i,
                include: path.resolve(__dirname, "./src"),
                loader: 'url-loader', 
                options: {
                    name: "[name]_[hash:6].[ext]",
                    outputPath: "images/",
                    limit: 210 * 1024, 

                },

            },
            {
                test: /\.js$/,
                include: path.resolve(__dirname, "./src"),
                use: {
                    loader: 'babel-loader',
                }
            }
        ]
    },
    optimization: {
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin(),
            new HtmlMinimizerPlugin(),
            new TerserPlugin()
        ],
    },
    plugins: [
        new htmlWebpackPlugin({
            title: "My App",
            filename: "index.html",
            template: "./src/index.html",
        }),
        new MiniCssExtractPlugin({
            filename: "css/[name]-[contenthash:8].css"
        }),
    ],

}

module.exports = merge(common, prodConfig);

webpack.test.js

const commonConfig = require("./webpack.common.js");
const devConfig = require("./webpack.dev.js");
const prodConfig = require("./webpack.prod.js");
const { merge } = require('webpack-merge');


module.exports = (env) =>{
    console.log("test",env)
    //如果外部传进 env.production ,是生产
    //如果外部没有 env.xxx 是开发

    if(env && env.production){
        return merge(commonConfig,prodConfig);
    }else{
        return merge(commonConfig,devConfig);
    }
}

package.json

"scripts": {
  "server": "webpack-dev-server",
  "dev": "webpack-dev-server --open --config webpack.dev.js ",
  "build": "webpack --config webpack.prod.js",
  "test:build":"webpack  --env.production   --config   webpack.test.js",
  "test:dev":"webpack-dev-server --config   webpack.test.js"
},

webpack.config.js

const path = require("path");

const htmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");



module.exports = {
    entry: "./src/index.js",
    mode: "development",
    optimization: {
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin(),
            new HtmlMinimizerPlugin(),
        ],
    },
    output: {
        clean: true,
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].js",
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                include: path.resolve(__dirname, "./src"),
                use: ['style-loader', 'css-loader'],
            },


            /**
             * 1.在webpack中需要处理css必须先安装如下两个loader  
             * npm install --save-dev css-loader style-loader
             * 
             * 2. 要处理less和添加浏览器前缀需要下载如下插件和loader
             * npm install less less-loader --save-dev
             * 
             * postcss-loader autoprefixer postcss 处理浏览器兼容,添加浏览器前缀
             * npm install --save-dev  postcss-loader autoprefixer postcss
             * 
             * 
             * 3.如下配置
             * postcss-loader  放在 css-loader  之前
             * 
             * 
             * 4.新建postcss.config.js
             * 
             */
            {
                test: /\.less$/i,
                include: path.resolve(__dirname, "./src"),
                use: [

                    // "style-loader",
                    MiniCssExtractPlugin.loader,
                    {
                        loader: "css-loader",
                        options: {
                            modules: true,//启用/禁用CSS模块及其配置,默认不开启
                        },

                    },
                    {
                        loader: "postcss-loader",
                    },
                    "less-loader"],
            },
            {
                test: /\.(png|jpe?g|gif)$/i,
                include: path.resolve(__dirname, "./src"),
                loader: 'url-loader', // file-loader  替换为 url-loader
                options: {
                    name: "[name]_[hash:6].[ext]",
                    outputPath: "images/",
                    //推荐使用 url-loader   因为 url-loader 支持 limit
                    //推荐小体积的图片资源转成 base64   
                    //图标库 iconfont  适合
                    //logo  不适合  因为转成base64,字符串长度太长,增加js的体积
                    //低于指定的限制时,可以返回一个 DataURL。
                    limit: 210 * 1024, //单位是字节 1024 = 1kb

                },

            },
            {
                test: /\.js$/,
                //exclude 排除
                //  exclude: /node_modules/,
                include: path.resolve(__dirname, "./src"),
                use: {
                    /**
                     * npm i babel-loader  -D
                     * 1.babel-loader 是 webpack 与 babel的通信桥梁,不会把es6转成es5的工作,这部分工作需要用到  @babel/preset-env
                     * 安装   npm i @babel/core @babel/preset-env  -D
                     * 
                     * 
                     * 执行顺序,实现步骤
                     * 1.检测到 js 模块 , 使用babel使  webpack 和 babel  babel/core 有一个通信
                     * 2.使用 preset-env 进行转换
                     */
                    loader: 'babel-loader',
                    // options: {
                    //语法转换插件 preset-env
                    // presets: [
                    // ['@babel/preset-env',
                    //     {
                    //         targets: {
                    //             edge: "17",
                    //             firefox: "60",
                    //             chrome: "67",
                    //             safari: "11.1"
                    //         },
                    //         corejs: 2,//新/版本需要指定核⼼心库版本                
                    //         useBuiltIns: "usage"//按需注⼊入
                    //     }
                    // ]
                    //     ],
                    // }
                }
            }
        ]
    },
    
    resolve: {
        //查找第三方依赖
        modules: [path.resolve(__dirname, 'src'), 'node_modules'],
        //减少查找过程
        //起别名
        // alias: {
        //     "@": path.resolve(__dirname, 'src'),
        //     "_lib": path.resolve(__dirname, './src/common/js'),
        //     "_css": path.resolve(__dirname, './src/common/css'),
        //     "_com": path.resolve(__dirname, './src/components'),
        //     "_img": path.resolve(__dirname, './src/assets'),
        // },

        // extensions: ['.js', '.json', '.jsx'],
    },
    externals: {
        //jquery通过script引⼊入之后,全局中即有了了 jQuery 变量量        
        // lodash : { // 这种其实和上面这种是一个道理,只不过分的更细一些
        //     commonjs: "lodash",
        //     amd: "lodash",
        //     root: "_" // indicates global variable
        //   },
        lodash: '_'
    },
    devtool:"inline-cheap-source-map",
    devServer: {
        /**
         * contentBase:告诉服务器内容的来源。仅在需要提供静态文件时才进行配置
         * 
         * 当开启webpack-dev-server的时候,服务器的目录指向 dist
         * 
         * 可以是相对路径
         * contentBase:"./dist"
         * 
         * contentBase: path.join(__dirname, 'public'),
         * 
         * 
         * contentBase: [
         *      path.join(__dirname, 'public'),
         *      path.join(__dirname, 'assets'),
         * ]
         */
        // contentBase: path.join(__dirname, 'dist'),
        contentBase: "./dist",
        /**
         * open :告诉dev-server在服务器启动后打开true浏览器。
         * 
         * open: true,  打开默认浏览器
         * 
         * open: 'Google Chrome',
         * 
         */
        // open:true,
        // open:'Google Chrome',
        compress: true,
        //port 指定端口号
        port: 8080,
        //代理
        proxy: {
            "/api": {
                target: "http://localhost:9092"
            }
        },
        //mock server
        before: function (app, server, compiler) {
            app.get('/api/mock.json', function (req, res) {
                res.json({
                    hello: 'express'
                });
            });
        },

        hot: true,
        //即便 HMR 没有生效,浏览器也不要自动刷新。
        // hotOnly:true,

    },
    plugins: [
        new htmlWebpackPlugin({
            title: "My App",
            filename: "index.html",
            template: "./src/index.html",
            minify: {
                collapseWhitespace: true, //删除空白符与换行符
                removeComments: true, //移除html中的注释
                minifyCSS: true
            }
        }),
        new MiniCssExtractPlugin({
            // filename: "[name][chunkhash:8].css" 
            filename: "css/[name]-[contenthash:8].css"
        }),
    ],

}



五、tree Shaking

webpack2.x开始支持 tree shaking 概念,顾名思义,“摇树”,清楚无用css,js(Dead Code)

Dead Code一般具有以下几个特征

  • 代码不会被执行,不可到达
  • 代码执行的结果不会被用到
  • 代码只会影响死变量(只写不读)
  • js tree shaking 只支持ES module的引入方式

Css tree shaking

npm i glob-all purify-css purifycss-webpack --save-dev
 
const PurifyCSS = require('purifycss-webpack') 
const glob = require('glob-all') 

plugins:[    
// 清除⽆无⽤用 css    
new PurifyCSS({      
	paths: glob.sync([        
	// 要做 CSS Tree Shaking 的路路径⽂文件        
		path.resolve(__dirname, './src/*.html'), // 请注意,我们同样需要对 html ⽂文 件进⾏行行 tree shaking        
		path.resolve(__dirname, './src/*.js')      
	])    
})

报错
在这里插入图片描述
在这里插入图片描述

解决方案:
purifycss-webpack
purifycss-webpack被弃用
改用purgecss-webpack-plugin

在这里插入图片描述

npm i purgecss-webpack-plugin -D

**npm官网案例**

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

+ const PurgeCSSPlugin = require('purgecss-webpack-plugin')

+ const PATHS = {
  + src: path.join(__dirname, 'src')
+ }

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
		{
			test: /\.less$/i,
			include: path.resolve(__dirname, "./src"),
			use: [
				// "style-loader",
				MiniCssExtractPlugin.loader,
				{
					loader: "css-loader",
					options: {
						modules: true,
					},
				},
				{
					loader: "postcss-loader",
				},
				"less-loader"],
		},
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name]-[contenthash:8].css"
    }),
    + new PurgeCSSPlugin({
      + paths: glob.sync(`${PATHS.src}/**/*`,  { nodir: true }),
    + }),
  ]
}


js tree shaking – 按需加载
只支持import方式引入,不支持commonjs的方式引入
案例:

math.js
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}


index.js
import {cube}  from "./common/js/math"
var a =  cube(2);



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

在这里插入图片描述

在这里插入图片描述

只要mode是production就会生效,development的tree shaking是不生效的,因为webpack为了方便你的调试

可以查看打包厚的代码注释以辨别是否生效

生产模式不需要配置,默认开启

在这里插入图片描述
package.json

 "sideEffects":false,  正常对所有模块进行tree shaking  , 仅生产模式有效,需要配合 usedExports

在这里插入图片描述

怎么解决?

package.json

"sideEffects":["*.less"],    不使用摇树

在这里插入图片描述

六、代码分割 code Splitting

代码分离指将代码分成不同的包/块,然后可以按需加载,而不是加载包含所有内容的单个包。

单页面应用spa
打包完后,所有页面只生成了一个bundle.js
代码体积变大

index.js

import _ from 'lodash';
console.log(_.join(['Another', 'module', 'loaded!'], ' '));

在这里插入图片描述
webpack.config.js

optimization: {
	//帮我们自动做代码分割
	splitChunks: {
		chunks: 'all',//默认支持异步的,我们使用all
	},
},

在这里插入图片描述
将lodash分割出来
在这里插入图片描述


webpack.config.js

optimization: {
	splitChunks: {
		chunks: 'all',
		cacheGroups:{ //缓存组
			lodash:{
				test:/lodash/,
				name:"lodash"
			},
			react:{
				test:/react|react-dom/,
				name:"react"
			}
		}
	},
},





index.js
import _ from 'lodash';

import React, { Component } from "react"; 
import ReactDom from "react-dom";

在这里插入图片描述

Webpack之prefetch和preload
所有新技术的出现都是为了解决现有的问题或者是就技术没法解决的问题。prefetch和preload也是如此

现在有两个问题:

  • 首先,代码的懒加载固然能使首页加载的速度变快,可是相应的,待到用户与页面的交互再加载相关交互代码会使得用户首次交互的体验不佳(长时间没有对操作响应)
  • 其次,在splitChunks的默认配置中,chunks字段默认是async的。也就是说webpack官方是希望我们使用异步的方法来进行模块的加载的

这就导致了一个新的问题,异步加载模块时webpack官方所希望的开发者实现的,而且异步加载也能提升首页加载速度。但是又会导致异步加载的那部分代码逻辑迟迟不能执行,可能导致用户的交互长时间没有响应

这个时候就需要prefetchpreload

所谓预取,就是提前获取将来可能会用到的资源。prefetch chunk会在父chunk加载完成之后进行加载。

魔法注释

document.addEventListener('click', async (e)=>{
    const { default: func } = await import(/* webpackPrefetch: true */'./click');
    func();
});

写完之后,重新打包刷新页面,可以看到在index.js加载完成之后,浏览器又会去自动加载click.js

preload和prefetch在用法上相差不大,效果上的差别如下(引自官方文档):

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
  • 浏览器支持程度不同。

七、作用域提升 Scope Hoisting

作用域提升(Scope Hoisting)是指webpack通过ES6语法的静态分析,分析出模块之间的依赖关系,尽可能地把模块放在同一个函数中,下面通过代码示例来理解。

hello.js
export default  "hello webpack";

index.js
import str from './common/js/hello'
console.log(str)

打包后,hello.js的内容和index.js会分开
通过配置optimization.concatenateModules=true 开启 Scope Hoisting

webpack.config.js
optimization: {
	concatenateModules: true,
}

告知 webpack 去寻找模块图形中的片段,哪些是可以安全地被合并到单一模块中
在这里插入图片描述

我们发现hello.js内容和index.js的内容合并在一起了。所以通过Scope Hoisting的功能可以让webpack打包出来的代码文件更小、运行的更快。

八、使用工具量化

  • speed-measure-webpack-plugin:可以测量各个插件和loader所花费的时间
npm install --save-dev speed-measure-webpack-plugin


const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

const smp = new SpeedMeasurePlugin();

const config = {
	//...webpack配置
}

module.exports = smp.wrap(config);

在这里插入图片描述

  • webpack-bundle-analyzer:分析webpack打包后的模块依赖关系
npm install --save-dev webpack-bundle-analyzer


const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  //......
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

在这里插入图片描述

启动webpack构建,会默认打开一个窗口

九、DllPlugin插件打包第三方类库 优化构建性能

Dll动态链接库 其实就是做缓存,只会提升webpack打包的速度,并不能减少最后生成的代码体积。

npx webpack
对所有的依赖都要构建一次?
react react-dom 有必要每次都重新构建吗?
告诉webpack, react react-dom 不用每次都进行构建 react react-dom
借助DllReferencePlugin告诉webpack有哪些依赖有对应的dll文件,借助dllplugin生成dll动态库文件

每次执行构建,当检查依赖模块,有相应的动态库文件,就不进行对其构建,从而减少构建时间。

1.知道如何使用dllplugin配置生成动态链接库文件
2.webpack如何使用dll文件?dll文件和manifest是干嘛的?webpack怎么知道哪些模块不需要再次构建?

webpack如何使用dll文件?
webpack并不能直接使用dll文件,需要借助插件DllReferencePlugin找到依赖的映射文件,才能判断出改模块有没有存在对应的dllwe文件

manifest是干嘛用的?
本质上就是一个第三方库的映射
webpack在构建的时候,使用DllReferencePlugin读取manifest.json文件,看看是否有这个第三方依赖
库名称 模块的名称 模块的id 模块的位置 …

.dll文件被称为动态链接库,在windows系统会经常看到
百度百科地址

项目中引用了很多第三方库,这些库在很长时间内,基本不会更新,打包的时候分开打包来提升打包速度,而DllPlugin动态链接库插件,其原理就是把网页依赖的基础模块抽离出来打包到dll文件中,当需要导入的模块存在于某个dll中时,这个模块不再被打包,而去dll中获取。

  • 动态链接库只需要被编译一次,项目中用到的第三方模块,很稳定,例如react,react-dom,只要没有升级的需求

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

  • DllPlugin:用于打包出一个个单独的动态链接库文件
  • DllReferencePlugin:用于在主要的配置文件中引入DllPlugin插件打包好的动态链接库文件

将splitChunks代码注释
新建webpack.config.dll.js文件,打包基础模块
我们在index.js中使用了第三方库 reactreact-dom,加下来我们对这两个库进行打包。

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

index.js
import React, { Component } from "react"; 
import ReactDom from "react-dom";

在package.json中添加

"dev:dll": "webpack --config ./webpack.config.dll.js"

运行

npm  run  dev:dll

在这里插入图片描述
你会发现多了一个dll文件夹,里面有dll.js文件,这样我们就把我们的react单独打包了

在这里插入图片描述

  • dll文件包含了大量模块的diamante,这些模块被放在一个数组里。用数组的索引为ID,通过变量将自己暴露在全局中,就可以在window.xxx访问到其中的模块
  • manifest.json描述了与其对应的dll.js包含了哪些模块,以及ID和路径

启动构建,拿到配置,
从入口文件index.js,有两个依赖react,react-dom,
会去找react-mainfest.json中去找有没有对应的模块
如果有,就走缓存文件react.dll.js。
不会将react,react-dom打包进bundle文件

接下来怎么使用呢?
react.dll.js用在html模板里面。
要给web项目构建接入动态链接库,需要完成以下事情:

  • 将网页依赖的基础模块抽离,打包到单独的动态链接库,一个动态链接库可以包含多个模块。
  • 当需要导入的模块存在于某个动态链接库中,不要再次打包,直接使用构建号的动态链接库即可。
webpack.config.js
const { DllReferencePlugin  } = require("webpack");

new DllReferencePlugin({      
	manifest: path.resolve(__dirname,"./dll/react-manifest.json")    
}),
  • 页面依赖的所有动态链接库都需要被加载
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= htmlWebpackPlugin.options.title %></title>
    + <script src="../dll/react.dll.js"></script>
    + <script src="../dll/react-manifest.json"></script>
</head>
<body>
    <div id="app">  aaa  </div>
</body>
</html>

手动添加使用,体验不好,这里推荐还用add-asset-html-webpack-plugin插件帮助我们做这个事情。
他会帮我们将打包后的dll.js文件注入到我们生成的index.html中。
在webpack.common.js中配置

 npm i add-asset-html-webpack-plugin -D

webpack.common.js
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
new AddAssetHtmlPlugin({ 
	filepath: path.resolve(__dirname, '../dll/react.dll.js') // 对应的 dll ⽂文件路路径  
}),

运行

npm  run  dev

以上全部结束。
这个理解起来不费劲,操作起来很费劲。
在webpack5中已经不使用它了,而是用hard-source-webpack-plugin。一样的优化效果,但是使用却极其简单。

  • 提供中间缓存的作用
  • 首次构建没有太大的变化
  • 第二次构建时间就会有较大的节省
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
new HardSourceWebpackPlugin()

为模块提供中间缓存步骤。
为了查看结果,需要使用此插件运行webpack两次:
第一次构建将花费正常的时间。
第二次构建将显着加快(大概提升90%的构建速度)。

在这里插入图片描述
代码使用的webpack5,报错未解决Cannot read property 'tap' of undefined

dll
把稳定的依赖库,做成动态链接库,减少构建时间
happypack
借助多线程的优势来优化构建

十、使用happypack并发执行任务

主要用在

  • 构建时间较久
  • 项目复杂程度较高

运行在Node之上的webpack是单线程模型的,也就是说webpack需要一个一个地处理任务,不能同时处理多个任务,Happy Pack 就能让webpack做到这一点,它将任务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程。从而发挥多核CPU电脑的威力。

npm install --save-dev happypack



webpack.config.js
module: {
    rules: [
        {
            test: /\.css$/,
            include: path.resolve(__dirname, "./src"),
            // use: ['style-loader', 'css-loader'],
            use: "happypack/loader?id=css"
        },
        {
            test: /\.less$/i,
            include: path.resolve(__dirname, "./src"),
            // use: [

            //     "style-loader",
            //     // MiniCssExtractPlugin.loader,
            //     {
            //         loader: "css-loader",
            //         options: {
            //             modules: true,
            //         },

            //     },
            //     {
            //         loader: "postcss-loader",
            //     },
            //     "less-loader"],
            use: "happypack/loader?id=less"
        },
        {
            test: /\.(png|jpe?g|gif)$/i,
            include: path.resolve(__dirname, "./src"),
            // loader: 'url-loader',
            use: "happypack/loader?id=pic"
            // options: {
            //     name: "[name]_[hash:6].[ext]",
            //     outputPath: "images/",

            //     limit: 210 * 1024,

            // },

        },
        {
            test: /\.js$/,
            include: path.resolve(__dirname, "./src"),
            use: 'happypack/loader?id=js'

                // loader: 'babel-loader',
                // ⼀一个loader对应⼀一个id
                 

            
        }
    ]
},

//----------------
//几个id就是几个happyPack
new HappyPack({
     // ⽤用唯⼀一的标识符id,来代表当前的HappyPack是⽤用来处理理⼀一类特定的⽂文件
    id: 'css',
    threads: 4,
    loaders: ['style-loader', 'css-loader']
}),

new HappyPack({
    id: 'less',
    threads: 2,
    // 如何处理理.js⽂文件,⽤用法和Loader配置中⼀一样
    loaders: [
        "style-loader",
        {
            loader: "css-loader",
            options: {
                modules: true,
            },

        },
        {
            loader: "postcss-loader",
        },
        "less-loader"
    ],
}),

new HappyPack({
    id: 'pic',
    threads: 4,
    loaders: ['url-loader']
}),

new HappyPack({
    id: 'js',
    threads: 4,
    loaders:['babel-loader'],
    //threadPool共享进程功能慎用:
    //项目较少的时候,开启happypack 和多线程都是需要时间的,有时候你会发现构建的时候反而增加了
    // threadPool: happyThreadPool 
    
}),

在这里插入图片描述

happyPack和mini-css-extract-plugin一起使用搭配不好,会报错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值