wordpress 设置网站目录权限,站长广告联盟平台,网站页面设计软件,wordpress检查php版本在使用webpack时提供了各种配置#xff0c;这里结合在业务中常用的配置汇总一下可以进行的一系列的webpack优化
缩小文件搜索范围
其原理是在构建时#xff0c;会以用户配置的Entry为开始依次递归遍历每个Module#xff0c;在遍历每个Module时会调用相应合适的Loader对原模…在使用webpack时提供了各种配置这里结合在业务中常用的配置汇总一下可以进行的一系列的webpack优化
缩小文件搜索范围
其原理是在构建时会以用户配置的Entry为开始依次递归遍历每个Module在遍历每个Module时会调用相应合适的Loader对原模块代码进行“翻译”。
优化Loader配置
Loader对文件的转换是比较耗时的我们可以在loader中通过 test、include、exclude三个配置规则更加精确的命中目标文件减少文件搜索的范围举个例子如果我们以ES6开发的文件为例子在配置Loader时可以这样
module.exports {module: {rules: [{// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/提升正则表达式性能test: /\.js$/,// babel-loader 支持缓存转换出的结果通过 cacheDirectory 选项开启use: [{loader: babel-loader,options: {cacheDirectory: path.resolve(__dirname, .cache/babel)}}],// 只对项目根目录下的 src 目录中的文件采用 babel-loaderinclude: path.resolve(__dirname, src),},]},
};上面cacheDirectory可以将 babel 编译的结果缓存到文件系统下次编译时如果文件没有变化直接使用缓存可以显著提升构建速度一般可以在开发环境中使用可以提高构建效率在生产环境关闭保障获得最新的构建结果
优化resolve.modules 配置
其默认值是[‘node_module’]含义是先去当前目录下的./node_module目录下找对应的模块如果没有就去找上一级的./node_modules以此类推这和Node.js的模块寻找很相似。如果当前安装的第三方目录都在当前项目的根目录下就没有必要一层层找可以直接指明第三方模块的绝对路径以减少寻找配置如下
module.exports {resolve: {// 使用绝对路径指明第三方模块存放的位置以减少搜索步骤// 其中 __dirname 表示当前工作目录也就是项目根目录modules: [path.resolve(__dirname, node_modules)]},
};优化resolveLoader
同理resolveLoader的机制也是一样指定对应的Loader的安装位置
优化 resolve.mainFields 配置
resolve.mainFields 用于配置第三方模块使用哪个入口文件。在我们使用第三方模块的时候在第三方模块中的package.json描述这个模块的属性其中有一些字段用于描述这个模块的入口文件在哪里而很多三方模块支持多环境的调用比如axios或者isomorphic-fetch支持浏览器环境和node环境的调用针对不同的环境就需要调用不同的代码以 isomorphic-fetch 为例它是 fetch API 的一个实现但可同时用于浏览器和 Node.js 环境。 它的 package.json 中就有2个入口文件描述字段
{browser: fetch-npm-browserify.js,main: fetch-npm-node.js
}点我查看isomorphic-fetch配置文件
因为在不同的环境就需要掉不同的代码来执行。而resolve.mainFields 用于配置采用哪个字段作为入口文件的描述。所以我们为了减少搜索的步骤当我们明确知道第三方模块的描述字段时可以设置的尽量少这样速度就会更快一般而言三方模块都会采用main字段所以可以这样配置
module.exports {resolve: {// 只采用 main 字段作为入口文件描述字段以减少搜索步骤mainFields: [main],},
};优化resolve.extensions配置
在导入语句没带文件后缀时Webpack 会自动带上后缀后去尝试询问文件是否存在。 resolve.extensions 用于配置在尝试过程中用到的后缀列表默认是
extensions: [.js, .json]举一个例子比如在构建时遇到require(‘./dataList’)时webpack会先去寻找./dataList.js如果没有该文件就会去找./dataList.json文件。如果还是找不到那就回报错。 如果这个列表越长或者正确的后缀越靠后那么寻找的次数就会越多从而影响构建时的性能。在配置resolve.extensions 时需要遵守以下几点以做到尽可能的优化构建性能:
后缀尝试列表要尽可能的小不要把项目中不可能存在的情况写到后缀尝试列表中频率出现最高的文件后缀要优先放在最前面以做到尽快的退出寻找过程在源码中写导入语句时要尽可能的带上后缀
对应的配置如下
module.exports {resolve: {// 尽可能的减少后缀尝试的可能性extensions: [tsx,ts, json],},
};优化 module.noParse 配置
该配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理这样做的好处是能提高构建性能。 原因是一些库例如 jQuery 、ChartJS 它们庞大又没有采用模块化标准让 Webpack 去解析这些文件耗时又没有意义。
const path require(path);module.exports {module: {noParse: [/echart/],},
};使用DllPlugin
DllPlugin 是 Webpack 5 中的一个插件用于优化构建速度尤其是在大型项目中。它允许将一些常用的第三方库如 React, Lodash 等预构建成动态链接库DLL然后在实际的项目构建中进行引用从而避免每次构建时都重新打包这些常用库提高构建效率。
作用
提高构建速度将一些不常改变的库单独打包成 DLL 文件之后每次构建时只需要引用这个 DLL 文件不需要重新构建这些依赖。优化缓存通过分离常用库和项目代码Webpack 可以更好地利用缓存机制避免不必要的重复构建。代码拆分DllPlugin 可以帮助更好地拆分代码减少每次打包的负担。
如何使用
创建一个 DLL 配置文件
在项目根目录下创建一个新的 webpack.dll.js 配置文件这个文件将用于专门打包第三方库。
// webpack.dll.js
const path require(path);
const webpack require(webpack);module.exports {entry: {vendor: [react, react-dom, lodash] // 列出你想预构建的库},output: {path: path.resolve(__dirname, dist),filename: [name].dll.js,library: [name]_library // 这会指定 DLL 文件的暴露全局变量名},plugins: [new webpack.DllPlugin({name: [name]_library,path: path.resolve(__dirname, dist, [name]-manifest.json) // 指定 manifest 文件的路径})]
};在上面的配置中
entry 指定了要打包的库vendor你可以根据需求添加其他常用的第三方库。 output.library 定义了 DLL 文件暴露的全局变量[name] 会被替换为 vendor。 DllPlugin 插件的 path 配置会生成一个 vendor-manifest.json 文件Webpack 在主构建时会根据这个 manifest 文件引用 DLL 文件。
使用 DLL 文件
在主 webpack.config.js 文件中通过 DllReferencePlugin 引用之前生成的 DLL 文件。
// webpack.config.js
const path require(path);
const webpack require(webpack);module.exports {entry: ./src/index.js,output: {path: path.resolve(__dirname, dist),filename: bundle.js},plugins: [// 引用 DllPlugin 生成的 DLL 文件new webpack.DllReferencePlugin({manifest: path.resolve(__dirname, dist, vendor-manifest.json)})]
};
这里 DllReferencePlugin 会确保你的主项目构建过程中引用 DLL 文件而不需要重新构建这些库。
运行构建
执行 webpack --config webpack.dll.js 来构建 DLL 文件这将会生成 vendor.dll.js 和 vendor-manifest.json。执行主项目构建webpack --config webpack.config.jsWebpack 会使用 DLL 文件来加速构建。
使用ParallelUglifyPlugin
当我们代码开发结束上线时都会有代码压缩这里流程最常见的代码压缩工具就是uglifyJS,webpack也已经内置了这个工具。当我们在上线时构建代码需要先将代码解析成AST语法树在去应用各种规则分析和处理AST这个过程计算量巨大非常的耗费时间。我们可以使用ParallelUglifyPlugin 多线程并行的去做这些事这样就能加快构建速度。ParallelUglifyPlugin 会开启多个子进程把对多个文件的压缩工作分配给多个子进程完成这样就能更快的完成压缩工作。 使用 ParallelUglifyPlugin 也非常简单把原来 Webpack 配置文件中内置的 UglifyJsPlugin 去掉后再替换成 ParallelUglifyPlugin相关代码如下
const path require(path);
const DefinePlugin require(webpack/lib/DefinePlugin);
const ParallelUglifyPlugin require(webpack-parallel-uglify-plugin);module.exports {plugins: [// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码new ParallelUglifyPlugin({// 传递给 UglifyJS 的参数uglifyJS: {output: {// 最紧凑的输出beautify: false,// 删除所有的注释comments: false,},compress: {// 在UglifyJs删除没有用到的代码时不输出警告warnings: false,// 删除所有的 console 语句可以兼容ie浏览器drop_console: true,// 内嵌定义了但是只用到一次的变量collapse_vars: true,// 提取出出现多次但是没有定义成变量去引用的静态值reduce_vars: true,}},}),],
};压缩代码
压缩代码 浏览器从服务器访问网页时获取的 JavaScript、CSS 资源都是文本形式的文件越大网页加载时间越长。 为了提升网页加速速度和减少网络传输流量可以对这些资源进行压缩。 压缩的方法除了可以通过 GZIP 算法对文件压缩外还可以对文本本身进行压缩
压缩ES6
虽然当前大多数 JavaScript 引擎还不完全支持 ES6 中的新特性但在一些特定的运行环境下已经可以直接执行 ES6 代码了例如最新版的 Chrome、ReactNative 的引擎 JavaScriptCore 运行 ES6 的代码相比于转换后的 ES5 代码有如下优点
一样的逻辑用 ES6 实现的代码量比 ES5 更少JavaScript 引擎对 ES6 中的语法做了性能优化例如针对 const 申明的变量有更快的读取速度
压缩ES6需要使用UglifyES而不是UglifyJS压缩ES5
压缩CSS
CSS 代码也可以像 JavaScript 那样被压缩以达到提升加载速度和代码混淆的作用。 目前比较成熟可靠的 CSS 压缩工具是 cssnano基于 PostCSS cssnano 能理解 CSS 代码的含义而不仅仅是删掉空格例如
margin: 10px 20px 10px 20px 被压缩成 margin: 10px 20pxcolor: #ff0000 被压缩成 color:red
Tree shaking
Tree shaking 可以用来剔除js中没有使用到的代码他依赖ES6的模块化语法比如通过import和export导出最早Tree shaking是在Rollup中出现后来在webpack2.0中引入。 举个例子比如我们有一个util.js的文件里面有两个函数
export function funcA() {}export function funcB() {}在我们的index.js文件中会导入util.jsindex.js代码如下
import { funcA } from ./util.js
funcA()经过Tree shaking之后的util的代码如下
export function funcA() {}因为我们只用到了funcA这一段函数代码没有用到的funcB代码就会被剔除 需要注意的是要让 Tree Shaking 正常工作的前提是交给 Webpack 的 JavaScript 代码必须是采用 ES6 模块化语法的 因为 ES6 模块化语法是静态的导入导出语句中的路径必须是静态的字符串而且不能放入其它代码块中这让 Webpack 可以简单的分析出哪些 export 的被 import 过了。 如果你采用 ES5 中的模块化例如 module.export{…}、require(xy)、if(x){require(‘./util’)}Webpack 无法分析出哪些代码可以剔除 目前的 Tree Shaking 还有些的局限性经实验发现
不会对entry入口文件做 Tree Shaking不会对按序加载出去的代码做 Tree Shaking
使用TreeShaking
webpack5中如果是在生产模式下会自动调用Scope Hoisting
开启 Scope Hoisting
Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快 它又译作 “作用域提升”是在 Webpack3 中新推出的功能。
什么是 Scope Hoisting
比如有两个文件分别是util.js和index.js其中代码分别如下
// util.jsexport default Hello, Scope hoisting//index.js
import str from ./util.js
console.log(str)以上代码打包之后如下
[(function (module, __webpack_exports__, __webpack_require__) {var __WEBPACK_IMPORTED_MODULE_0__util_js__ __webpack_require__(1);console.log(__WEBPACK_IMPORTED_MODULE_0__util_js__[a]);}),(function (module, __webpack_exports__, __webpack_require__) {__webpack_exports__[a] (Hello,Webpack);})
]开启了Scope hoisting 之后打包的代码如下
[(function (module, __webpack_exports__, __webpack_require__) {var util (Hello,Webpack);console.log(util);})
]可以看到开启 Scope hoisting 之后函数申明由两个直接变成了一个util.js中的定义内容直接被注入到了index.js模块中这样的好处是
代码体积更小因为函数申明语句会产生大量代码代码在运行时因为创建的函数作用域更少了内存开销也随之变小
Scope Hoisting 的基本原理是分析出模块之间的依赖关系尽可能的把打散的模块合并到一个函数中去但前提是不能造成代码冗余所以只有那些被引用了一次的模块才能被合并。 和Tree shaking一样使用Scope Hoisting需要分析出模块之间的依赖关系所以源代码必须使用ES6模块化语句。不然无法生效
配置Scope Hoisting
webpack5中如果是在生产模式下会自动调用Scope Hoisting
提取公共代码
在日常开发中一个站点往往是由很多页面组成的这些页面一般都是采用相同的技术栈和同一套代码样式并且我们在开发时也会抽离很多可复用的代码。 如果每个页面都把这些公共的代码打包进去就会造成下面的问题
相同的资源被重复加载浪费用户的流量和服务器的成本每个页面需要加载的资源太大导致网页首屏加载缓慢影响用户体验
针对上面的问题我们可以把公共的代码抽离成单独的文件就可以优化上面的问题原因是如果用户访问了网站中的某个页面那么访问这个站点下的其他页面的概率也非常大。用户在第一次访问之后这些公共代码就被缓存起来这样用户切换到其他页面时缓存的代码文件就不会被重复加载。可以直接从缓存中获取这样就有以下好处
减少网络传输流量降低服务器成本虽然用户第一次打开网站的速度得不到优化但之后访问其它页面的速度将大大提升 在webpack5中可以使用optimization.splitChunks 提取公共代码一个简单的示例如下 optimization: {moduleIds: deterministic,usedExports: true,splitChunks: {chunks: all,minSize: 20000,minChunks: 1,maxAsyncRequests: 30,maxInitialRequests: 30,enforceSizeThreshold: 50000,cacheGroups: {// React相关库react: {test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,name: react,priority: 0},// UI库antd: {test: /[\\/]node_modules[\\/]antd[\\/]/,name: antd,priority: -1},// 其他第三方库vendors: {test: /[\\/]node_modules[\\/]/,name: vendors,priority: -10},// 分离公共组件commons: {name: commons,minChunks: 2,priority: -20}}}},分割代码按需加载
为什么要分割代码按需加载
随着互联网的发展一个网页需要承载的功能越来越多。 对于采用单页应用作为前端架构的网站来说会面临着一个网页需要加载的代码量很大的问题因为许多功能都集中的做到了一个 HTML 里。 这会导致网页加载缓慢、交互卡顿用户体验将非常糟糕。 导致这个问题的根本原因在于一次性的加载所有功能对应的代码但其实用户每一阶段只可能使用其中一部分功能。 所以解决以上问题的方法就是用户当前需要用什么功能就只加载这个功能对应的代码也就是所谓的按需加载。
如何使用按需加载
在给单页应用做按需加载优化时一般采用以下原则
把整个网站划分成一个个小功能再按照每个功能的相关程度把它们分成几类把每一类合并为一个 Chunk按需加载对应的 Chunk对于用户首次打开你的网站时需要看到的画面所对应的功能不要对它们做按需加载而是放到执行入口所在的 Chunk 中以降低用户能感知的网页加载时间对于个别依赖大量代码的功能点例如依赖 Chart.js 去画图表、依赖 flv.js 去播放视频的功能点可再对其进行按需加载
被分割出去的代码的加载需要一定的时机去触发也就是当用户操作到了或者即将操作到对应的功能时再去加载对应的代码。 被分割出去的代码的加载时机需要开发者自己去根据网页的需求去衡量和确定。 由于被分割出去进行按需加载的代码在加载的过程中也需要耗时你可以预言用户接下来可能会进行的操作并提前加载好对应的代码从而让用户感知不到网络加载时间。
如何用 Webpack 实现按需加载
在 Webpack 5 中按需加载Lazy Loading 是通过 动态导入Dynamic Import 来实现的主要依靠 ES6 提供的 import() 语法。这样可以让 Webpack 在需要的时候才加载特定的模块从而减少初始加载时间提高应用的性能
Webpack 遇到 import() 语法时会自动将导入的模块拆分成一个单独的 chunk并在运行时动态加载它。
比如现在首页有一个按钮只有点击这个按钮的时候才会去加载对应的工具文件去调用里面的计算方法。代码大体如下
const loadDemo async () {try {// 使用 webpack 的动态导入语法const { sum, multiply } await import(/* webpackChunkName: demo */ ./demo).then(module module);console.log(Dynamic import result:, sum(2, 3));console.log(Dynamic import result:, multiply(2, 3));} catch (error) {console.error(Failed to load demo module:, error);}
};// 创建一个按钮来触发动态加载
const button document.createElement(button);
button.textContent Load Demo Module;
button.onclick loadDemo;
document.body.appendChild(button);//demo.ts
export const sum (a: number, b: number): number {console.log(sum function is loaded dynamically);return a b;
};export const multiply (a: number, b: number): number {console.log(multiply function is loaded dynamically);return a * b;
};
import(‘./demo.js’) 会告诉 Webpack 将 demo.js 单独打包为一个 chunk如 demo.bundle.js。 当 btn 按钮被点击时才会请求加载 demo.bundle.js这样就实现了按需加载。
webpack输出分析
我们需要对输出结果做分析以决定下一步的优化方向。 但由于 Webpack 输出的代码可读性非常差而且文件非常大这会让你非常头疼。 为了更简单直观的分析输出结果社区中出现了许多可视化的分析工具。这些工具以图形的方式把结果更加直观的展示出来可以帮助我们快速看到问题所在。 接下来看如何使用这些工具帮我们做优化 在启动 Webpack 时支持两个参数分别是
–profile记录下构建过程中的耗时信息–json以 JSON 的格式输出构建结果最后只输出一个 .json 文件这个文件中包括所有构建相关的信息 在启动 Webpack 时带上以上两个参数启动命令如下 webpack --profile --json stats.json你会发现项目中多出了一个 stats.json 文件。 这个 stats.json 文件是给后面介绍的可视化分析工具使用的。 webpack --profile --json 会输出字符串形式的 JSON stats.json 是 UNIX/Linux 系统中的管道命令、含义是把 webpack --profile --json 输出的内容通过管道输出到 stats.json 文件中。
官方的可视化工具
Webpack 官方提供了一个可视化分析工具 Webpack Analyse它是一个在线 Web 应用。 打开 Webpack Analyse 链接的网页后你就会看到一个弹窗提示你上传 JSON 文件这个就是上面生成的json文件传上去之后会有这样的一个面板展示 可以看到有6个模块当我们点击module中就可以看到当前各个模块的依赖关系 点击 Hints查看输出过程中的耗时分布效果图如下
webpack-bundle-analyzer
webpack-bundle-analyzer 是另一个可视化分析工具 它虽然没有官方那样有那么多功能但比官方的要更加直观。 先来看下它的效果图
它能方便的让你知道
打包出的文件中都包含了什么每个文件的尺寸在总体中的占比一眼看出哪些文件尺寸大模块之间的包含关系每个文件的 Gzip 后的大小 接入 webpack-bundle-analyzer 的方法很简单步骤如下
安装 webpack-bundle-analyzer 到全局执行命令 npm i -g webpack-bundle-analyzer按照上面提到的方法生成 stats.json 文件在项目根目录中执行 webpack-bundle-analyzer 后浏览器会打开对应网页看到以上效果。
如何在webpack中使用
在配置文件中添加下面的配置
// webpack.config.js
const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin;new BundleAnalyzerPlugin({analyzerMode: server,analyzerHost: 127.0.0.1,analyzerPort: 8888,reportFilename: report.html,defaultSizes: parsed,openAnalyzer: true,generateStatsFile: false,statsFilename: stats.json,statsOptions: null,logLevel: info}),在package.json中添加下面的命令
// package.jsonanalyze: cross-env NODE_ENVproduction webpack --profile --json stats.json webpack-bundle-analyzer stats.json直接运行即可对当前项目进行分析