Webpack4.0 的相关优化配置

所谓的优化,也就是引入一些插件,使得代码的体积变小。

1. 删除没有意义的样式

在css文件中有一些没有用到的样式,希望在打包的过程中 删除掉它们。
安装两个插件 cnpm install purgecss-webpack-plugin glob --save-dev

glob库:主要功能是查找匹配的文件

// 查找src文件夹下的所有文件夹的所有文件。
// {nodir: true} 不包含文件夹
/*
	src-- index.js
	   -- index.css
	   -- index.html
	   -- a
	   	  -- index.js
*/
const glob = require('glob')
let paths = glob.sync("./src/**/*",{nodir: true})
/*
[ './src/a/index.js',
  './src/index.css',
  './src/index.html',
  './src/index.js' ]
*/
const glob = require('glob')
// 只要作用是删除没有意义的css。但不能在style标签中删除,只能配合 mini-css-extract-plugin 插件使用,先抽离css再去优化。
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin')

plugins: [
	new PurgecssWebpackPlugin({
		paths: glob.sync("./src/**/*",{nodir: true})
		// glob.sync(["./src/**/*","!x.js"],{nodir: true}) 忽略x.js文件
	})
]

2. 图片优化

安装loader cnpm install image-webpack-loader --save-dev
image-webpack-loader 要配置在 file-loader 之后

module: [
	{
		test: /\.(jpe?g | png | gif)/,
		use: [
			{
				loader: 'file-loader'
			},
			{
				loader: 'image-webpack-loader',
				// options 配置格式固定。
				options: {
					// 对关于一些图片清晰度的设置。
			        mozjpeg: {
			          progressive: true,
			          quality: 65
			        },
			        // 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
			        }
			    }
			}
		]
	}
]

3. CDN引入文件加载

这里在index.html中引入了jq的cdn版,可直接使用$,但为了防止因为直接使用 $ 而产生 来源的疑惑,我们需要手动import jquery, 但是的话,这样 webpack 会在 俩个文件中各打包一次jq。所以 我们还需要进行配置。

// index.html
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

// index.js
import $ from 'jquery'; // => 表明来源,但不打包
console.log($)

// webpack.config.js
module.exports = {
	externals: {
        'jquery': '$' // 不去打包代码里的jquery (脱离webpack打包,不被打入bundle中)
    }
}

如果引入的 cnd文件 有点多的话,一个一个的引太麻烦,可以使用 插件 add-asset-html-cdn-webpack-plugin。注意 它是 基于 html-webpack-plugin 的。
但是:我用的时候这个插件不能用,不知道为啥,可能是版本的原因,毕竟是1.0版本。
我们改用webpack-cdn-plugin 代替它,配置也很简单。

// webpack.config.js
let AddCdnPlugin = require('add-asset-html-cdn-webpack-plugin');


plugins: [
	new HtmlWebpackPlugin({
       template: './src/index.html',
       filename: 'index.html'
    }),
    // 添加cnd的插件
    /*
    new AddCdnPlugin(true,{
       'jquery': 'https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js'
    })
    */
    new WebpackCdnPlugin({
       modules: [
          {
              name: 'jquery',
              path: 'https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js'
          }
       ]
   })
]

4. Tree-Shaking

‘摇晃树’,作用是 去除掉 js 中多余的代码。并且只支持es6,webpack内置的。
es6 import导入语法为静态导入,require为动态导入,所谓动态导入,就是可以在一些判断语句中可以导入。 if(){ require('...') }

tree-shaking 只能在 生产环境下使用。

删除’副作用’代码:在package.json 中添加 sideEffects: false,只要引入的没有用到,就被删除。但是,如果只单纯写 false,那么引入的 import './index.css' 也会被 tree-shaking掉。

// webpack.config.js
optimization: {
  usedExports: true // 使用了哪个模块你说一下。
}

// calc.js

export const add = (a,b) => {
    return a + b + 'add'
}

export const re = (a,b) => {
    return a - b + 're'
}

// test.js
function test(){
    return '123'
}
console.log(test())

export default test

// index.js
import {add} from './ache';

// 在 引入的 test 文件 中 内部 代码自己执行,导入的 test 应该是没用的代码,不过webpack依然认为它是有用的。我们需要手动处理这样的代码。
import test from './test' // 副作用代码,可能开发的时候是无意义的。应该被删。

import './index.css' //  package.json 里 "sideEffects":["**/*.css",...],声明他不是副作用代码。

console.log(add())

5. Scope hoisting

作用域提升,减少作用域。它也是webpack内置的,不需要配。
因为每个模块导出的都是一个函数。

6. DLL打包

在打包的过程中,例如 react,react-dom 每一次webpack都会重新进行打包,我们希望,可以将 react,react-dom 这类固定文件提前打包好,当下一次打包的时候,直接去打包其他的代码即可,react,react-dom 可以直接拿出来用。

webpack默认打包之后的代码形式是这样的:

// module.exports = 'hello world'

(function () {
  return 'hello world'
})()
// 代码是一个自执行函数

// 当我们在 output 配置中 配置 library:xxx,则会把导出的结果赋值给 xxx。
// let xxx = (function(){...})()
// 除了 library 之外,还有 libraryTarget ,根据变量使用的环境来选择你需要的模式,(var,commonjs,commonjs2,...)

以 raect react-dom 为例 ,生成一个只有 react,react-dom 的文件。

// 新建一个webpack.xxx.js (这里以webpack.dll.js为例)
// webapck.dll.js
const path = require('path');

module.exports = {
    mode: 'development',
    entry: ['react','react-dom'],
    output: {
        library: 'react', // 打包后接受自执行函数的名字叫ache
        // libraryTarget: 'commonjs2',
        filename: 'react.dll.js',
        path: path.resolve(__dirname,'dll')
    }
}

// package.json
"ache": "webpack --config ./webpack.dll.js"

然后,配置DLLPlugin插件,生成manifest.json文件。我们叫这个文件为缓存列表,通过这个缓存列表可以找到对应需要的 模块( react, react-dom )

const DLLPlugin = request('webpack').DLLPlugin

const path = require('path');
const DLLPlugin = require('webpack').DllPlugin
module.exports = {
    mode: 'development',
    entry: ['react','react-dom'],
    output: {
        library: 'react', // 打包后接受自执行函数的名字叫ache
        // libraryTarget: 'commonjs2',
        filename: 'react.dll.js',
        path: path.resolve(__dirname,'dll')
    },
    plugins: [
        new DLLPlugin({
            name: 'react', // 暴露函数的名字。这里的name必须要与dll.js中函数名保持一致。
            path: path.resolve(__dirname,'dll/manifest.json') //  manifest.json 文件的绝对路径
        })
    ]
}
// 我本地使用了 import React 语法,需要先去 manifest.json查找,找到后会加载对应的库的名字,可能会引用某个模块,会去dll.js文件查找。

然后,引入DLL引用插件,让在打包的时候去找minifest.json文件。

// webpack.config.js
const DLLReferencePlugin= require('webpack').DLLReferencePlugin;

plugins:[
	new DLLReferencePlugin({
		// 我打包 react ,会先去manifest.json文件中查找,找到后再去找真实的文件(dll.js)。
		manifest: path.resolve(__dirname,'dll/manifest.json')
	}),
	...
]

通过上面以及配置差不多了,但是通过 manifest.json,会去找 name 对应 dll.js文件中的 变量,发现 dll.js文件包 没有被引入,找不到 react。
手动引太麻烦,这时还得需要一个插件:add-asset-html-webpack-plugin
cnpm install add-asset-html-webpack-plugin --save-dev
这个插件 会将 dll.js 文件拷贝到 dist 目录下,然会在自动引入 index.html 中。

// webpack.config.js
const AddAssetHtmlPlugin= require('add-asset-html-webpack-plugin')
plugins: [
	new AddAssetHtmlPlugin({
       filepath: path.resolve(__dirname,'./dll/react.dll.js') // 将该文件拷贝到dist目录下
    }),
    ...
]

一般来说,DLL插件是用在开发环境下的。

7.动态加载

用来实现动态分离文件
import 语法引入。
核心原理是 使用jsonp动态导入需要引入的文件。
import 可以实现代码分割。

// index.js
button.addEventListener('click',() => {
	// 动态导入,类比 路由懒加载 import语法
	import('./ache.js').then(data => {
		console.log(data.add(11,22))
	})
})

给动态引入的文件增加名字

output: {
	filename: 'xxx' // 同步打包的名字
	chunkFilename: '[name].main.js' // 异步打包的名字 name 默认是0,1,2...递增的。
}
import(/* webpackChunkName: "video" */'./video.js').then(data => { // 会把 "video" 传入到 上面 [name] 中。
	console.log(data)
})
// 打包后 video.main.js

8. 打包文件分析工具

8.1 多入口

// webpack.config.js
{	// entry 有三种写法:字符串,数组,对象
	entry: {
		'a': './a.js',
		'b': './b.js'
	}
}

plugins: [
	new HtmlWebpackPlugin({
		template: './src/index.html',
		filename: 'index.html',
		chunks: ['a'] //要引入打包后的 'a' 代码块。
	}),
	new HtmlWebpackPlugin({
		template: './src/index.html',
		filename: 'login.html',
		chunks: ['b'], // 如果是 ['b','a'],我们希望 login.html中是按照 这个数组的顺序引进的,但是webpack是按照entry的顺序引入的,所以 我们要配置 chunkSortMode。
		chunkSortMode: 'manual' // 手动按照我的顺序来执行
	})
]

8.2 分析依赖

按装 cnpm install webpack-bundle-analyzer --save-dev 可视化分析工具。
多用在生产环境下,并且可以帮助我们将第三方包进行分离。
会自动生成一个8888端口。

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

plugins: [
	env !== 'development' && new BundleAnalyzerPlugin()
].filter(Boolean)

9. 第三方模块的抽离

当遇到类似于 react react-dom 这样较大的库时,需要抽离打包。可以使用下面属性。
dllPlugin 不要和 splitchunks 共同使用。

// webpack.config.js
optimization: {
            splitChunks: {
            	// chunk: initial 只操作同步的 ;all所有的;async异步的。
                chunks: 'async', // 默认支持异步代码分割 import()
                minSize: 30000, // 文件超过30K 我就回抽离它
                maxSize: 0,
                minChunks: 1, // 最少模板引用一次才抽离
                maxAsyncRequests: 30, // 最多5个请求
                maxInitialRequests: 30, // 最多首屏加载30个请求
                automaticNameDelimiter: '~', // xxx~a~b
                enforceSizeThreshold: 50000,
                automaticNameMaxLength: 30,  // 最长名字大小
                cacheGroups: { // 缓存组, cacheGroups内配置优先级高于外部配置
                    // react: {
                    //     test: /[\\/]node_modules[\\/]\/react|react-dom/, // 要抽离的模块
                    //     priority: 1 // 优先级
                    // },
                    defaultVendors: {
                        test: /[\\/]node_modules[\\/]/,
                        priority: -10
                    },
                    default: { // common~a~b
                        minChunks: 2,
                        priority: -20,
                        reuseExistingChunk: true
                    }
                }
            }
        }

// a.js
import ('jquary') // 异步
import React from 'react' // 同步

10. 费时分析

测试当前打包的速度

const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin () //在外面new

// 将我们的打包代码包起来
module.exports = env => {
	return smv.wrap({
		mode: env,
		entry: ...
		output: ...
	})
}

猜你喜欢

转载自blog.csdn.net/qq_42387542/article/details/107883506