Webpack的优化方案总结

57 阅读6分钟

开发环境性能优化:

  • 优化打包构建的速度

-----------HMR(热模块替换)

  • 优化代码调试

-----------source-map

生产环境性能优化:

  • 优化打包构建的速度

-----------oneOf

-----------babel缓存

  • 优化代码运行的性

-----------缓存(hash-chunkhash-contenthash)

-----------tree shaking(生产模式自启动)

-----------code split

-----------懒加载/预加载

-----------PWA

-----------externals

-----------dll

1.开发环境性能优化

1. HMR热模块替换

hot module replacement

作用:

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

devServer:{
   hot:true    //开启HMR功能 当修改了webpack配置,必须重新启动webpack才会生效
}

样式文件:可以使用HMR功能,因为style-loader内部实现了(开发环境最好使用style-loader)

js文件:默认不能使用HMR功能

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

html解决方案

修改entry入口,将html文件引入(因为只有一个HTML文件,所以不需要HMR)

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

js文件解决方案

需要修改js代码,添加支持HMR功能的代码

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

import print from './print'
//一旦module.hot为true,说明开启了HMR功能-----》让HMR功能代码生效
if(module.hot){
  module.hot.accept('./print.js',function(){
 //方法会监听print.js文件的变化,就会重新打包print.js
 //会执行后面的回调函数
    print()
  })
}

2.source-map

一种提供源代码到构建后代码映射技术,如果构建后代码出错了,通过映射可以追踪源代码错误的地方

配置权衡

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

​ 速度快(eval>inline>cheap>...)

​ eval-cheap-source-map

​ eval-source-map

​ 调试更加友好

​ source-map

​ cheap-module-source-map

​ cheap-source-map

综上:速度快、调试更友好为------>eval-source-map / eval-cheap-module-source-map

生产环境:源代码隐藏,调试友好,代码体积

内联会使代码体积变大,故排除

综上:-----> source-map / cheap-module-source-map(快一点)

由上可得

开发环境:eval-source-map

生产环境:source-map

添加devtool属性

devtool:'source-map'

devtool有以下设置

外部和内联的区别:

1.外部生成了文件,内联没有

2.内联构建速度更快

  • source-map(外部)

可以反馈错误代码准确信息,和源代码的错误位置

  • inline-source-map(内联)

可以反馈错误代码准确信息,和源代码的错误位置

  • hidden-source-map(外部)

反馈错误代码的错误原因,但是没有错误原因

不能追踪源代码的错误,只能提示到构建后代码的错误位置

  • eval-source-map(内联)

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

可以反馈错误代码准确信息,和源代码的错误位置,对js文件进行了hash化

  • nosource-source-map

可以反馈错误代码准确信息,隐藏任何源代码信息

  • cheap-source-map

可以反馈错误代码准确信息,和源代码的错误位置

只能精确到行

  • cheap-module-source-map

可以反馈错误代码准确信息,和源代码的错误位置

module会将loader的source-map加进来

2.开发环境性能优化

1.oneOf优化

一个数组,oneOf数组里的loader只会匹配一个

所以oneOf里面,不能存在处理同一文件的loader

这样一个文件的处理不需要一直匹配

module:{
  rules:[
     test:/.js$/,enforce:'pre',...},  //让它优先执行
     {
      oneOf:[
       {test:/.js$/,...},
       {test:/.css$/,...}
      ]
     }
    ]
  }

2.缓存

1.babel缓存

在babel-loader中新增属性

可以让第二次打包构建速度更快

{
        test:/.js$/,
        exclude:/node_modules/,
        loader:'babel-loader',
        options:{
         //开启babel缓存,会读取之前的缓存
         cacheDirectory:true   
        }
}

2.文件资源缓存

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

缺点:因为js和css同时使用一个hash值,如果重新打包会导致所有缓存失效(可我却只改动一个文件)

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

缺点:js和css的hash值还是一样,因为css是在js中被引入的,所以同属于一个chunk

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

优点:让代码上线运行缓存更好使用

 new MiniCssExtractPlugin({
      filename:'css/build.[hash:10].css'
    })

output:{
    filename:'js/build.[hash:10].js',
    path:resolve(__dirname,'build')
  }

3.tree shaking

去除无效代码

使用条件:1. 必须使用es6模块化 2. 开启production环境

作用:减少代码体积

在package.json中配置

"sideEffects":false  //设置所有代码都可以进行tree shaking
                     //缺点:会把没使用到的文件全部去除
"sideEffects":["*.css","*.less"] //排除数组中的文件不被去除掉

4.code split(代码分割)

1.多入口打包

有几个入口就输出几个bundle.js

可以在output设置输出文件名,来区分是哪个入口的文件

[name].[contenthash:10].js

entry:{
    index:'./src/index.js',
    test:'./src/test.js'
}

2.optimization分割配置

1.可以将node_modules中代码单独打包一个chunk最终输出

2.自动分析多入口chunk中,有没有公共文件。如果有会打包成单独的chunk文件

optimization:{
    splitChunks:{
        chunks:'all'
    }
}

另外可以通过js代码,让某个文件被单独打包成一个chunk

import动态导入语法:能让某个文件被单独打包

注释为取得名字

import(/*webpackChunkName:test*'./test'/)
	.then(({mul,count}) =>{
	console.log("----")
	})
	.catch(()=>{
	console.log("----")
	})

5.lazy loading(懒加载)

正常加载:可以认为是并行加载,同时加载多个文件,没有顺序

懒加载:当文件需要时再加载,可能会造成卡顿

预加载(prefetch):等其他资源加载完毕,浏览器空闲了,再偷偷加载资源(兼容性差)

import(/*webpackChunkName:'test',webpackPrefetch:true*/'./test').then(res =>console.log(res));

6.PWA

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

workbox ---> workbox-webpack-plugin

cnpm i -D workbox-webpack-plugin
plugins:[
  new WorkWebpackPlugin.GenerateSW({
     //生成一个serviceworker快速启动
     //删除旧的serviceworker
     //生成一个serviceworker配置文件
     clientsClaim:true,
     skipWaiting:true
  })
]
  • 注册serviceworker
  • 处理兼容性问题
//在入口文件中
if('serviceworker' in navigator){
    window.addEventListener('load',() =>{
      navigator.serviceworker
        .register('/service-worker.js')
        .then(()=>console.log('sw注册成功了'))
        .catch(() =>{console.log('sw注册失败了')})
    })
}

这样会有几个问题

  1. eslint不认识window、navigator全局变量

    解决:需要修改package.json中的eslint配置

  2. sw代码必须运行再服务器上,以下方式进行测试

-->node.js

--> cnpm i serve -g

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

"eslintConfig":{
    "env":{
        "browser":true   //支持浏览器端的全局变量
    }
}

7.多进程打包

加载thread-loader,一般给babel-loader使用

babel-loader处理完交给thread-loader,开启多进程打包

缺点:进程启动大概600ms,进程通信也有开销

只有工作消耗时间比较长,才需要多 进程打包

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

8.externals

拒绝一些包被打包进项目

让着这些包使用cdn引入,减轻代码负担

添加属性

externals:{
    jquery:'jQuery' //拒绝jQuery打包进来
}

9.dll

使用dll技术,对某些库(第三方库:jquery、react、vue)进行单独打包

不想写了 看视频