响应式设计 手机网站,手机自己制作app软件,58同城推广怎么收费,用网站建设与管理创业上一篇#xff1a;Node.js与webpack#xff08;三#xff09;-CSDN博客
webpack原理 yu 优化 本章节主要介绍 Webpack 高级配置。 所谓高级配置其实就是进行 Webpack 优化#xff0c;让我们代码在编译/运行时性能更好~ 我们会从以下角度来进行优化#xff1a; 提升开发体…上一篇Node.js与webpack三-CSDN博客
webpack原理 yu 优化 本章节主要介绍 Webpack 高级配置。 所谓高级配置其实就是进行 Webpack 优化让我们代码在编译/运行时性能更好~ 我们会从以下角度来进行优化 提升开发体验提升打包构建速度减少代码体积优化代码运行性能 待优化配置文件webpack.prod.js
// node.js核心模块path专门处理路径
const path require(path);// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinizerPlugin require(css-minimizer-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径entry: ./src/main.js,// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的filename: static/js/main.js,// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,exclude: /node_modules/, // 排除第三方js文件loader: babel-loader,},]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/main.css,}),// CSS压缩new CssMinizerPlugin(),],// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
}1.提升开发体验
SourceMap增强调试开发和生产模式 SourceMap源代码映射是一个用来生成源代码与构建后代码一一映射的文件的方案。 它会生成一个 xxx.map 文件里面包含源代码和构建后代码每一行、每一列的映射关系。 当构建后代码出错了会通过 xxx.map 文件从构建后代码出错位置找到映射后源代码出错位置从而让浏览器提示源代码文件出错位置帮助我们更快的找到错误根源。 问题引入 所有 css 和 js 合并成了一个文件并且多了其他代码。此时如果代码运行出错那么提示代码错误位置我们是看不懂的。 一旦将来开发代码文件很多那么很难去发现错误出现在哪里。 所以我们需要更加准确的错误提示来帮助我们更好的开发代码。 开发时我们的在开发模式下编译输出的效果是这样的(开发模式一般不输出所以可以在浏览器中查看) 一旦发生了报错 这里的报错提示有可能不太正确 一旦项目代码多了起来这里的报错提示等于没有 官方文档教程
Devtool | webpack 中文文档 (docschina.org) 乱七八糟有很多不过老师说一般开发用这两种 cheap-module-source-map开发模式 cheap(便宜): 只映射行不管列缺点 打包构建速度快一些优点 module.exports {// 其他省略mode: development,devtool: cheap-module-source-map,
}; source-map生产模式 行列都映射优点 打包构建速度更慢缺点 module.exports {// 其他省略mode: production,devtool: source-map,
}; 使用 正常打包 2.提升打包构建速度
HMR热模块开发模式 HotModuleReplacementHMR/热模块替换在程序运行中替换、添加或删除模块而无需重新加载整个页面。 问题引入 开发时我们修改了其中一个模块代码Webpack 默认会将所有模块全部重新打包编译速度很慢。几分钟甚至几小时 所以我们需要做到修改某个模块代码就只有这个模块代码需要重新打包编译其他模块不变这样打包速度就能很快。 module.exports {// 其他省略devServer: {host: localhost, // 启动服务器域名port: 3000, // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能只能用于开发环境生产环境不需要了},
}; 此时 css 样式经过 style-loader 处理已经具备 HMR 功能了。 但是 js 还不行。 入口js代码 npm start 修改一下设置了热更新的count.js OneOf开发和生产模式
问题引入 假如资源目录中有一个.css文件他要先找到【css-loader】如果第一个loader不是【css-loader】那么他会一直向下找即使找到后并不会立即处理css文件而是接着匹配下面所有的loader为止。即使第一个就是要找的【css-loader】他也会把下面所有loader匹配一遍。每个文件都要将所有loader匹配一遍才会处理文件太慢。 我们要优化的效果是匹配成功就不找了直接处理文件。 将下列所有loader剪切到{ oneOf: [] }中 {oneOf: []
}
{// 每个文件匹配loader成功则立即处理不再匹配其他loaderoneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中use: [style-loader, css-loader],},// 处理less资源{test: /\.less$/,use: [style-loader, css-loader, less-loader],},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,use: [style-loader, css-loader, sass-loader],},// 处理stylus资源{test: /\.styl$/,use: [style-loader, css-loader, stylus-loader],},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,exclude: /node_modules/, // 排除第三方js文件loader: babel-loader,},] Include/exclude开发和生产模式
问题引入 之前学过这个配置之前用的是忽略(exclude)现在加一个Include(只包含) 开发时我们需要使用第三方的库或插件所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。 所以我们在对 js 文件处理时要排除 node_modules 下面的文件。 比如我这个配置里的babel 和 eslint是用来处理js文件的 include
包含只处理 xxx 文件exclude
排除除了 xxx 文件以外其他文件都处理 Cache缓存开发和生产模式
问题引入 每次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译速度比较慢。 我们可以缓存之前的 Eslint 检查 和 Babel 编译结果这样第二次打包时速度就会更快了。 构建时处理样式图片等都比较快就是js最慢 优化效果只对于修改状态的js代码进行Eslint检查和Babel编译对于没修改的使用之前缓存的的检查结果和编译结果 Thread多进程打包开发和生产模式 问题引入 当项目越来越庞大时打包速度越来越慢甚至于需要一个下午才能打包出来代码。这个速度是比较慢的。 我们想要继续提升打包速度其实就是要提升 js 的打包速度因为其他文件都比较少。 而对 js 文件处理主要就是 eslint 、babel、Terser(webpack自带生产模式自动压缩) 三个工具所以我们要提升它们的运行速度。 我们可以开启多进程同时处理 js 文件这样速度就比之前的单进程打包更快了。 安装依赖
npm i thread-loader -D
生产模式 给eslint babel terser等处理js资源的插件或loader配置多进程 生产模式webpack 会自动启动这个插件我们只是要设置一些配置所以要重写 webpack.prod.js
// node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径entry: ./src/main.js,// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的filename: static/js/main.js,// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/main.css,}),],optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
} 开发模式不需要压缩
webpack.dev.js
// node.js核心模块path专门处理路径
const path require(path);// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// node.js核心模块直接使用
const os require(os);
// 检查服务器cpu核数
const threads os.cpus().length;// webpack默认支持的是CommonJS语法
module.exports {// publicPath : /static/,// 入口相对路径从项目根目录为起点 这里还必须加 ./ entry: ./src/main.js,// 输出output: {// __dirname获取当前项目根目录的绝对路径path: undefined, // 开发模式不输出// 出口文件名dist下的filename: static/js/main.js,// 打包时自动清空上次打包资源// clean: true, // 开发模式没有输出不清空},// loader加载器module: {// 写正则作为匹配规则rules: [{// 每个文件匹配loader成功则立即处理不再匹配其他loaderoneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中use: [style-loader, css-loader],},// 处理less资源{test: /\.less$/,use: [style-loader, css-loader, less-loader],},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,use: [style-loader, css-loader, sass-loader],},// 处理stylus资源{test: /\.styl$/,use: [style-loader, css-loader, stylus-loader],},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src),// 只处理../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 关闭缓存文件的压缩}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略node_modules下的js文件cache: true, // 开启缓存cacheLocation: path.resolve(__dirname,../node_modules/.cache/eslintcache),threads, // 开启多进程和设置进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),],// 开发服务器配置, webpack官方提供的小型服务器devServer: {host: localhost, // 启动服务器域名port: 3030, // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能只能用于开发环境生产环境不需要了// historyApiFallback: true// historyApiFallback:{// index:/dist/static/index.html// },},// 模式mode: development, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: cheap-module-source-map, // 开发模式专用只映射行不管列
}3.减少代码体积
Tree Shaking默认 开发时我们定义了一些工具函数库或者引用第三方工具函数库或组件库。 如果没有特殊处理的话我们打包时会引入整个库但是实际上可能我们可能只用上极小部分的功能。 这样将整个库都打包进来体积就太大了。 Tree Shaking 是一个术语通常用于描述移除 JavaScript 中的没有使用上的代码。 注意它依赖 ES Module。 Webpack 已经默认开启了这个功能无需其他配置。 Babel开发和生产模式 Babel 为编译的每个文件都插入了辅助代码使代码体积过大 Babel 对一些公共方法使用了非常小的辅助代码比如 _extend。默认情况下会被添加到每一个需要它的文件中。 你可以将这些辅助代码作为一个独立模块来避免重复引入。避免重复定义定义一次只要引用就好 babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入而是引入 babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。 npm i babel/plugin-transform-runtime -D Image Minimizer开发和生产模式安装依赖失败 开发如果项目中引用了较多图片那么图片体积会比较大将来请求速度比较慢。 我们可以对图片进行压缩减少图片体积。 注意如果项目中图片都是在线链接那么就不需要了。本地项目静态图片才需要进行压缩。老师说在线图片无法进行压缩 安装依赖Github要加速喔
npm i image-minimizer-webpack-plugin imagemin -D关于无损压缩与有损压缩有损压缩 与 无损压缩-CSDN博客
无损压缩我这里使用这个
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D安装依赖失败就不压缩了等以后选择其他的方法压缩图片 4.优化代码运行性能
Code Split生产模式开发也可以但是我不用
多入口(代码分割) 代码分割Code Split主要做了两件事 分割文件将打包生成的文件进行分割生成多个 js 文件。按需加载需要哪个文件就加载哪个文件。 打包代码时会将所有 js 文件(入口文件)打包到一个js文件(出口文件)中体积太大了。我们如果只要渲染首页就应该只加载首页的 js 文件其他文件不应该加载。 所以我们需要将打包生成的文件进行代码分割生成多个 js 文件渲染哪个页面就只加载某个 js 文件这样加载的资源就少速度就更快。 文件目录(之前的main.js删掉)
├── public
├── src
| ├── app.js
| └── count.js
├── package.json
└── webpack.config.js // node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径// entry: ./src/main.js, // 单入口用字符串entry: {// 有多个入口文件多入口app: ./src/js/app.js,main: ./src/js/count.js,},// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的// filename: static/js/main.js, // 单入口filename: static/js/[name].js, // webpack命名方式[name]以入口文件名命名入口文件使得名字相同// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: [babel/plugin-transform-runtime], // 减少代码体积}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/main.css,}),],optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
}提取重复代码 如果多入口文件中都引用了同一份代码我们不希望这份代码被打包到两个文件中导致代码重复体积更大。 我们需要提取多入口的重复代码只打包生成一个 js 文件其他文件引用它就好。 重复引用的js代码写到一个文件中复用 webpack.prod.js
// node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径// entry: ./src/main.js, // 单入口用字符串entry: {// 有多个入口文件多入口app: ./src/js/app.js,main: ./src/js/main.js,},// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的// filename: static/js/main.js, // 单入口filename: static/js/[name].js, // webpack命名方式[name]以入口文件名命名入口文件使得名字相同// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: [babel/plugin-transform-runtime], // 减少代码体积}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/main.css,}),],// 压缩和优化都在这里做optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],// 代码分割配置splitChunks: {chunks: all, // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests// cacheGroups: { // 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// default: { // 其他没有写的配置会使用上面的默认值// minChunks: 2, // 这里的minChunks权重更大// priority: -20,// reuseExistingChunk: true,// },// },// 修改配置cacheGroups: {// 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// 上面默认的都是公共配置这里自己写配置覆盖就好default: {// 其他没有写的配置会使用上面的默认值minSize: 0, // 我们定义的文件体积太小了所以要改打包的最小文件体积minChunks: 2,priority: -20,reuseExistingChunk: true,},},},},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
} math.js
export const sum (...args) {return args.reduce((p, c) p c, 0);};
app.js
import { sum } from ./math;console.log(hello app);
console.log(sum(1, 2, 3, 4, 5));main.js
import { sum } from ./math;console.log(hello main);
console.log(sum(1, 2, 3, 4, 5));打包
npm run build 按需加载动态导入
问题引入 默认情况下用户进入网站所有的资源都会同时加载如果用户只是想点个按钮还要等其他资源加载好会发生阻塞 我们想做到按需加载点击按钮就加载按钮的资源不需要的资源可以不加载提高性能 使用 import 动态导入语法来进行代码按需加载我们也叫懒加载比如路由懒加载就是这样实现的 app.js
console.log(hello app);main.js
// import { sum } from ./math.js;console.log(hello main);document.getElementById(btn).onclick function () {// 动态导入 -- 实现按需加载// 即使只被引用了一次也会代码分割import(./math.js).then((res) {console.log(模块加载成功, res.sum(1, 2, 3));}).catch((err) {console.log(模块加载失败, err);})
};math.js
export const sum (...args) {return args.reduce((p, c) p c, 0);}; 页面加个按钮
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title/head
bodyspan哈哈哈/spanbutton idbtn计算/button
/body
/html
打包动态导入的代码没有直接加载 只是加载了两个hello 点击按钮才加载 报错 这个错误通常是由于ESLint配置不正确或不支持ES6模块的语法导致的。(我也不懂明明一直用起来都支持ES module和CommonJS的一到按需加载就报错) 后来得知eslint不能识别动态导入需要需要额外追加配置 解决办法
参考文章ESLint配置中报错Parsing error: import and export may only appear at the top leve-代码-码姐姐 (dude6.com)
安装依赖
npm i babel/eslint-parser -D
.eslintrc.js 还有一个办法是这样的我也没用过 单入口 开发时我们可能是单页面应用SPA只有一个入口单入口。 // node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径entry: ./src/main.js, // 单入口用字符串// entry: {// // 有多个入口文件多入口// app: ./src/js/app.js,// main: ./src/js/main.js,// },// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的// filename: static/js/main.js, // 单入口filename: static/js/[name].js, // webpack命名方式[name]以入口文件名命名入口文件使得名字相同// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选generator: {filename: static/img/[hash: 8][ext][query],},}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的generator: {filename: static/media/[hash: 8][ext][query],},},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: [babel/plugin-transform-runtime], // 减少代码体积}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/main.css,}),],// 压缩和优化都在这里做optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],// 代码分割配置splitChunks: {chunks: all, // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests// cacheGroups: { // 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// default: { // 其他没有写的配置会使用上面的默认值// minChunks: 2, // 这里的minChunks权重更大// priority: -20,// reuseExistingChunk: true,// },// },// 修改配置// cacheGroups: {// // 组哪些模块要打包到一个组// // defaultVendors: { // 组名// // test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// // priority: -10, // 权重越大越高// // reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// // },// // 上面默认的都是公共配置这里自己写配置覆盖就好// default: {// // 其他没有写的配置会使用上面的默认值// minSize: 0, // 我们定义的文件体积太小了所以要改打包的最小文件体积// minChunks: 2,// priority: -20,// reuseExistingChunk: true,// },// },},},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
}
动态导入分割模块自定义命名魔法命名
问题引入 默认的命名莫名其妙我们希望自定义命名 要分割的部分自定义命名为math webpack.prod.js 这里的[name]引用的是自定义的魔法命名后缀多加的.chunk也可以不加主要是用来区分 统一命名配置 推荐使用这种命名规则([name])单入口和多入口随意切换还能自定义文件名 目前涉及到的命名有 图片、字体等资源 样式资源也可能会多入口 Preload / Prefetch生产模式
webpack配置preload和prefetch预加载技术_webpack 预加载-CSDN博客 我们前面已经做了代码分割同时会使用 import 动态导入语法来进行代码按需加载我们也叫懒加载比如路由懒加载就是这样实现的。 但是加载速度还不够好比如是用户点击按钮时才加载这个资源的如果资源体积很大那么用户会感觉到明显卡顿效果。 我们想在浏览器空闲时间加载后续需要使用的资源。我们就需要用上 Preload 或 Prefetch 技术。 Preload告诉浏览器立即加载资源。 Prefetch告诉浏览器在空闲时才开始加载资源偷偷加载。 它们共同点 都只会加载资源并不执行。都有缓存。 它们区别 Preload加载优先级高Prefetch加载优先级低。Preload只能加载当前页面需要使用的资源加载下一个页面时上一个页面的资源会清空Prefetch可以加载当前页面资源也可以加载下一个页面需要使用的资源不会清空有缓存。 总结 当前页面优先级高的资源用 Preload 加载。下一个页面需要使用的资源用 Prefetch 加载。 它们的问题兼容性较差。 我们可以去 Can I Useopen in new window 网站查询 API 的兼容性问题。Preload 相对于 Prefetch 兼容性好一点。 安装依赖
npm i vue/preload-webpack-plugin -D // node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 预加载
const PreloadWebpackPlugin require(vue/preload-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径entry: ./src/main.js, // 单入口用字符串// entry: {// // 有多个入口文件多入口// app: ./src/js/app.js,// main: ./src/js/main.js,// },// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的// filename: static/js/main.js, // 单入口filename: static/js/[name].js, // webpack命名方式[name]以入口文件名命名入口文件使得名字相同chunkFilename: static/js/[name].chunk.js, // 这里name引用的是自定义的魔法命名assetModuleFilename: static/media/[name].[hash: 8][ext], // 图片、字体等资源命名方式注意用hash// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选// generator: {// filename: static/img/[hash: 8][ext][query],// },}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的// generator: {// filename: static/media/[hash: 8][ext][query],// },},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: [babel/plugin-transform-runtime], // 减少代码体积}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/[name].css,chunkFilename: static/css/[name].chunk.css,}),// 空闲时加载资源new PreloadWebpackPlugin({rel: preload, // preload兼容性更好as: script,// rel: prefetch // prefetch兼容性更差}),],// 压缩和优化都在这里做optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],// 代码分割配置splitChunks: {chunks: all, // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests// cacheGroups: { // 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// default: { // 其他没有写的配置会使用上面的默认值// minChunks: 2, // 这里的minChunks权重更大// priority: -20,// reuseExistingChunk: true,// },// },// 修改配置// cacheGroups: {// // 组哪些模块要打包到一个组// // defaultVendors: { // 组名// // test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// // priority: -10, // 权重越大越高// // reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// // },// // 上面默认的都是公共配置这里自己写配置覆盖就好// default: {// // 其他没有写的配置会使用上面的默认值// minSize: 0, // 我们定义的文件体积太小了所以要改打包的最小文件体积// minChunks: 2,// priority: -20,// reuseExistingChunk: true,// },// },},},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
}Network Cache生产模式 将来开发时我们对静态资源会使用缓存来优化这样浏览器第二次请求资源就能读取缓存了速度很快。 但是这样的话就会有一个问题, 因为前后输出的文件名是一样的都叫 main.js一旦将来发布新版本因为文件名没有变化导致浏览器会直接读取缓存不会加载新资源项目也就没法更新了。 所以我们从文件名入手确保更新前后文件名不一样这样就可以做缓存了。 它们都会生成一个唯一的 hash 值。 fullhashwebpack4 是 hash 每次修改任何一个文件所有文件名的 hash 至都将改变。所以一旦修改了任何一个文件整个项目的文件缓存都将失效。 chunkhash 根据不同的入口文件(Entry)进行依赖文件解析、构建对应的 chunk生成对应的哈希值。我们 js 和 css 是同一个引入会共享一个 hash 值。 contenthash 根据文件内容生成 hash 值只有文件内容变化了hash 值才会变化。所有文件 hash 值是独享且不同的。 runtime文件 问题 当我们修改 math.js 文件再重新打包的时候因为 contenthash 原因math.js 文件 hash 值发生了变化这是正常的。 但是 main.js 文件的 hash 值也发生了变化这会导致 main.js 的缓存失效。明明我们只修改 math.js, 为什么 main.js 也会变身变化呢 原因 更新前math.xxx.js, main.js 引用的 math.xxx.js更新后math.yyy.js, main.js 引用的 math.yyy.js, 文件名发生了变化间接导致 main.js 也发生了变化 解决 将 hash 值单独保管在一个 runtime 文件中。 我们最终输出三个文件main、math、runtime。当 math 文件发送变化变化的是 math 和 runtime 文件main 不变。 runtime 文件只保存文件的 hash 值和它们与文件关系整个文件体积就比较小所以变化重新请求的代价也小。 main.js引入了math.js当修改math.js的内容时math.js的hash值文件名会改变但同样也带着main.js改变因为引入了 // node.js核心模块path专门处理路径
const path require(path);
// node.js核心模块os
const os require(os);
// 获取服务器的cpu核数
const threads os.cpus().length;// 处理HTML文件
const HtmlWebpackPlugin require(html-webpack-plugin);// Eslint插件
const ESLintPlugin require(eslint-webpack-plugin);// 处理CSS从默认style标签 到 link标签
const MiniCssExtractPlugin require(mini-css-extract-plugin);// CSS压缩
const CssMinimizerPlugin require(css-minimizer-webpack-plugin);// js压缩
const TerserPlugin require(terser-webpack-plugin);// 预加载
const PreloadWebpackPlugin require(vue/preload-webpack-plugin);// 获取处理样式的Loaders
const getStyleLoaders (preProcessor) {return [MiniCssExtractPlugin.loader,css-loader,{loader: postcss-loader,options: {postcssOptions: {plugins: [postcss-preset-env, // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);};// webpack默认支持的是CommonJS语法
module.exports {// 入口相对路径// entry: ./src/main.js, // 单入口用字符串entry: {// 有多个入口文件多入口app: ./src/js/app.js,main: ./src/js/main.js,},// 输出output: {// __dirname获取当前项目根目录的绝对路径// dist 为输出目录自动创建path: path.resolve(__dirname, ../dist), // 生产模式需要输出// 出口文件名dist下的// filename: static/js/main.js, // 单入口filename: static/js/[name].[contenthash:8].js, // webpack命名方式[name]以入口文件名命名入口文件使得名字相同chunkFilename: static/js/[name].[contenthash:8].chunk.js, // 这里name引用的是自定义的魔法命名assetModuleFilename: static/media/[name].[hash: 8][ext], // 图片、字体等资源命名方式注意用hash// 打包时自动清空上次打包资源clean: true,},// loader加载器module: {// 写正则作为匹配规则rules: [{oneOf: [// 处理css资源{// 正则匹配一个 以 .css结尾的文件test: /\.css$/,// loader在数组中的执行顺序是从右到左// style-loader将js中的css通过创建style标签的形式添加到html文件中生效// css-loader将css资源编译成commonJS的模板到js中// use: [style-loader, css-loader],// use: [MiniCssExtractPlugin.loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// css-loader],use: getStyleLoaders(),},// 处理less资源{test: /\.less$/,// use: [style-loader, css-loader, less-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// less-loader],use: getStyleLoaders(less-loader),},// 处理sass 和 scss资源{test: /\.s[ac]ss$/,// use: [style-loader, css-loader, sass-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// sass-loader],use: getStyleLoaders(sass-loader),},// 处理stylus资源{test: /\.styl$/,// use: [style-loader, css-loader, stylus-loader],// use: [MiniCssExtractPlugin.loader, css-loader, // {// loader: postcss-loader,// options: {// postcssOptions: {// plugins: [// postcss-preset-env, // 能解决大多数样式兼容性问题// ],// },// },// },// stylus-loader],use: getStyleLoaders(stylus-loader),},// 处理媒体图片资源{test: /\.(png|jpe?g|gif|webp|svg)$/,// asset提供dataUrl来处理小图片转base64编码type: asset,parser: {// 小于 10k的图片进行转字符串处理// 体积略大一点换来的是减少一次资源请求dataUrlCondition: {maxSize: 10 * 1024, },// 修改图片资源输出路径, dist下的// hash: 8命名只取生成的hash值的前8位// ext: 后缀名// query: 请求头查询字符串可选// generator: {// filename: static/img/[hash: 8][ext][query],// },}},// 处理字体图标文件以及音视频文件{test: /\.(ttf|woff2?|mp3|mp4|avi)$/,type: asset/resource,// 为什么是这个类型我也不知道官网上写的// 修改媒体资源输出路径dist下的// generator: {// filename: static/media/[hash: 8][ext][query],// },},// 处理JS文件JS解析器Babel{test: /\.js$/,// exclude: /node_modules/, // 排除第三方js文件include: path.resolve(__dirname, ../src), // 只处理 ../src下的js文件use: [{loader: thread-loader,// 开启多进程options: {works: threads, // 设置多进程数量}},{loader: babel-loader,options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: [babel/plugin-transform-runtime], // 减少代码体积}}]},]}]},// 插件plugins: [// plugin的配置new ESLintPlugin({// context指定需要检查的文件根目录一般只检查src下的文件context: path.resolve(__dirname, ../src),exclude: node_modules, // 忽略 node_modules下的js文件cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,../node_modules/.cache/.eslintcache),threads, // 开启多进程和设置多进程数量}),new HtmlWebpackPlugin({// 以public/index.html为模板创建文件// 新html文件内容结构与源文件一致自动引入打包生成的js等资源template: path.resolve(__dirname, ../public/index.html),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: static/css/[name].[contenthash:8].css,chunkFilename: static/css/[name].[contenthash:8].chunk.css,}),// 空闲时加载资源new PreloadWebpackPlugin({rel: preload, // preload兼容性更好as: script,// rel: prefetch // prefetch兼容性更差}),],// 压缩和优化都在这里做optimization: {minimize: true,// 开启最小化压缩minimizer: [// css压缩也可以写到optimization.minimizer里面效果一样的new CssMinimizerPlugin(),// js压缩当生产模式会默认开启TerserPlugin但是我们需要进行其他配置就要重新写了new TerserPlugin({parallel: threads // 开启多进程和设置进程数量})],// 代码分割配置splitChunks: {chunks: all, // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests// cacheGroups: { // 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// default: { // 其他没有写的配置会使用上面的默认值// minChunks: 2, // 这里的minChunks权重更大// priority: -20,// reuseExistingChunk: true,// },// },// 修改配置cacheGroups: {// 组哪些模块要打包到一个组// defaultVendors: { // 组名// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块// priority: -10, // 权重越大越高// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块则它将被重用而不是生成新的模块// },// 上面默认的都是公共配置这里自己写配置覆盖就好default: {// 其他没有写的配置会使用上面的默认值minSize: 0, // 我们定义的文件体积太小了所以要改打包的最小文件体积minChunks: 2,priority: -20,reuseExistingChunk: true,},},},// 提取runtime文件runtimeChunk: {name: (entrypoint) runtime~${entrypoint.name}, // runtime文件命名规则},},// 开发服务器配置, webpack官方提供的小型服务器// devServer: {// host: localhost, // 启动服务器域名// port: 3030, // 启动服务器端口号// open: true, // 是否自动打开浏览器// },// 模式mode: production, // 选择开发模式//webpack官方提供映射源代码与打包输出代码增强调式生成.map文件devtool: source-map, // 生产模式专用行列都映射
}
Core-js生产模式
问题引入 过去我们使用 babel 对 js 代码进行了兼容性处理其中使用babel/preset-env 智能预设来处理兼容性问题。 它能将 ES6 的一些语法进行编译转换比如箭头函数、点点点运算符等。但是如果是 async 函数、promise 对象、数组的一些方法includes等它没办法处理。 所以此时我们 js 代码仍然存在兼容性问题一旦遇到低版本浏览器会直接报错。所以我们想要将 js 兼容性问题彻底解决 core-js 是专门用来做 ES6 以及以上 API 的 polyfill。 polyfill翻译过来叫做垫片/补丁。就是用社区上提供的一段代码让我们在不兼容某些新特性的浏览器上使用该新特性。 此时 Eslint 会对 Promise 报错
import count from ./js/count;
import sum from ./js/sum;
// 引入资源Webpack才会对其打包
import ./css/iconfont.css;
import ./css/index.css;
import ./less/index.less;
import ./sass/index.sass;
import ./sass/index.scss;
import ./styl/index.styl;const result1 count(2, 1);
console.log(result1);
const result2 sum(1, 2, 3, 4);
console.log(result2);
// 添加promise代码
const promise Promise.resolve();
promise.then(() {console.log(hello promise);
});安装依赖
npm i babel/eslint-parser -D
npm i core-js 三种引入方法
手动全部引入 手动按需引入 自动按需引入推荐 babel.config.js
module.exports {// 智能预设能够编译ES6语法presets: [[babel/preset-env,// 按需加载core-js的polyfill{ useBuiltIns: usage, corejs: { version: 3, proposals: true } },],],
};PWA生产模式
问题引入 开发 Web App 项目项目一旦处于网络离线情况就没法访问了。 我们希望给项目提供离线体验。 渐进式网络应用程序(progressive web application - PWA)是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。 其中最重要的是在 离线(offline) 时应用程序能够继续运行功能。 内部通过 Service Workers 技术实现的。 安装依赖
npm i workbox-webpack-plugin -D js文件中插入代码 if (serviceWorker in navigator) {window.addEventListener(load, () {navigator.serviceWorker.register(/service-worker.js).then((registration) {console.log(SW registered: , registration);}).catch((registrationError) {console.log(SW registration failed: , registrationError);});});
}
BUG 此时如果直接通过 VSCode 访问打包后页面在浏览器控制台会发现 SW registration failed。 因为我们打开的访问路径是http://127.0.0.1:5500/dist/index.html。此时页面会去请求 service-worker.js 文件请求路径是http://127.0.0.1:5500/service-worker.js这样找不到会 404。 实际 service-worker.js 文件路径是http://127.0.0.1:5500/dist/service-worker.js。 安装依赖
npm i serve -g serve 也是用来启动开发服务器来部署代码查看效果的。 运行指令
serve dist 此时通过 serve 启动的服务器我们 service-worker 就能注册成功了。 总结 我们从 4 个角度对 webpack 和代码进行了优化 提升开发体验 使用 Source Map 让开发或上线时代码报错能有更加准确的错误提示。 提升 webpack 提升打包构建速度 使用 HotModuleReplacement 让开发时只重新编译打包更新变化了的代码不变的代码使用缓存从而使更新速度更快。使用 OneOf 让资源文件一旦被某个 loader 处理了就不会继续遍历了打包速度更快。使用 Include/Exclude 排除或只检测某些文件处理的文件更少速度更快。使用 Cache 对 eslint 和 babel 处理的结果进行缓存让第二次打包速度更快。使用 Thead 多进程处理 eslint 和 babel 任务速度更快。需要注意的是进程启动通信都有开销的要在比较多代码处理时使用才有效果 减少代码体积 使用 Tree Shaking 剔除了没有使用的多余代码让代码体积更小。使用 babel/plugin-transform-runtime 插件对 babel 进行处理让辅助代码从中引入而不是每个文件都生成辅助代码从而体积更小。使用 Image Minimizer 对项目中图片进行压缩体积更小请求速度更快。需要注意的是如果项目中图片都是在线链接那么就不需要了。本地项目静态图片才需要进行压缩。 优化代码运行性能 使用 Code Split 对代码进行分割成多个 js 文件从而使单个文件体积更小并行加载 js 速度更快。并通过 import 动态导入语法进行按需加载从而达到需要使用时才加载该资源不用时不加载资源。使用 Preload / Prefetch 对代码进行提前加载等未来需要使用时就能直接使用从而用户体验更好。使用 Network Cache 能对输出资源文件进行更好的命名将来好做缓存从而用户体验更好。使用 Core-js 对 js 进行兼容性处理让我们代码能运行在低版本浏览器。使用 PWA 能让代码离线也能访问从而提升用户体验。 本节完
下一节