前言

  本文来总结写webpack 在性能方面常见的优化方案。

正文

  本文分别总结开发环境和生产环境中在打包构建速度和代码调试功能方面的优化方案,如下:

  1、开发环境性能优化

  (1)优化打包构建速度

  a、HMR: hot module replacement ,热模块替换,作用:当一个模块发生变化的时候,只会重新打包发生变化的模块,并不会打包所有模块,极大的提升了代码构建速度。

  只需要在webpack.config.js中的devserver中添加:

  devServer: {
// contentBase: resolve(__dirname, "build"),// 这个配置在新版本的webpack中使用下面的方式配置,否则会报错
static: { // static: ['assets']
directory: resolve(__dirname, "build")
},
// 启用gzip压缩
compress: true,
// 端口号
port: 3000,
open:true,// 自动打开浏览器
hot:true// 开启HMR功能
},

  执行 npx webpack-dev-server命令,然后修改其中的文件,会发现控制台提示修改的文件,如下:

  经测试发现总结:

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

  js文件:默认不能使用HRM功能,(解决方法:修改js代码,添加支持)

  html 文件:默认不能使用HRM 功能,同时会导致html文件不能热更新了(解决方法:修改入口文件entry的引入即可,如下:)开发中不需要做

    //入口文件
entry: ["./src/index.js","./src/index.html"],

  (2)优化代码调试性能

  source-map:是一种提供源代码到构建后代码的映射技术(如果构建后代码出错,通过映射关系能够追溯到源代码错误)

  只需要在webpack.config.js中添加如下:

    devtool:"eval-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(),能够准确提示文件报错的具体位置和准确信息,不过多了源代码文件名加了hash值。

  nosources-source-map:外部,能够找到错误信息,但是没有任何源代码错误位置信息。

  cheap-source-map:外部,能够准确提示文件报错的具体位置和准确信息,只能精确到代码行。

  cheap-module-source-map:外部,能够准确提示文件报错的具体位置和准确信息,只能精确到代码行。但是module会将loader的 source-map 加入进来

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

  使用方法: 开发环境:速度快优先,便于调试( eval > inline > cheap > 其他),因此 eval-source-map最优

       生产环境:源代码要不要隐藏,调试要不要友好,nosources-source-map 或者 hidden-source-map

  2、生产环境性能优化

  (1)优化打包构建速度

  (2)优化代码调试性能

  a、oneOf:在loader中配置了很多个用于处理不同的文件,但是针对不同文件不能全部使用多个loader,只能使用其中一个或者几个,每次使用不需要都对loader检查一遍,因此可以对loader中代码进行改造。由于对js文件即使用了eslint-loader 又使用了babel-loader,所有把eslint-loader 提取到外部即可。如下:

 module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules
enforce: "pre", // 优先执行,先检查eslint 再执行babel-loader
loader: "eslint-loader",
options: {
fix: true, // 自动修复eslint错误
},
},
{
oneOf: [
{
test: /\.css$/,
use: [
// "style-loader", // 创建style标签将样式放入
//这个loader取代style-loader ,作用是将提取js中的css成单独的文件
MiniCssExtractPlugin.loader,
"css-loader", // 将css文件整个到js中
/* CSS兼容性处理:postcss ==>postcss-loader postcss-preset-env
帮助postcss扎到packae.json 中browserslist里的配置,通过配置加载指定的css兼容样式
"browserlist":{
// 开发环境==》设置node环境变量:process.env.NODE_ENV="development"
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境 默认是生产环境
"production":[
">0.2%",
"not dead",
"not op_mini all"
]
}*/
// 使用loader的默认配置
// post-loader
// 修改loader配置
{
loader: "postcss-loader",
// options:{
// ident:"postcss",
// plugins:()=>{
// require("postcss-preset-env")
// }
// }
},
],
}, {
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/, // 排除node_modules
options: {
// 预设:指示babel做哪些兼容性处理
presets: [
// "@babel/presets-env",
{
useBuiltins: "usage", // 按需加载
corejs: {
version: 3, // 指定corejs版本
},
targets: {
// 指定兼容到哪些浏览器
chorme: "60",
firefox: "60",
ie: "9",
safiri: "10",
edge: "17",
},
},
],
},
},
// 这种方式无法处理html文件中的图片
{
test: /\.(jpg|png|gif)$/,
loader: "url-loader",
options: {
// 图片大小小于8kb,会被解析为base64处理,优点减少请求数量减轻服务器压力,缺点是图片体积更大,文件请求速度更慢
limit: 8 * 1024,
esModule: false,
// 这样可以修改图片名称,[hash:10]表示取hash值的前十位,[ext]表示原文件扩展名
name: "img/[hash:8].[name].[ext]",
},
},
{
// 因为url-loader默认适用了es6模块解析,而html-loader 引入图片适用commonJS处理,解析时会出现[object Module],需要关闭url-loader的es6模块化解析
test: /\.html$/,
// 处理html 文件的img文件
loader: "html-loader",
},
{
// 打包其他资源(除了css,js,html,less,json资源以外的资源)
exclude: /\.(css|js|html|less|json)$/,
loader: "file-loader",
},
],
},
],
},

  b、缓存优化

  <1>babel缓存优化

  babel缓存指在通过babel实现 js 兼容性处理的时候,默认我们每次会对所有的js代码进行babel处理,而实际构建过程中,我们并不hi修改全部js的代码,因此只需要对我们修改的js代码进行babel兼容性处理即可,没有修改的可以使用缓存的处理就行。

  只需要在babel的配置中添加配置,如下即可:

         {
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/, // 排除node_modules
options: {
//... 其他处理
// 开启babel缓存,第二次构建时,会读取之前的缓存
cacheDirectory: true,
},
},

  <2>打文件资源的优化

  给生成的文件添加hash值(webpack每次打包生成的唯一值),每次通过对比hash值的变化来确定是否使用缓存的文件。但是问题是:因为js和css同时使用一个hash值,重新打包,会导致所有缓存失效,无法满足缓存使用。

  给生成的文件添加 chunkhash :根据生成的hash值,如果打包来源于同一个chunk,那么hash值就一样。但是问题是:因为css是在js中引入的,所有同属于一个chunk。

  给生成的文件添加contenthash :根据文件的内容生成的hash值,不同文件hash值一定不一样。只需要在filename中添加如下代码:

 output: {
// 输出文件名
filename: "js/build.[contenthash:10]js",
// 输出路径
// __dirname 是node.js的变量,代表当前文件的目录的绝对路径
path: resolve(__dirname, "build"), // 代表输出到当前目录下创建的build文件夹下
},

  c、tree shaking:去除无用代码(js和css)

  前提:1、使用es6 模块化 2、开启production环境

  作用:减少打包代码体积

  需要在package.json 中配置如下:这样可以把一些没有使用的副作用文件保存在构建包中,比如没有css,less文件

  "sideEffects":[
"*.css",
"*.less"
]

  d、code split代码分割

  在分割之前,js文件是打包成为一个文件,然后在html中引入,无法实现我们的按需加载,对性能影响很大。

  (1)设置多入口,只要有一个入口,最终就会输出一个bundle ,通过拆分入口实现生成多个bundle

  entry:{
main:"./src/js/index.js",
test:"./src/js/index.js"
},

  (2)在webpack.config.js中添加如下配置,对项目依赖单独打包

  // 可以将node_modules中代码单独打包一个chunk输出
// 自动分析多个入口chunk中,有没有公共的文件,如果有会打包成一个单独的chunk
optimization:{
splitChunks:{
chunks:"all"
}
},

  不使用如上配置,当我们的代码中A.js 和 B.js 两个文件同时引入了 jquery 依赖的时候,都会生成对应的AB打包的文件,这两个文件中就会重复存在jquery的打包文件,这时就需要单独分离出依赖文件,单独进行打包即可。

  (3)如果不想设置多个入口文件,可以通过js代码将某个文件单打包成一个chunk

import (/*webpackChunkName:"test"*/"./test.js")
.then((res)=>{
// 文件加载成功
console.log(res);
})
.catch(()=>{
console.log("文件加载失败");
})

  通过上面的 import动态导入语法, test.js 文件就会被单独打包

  e、懒加载和预加载

  懒加载是使用上面提到的import动态导入语法,将代码放在一个异步的函数中,如下:

document.getElementById("btn").onclick = function () {
import(/*webpackChunkName:"test"*/ "./test.js")
.then((res) => {
// 文件加载成功
console.log(res);
})
.catch(() => {
console.log("文件加载失败");
});
};

  在index.js 文件中,点击btn按钮,进行test.js 文件 的懒加载,再次点击的时候,不会重复加载test文件,此时会利用缓存里的test文件即可。

  预加载prefetch,会在使用之前提前加载js文件,在使用的时候直接在缓存里面取,代码如下:

document.getElementById("btn").onclick = function () {
import(/*webpackChunkName:"test",webpackPrefetch:true*/ "./test.js")
.then((res) => {
// 文件加载成功
console.log(res);
})
.catch(() => {
console.log("文件加载失败");
});
};

  正常加载可以认为是并行加载,同一时间加载多个文件,预加载prefetch是等其他资源加载完毕,浏览器空闲的时候取加载其他资源。极大提高性能和用户体验。但是预加载兼容性较差,谨慎使用。

  f、PWA (Progressive Web App),即渐进式WEB应用。

  • 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏

  • 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能

  • 实现了消息推送

    使用workbox技术 ===》workbox-webpack-plugin配置插件

    首先下载workbox-webpack-plugin库

npm i workbox-webpack-plugin -D

  在plugins中添加配置

   new WorkboxWebpackPlugin.GenerateSW({
/**
* 1.帮助serviceworker快速启动
* 2.删除旧的serviceworker
*
* 生成一个serviceworker配置文件~
*/
clientsClaim:true,
skipWaiting:true
})

  在入口文件或者html文件中可以添加如下代码注册serviceworker

// 注册serviceworker
// 处理兼容性问题
/**
* 1、eslint不能识别window.navigator这些变量,需要加package.json中的eslintConfig中配置
* "eslintConfig": {
"extends": "airbnb-base",
"env":{
"browser": true
}
},
2、 serviceworker 代码必须运行在服务器上
==》通过node.js部署
==》npm i serve -g 安装serve,然后serve -s build启动服务器,将build目录下所有资源作为静态资源暴露出去
*/
if("serviceWorker" in navigator){
window.addEventListener("load",()=>{
navigator.serviceWorker.register("/service-worker.js")// 该js文件由插件生成
.then(()=>{
console.log("注册成功");
})
.catch(()=>{
console.log("注册失败");
})
})
}

  g、thread-loader多进程打包

  安装loader:

npm i thread-loader -D

  经常给babel-loader使用:

         {
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules
use: [
{
// 开启多进程打包 进程开启大概600ms,进程通信也要花时间开销
loader:"thread-loader",
options:{
workers:2,// 两个进程打包
}
},
{
loader: "babel-loader",
options: {
// 预设:指示babel做哪些兼容性处理
presets: [
// "@babel/presets-env",
{
useBuiltins: "usage", // 按需加载
corejs: {
version: 3, // 指定corejs版本
},
targets: {
// 指定兼容到哪些浏览器
chorme: "60",
firefox: "60",
ie: "9",
safiri: "10",
edge: "17",
},
},
],
// 开启babel缓存,第二次构建时,会读取之前的缓存
cacheDirectory: true,
},
},
],
},

  h、externals

  在webpack.cinfig.js 添加入下中配置

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

  这样jQuery这个包就不会用过npm的方式被打包到项目中,可以通过cdn的方式引入,比如在index.html 文件中插入jQuery 的script标签。通过cdn的引入,提升性能

  i、dll 对代码库进行单独打包

  创建单独的js文件,比如webpack.dll.js

/**
* 使用dll技术,对某些(第三方库:jquery、react、vue...)进行单独打包,同时生成一个manifest.json文件,提供了之间的映射关系
* 当运行webpack指令 默认运行的是webpack.config.js配置文件,需要通过webpack --config webpack.dll.js 指令运行webpack.dll.js配置文件
*/
const { resolve } = require("path");
const webpack = require("webpakck"); module.exports = {
entry: {
// 最终打包生成的[name] ==>jquery
jquery: ["jquery"],
},
output: {
filename: "[name].js",
path: resolve(__dirname, "dll"),
library: "[name]_[hash]", // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个manifest.json 文件(该文件提供映射关系)
new webpack.DllPlugin({
name: "[name]_[hash]", // 映射库的暴露的内容名称
path: resolve(__dirname, "dll/manifest.json"), // 输出文件路径
}),
],
mode: "production",
};

  通过上面的指令,会将jquery单独进行打包。

  然后在webpack.config.js 中引入新的插件,告诉webpack不去打包哪些依赖(忽略jquery依赖)

  plugins: [
// 告诉webpack那些库不参与打包,同时使用时的名称也得改变
new webpack.DllPlugin({
manifest:resolve(__dirname,"dll/mainfest.json")
}),
],

  然后执行下面命令下载add-asset-html-webpack-plugin

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

  继续如下配置(再将之前dll.js 文件打包的jquery引入进来):

  plugins: [
// 将某个文件打包输出,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath:resolve(__dirname,"dll/jquery.js")
})
],

  通过上面的配置优化了jquery重复打包的过程提高了性能。

写在最后

  以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。

webpack--性能优化之打包构建速度和代码调试优化的更多相关文章

  1. vscode调试webpack的启动和打包部署过程,nodejs调试

    launch.json ``` {   // 使用 IntelliSense 了解相关属性.    // 悬停以查看现有属性的描述.   // 欲了解更多信息,请访问: https://go.micr ...

  2. Android 项目优化(一):项目代码规范优化

    项目代码规范为主要包含:类,常量,变量,ID等命名规范:注释规范:分包规范:代码风格规范. 项目代码规范是软件开发过程中非常重要的优化环节. 目前的开发社区提供了很多的开发规范文档,阿里巴巴推出了&l ...

  3. webpack 性能优化小结

    背景 如今前端工程化的概念早已经深入人心,选择一款合适的编译和资源管理工具已经成为了所有前端工程中的标配,而在诸多的构建工具中,webpack以其丰富的功能和灵活的配置而深受业内吹捧,逐步取代了gru ...

  4. [译] 优化 WEBPACK 以更快地构建 REACT

    原文地址:OPTIMIZING WEBPACK FOR FASTER REACT BUILDS 原文作者:Jonathan Rowny 译文出自:掘金翻译计划 本文永久链接:https://githu ...

  5. Webpack5构建速度提升令人惊叹,早升级早受益

    为什么要升级? webpack4用的好好的,运行稳定,为什么要升级到webpack5, 每次升级,都要经历一场地震,处理许多loader和plugin API的破坏性改变. 请给我们一个充分的升级理由 ...

  6. 嵌入式程序设计中C/C++代码的优化

    虽然使软件正确是一个工程合乎逻辑的最后一个步骤,但是在嵌入式的系统开发中,情况并不总是这样的.出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力.所以在嵌入式软件设计的最后 ...

  7. 嵌入式实时程序设计中C/C++代码的优化

    1 引言 计算机技术和信息技术的高速发展的今天,计算机和计算机技术大量应用在人们的日常生活中,嵌入式计算机也得到了广泛的应用.嵌入式计算机是指完成一种或多种特定功能的计算机系统,是软硬件的紧密结合体. ...

  8. 优化 Webpack 的构建速度

    1.使用高版本的 Webpack 和 Node.js 2.多进程/多实例构建:HappyPack(不维护了).thread-loader 3.压缩代码 webpack-paralle-uglify-p ...

  9. webpack 性能优化 dll 分包

    webpack 性能优化 dll 分包 html-webpack-externals-plugin DLLPlugin https://www.webpackjs.com/configuration/ ...

随机推荐

  1. AOP自定义注解鉴权

    刚出来工作那会或者在学校的时候,经常听到说AOP(面向对象编程,熟称切面)的用途是日志.鉴权等.但是那会不会,后面学会了,又没有写博客记录,今天写给大伙,希望能帮到大家 一.学习目标:利用AOP+自定 ...

  2. centos安装Libzip

    2018年06月29日 11:12:15 oxiaobaio 阅读数 4827   wget https://nih.at/libzip/libzip-1.2.0.tar.gztar -zxvf li ...

  3. 如何实现固定宽高的DOM元素的水平垂直居中

    效果:

  4. 区段统计 mysql 语句 case when then end as

    EXPLAIN SELECT COUNT(*),CASEWHEN device_width > 729 THEN '>729'WHEN device_width BETWEEN '720' ...

  5. iOS中通过链接地址打开指定APP并传参 by徐文棋

    基于项目需要,有时候需要通过一个链接,或者二维码扫描来直接打开我们所开发的客户端. 当然了.客户端也不仅仅是需要被打开,而且还要跳到相应的页面去,因此这里需要传参. 客户端想用链接打开,必须要在inf ...

  6. Linux运维-常用操作-培训用例

    一.服务器环境 Centos 7.9 二.常用连接工具(免费) 1.Finalshell 2.MobaXterm 3.Putty + WinSCP 三.Linux  系统目录结构 /bin :是 Bi ...

  7. Mysql的基本操作知识

    顺带,我会在后面把我整理的一整套CSS3,PHP,MYSQL的开发的笔记打包放到百度云,有需要可以直接去百度云下载,这样以后你们开发就可以直接翻笔记不用百度搜那么麻烦了.  笔记链接:http://p ...

  8. Note/Solution -「洛谷 P5158」「模板」多项式快速插值

    \(\mathcal{Description}\)   Link.   给定 \(n\) 个点 \((x_i,y_i)\),求一个不超过 \(n-1\) 次的多项式 \(f(x)\),使得 \(f(x ...

  9. MySQL基本数据类型与约束条件

    昨日内容回顾 数据存储的演变 # 方向: 朝着更加统一和方便管理 数据库的发展史 # 由本地保存逐步演变为线上保存 数据库的本质 # 本质上就是一款CS架构的软件 """ ...

  10. C#字符串Base64编解码

    C#字符串Base64编解码 首先讲一下什么是Base64编码所谓Base64就是一种基于64个可打印字符来表示二进制数据的方法.Base64编码是从二进制到字符的过程,常用于在网络上传输不可见字符( ...