Webpack性能优化

源码地址:https://github.com/h2huanghui/WEBPACK-OPTIMIZATION

1. 基本配置见webpack.config.js babel如下

{
    test: /\.js$/,
    use: 'babel-loader',
},

=>

{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: { //.babelrc
            presets: [
                "@babel/preset-env",
                "@babel/preset-react"
            ]
        }
    }
}

2. CSS优化,尽量去删除无用的样式(CSS样式名尽量不要去html标签相同)

npm i purgecss-webpack-plugin glob -D

const glob = require('glob') //主要功能是查找匹配文件
let paths = glob.sync('./src/**/*')//任意文件夹的任意文件
console.log(paths)
结果:
[ './src/index.css',
  './src/index.js',
  './src/main.js',
  './src/template.html' ]

注意:

  1. 不能在style中删除 只能配合MiniCssExtractPlugin使用

  2. 动态生成的样式名,可以单独放在一个文件里面,不要去匹配就可以了,语法glob参考文档

3.图片压缩插件(降低分辨率和清晰度)

npm install image-webpack-loader -D

在file-loader之前使用压缩图片插件 (对源文件处理不太合理)

npm run build之后,可以看到图片大小被压缩了

4.CDN加载文件 (将jquery不打包)

Bootcdn

  1. 在html中插入一个script标签

问题:存在一个问题。js中引入jquery,还是会打包。所以加一个外部变量,这样就不会被打包了。

 externals: {
            'jquery':'$' //不去打包代码中的jquery
        },
  1. 如果是多个文件,需要多次引入。可以通过js来动态插入
npm i add-asset-html-cdn-webpack-plugin -D

new AddAssetHtmlCdnPlugin(true, {
    'jquery':'https://cdn.bootcss.com/jquery/3. jquery.min.js'
}),

5.Tree-shaking

随着项目变大时,摇晃掉无用的代码(只支持es6语法,而且默认只在生产环境使用!!!)

calc.js

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

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


main.js

import { minus } from './calc'
console.log(minus(1,1))

只使用到minus,没有使用到sum 。希望开发环境模拟线上打包时,能有提示

optimization: {
    usedExports: true //把用到的模块和我说一下
},

会发现bundle.js中多了注释如下:

/*! exports provided: sum, minus */(本来就有)
/*! exports used: minus */

5.1会存在一个问题。

main.js中引入了test.js,但是main.js引入了但是并未使用。test.js代码如下:

function test() {
    return 'hello'
}
console.log(test()) 
export default test

test.js内部自己执行了代码。(被称为副作用,有可能是外面文件引错了,但代码本身还是执行了)

webpack不认为这是无用的代码,还是执行到代码。所以需要我们配置来手动删除

package.json中

"sideEffects":false, //默认是true(使用副作用),设置为false(不要副作用)

5.2这种方式存在一个问题,即在main.js中引入css文件(css引入但也不会被去使用,就会也把这行代码删除失效)

  1. 通过require(只针对es6语法,但有些格格不入)
  2. package.json配置css文件不是副作用
"sideEffects": ["**/*.css"],

6.Scope Hoisting(wepack内置) 作用域提升(只在生产环境使用)

每个模块都是函数,会导致内存过大。(在浏览器中跑的时候,都会产生一个作用域;一声明也会有作用域)

7.DllPlugin (动态连接库,可以用作生产环境,可能有100多个,会有更好地方法)

dll功能在开发之前 就先抽离好 打好包 以后就不用打包了

场景:每次编译都会重新构建react 和 react-dom

react react-dom先打包好放好。

但是问题是,代码中还是引入的node modules下的啊...

建立缓存列表 mainfest.json(先去找这个里面有没有)

react:打包后的文件

react-dom:打包后的文件

如何打包第三方库!!!

打包单个文件webpack.dll.js

const path = require('path')
module.exports = {
    mode: 'development',
    entry: './src/calc.js', //sum minus
    output: {
        library: 'calc', //打包后接收自执行函数的名字叫calc
        libraryTarget:'commonjs2', //默认用var模式,也可以用commonjs模式(结果是exports["calc"]),也可以是commonjs(module.exports) umd this 
        filename: 'calc.js',
        path: path.resolve(__dirname,'dll')
    }
}

package.json

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

打包多个文件,entry改为数组

const path = require('path')
module.exports = {
    mode: 'development',
    entry: ['react','react-dom'], 
    output: {
        library: 'react', //打包后接收自执行函数的名字叫calc
        // libraryTarget:'commonjs2', //默认用var模式,也可以用commonjs模式(结果是exports["calc"]),也可以是commonjs(module.exports) umd this 
        filename: 'react.dll.js',
        path: path.resolve(__dirname,'dll')
    }
}

需要产生一个缓存列表,这个列表需要指向真实的文件

   plugins: [
        new DllPlugin({
            name: 'react', //缓存列表指向真实的文件
            path: path.resolve(__dirname,'dll/mainfest.json')
        })
    ]

本地使用了import React语法 ,需要去mainfest.json查找,找到后会加载对应的库的名字(也就是react.dll.js文件),可能会引用某个模块(列表中的某个模块),会去dll.js文件中查找(这里面存放的就是真实的文件)

告诉webpack去mainfest.json里面去查找

const DllReferencePlugin = require('webpack').DllReferencePlugin;
 new DllReferencePlugin({
    manifest: path.resolve(__dirname,'dll/mainfest.json')
})

没有放在dist文件夹的原因是,build的时候配置了 cleanWebpackPlugin。每次一大包删掉就没有意义。打包好就放在那里

问题:dll下的react.dll.js并未在页面中被引用

npm run dev报错

external_"react":1 Uncaught ReferenceError: react is not defined
  1. 可以在html中插入script标签(比较麻烦)
 <script src="../dll/react.dll.js"></script>
  1. 插件(和cdn类似)
    npm i add-asset-html-webpack-plugin -D
//把这个文件拷贝到dist文件夹下
new AddAssetHtmlPlugin({ filepath: require.resolve('dll/react.dll.js') }),

8.动态加载(原理就是jsonp)

比如页面一个按钮,只有点击了才调用。但如果在页面初始化的时候,就去打包,性能会比较差。我们希望的是点击按钮的时候,才会动态加载这个文件

import { sum } from './calc'
let button = document.createElement('button')

button.addEventListener('click', () => {
    console.log(sum(1, 2))
})
button.innerHTML = 'Click Me'
document.body.appendChild(button)

=>

let button = document.createElement('button')

button.addEventListener('click', () => {
    //草案语法 
    //动态加载 类似于路由懒加载 import()语法-结果是一个promise
    //webpack遇到这种语法,会使用jsonp动态加载这个calc文件
    import('./calc').then(data => {
        console.log(data.sum(1,7))
    })
})
button.innerHTML = 'Click Me'
document.body.appendChild(button)

点击按钮,可以看到浏览器请求0.bundle.js(代码分割被分割了)

0可以修改(chunkFilename)

output: { //出口
    filename: 'bundle.js', //同步打包的名字
    chunkFilename: '[name].min.js', //异步打包的名字,name默认是从0开始,1,2, ...也可以修改
    path: path.resolve(__dirname, "dist")
},
import(/* webpackChunkName:'video' */'./calc').then(data => {
    console.log(data.sum(1,7))
})

9.实现多入口打包

 entry: {
            'a': './src/a.js',
            'b': './src/b.js'
        },

这个时候npm run build会报错,因为出口文件还是只有一个bundle.js

Conflict: Multiple chunks emit assets to the same filename bundle.js (chunks a and b)

需要修改output,这里的name就是对应的entry里面的a和b,最终打包到dist里面的就是a.js和b.js

  output: { //出口
            filename: '[name].js', //同步打包的名字
            chunkFilename: '[name].min.js', //异步打包的名字,name默认是从0开始,1,2,...也可以修改
            path: path.resolve(__dirname, "dist")
        },

但这个时候a.js和b.js都在index.html中,而我们需要的是分别在两个html中引入

 new HtmlWebpackPlugin({
                template: './src/template.html',
                filename: 'index.html',
                chunks:['a']
            }),
            new HtmlWebpackPlugin({
                template: './src/template.html',
                filename: 'login.html',
                chunks:['b']
            }),

如果一个login.html想先引入b,再引入a,需要手动配置

   new HtmlWebpackPlugin({
                template: './src/template.html',
                filename: 'login.html',
                chunksSortMode:'manual',//手动排序
                chunks:['b','a'] //(默认顺序以entry的顺序) 按照我自己配置的顺序引入,先引入b,再引入a。
            }),

应用场景是,entry下面还有个common,需要引入common再引入具体的其他模块

多入口好处,首页和登录页,引入的js不一样

10.打包文件分析工具(依赖关系以及打包大小)

npm install webpack-bundle-analyzer -D

场景1:a.js和b.js都引入了jq,那么在访问login.html会加载一次jq,访问index.html还会再加载一次jq,不太合理

解决方案-把jq抽取

1)不和业务逻辑放在一起

2)增加缓存(文件不变,可以从缓存中读取)

11.在生产环境下, 将第三方模块进行抽离(开发环境也可以)

参考webpack官方配置

 optimization: {
            splitChunks: {
                chunks: 'async', //默认支持异步代码的代码分割 impot()
                minSize: 30000, //文件超过30k 就会抽离
                maxSize: 0,
                minChunks: 1, //最少模块引用一次才抽离
                maxAsyncRequests: 5, //最多5个请求
                maxInitialRequests: 3, //最多首屏加载3个请求
                automaticNameDelimiter: '~', //xxx~a~b 
                automaticNameMaxLength: 30, //最长名字大小不超过30
                name: true,
                cacheGroups: { //缓存组
                    vendors: {
                        test: /[\\/]node_modules[\\/]/,
                        priority: -10
                    },
                    common: { // default~a~b 改成common
                        name: 'common',
                        minChunks: 2, //把上面的覆盖掉
                        priority: -20,
                        reuseExistingChunk: true
                    }
                }
            }
        },

12. 代码分割(主要用作上线前,分割第三方代码 也可以用于抽离自己写的js文件)

chunks:

值有initial 同步代码被抽离

all 所有代码

async(默认) 异步代码 import('')

注意:dllPlugin不要和 splitChunks共同使用

dllPlugin和代码分割区别:

  1. dllPlugin是在构建之前(提高开发的打包速度)

  2. 代码分割是在编译时(上线前把第三方代码分割出来)

13. 费时分析

npm install --save-dev speed-measure-webpack-plugin

smp.wrap

 SMP  ⏱
General output time took 8.21 secs

 SMP  ⏱  Plugins
HtmlWebpackPlugin took 5.11 secs
CleanWebpackPlugin took 0.02 secs

 SMP  ⏱  Loaders
D:\frontEnd\WEBPACK-OPTIMIZATION\node_modules\babel-loader\lib\index.js took 7.84 secs
  module count = 50
modules with no loaders took 4.069 secs
  module count = 3
D:\frontEnd\WEBPACK-OPTIMIZATION\node_modules\html-webpack-plugin\lib\loader.js took 0.046 secs

14.热更新

15 懒加载

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容