webpack5 入门学习笔记(四)性能优化
webpack5 入门学习笔记(四)性能优化
- 写在前面
- webpack优化配置
- 开发环境性能优化
- 生产环境性能优化
- HMR
- 定义
- 作用
- 使用方法
- 要点记录
- 代码实现
- source-map
- 定义
- 作用
- 使用方法
- 代码实现
- oneOf
- 定义
- 作用
- 使用方法
- 要点记录
- 代码实现
- 缓存
- 作用
- 使用方法
- 要点记录
- 代码实现
- tree shaking
- 定义
- 作用
- 使用方法
- 要点记录
- 代码实现
- code split
- entry points(入口起点)
- 定义
- 使用方法
- 要点记录
- 代码实现
- prevent duplication(防止重复)
- 定义
- 使用方法
- Entry dependencies
- SplitChunksPlugin
- 要点记录
- 代码实现
- import(动态引入)
- 定义
- 使用方法
- 要点记录
- 代码实现
- lary loading
- 定义
- 使用方法
- 要点记录
- 代码实现
- PWA
- 定义
- 使用方法
- 要点记录
- 代码实现
- 多进程打包
- 定义
- 使用方法
- 要点记录
- 代码实现
- external
- 定义
- 使用方法
- 要点记录
- 代码实现
- 写在后面
写在前面
webpack优化配置
开发环境性能优化
- 优化打包构建速度
HMR
模块热替换- 优化代码调试
source-map
生产环境性能优化
- 优化打包构建速度
oneOf
babel 缓存
多进程打包
同一时间干多件事,提升打包速度,进程开启和交流也有开销,消耗时间比较长的任务,使用 thread-loader 对 babel-loader 进行优化external
声明不用打包的库,再用cdn
引进来- 优化代码运行的性能
缓存
(hash-chunkhash-contenthash)tree shaking
(es6 模块,production)(package.json 中 sideEffects:[‘xxx’])code split
{单页面,单入口通过 import 引入的一定会被分割成单独的打包,optimization}懒加载/预加载
- 代码分割是异步执行,使用懒加载
- 预加载等其他资源加载完再加载,预加载兼容性问题大用之慎重
pwa
离线可访问,serviceworker 和 cache,使网络离线可访问,兼容性问题严重
HMR
定义
- hot module replacement 热模块替换 / 模块热替换
作用
- 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
使用方法
devServer
中加上hot:true
devServer: {hot:true}
- 样式文件:可以使用
HMR
功能 ,因为style-loader
内部实现了,所以开发环境下用style-loader
,但是生产环境下还是提取css
为单独文件,就不能使用了loader:'style-loader'
- html文件: 默认不能使用
HMR
功能,开启HMR
同时会导致- 问题:
html
文件不能热更新了 - 解决:修改
entry
入口,将html
文件引入(html
文件只有一个,不用做HMR
功能)
//入口文件entry:['./src/js/index.js','./src/index.html'],
- 问题:
- js文件:默认不能使用
HMR
功能 -->需要修改js
代码,添加支持HMR
功能的代码- 注意:
HMR
功能对js
的处理,只能处理非入口js
文件的其他文件 - 在
index.js
文件中加入以下代码
if (module.hot) {//全局中寻找hot//一旦module.hot为true,说明开启了HMR功能。 -->让HMR功能代码生效module.hot.accept('./print.js', function () {//方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建//会执行后面的回调函数print()}) }
- 注意:
要点记录
- 当修改了
webpack
配置,新配置要想生效必须重启webpack
服务
代码实现
const { resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//入口文件entry:['./src/js/index.js','./src/index.html'],//出口output: {//输出的文件名filename: 'js/bundle.js',//输出的路径path: resolve(__dirname, 'dist'),clean:true //清理旧文件},module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},{test: /\.less$/,use: ['style-loader', 'css-loader','less-loader']},{//处理图片资源(样式中的图片资源,html中的是处理不了的)test: /\.(jpg|png|gif)$/,loader: 'url-loader', //通过es6module解析options: {limit: 8 * 1024,name: '[name].[hash:10].[ext]',//关闭es6模块化,开启commonjs模块化esModule: false,outputPath:'img'}},{//处理html中的img资源test: /\.html$/,loader: 'html-loader',options: {esModule: false}},{//处理其他资源exclude: /\.(html|js|css|less|jpg|png|gif)$/,loader: 'file-loader', //url-loader是基于file-loader封装的,多一些压缩功能,比如讲图片转成base64格式options: {name: '[name].[hash:10].[ext]',outputPath:'media'}}]},plugins: [new HtmlWebpackPlugin({template: './src/index.html',//inject: 'head',scriptLoading: 'blocking',title:'Hot Module Replacement'}),],mode: 'development',target: 'web',devServer: {contentBase: './dist',//启动gzip压缩compress: true,//端口号port: 8888,//首次运行自动打开浏览器open: 'Chrome',// 开启HMR功能//当修改了webpack配置,新配置要想生效必须重启webpack服务hot: true}};
source-map
定义
source-map
: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射关系可以追踪源代码错误)
作用
- 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
使用方法
devtool:'source-map'
基本配置devtool: 'source-map' //生产环境选择devtool: 'eval-source-map' //开发环境选择
- 几个可选参数:
[inline-|hidden|eval-][nosources-][cheap-[module-]]source-map
- 内联和外部
- 外部生成了
map
文件 - 内联在
bundle.js
文件中 - 内联构建速度更快
- 外部生成了
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-cheap-source-map
第一eval-source-map
- 调试友好
source-map
第一cheap-module-source-map
cheap-source-map
- 又快又好 – >
eval-source-map / eval-cheap-module-source-map
- 生产环境:源代码要不要隐藏? 调试要不要更友好
- 内联会让代码体积变大,所以在生产环境不用内联
- 源代码隐藏
nosources-source-map
全部隐藏hidden-source-map
只隐藏源代码,会提示构建后代码错误- – >
调试友好 source-map / 速度快 cheap-module-source-map
代码实现
const { resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//入口文件entry:['./src/js/index.js','./src/index.html'],//出口output: {//输出的文件名filename: 'js/bundle.js',//输出的路径path: resolve(__dirname, 'dist'),clean:true //清理旧文件},module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},{test: /\.less$/,use: ['style-loader', 'css-loader','less-loader']},{//处理图片资源(样式中的图片资源,html中的是处理不了的)test: /\.(jpg|png|gif)$/,loader: 'url-loader', //通过es6module解析options: {limit: 8 * 1024,name: '[name].[hash:10].[ext]',//关闭es6模块化,开启commonjs模块化esModule: false,outputPath:'img'}},{//处理html中的img资源test: /\.html$/,loader: 'html-loader',options: {esModule: false}},{//处理其他资源exclude: /\.(html|js|css|less|jpg|png|gif)$/,loader: 'file-loader', //url-loader是基于file-loader封装的,多一些压缩功能,比如讲图片转成base64格式options: {name: '[name].[hash:10].[ext]',outputPath:'media'}}]},plugins: [new HtmlWebpackPlugin({template: './src/index.html',//inject: 'head',scriptLoading: 'blocking'}),],mode: 'development',target: 'web',devServer: {contentBase: './dist',//启动gzip压缩compress: true,//端口号port: 8888,//首次运行自动打开浏览器open: 'Chrome',// 开启HMR功能//当修改了webpack配置,新配置要想生效必须重启webpack服务hot: true},//devtool: 'source-map' //生产环境选择devtool: 'eval-source-map' //开发环境选择};
oneOf
定义
- 以下
loader
只会匹配一个
作用
- 优化生产环境打包构建速度
使用方法
- 在众多
loader
外嵌套一层oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},//...]
要点记录
oneOf
里不能有两个配置处理同一种类型文件
代码实现
/*** oneOf:优化生产环境打包构建速度*///绝对路径解析方法const { resolve } = require("path");//提取css成单独文件插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')//最新压缩css插件const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');//手动添加webpack内部js压缩器const TerserWebpackPlugin = require('terser-webpack-plugin');//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production'//复用loaderconst commonCssloader = [MiniCssExtractPlugin.loader,//提取css为单独文件'css-loader',{//兼容性处理 还需要在package.json中定义browserslistloader: 'postcss-loader',options: {postcssOptions: {ident: 'postcss',plugins: ['postcss-preset-env']}}}]module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},//压缩cssoptimization: {minimize: true,minimizer: [new TerserWebpackPlugin(),new CssMinimizerPlugin()]},module: {rules: [/*** 正常来讲,一个文件只能被一个loader处理* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序* 在loader中设置enforce:'pre',最先执行*/{//优化生产环境打包构建速度//以下loader只会匹配一个//注意:这里不能有两个配置处理同一种类型文件oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},{//less兼容性处理test: /\.less$/,use: [...commonCssloader,//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间'less-loader'//将less转成css文件],},{//js兼容性处理test: /\.js$/,exclude: /node_modules/,loader:'babel-loader' //配置在.babelrc文件中},{//图片处理test: /\.(jpg|png|gif)$/,loader: 'url-loader',options: {limit: 8 * 1024,//对8kb以下的图片进行base64处理name: '[name].[hash:8].[ext]',//对处理后的图片重命名outputPath: 'img', //设置处理后的图片路径publicPath: './', //指定打包后的css图片的基础路径,esModule:false //关闭es6模块化}},{//处理html中的图片问题test: /\.html$/,loader: 'html-loader',options: {esModule:false}},// 打包iconfont字体图标,和打包图片类似{test:/\.ttf$/,use: {loader: 'file-loader',options: {esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为falsename: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主// 是对页面引入资源的补充,比如img标签引入或者css引入等.// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件// 一般只设置output的publicPath,方便统一管理limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大}}},{//处理其他文件exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,loader: 'file-loader',options: {name: '[name].[hash:8].[ext]',outputPath:'media'},// type: 'asset/resource',// generator: {// filename: 'media/[name].[hash:6].[ext]'// }},]}]},devtool: 'source-map',//错误追踪plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.css'}),//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}})],//mode为production,js就自动压缩了mode:'production'}
缓存
作用
- babel缓存
- 让第二次打包构建速度更快
- 文件缓存
- 让代码上线运行缓存更好使用 上线代码性能优化
使用方法
- 开启
babel
缓存{//js兼容性处理test: /\.js$/,exclude: /node_modules/,loader: 'babel-loader', //配置在.babelrc文件中options: {//开启babel缓存,第二次构建时会读取之前的缓存//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。cacheDirectory:true }}
- 添加
contenthash
值output: {filename: 'js/bundle.[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.[contenthash:10].css'}),}
- 创建临时服务器 新建server.js文件 启动指令 node server.js
/*** 服务器代码* 启动指令 node server.js* 使用nodemon server.js 需要全局安装nodemon npm i nodemon -g*/const express = require('express')const app = express();app.use(express.static('dist', { maxAge: 1000 * 3600 }));app.listen(3000);
- 打包之后,启动服务器,在浏览器中查看,
network
,刷新就能看到,后面是通过读取缓存内容显示的,速度也会更快
要点记录
- babel缓存
- 将
babel
处理后的资源缓存起来(哪里的js
改变就更新哪里,其他js
还是用之前缓存的资源),让第二次打包构建速度更快 - 因为
js
文件最多 编译过程 类似HMR
功能,但是生产环境不能用HMR功能 - 所以在生产环境下 开启
babel缓存
,第二次构建时,会读取之前的缓存 - "cacheDirectory":true
- 问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。
- 解决:使用
hash
命名,通过更换文件名来判断哪些文件需要更新
- 文件缓存
- hash :每次
webpack
构建时会生成一个唯一的hash
值 - 问题:因为
js
和css
同时使用一个hash
值。如果重新打包,会导致所有的缓存失效。但是我可能是只改动了一个文件 - chunkhash:根据
chunk
生成的hash
值,如果打包来源于同一个chunk
,那么hash
值就一样 - 问题:
js
和css
的hash
值还是一样的,因为css
是在js
中被引入的,所以同属于一个chunk
- contenthash:根据文件的内容生成
hash
值,不同文件的hash
值一定不一样
代码实现
//绝对路径解析方法const { resolve } = require("path");//提取css成单独文件插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')//最新压缩css插件const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');//手动添加webpack内部js压缩器const TerserWebpackPlugin = require('terser-webpack-plugin');//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production'//复用loaderconst commonCssloader = [MiniCssExtractPlugin.loader,//提取css为单独文件'css-loader',{//兼容性处理 还需要在package.json中定义browserslistloader: 'postcss-loader',options: {postcssOptions: {ident: 'postcss',plugins: ['postcss-preset-env']}}}]module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},//压缩cssoptimization: {minimize: true,minimizer: [new TerserWebpackPlugin(),new CssMinimizerPlugin()]},module: {rules: [/*** 正常来讲,一个文件只能被一个loader处理* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序* 在loader中设置enforce:'pre',最先执行*/{//优化生产环境打包构建速度//以下loader只会匹配一个//注意:这里不能有两个配置处理同一种类型文件oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},{//less兼容性处理test: /\.less$/,use: [...commonCssloader,//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间'less-loader'//将less转成css文件],},{//js兼容性处理test: /\.js$/,exclude: /node_modules/,loader: 'babel-loader', //配置在.babelrc文件中options: {//开启babel缓存,第二次构建时会读取之前的缓存//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。cacheDirectory:true }},{//图片处理test: /\.(jpg|png|gif)$/,loader: 'url-loader',options: {limit: 8 * 1024,//对8kb以下的图片进行base64处理name: '[name].[hash:8].[ext]',//对处理后的图片重命名outputPath: 'img', //设置处理后的图片路径publicPath: './', //指定打包后的css图片的基础路径,esModule:false //关闭es6模块化}},{//处理html中的图片问题test: /\.html$/,loader: 'html-loader',options: {esModule:false}},// 打包iconfont字体图标,和打包图片类似{test:/\.ttf$/,use: {loader: 'file-loader',options: {esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为falsename: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主// 是对页面引入资源的补充,比如img标签引入或者css引入等.// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件// 一般只设置output的publicPath,方便统一管理limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大}}},{//处理其他文件exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,loader: 'file-loader',options: {name: '[name].[hash:8].[ext]',outputPath:'media'},// type: 'asset/resource',// generator: {// filename: 'media/[name].[hash:6].[ext]'// }},]}]},devtool: 'source-map',//错误追踪plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.[contenthash:10].css'}),//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}})],//mode为production,js就自动压缩了mode: 'production'}
tree shaking
定义
- 将应用程序想象成一棵树。实际使用的源代码和库表示树的绿色活叶。死代码表示秋天被树木消耗的褐色枯叶,为了摆脱枯叶,必须摇晃树,使它们掉落
- 官方文档
作用
tree shaking
:去除无用代码,
使用方法
- 使用
ES2015
模块语法(即import
和export
) - 开启
production
环境 (webpack4
还必须使用es6
模块化) - 在
package.json
中配置无副作用的文件{"name":"your-project","sideEffects": ["*.css","*.less","./src/xxx"], }
要点记录
- “副作用”:在导入时执行特殊行为的代码,而不公开一个或多个导出。
"sideEffects":false
- 所有代码都没有副作用(都可以进行tree shaking)问题:可能会把css文件删掉
"sideEffects": true
所有文件都有副作用,
- 全都不可 tree-shaking
"sideEffects": ["*.css","*.less"]
- 除了css,less文件有副作用,所有其他文件都可以 tree-shaking,但会保留这些文件
"sideEffects"
类似于,/*#__PURE__*/
但在模块级别而不是语句级别。- 通过使用
/*#__PURE__*/
注释,可以告诉webpack函数调用是无副作用的(纯净的)。可以将其放在函数调用之前,以将其标记为无副作用。传递给函数的参数不会被注释标记,可能需要单独标记。当未使用变量的变量声明中的初始值被视为无副作用(纯净)时,它将被标记为无效代码,不被minimizer
执行并丢弃,optimization.innerGraph
设置为true
的时候启用。/*#__PURE__*/ double(55);
代码实现
//绝对路径解析方法const { resolve } = require("path");//提取css成单独文件插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')//最新压缩css插件const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');//手动添加webpack内部js压缩器const TerserWebpackPlugin = require('terser-webpack-plugin');//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production'//复用loaderconst commonCssloader = [MiniCssExtractPlugin.loader,//提取css为单独文件'css-loader',{//兼容性处理 还需要在package.json中定义browserslistloader: 'postcss-loader',options: {postcssOptions: {ident: 'postcss',plugins: ['postcss-preset-env']}}}]module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},//压缩cssoptimization: {minimize: true,minimizer: [new TerserWebpackPlugin(),new CssMinimizerPlugin()]},module: {rules: [{oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},{//less兼容性处理test: /\.less$/,use: [...commonCssloader,//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间'less-loader'//将less转成css文件],},{//js兼容性处理test: /\.js$/,exclude: /node_modules/,loader: 'babel-loader', //配置在.babelrc文件中options: {//开启babel缓存,第二次构建时会读取之前的缓存//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。cacheDirectory:true }},{//图片处理test: /\.(jpg|png|gif)$/,loader: 'url-loader',options: {limit: 8 * 1024,//对8kb以下的图片进行base64处理name: '[name].[hash:8].[ext]',//对处理后的图片重命名outputPath: 'img', //设置处理后的图片路径publicPath: './', //指定打包后的css图片的基础路径,esModule:false //关闭es6模块化}},{//处理html中的图片问题test: /\.html$/,loader: 'html-loader',options: {esModule:false}},// 打包iconfont字体图标,和打包图片类似{test:/\.ttf$/,use: {loader: 'file-loader',options: {esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为falsename: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主// 是对页面引入资源的补充,比如img标签引入或者css引入等.// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件// 一般只设置output的publicPath,方便统一管理limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大}}},{//处理其他文件exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,loader: 'file-loader',options: {name: '[name].[hash:8].[ext]',outputPath:'media'},// type: 'asset/resource',// generator: {// filename: 'media/[name].[hash:6].[ext]'// }},]}]},devtool: 'source-map',//错误追踪plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.[contenthash:10].css'}),//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}})],//mode为production,js就自动压缩了mode: 'production'}
code split
- 可以将代码分成多个
bundles
,然后可以按需或并行加载,可以用于实现更小的bundle
和控制资源加载优先级 - 官方文档
entry points(入口起点)
定义
- 使用
entry
配置手动地分离代码 - 官方文档
使用方法
- 将入口设置为一个对象
- 更改输出文件名
//单入口//entry: './src/js/index.js',entry: {index: './src/index.js',another: './src/another-module.js',},output: {//filename: 'main.js',filename: '[name].bundle.js'},
要点记录
- 多入口:一个入口 ,输出就有一个
bundle
- 问题:如果入口
chunk
之间包含一些重复的模块,那些重复模块都会被引入到各个bundle
中。 - 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。
代码实现
/*** code split entry point* 入口为一个对象* entry:{}*///绝对路径解析方法const { resolve } = require("path");//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//单入口//entry: './src/js/index.js',entry: {//多入口:一个入口 --> 输出就有一个bundleindex: './src/js/index.js',add: './src/js/add.js'},output: {//[name]:取文件名filename: 'js/[name].[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},plugins: [//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式})],mode: 'development',}
prevent duplication(防止重复)
定义
- 使用
Entry dependencies
或者SplitChunksPlugin
去重和分离chunk
- 官方文档
使用方法
Entry dependencies
- 配置
dependOn option
选项然后再加上optimization.runtimeChunk: 'single'
,否则会出问题,这样可以在多个chunk
之间共享模块module.exports = {mode: 'development',entry: {index: {import: './src/index.js',dependOn: 'shared',},another: {import: './src/another-module.js',dependOn: 'shared',},shared: 'lodash',},output: {filename: '[name].bundle.js'},optimization: {runtimeChunk: 'single',}, };
除了生成
shared.bundle.js
,index.bundle.js
和another.bundle.js
之外,还生成了一个runtime.bundle.js
文件
SplitChunksPlugin
SplitChunksPlugin
插件可以将公共的依赖模块提取到现有的入口chunk
中,或者提取到一个新生成的chunk
。让我们使用这个插件,将在此之前的示例中重复的lodash
模块移除module.exports = {mode: 'development',entry: {index: './src/index.js',another: './src/another-module.js',},output: {filename: '[name].bundle.js',path: path.resolve(__dirname, 'dist'),},optimization: {splitChunks: {chunks: 'all',},},};
有了
optimization.splitChunks
配置选项后,我们现在应该看到从index.bundle.js
中删除了重复的依赖项another.bundle.js
。插件将lodash
分离到单独的块
要点记录
- 尽管可以在
webpack
中允许每个页面使用多入口,应确保避免使用多入口的入口:entry: { page: ['./analytics', './app'] }
。如此,在使用async
脚本标签时,会有更好的优化以及一致的执行顺序 index.js
文件这样引入jquery
import $ from 'jquery';console.log($);
直接打包会将
jquery
和index.js
打包成一个文件,加上splitChunks.chunks为all
,就能将引入的第三方库,单独打包出来,还能分析多入口chunk
中有没有公共文件(>30kb),有就单独打包
代码实现
/*** prevent duplication(防止重复)*///绝对路径解析方法const { resolve } = require("path");//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//单入口entry: './src/js/index.js',// entry: {// index: './src/js/index.js',// add: './src/js/add.js'// },output: {//[name]:取文件名filename: 'js/[name].[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},optimization: {/*** 单入口只有1,多入口1、2都有* 1. 可以将node_modules中代码单独打包成一个chunk最终输出* 2. 自动分析多入口chunk中有没有公共的文件(>30kb),如果有会单独打包成一个chunk*/splitChunks: {chunks:'all'}},plugins: [//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式})],mode: 'development',}
import(动态引入)
定义
- 通过模块的内联函数调用来分离代码
- 官方文档
- import()
function(string path):Promise
动态加载模块。详细 - require.ensure() 特定于webpack 详细
- import()
使用方法
- 通过
js
代码,让某个文件被单独打包成一个chunk
import
动态导入语法:能将某个文件单独打包/*** 通过js代码,让某个文件被单独打包成一个chunk* import动态导入语法:能将某个文件单独打包*/import(/* webpackChunkName:'add' */'./add').then(({ mul, count }) => {console.log('add文件加载成功');console.log("3+9=" + mul(3, 9));console.log("3-9="+count(3,9));}).catch(() => {console.log('文件加载失败');})
要点记录
- 语法
import(/* webpackChunkName:'test' */'./test')
代码实现
/*** code split* 单入口,import()动态引入*///绝对路径解析方法const { resolve } = require("path");//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//单入口entry: './src/js/index.js',output: {//[name]:取文件名filename: 'js/[name].[contenthash:5].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},plugins: [//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式})],mode: 'development',}
lary loading
定义
- 懒加载 前提:进行代码分割,当文件需要使用时才加载
- 官方文档
- 预加载
prefetch
:会在使用之前,提前加载js
文件 使用慎之又慎。详细- 正常加载可以认为是并行加载(同一时间加载多个文件)
- 预加载 :等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
使用方法
- 通过
js
代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包document.getElementById('btn').onclick = function () {import(/* webpackChunkName:'add',webpackPrefetch:true */'./add').then(({ mul }) => {console.log(mul(4, 5));})}
要点记录
- 增加一个交互,当用户单击按钮的时候用
console
打印一些文字,但是会等到第一次交互的时候再加载那个代码块(print.js
)
代码实现
/*** lary 懒加载*///绝对路径解析方法const { resolve } = require("path");//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {//单入口entry: './src/js/index.js',output: {//[name]:取文件名filename: 'js/[name].[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},optimization: {splitChunks: {chunks:'all'}},plugins: [//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}})],//mode为production,js就自动压缩了mode: 'production',}
PWA
定义
- 渐进式Web应用程序(或
PWA
)是可提供与本机应用程序相似的体验的Web
应用程序。有很多事情可以促成这一点。在这些功能中,最重要的是使应用程序能够在离线状态下运行的能力,这是通过使用称为Service Workers
的网络技术来实现的 - 官方文档
使用方法
- 安装插件
npm i workbox-webpack-plugin -D
- webpack.config.js中添加
new WorkboxWebpackPlugin.GenerateSW({/*** 1. 帮助serviceworker快速启动* 2. 删除旧的 serviceworker* * 生成一个 serviceworker 配置文件 index.js中注册*/clientsClaim: true,skipWaiting:true})
- index.js中注册serviceWorker 处理兼容性问题
if ('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/service-worker.js').then(() => {console.log('sw注册成功了');}).catch(() => {console.log('sw注册失败了');})})}
- 安装
npm i serve -g
- 启动服务器,将
dist
目录下所有资源作为静态资源暴露出去 把网络设置为离线之后就可以在application
中查看serviceworker
中的文件了,使得网站可以离线访问serve -s dist
要点记录
serviceworker
代码必须运行在服务器上
代码实现
/*** PWA:渐进式网络开发应用程序(离线可访问)* workbox --> workbox-webpack-plugin*/// workbox 下载插件const WorkboxWebpackPlugin =require('workbox-webpack-plugin')//绝对路径解析方法const { resolve } = require("path");//提取css成单独文件插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')//最新压缩css插件const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');//手动添加webpack内部js压缩器const TerserWebpackPlugin = require('terser-webpack-plugin');//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production'//复用loaderconst commonCssloader = [MiniCssExtractPlugin.loader,//提取css为单独文件'css-loader',{//兼容性处理 还需要在package.json中定义browserslistloader: 'postcss-loader',options: {postcssOptions: {ident: 'postcss',plugins: ['postcss-preset-env']}}}]module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},//压缩cssoptimization: {minimize: true,minimizer: [new TerserWebpackPlugin(),new CssMinimizerPlugin()]},module: {rules: [/*** 正常来讲,一个文件只能被一个loader处理* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序* 在loader中设置enforce:'pre',最先执行*/{//优化生产环境打包构建速度//以下loader只会匹配一个//注意:这里不能有两个配置处理同一种类型文件oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},{//less兼容性处理test: /\.less$/,use: [...commonCssloader,//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间'less-loader'//将less转成css文件],},{//js兼容性处理test: /\.js$/,exclude: /node_modules/,loader: 'babel-loader', //配置在.babelrc文件中options: {//开启babel缓存,第二次构建时会读取之前的缓存//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。cacheDirectory:true }},{//图片处理test: /\.(jpg|png|gif)$/,loader: 'url-loader',options: {limit: 8 * 1024,//对8kb以下的图片进行base64处理name: '[name].[hash:8].[ext]',//对处理后的图片重命名outputPath: 'img/', //设置处理后的图片路径publicPath: './img', //指定打包后的css图片的基础路径,esModule:false //关闭es6模块化}},{//处理html中的图片问题test: /\.html$/,loader: 'html-loader',options: {esModule: false}},// 打包iconfont字体图标,和打包图片类似{test:/\.ttf$/,loader: 'file-loader',options: {esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为falsename: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主// 是对页面引入资源的补充,比如img标签引入或者css引入等.// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件// 一般只设置output的publicPath,方便统一管理limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大}},{//处理其他文件exclude: /\.(js|css|less|html|jpg|png|gif)$/,loader: 'file-loader',options: {// esModule: false,name: '[name].[hash:8].[ext]',outputPath: 'media/',publicPath:'../media'},// type: 'asset/resource',// generator: {// filename: 'media/[name].[hash:6].[ext]'// }},]}]},plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.[contenthash:10].css'}),//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}}),new WorkboxWebpackPlugin.GenerateSW({/*** 1. 帮助serviceworker快速启动* 2. 删除旧的 serviceworker* * 生成一个 serviceworker 配置文件 index.js中注册*/clientsClaim: true,skipWaiting:true})],//mode为production,js就自动压缩了mode: 'production',devtool:'source-map'}
多进程打包
定义
- 在大项目中使用
thread-loader
,提升构建速度 - 官方文档
使用方法
- 安装插件
npm i thread-loader -D
- 开启多进程打包,在
babel-loader
前面添加,可以在options
中进行配置{loader: 'thread-loader',// options: {// workers:2 // 产生的工作线程的数量,默认为(cpu数量- 1)或当require('os').cpus()未定义时,返回1// }},
要点记录
js
一般比较多,一般给babel-loader
用,工作时间最长的loader
,所以用thread-loader
优化- 使用了这个
loader
之后:loaders
不能发出文件loaders
不能使用自定义loader
API(即通过插件)loaders
无法访问webpack options
- 项目较大,打包较慢,开启多进程能提高速度
- 项目较少,打包很快,开启多进程会降低速度,进程启动大概为600ms,进程通信也有开销
代码实现
/*** 多进程打包*/// workbox 下载插件const WorkboxWebpackPlugin =require('workbox-webpack-plugin')//绝对路径解析方法const { resolve } = require('path');//提取css成单独文件插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')//最新压缩css插件const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');//手动添加webpack内部js压缩器const TerserWebpackPlugin = require('terser-webpack-plugin');//对html文件处理const HtmlWebpackPlugin = require('html-webpack-plugin');//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production'//复用loaderconst commonCssloader = [MiniCssExtractPlugin.loader,//提取css为单独文件'css-loader',{//兼容性处理 还需要在package.json中定义browserslistloader: 'postcss-loader',options: {postcssOptions: {ident: 'postcss',plugins: ['postcss-preset-env']}}}]module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.[contenthash:10].js',path:resolve(__dirname,'dist'),clean: true,publicPath:'./'},//压缩cssoptimization: {minimize: true,minimizer: [new TerserWebpackPlugin(),new CssMinimizerPlugin()]},module: {rules: [/*** 正常来讲,一个文件只能被一个loader处理* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序* 在loader中设置enforce:'pre',最先执行*/{//优化生产环境打包构建速度//以下loader只会匹配一个//注意:这里不能有两个配置处理同一种类型文件oneOf: [{//css兼容性处理test: /\.css$/,use: [...commonCssloader],},{//less兼容性处理test: /\.less$/,use: [...commonCssloader,//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间'less-loader'//将less转成css文件],},{//js兼容性处理test: /\.js$/,exclude: /node_modules/,use: [/*** 开启多进程打包* 进程启动大概为600ms,进程通信也有开销* 只有工作消耗时间比较长,才需要多进程打包* js一般比较多,工作时间最长的loader,所以用thread-loader优化*/{loader: 'thread-loader',options: {workers:2 //进程2个 ,产生的工作线程的数量,默认为(cpu数量- 1)CPU核数减一}},{loader: 'babel-loader', //配置在.babelrc文件中options: {//开启babel缓存cacheDirectory:true }}]},{//图片处理test: /\.(jpg|png|gif)$/,loader: 'url-loader',options: {limit: 8 * 1024,//对8kb以下的图片进行base64处理name: '[name].[hash:8].[ext]',//对处理后的图片重命名outputPath: 'img/', //设置处理后的图片路径publicPath: './img', //指定打包后的css图片的基础路径,esModule:false //关闭es6模块化}},{//处理html中的图片问题test: /\.html$/,loader: 'html-loader',options: {esModule:false}},{//处理其他文件exclude: /\.(js|css|less|html|jpg|png|gif)$/,loader: 'file-loader',options: {name: '[name].[hash:8].[ext]',outputPath: 'media/',publicPath:'../media'},// type: 'asset/resource',// generator: {// filename: 'media/[name].[hash:6].[ext]'// }},]}]},plugins: [//提取css为单独文件new MiniCssExtractPlugin({filename:'css/built.[contenthash:10].css'}),//对html文件处理new HtmlWebpackPlugin({template: './src/index.html',scriptLoading: 'blocking',//去除script defer模式//html压缩mimify: {//移除空格collaspeWhitespace: true,//移除注释removeComments:true}}),new WorkboxWebpackPlugin.GenerateSW({/*** 1. 帮助serviceworker快速启动* 2. 删除旧的 serviceworker* * 生成一个 serviceworker 配置文件 index.js中注册*/clientsClaim: true,skipWaiting:true})],//mode为production,js就自动压缩了mode: 'production',devtool:'source-map'}
external
定义
Externals
配置项用来告诉Webpack
要构建的代码中使用了哪些不用被打包的模块,也就是说这些模版是外部环境提供的,Webpack
在打包时可以忽略它们- 官方文档
使用方法
- 在module.exports中添加,要排除的包名
externals: {//拒绝jQuery被打包进来 在html界面通过script标签引入cdn链接jquery:'jQuery'}
- 在html界面通过script标签引入cdn链接
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
要点记录
- cdn
boostrapcdn
开源网站,优化webpack
打包 external
可以防止框架的二次引用
代码实现
const { resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {entry: './src/js/index.js',output: {filename: 'js/bundle.js',path: resolve(__dirname, 'dist')},plugins: [new HtmlWebpackPlugin({template: './src/index.html'})],mode: 'production',externals: {//拒绝jQuery被打包进来 在html界面通过script标签引入cdn链接jquery:'jQuery'}};
写在后面
优化配置,这里终于告一段落,学习的过程中有很多都已经变化了,所以附了很多官方文档的链接,还是要结合最新的文档配合学习。
未选择的路,心里永远会有一个设想,如果当初选择了另一条路会怎样,其实无论是什么样子,总不会是自己在这种情境下期待的样子,当你身处局中时,你的选择改变不了未来的,我们只能走一条路,在踏上的那一刻,就是一条全新的路了,未来因此刻的选择而变化,过去的结点没有办法回转。
webpack5 入门学习笔记(四)性能优化相关推荐
- ROS中的roslaunch命令和launch文件(ROS入门学习笔记四)
ROS中的基本对象和概念学习笔记(ROS入门学习笔记一) ROS中创建工作区和包(ROS入门学习笔记二) ROS功能包中CMakeLists.txt的说明(ROS入门学习笔记三) 1.roslaunc ...
- Android八大模块进阶学习笔记(性能优化、百大框架、高级UI、Flutter、Kotlin...)
今年来,Android开发行业的就业形势愈加严峻,无论刚刚入门Android学习没有头绪的.还是开发多年想要突破薪资范畴的,都需要跳出编码和业务的局限,学会选型.扩展, 提升编程思维,建立良好的职业规 ...
- 《南溪的目标检测学习笔记》——性能优化的学习笔记
介绍 性能优化有两种方式: 理论分析 实验测试 1 理论分析 关于模型的性能分析,请参考博文<Roofline Model与深度学习模型的性能分析 (by Michael Yuan)>:
- 动态博弈、威胁与承诺(博弈论入门学习笔记四)
0 动态博弈 动态博弈:行动有先后顺序,不同的参与人在不同时点行动,先行动者的选择影响后行动者的选择空间,后行动者可以观察到先行动者做了什么选择.例如下棋.消费者和商家讨价还价.谈婚论嫁.企业之间的价 ...
- 斋藤康毅-深度学习入门 学习笔记四
ch 神经网络的学习 损失函数 1.1 均方误差 import numpy as npdef mean_squared_error(y, t):return 0.5 * np.sum((y - t) ...
- 代理重加密-入门学习笔记(四)
代理重加密(PRE)(重密码学!) 原文: https://blog.csdn.net/Black_BearB/article/details/81228030 1.基本思想-流程结算 在云计算中,云 ...
- UE4入门学习笔记——纪念学习虚幻引擎满一周年
UE4入门学习笔记 前言: 今天是正式学习ue4一周年.一年前的今天,我结束了PBR流程的学习,怀揣着对游戏制作的热爱,正式开始学习ue4,继续追寻儿时的那个大厂梦.谁也没想到,一年后的今天,我会在T ...
- MySQL高级学习笔记(四)
文章目录 MySQL高级学习笔记(四) 1. MySql中常用工具 1.1 mysql 1.1.1 连接选项 1.1.2 执行选项 1.2 mysqladmin 1.3 mysqlbinlog 1.4 ...
- 激光SLAM入门学习笔记
激光SLAM入门学习笔记 激光SLAM入门学习笔记 一.推荐阅读书籍 二.推荐公众号.知乎.博客 1.公众号 2.知乎 3.博客 三.推荐阅读论文&代码(参考泡泡机器人) 2D激光SLAM 3 ...
- .net core底层入门学习笔记(十一-JIT编译器)
.net core底层入门学习笔记(十一) 本篇开始记录JIT编译器实现 文章目录 .net core底层入门学习笔记(十一) 前言 一.JIT编译器介绍 二.JIT编译流程 1.JIT编译触发 2. ...
最新文章
- ionic中的后退方法
- Babylon.js 3.3发布:更强大的粒子系统和WebVR支持
- Repeater 嵌套 绑定数据,嵌套的Repeater无法绑定的问题
- c语言%f小数位第六位是错的,c语言中输出浮点型数据,如果不指定输出位数,%f输出几位小数?...
- python实现udp聊天室_python网络编程基础--socket的简介,以及使用socket来搭建一个简单的udp小程序...
- JavaScript一个简易枚举类型实现扑克牌
- JAVA Useful Program(1)
- excel实时获取基金信息的实现方法
- 智能优化算法:粒子群算法相关代码
- iOS 版本更新迭代
- ATTCK实战系列一(内网渗透入门)
- 虚拟机运行python_虚的解释|虚的意思|汉典“虚”字的基本解释
- 妙用thead封装vue组件
- d630 无线驱动 linux,DELL D630安装CentOS6的无线网卡驱动
- 最大传输单元:MTU
- #4【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃鸡!(未完成)
- mongoose用模型更新不了,因为模型对象中默认带有_id会提示errmsg: “Performing an update on the path ‘_id‘ would modify the i
- poj 1659 Havel-hakimi定理
- 负载均衡、DNS、F5、反向代理、LVS、四层与七层、CDN
- OWASP TOP 10 漏洞指南(2021)
热门文章
- 保护模式下的80386及其编程03:保护虚拟地址方式
- 计算机财务基础知识,财务部计算机基础知识培训.ppt
- linux系统下面所有命令都失效了,显示bash: xxxxx: command not found...
- 贴片铝电容识别及型号_电路板上的贴片电容怎样核实它的型号及参数是多少
- 【可视化】使用PS将图片从白底换成其他底色时,如何保留头发边缘的发丝
- 【Photoshop】证件照换底色
- matlab基波有效值,基波有效值
- 电流测试c语言算法,真有效值的定义及其C语言算法推导
- phpcms v9给栏目添加自定义英文栏目名称字段图文教程
- c# 指定打开某个路径下的CMD_C# 插入、删除Excel分页符