四、webpack优化配置

webpack性能优化

1、开发环境性能优化

(一) 优化打包构建速度

(1)HMR:hot module replacement 热模块替换 

作用:一个模块发生变化,只会重打包这个模块(而不是打包所有模块),极大提升构建速度

样式文件:可以使用HMR功能,因为style-loader内部实现了

JS文件:默认不能使用HMR功能 --->需要修改js代码,添加支持HMR功能的代码,例如

if(module.hot){
    //一旦module.hot为true,说明开启了HMR功能,--->让HMR功能代码生效
    module.hot.accept('./print.js',function(){
        //方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
        //会执行后面的回调函数
        print();
    })
}

注意:HMR功能对js文件的处理,只能处理非入口js文件

html文件:默认不能使用HMR功能,同时会导致问题:html文件不能热更新了(不需要HMR功能)

解决方法:修改entry入口,将html文件引入,如以下:

  entry: ['./src/index.js','./src/index.html'],

  (二)优化代码调试

(1)source-map

定义:一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误位置,并给出相应的错误提示)

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map:外部

错误代码准确信息和源代码的错误位置

inline-source-map 内联

只生成一个内联source-map

错误代码准确信息和源代码的错误位置

hidden-source-map 外部

错误代码错误原因,但是没有错误位置,不能追踪源代码错误,只能提示到构建后代码的错误位置

eval-source-map 内联

每一个文件都生成对应的source-map,都在eval

错误代码准确信息和源代码的错误位置

nosources-source-map:外部

错误代码准确信息,但是没有任何源代码信息 

cheap-source-map:外部

错误代码准确信息和源代码的错误位置

只能精确到行

cheap-module-source-map:外部

错误代码准确信息和源代码的错误位置

moudle会将loader的source map加入

内联和外部的区别:1、外部生成了文件,内联没有   2、内联构建速度更快

开发环境:速度快,调试更友好 

速度快(eval>inline>cheap>eval-cheap-source-map>eval-source-map)

调试更友好 (source-map>cheap-module-source-map>cheap-source-map)

--》eval-source-map比较好

生产环境:源代码要不要隐藏?调试要不要友好?
内联会让代码体积变大,所以在生产环境中不用内联

nosources-source-map:全部隐藏

hidden-source-map   只隐藏源代码,会提示构建后代码错误信息

--》source-map/cheap-module-source-map

2、生产环境性能优化

(1) oneOf

(2)缓存:

  2.1 babel缓存:cacheDirectory:true //第二次构建时,会读取之前的缓存--->让第二次打包构建速度更快】

 2.2文件资源缓存:重新构建后,会读取本地缓存,可搭建一个express服务器用于测试

---》【让代码上线运行缓存更好使用】

但是这样会存在一个问题,如果文件发生改变,并不会实时更新

如何解决?维护hash值的变化

hash:每次webpack构建时会生成一个唯一的hash值。

例如:

output:{
        filename:'js/build[hash:10].js',
        path:resolve(__dirname,'build')
    },
//将css提取成单独文件
  new MiniCssExtractPlugin({
     filename:'css/build[hash:10].css'
  }),

问题:因为js和css同时使用一个hash值

如果重新打包,会导致所有缓存是失败(可是只有一个文件改动) ×

chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样

问题:js和css的hash值还是一样的? ×

因为css是在js中被引入的,所以同属于同一个chunk

contenthash:根据文件的内容生成hash值,不同文件hash值一定不一样  √

output:{
        filename:'js/build[contenthash:10].js',
        path:resolve(__dirname,'build')
    },
//将css提取成单独文件
  new MiniCssExtractPlugin({
     filename:'css/build[contenthash:10].css'
  }),

(3)tree shaking

目的:为了去掉代码构建中无用代码

前提: 1、必须使用ES6模块化  2、开启producction环境

作用:减少代码体积

在packing.json中配置

“sideEffects”:false 所有代码都没有副作用(都可以进行tree shaking)

问题:可能会把css/@babel/polyfill (副作用) 文件干掉

“sideEffects”:["*.css"]  这样会使得css不会被处理 (*代表任意的xxx文件)

(4)code split(代码分割)

demo1:

const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //单入口
    // entry: './src/index.js',
    entry:{
       //多入口:有几个入口文件输出几个bundle
       index:'./src/index.js',
       test:'./src/test.js'
    },
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash10].js',
        path: resolve(__dirname, 'build')
    },
   
    plugins: [

        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html代码
            minify:{
                //移除空格
                collapseWhitespace:true,
                //移除注释
                removeComments:true
            }
        }),
    ],
    mode: 'production',
}

demo2:

//webpack.config.js
const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //单入口
    entry: './src/index.js',  
//如果需要将test.js文件单独打包,要怎么处理? 注意看一下index.js文件中的相关配置
    // entry:{
    //    //多入口:有几个入口文件输出几个bundle
    //    index:'./src/index.js',
    //    test:'./src/test.js'
    // },
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html代码
            minify:{
                //移除空格
                collapseWhitespace:true,
                //移除注释
                removeComments:true
            }
        }),
    ],
    /*
    1. 可以将 node_modules 中代码单独打包一个 chunk 最终输出
    2. 自动分析多入口 chunk 中, 有没有公共的文件。 如果有会打包成单独一个 chunk
    */
    optimization:{
        splitChunk:{
            chunks:'all'
        }
    },
    mode: 'production',
}

//index.js
import {mul} from './test';
function sum(...args){
    return args.reduce((p,c)=>p+c,0)
}

/**
 * 通过js代码,让某个文件被单独打包成一个chunk
 * import动态导入语法,能将某个文件单独打包
 * 通过这个注释:“/*webpackChunkName:'test'*/'”,能给打包后的文件,一个比较固定的命名
 */
import(/*webpackChunkName:'test'*/'./test').then(({mul,count})=>{
    console.log("文件加载成功",mul(2,5));
}).catch(()=>{
    console.log("文件加载失败~")
})
//eslint-disable-next-line //使用eslintl规则禁用下一行语句
console.log(mul(2,8))

 demo3:

const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    //单入口
    // entry: './src/index.js',
    entry:{
       //多入口:有几个入口文件输出几个bundle
       index:'./src/index.js',
       test:'./src/test.js'
    },
    output: {
        //[name]:取文件名
        filename: 'js/[name].[contenthash10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            //压缩html代码
            minify:{
                //移除空格
                collapseWhitespace:true,
                //移除注释
                removeComments:true
            }
        }),
    ],
    /*
    1. 可以将 node_modules 中代码单独打包一个 chunk 最终输出
    2. 自动分析多入口 chunk 中, 有没有公共的文件。 如果有会打包成单独一个 chunk
    */
    optimization:{
        splitChunk:{
            chunks:'all'
        }
    },
    mode: 'production',
}

(5)懒加载和预加载

例如针对test.js文件的懒加载,可以在index.js文件中,进行如下配置

//index.js
console.log('index.js文件被加载了');

document,getElementById('btn').onclick=function(){
//懒加载:当文件需要使用时才加载
//预加载prefetch:会在使用之前,提前加载js文件,等其他资源加载完毕,等浏览器空闲了,再偷偷加载
//正常加载:可以认为是并行加载(同一个时间加载多个文件)
import(/*webpackChunkName:'test',webpackPrefetch:true*/'./test').then({mul})=>{
   console.log(mul(4,5))
});
}

(6)PWA:渐进式网络开发应用程序(离线可访问)

workbox-->workbox-webpack-plugin

1、安装插件

npm install --save-dev workbox-webpack-plugin

配置:

const WorkboxWebpackPlugin=require('workbox-webpack-plugin') //WPA
...
 new WorkboxWebpackPlugin.GenerateSW({
            /**
                * 1、帮助servicework快速启动
                * 2.删除旧的servicework
                * 生成一个servicework配置文件
                */
            clientsClaim:true,
            skipWaiting:true
        })

注册serviceWorker

//注册serviceworker
//处理兼容性问题
//sw代码必须运行在服务器上
// -->node.js
//npm i serve -g
//serve -s build启动服务器,将build目录下所有资源作为静态资源暴露出去

if('serviceWorker' in navigator){
    window.addEventListener('load',()=>{
      navigator.serviceWorker.register('/service-worker.js')
        .then(()=>{
            console.log("sw注册成功了~")
        }).catch(()=>{
            console.log("sw注册失败~")
        })
    })
}

注意:

1、eslint不认识window、navgator全局变量

解决:需修改package.json中eslintConfig配置

"env":{

"browser":true  //支持浏览器全局变量

}

2、命令是npm i serve -g (不是server!!!)

2、sw代码必须运行在服务器上

 -->node.js

npm i serve -g

serve  -s build启动服务器,将build目录下所有资源作为静态资源暴露出去

(7)多进程打包

1、安装插件:

npm install --save-dev thread-loader

package.json中的配置

 {
    loader:'thread-loader',
    options:{
        worker:2 //进程2个
      }
 },

开启多进程打包注意:

1、 进程启动大概600ms,进程通信也有开销。

 2、只有工作消耗时间比较长,才需要多进程打包【比如打包js文件】

3、不要滥用!!

(8)externals

如果一些包是通过cdn引入,在打包的时候,不希望参与进来打包,可以做如下配置:

//webpack.config.js
mode: 'production', //注意是生产环境
externals: {
// 拒绝 jQuery 被打包进来
//写法:库名 --npm包名
jquery: 'jQuery'
}

(9)dll【动态链接库】

对代码进行单独打包

首先需要配置2个文件:

1、webpack.dll.js文件:

/***
 * 通过dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
 * 当你运行webpack时,默认查找webpack.config.js配置文件
 * 需求:需要运行webpack.dll.js文件
 * 指令要改成:webpack --config webpack.dll.js
 */
const {resolve} =require('path');
const webpack=require('webpack')
module.exports={
    entry:{
        //最终打包生成的[name]-->jquery
        //['jquery']--->要打包的库是jquery
      jquery:['jquery']
    },
    output:{
        filename:'[name].js',
        path:resolve(__dirname,'dll'),
        library:'[name]_[hash]', //打包的库里面向外暴露出去的内容叫什么名字

    },
    plugins:[
        //打包生成一个mainfest.json ---》提供和jquery映射,告诉webpack,不需要重复打包了
        new webpack.DllPlugin({
            name:'[name]_[hash]', //映射库的暴露内容名称,
            path:resolve(__dirname,'dll/mainfest.json') //输出文件路径
        })
    ],
    mode:'production'
}

以上文件的作用:创建 dll 文件,这个相当于我们对第一次的请求内容进行存储,然后我们还要创建一个映射表,告诉程序我们把啥文件做成 dll 了,此处的话,相当于把jquery做成了dll

2、webpack.config.js文件

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
//需要安装add-asset-html-webpack-plugin插件,npm i xxx
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
 // 顾名思义,把资源加到 html 里,用这个插件把 dll 加入到 index.html 里
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        // 告诉 webpack 哪些库不参与打包, 同时使用时的名称也得变~
        new webpack.DllReferencePlugin({
            manifest: resolve(__dirname, 'dll/manifest.json')
        }),
        // 将某个文件打包输出去, 并在 html 中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
            filepath: resolve(__dirname, 'dll/jquery.js')
        })
    ],
    mode: 'production'
};

以上文件的作用:链接 dll 文件,也就是告诉 webpack 可以命中的 dll 文件

总结:

生产环境性能优化:

(1)优化打包构建速度

oneOf 、babel缓存 、多进程打包

(2)优化代码运行的性能

文件资源缓存、tree shaking 、code split 、懒加载/预加载 、PWA、externals、dll

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值