Re:用webpack从零开始的vue-cli搭建'生活'

有了vue-cli的帮助,我们创建vue的项目非常的方便,使用vue create然后选择些需要的配置项就能自动帮我们创建配置好的webpack项目脚手架了,实在是‘居家旅行’必备良药。这次借着学习webpack的机会,不用vue-cli搭建一个vue项目。

注:基于webpack5,其运行于 Node.js v10.13.0+ 的版本。

完整代码:https://github.com/mashiro-cat/learn_webpack

webpack基础

webpack官网:https://webpack.js.org/
webpack中文官网:https://webpack.docschina.org/
安装:

npm i webpack webpack-cli -D 

运行:

npx webpack ./src/main.js --mode=development # 根目录有配置文件 npx webpack 

打开文档就能看到五个核心配置点:

  1. 入口(entry)
  2. 输出(output)
  3. loader
  4. 插件(plugin)
  5. 模式(mode)

webpack本身只提供了对js中ES Module和压缩的支持,很多功能都要通过使用loader或者plugin拓展。

webpack.config.js配置文件编写:

module.exports = {   // 入口 多入口则配置成对象形式   entry:"",   // 输出 需使用绝对路径   output:{},   // loader   module:{     rules:[]   },   // 插件   plugins:[],   // development 或者 production   // 生产模式默认开启js和html压缩   mode:"development" } 

样式资源处理

配置资源输出的路径和名称

输出:

output: {     path: path.resolve(__dirname, 'dist'),     filename: 'static/js/main.js' // 将js输出到 static/js 目录中   } 

module中:

generator: {           // 将图片文件输出到 static/imgs 目录中           // 将图片文件命名 [hash:8][ext][query]           // [hash:8]: hash值取8位 直接[hash]则不截取           // [ext]: 使用之前的文件扩展名           // [name]: 会使用之前的名字           // [query]: 添加之前的query参数           filename: "static/imgs/[hash:8][ext][query]",         }, 

css处理

安装两个loader,其使用顺序是css-loader会处理css,而将编译的css经style-loader后会动态创建style标签。
css-loader

# 安装 npm i css-loader style-loader -D 
rules: [       // 两个loader顺序按此 它会先使用后面的       { test: /.css$/i, use: ["style-loader", "css-loader"] }     ] 
提取css到单独文件

现在是css全部是打包到js中,然后动态插入的。若需要提取到单独文件,则可以借助插件。

// 安装插件 npm i mini-css-extract-plugin -D  // 配置插件 const MiniCssExtractPlugin = require("mini-css-extract-plugin");  // 将styel-loader改为MiniCssExtractPlugin.loader {   // 用来匹配 .css 结尾的文件   test: /.css$/,   // use 数组里面 Loader 执行顺序是从右到左   use: [MiniCssExtractPlugin.loader, "css-loader"], },  plugins:[ new MiniCssExtractPlugin({       // 定义输出文件名和目录       filename: "static/css/main.css",     }), ] 
css兼容处理
// 安装 npm i postcss-loader postcss postcss-preset-env -D  // 配置  {         // 用来匹配 .css 结尾的文件         test: /.css$/,         // use 数组里面 Loader 执行顺序是从右到左         use: [           MiniCssExtractPlugin.loader,           "css-loader",           { // 在css-loader之后,预处理器loader之前             loader: "postcss-loader",             options: {               postcssOptions: {                 plugins: [                   "postcss-preset-env", // 能解决大多数样式兼容性问题                 ],               },             },           },         ],       },  

控制兼容性:
package.json 文件中添加 browserslist 来控制样式的兼容性的程度:

{   // 其他省略   //"browserslist": ["ie >= 8"]   // 实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:   // 所有浏览器的最新两个版本 支持市面上99%浏览器 还没死的浏览器   "browserslist": ["last 2 version", "> 1%", "not dead"] } 
css压缩

安装插件:

npm i css-minimizer-webpack-plugin -D 

webpack配置:

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");  plugins:[   // css压缩   new CssMinimizerPlugin(), ]  

预处理器

使用less,scss等预处理都要安装对应的loader进行编译,webpack才能识别处理。

less的使用:

// 安装less-loader npm i less-loader -D  // 配置 // less-loader将less转为css后还是要交给css-loader处理的 {   test: /.less$/,   use: ["style-loader", "css-loader", "less-loader"] } 

scss, sass的使用:

// 安装 npm i sass-loader sass -D  // 配置 {   test: /.s[ac]ss$/,   use: ["style-loader", "css-loader", "sass-loader"], }, 
点击查看完整配置
 const path = require('path')  module.exports = {   // 入口 多入口则配置成对象形式   entry: "./src/main.js",   // 输出 需使用绝对路径   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'main.js'   },   // loader   module: {     rules: [       // 两个loader顺序按此 它会先使用后面的       { test: /.css$/i, use: ["style-loader", "css-loader"] },       // less-loader将less转为css后还是要交给css-loader处理的       { test: /.less$/, use: ["style-loader", "css-loader", "less-loader"] },       {         test: /.s[ac]ss$/,         use: ["style-loader", "css-loader", "sass-loader"],       },     ]   },   // 插件   plugins: [],   // development 或者 production   mode: "development" } 

图片资源处理

Webpack4使用file-loader 和 url-loader处理图片资源,而webpack5将那俩都内置了,直接配置开启就可。

{   test: /.(png|jpe?g|gif|webp)$/,   type: "asset", }, 

将小于某个大小的图片转化成Base64可添加此配置:

{         test: /.(png|jpe?g|gif|webp)$/,         type: "asset",         parser: {           dataUrlCondition: {             maxSize: 10 * 1024 // 小于10kb的图片会被base64处理           }         } }, 
点击查看完整配置
 const path = require('path')  module.exports = {   // 入口 多入口则配置成对象形式   entry: "./src/main.js",   // 输出 需使用绝对路径   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'main.js'   },   // loader   module: {     rules: [       // 两个loader顺序按此 它会先使用后面的       { test: /.css$/i, use: ["style-loader", "css-loader"] },       // less-loader将less转为css后还是要交给css-loader处理的       { test: /.less$/, use: ["style-loader", "css-loader", "less-loader"] },       {         test: /.s[ac]ss$/,         use: ["style-loader", "css-loader", "sass-loader"],       },       {         test: /.(png|jpe?g|gif|webp)$/,         type: "asset",         parser: {           dataUrlCondition: {             maxSize: 10 * 1024 // 小于10kb的图片会被base64处理           }         }       },     ]   },   // 插件   plugins: [],   // development 或者 production   mode: "development" } 

其它资源处理

若项目中引用了字体,视频等资源,则是希望不要处理它,直接输出就好了。配置为type: "asset/resource"它就会原封不动的输出了。

{         // 处理字体图标或者视频等其它资源         test: /.(ttf|woff2?|map4|map3)$/,         type: "asset/resource",         generator: {           filename: "static/media/[hash:8][ext][query]",         },       } 
点击查看完整配置
 const path = require('path')  module.exports = {   // 入口 多入口则配置成对象形式   entry: "./src/main.js",   // 输出 需使用绝对路径   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'static/js/main.js', // 将js输出到 static/js 目录中     clean: true   },   // loader   module: {     rules: [       // 两个loader顺序按此 它会先使用后面的       { test: /.css$/i, use: ["style-loader", "css-loader"] },       // less-loader将less转为css后还是要交给css-loader处理的       { test: /.less$/, use: ["style-loader", "css-loader", "less-loader"] },       {         test: /.s[ac]ss$/,         use: ["style-loader", "css-loader", "sass-loader"],       },       {         test: /.(png|jpe?g|gif|webp)$/,         type: "asset",         parser: {           dataUrlCondition: {             maxSize: 10 * 1024 // 小于10kb的图片会被base64处理           }         },         generator: {           // 将图片文件输出到 static/imgs 目录中           // 将图片文件命名 [hash:8][ext][query]           // [hash:6]: hash值取6位           // [ext]: 使用之前的文件扩展名           // [query]: 添加之前的query参数           filename: "static/imgs/[hash:6][ext][query]",         },       },       {         // 处理字体图标或者视频等其它资源         test: /.(ttf|woff2?|map4|map3)$/,         type: "asset/resource",         generator: {           filename: "static/media/[hash:8][ext][query]",         },       },     ]   },   // 插件   plugins: [],   // development 或者 production   mode: "development" } 

js资源处理

代码质量检测 Eslint

安装:

npm i eslint-webpack-plugin eslint -D 

在webpack配置中使用eslint插件

const ESLintWebpackPlugin = require("eslint-webpack-plugin");  plugins: [     new ESLintWebpackPlugin({       // 指定检查文件的根目录       context: path.resolve(__dirname, "src"),     }),   ], 

编写配置:
配置文件由很多种写法:.eslintrc.*:新建文件,位于项目根目录

  • .eslintrc
  • .eslintrc.js
  • .eslintrc.json

区别在于配置格式不一样package.json 中 eslintConfig:不需要创建文件,在原有文件基础上写,ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可

根目录创建.eslintrc.js配置文件

// .eslintrc.js module.exports = {   // 解析配置项   parserOptions: {     ecmaVersion: 6, // ES 语法版本     sourceType: "module", // ES 模块化   },   env: {     node: true, // 启用node中全局变量     browser: true, // 启用浏览器中全局变量 不开启则像 console Math 等全局变量无法使用   },   // 继承规则   extends: ['eslint:recommended'],   // 检测规则   // 自定义的规则会覆盖继承的规则   // "off" 或 0 - 关闭规则   // "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)   // "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)   rules: {     semi: "off", // 禁止使用分号     'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告     'default-case': [       'warn', // 要求 switch 语句中有 default 分支,否则警告       { commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了     ],     eqeqeq: [       'warn', // 强制使用 === 和 !==,否则警告       'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告     ],   }, } 

babel兼容处理

安装:

npm i babel-loader @babel/core @babel/preset-env -D 

babel配置编写:
配置文件由很多种写法:

  1. babel.config.*:新建文件,位于项目根目录
  • babel.config.js
  • babel.config.json
  1. .babelrc.*:新建文件,位于项目根目录
  • .babelrc
  • .babelrc.js
  • .babelrc.json

package.json 中 babel:不需要创建文件,在原有文件基础上写

presets 预设:
简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。
@babel/preset-react:一个用来编译 React jsx 语法的预设
@babel/preset-typescript:一个用来编译 TypeScript 语法的预设

// 创建.babelrc.js module.exports = {   presets: ["@babel/preset-env"], }; 

webpack增加babel

{   test: /.js$/,   exclude: /node_modules/, // 排除node_modules代码不编译   loader: "babel-loader", }, 

html自动导入处理

安装html-webpack-plugin

npm i html-webpack-plugin -D 

webpack配置, 配置好后就会自动的引入所需的js了。

const HtmlWebpackPlugin = require("html-webpack-plugin");  plugins: [     new HtmlWebpackPlugin({       // 以 public/index.html 为模板创建文件       // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源       template: path.resolve(__dirname, "public/index.html"),     }), ] 

webpackSever

使用webpacksever后,在开发时就能自动检测文件变化,并实时编译展示出来了。

// 安装 npm i webpack-dev-server -D  // 配置 devServer: {     host: "localhost", // 启动服务器域名     port: "3000", // 启动服务器端口号     open: true, // 是否自动打开浏览器   }, 

运行:

// 此时不会打包生成文件,都是在内存中进行编译的 npx webpack serve 
点击查看完整配置
 const path = require('path') const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");  module.exports = {   // 入口 多入口则配置成对象形式   entry: "./src/main.js",   // 输出 需使用绝对路径   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'static/js/main.js', // 将js输出到 static/js 目录中     clean: true   },   // loader   module: {     rules: [       // 两个loader顺序按此 它会先使用后面的       { test: /.css$/i, use: ["style-loader", "css-loader"] },       // less-loader将less转为css后还是要交给css-loader处理的       { test: /.less$/, use: ["style-loader", "css-loader", "less-loader"] },       {         test: /.s[ac]ss$/,         use: ["style-loader", "css-loader", "sass-loader"],       },       {         test: /.(png|jpe?g|gif|webp)$/,         type: "asset",         parser: {           dataUrlCondition: {             maxSize: 10 * 1024 // 小于10kb的图片会被base64处理           }         },         generator: {           // 将图片文件输出到 static/imgs 目录中           // 将图片文件命名 [hash:8][ext][query]           // [hash:6]: hash值取6位           // [ext]: 使用之前的文件扩展名           // [query]: 添加之前的query参数           filename: "static/imgs/[hash:6][ext][query]",         },       },       {         // 处理字体图标或者视频等其它资源         test: /.(ttf|woff2?|map4|map3)$/,         type: "asset/resource",         generator: {           filename: "static/media/[hash:8][ext][query]",         },       },       { // babel配置         test: /.js$/,         exclude: /node_modules/, // 排除node_modules代码不编译         loader: "babel-loader",       },     ]   },   // 插件   plugins: [     new ESLintWebpackPlugin({       // 指定检查文件的根目录       context: path.resolve(__dirname, "src"),     }),     new HtmlWebpackPlugin({ // html处理的插件       // 以 public/index.html 为模板创建文件       // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源       template: path.resolve(__dirname, "public/index.html")     })   ],   devServer: {     host: "localhost", // 启动服务器域名     port: "666", // 启动服务器端口号     open: true, // 是否自动打开浏览器   },   // development 或者 production   mode: "development" } 

webpack进阶

使用sourcemap

SourceMap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。
通过查看Webpack DevTool文档可知,SourceMap 的值有很多种情况.
开发时我们只需要关注两种情况即可:
开发模式:cheap-module-source-map,优点:打包编译速度快,只包含行映射,缺点:没有列映射
生产模式:source-map,优点:包含行/列映射,缺点:打包编译速度更慢。

配置:

devtool: "cheap-module-source-map", 

提升打包速度

HotModuleReplacement:它(HMR/热模块替换):在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。

module.exports = {   // 其他省略   devServer: {     host: "localhost", // 启动服务器域名     port: "3000", // 启动服务器端口号     open: true, // 是否自动打开浏览器     hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)   }, }; 

此时 css 样式经过 style-loader 处理,已经具备 HMR 功能了。 但是 js 可以使用vue-loader, react-hot-loader实现。

OneOf配置(开发和正式都能用):
匹配到一条规则就不继续匹配了

module: {     rules: [         {             oneOf: [                 { test: /.css$/, use: ["style-loader", "css-loader"] },                 .........             ]         }     ] } 

Include/Exclude
如在配置babel排除node_moudels文件夹

使用缓存Cache
每次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。我们可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包时速度就会更快了

// babel {             test: /.js$/,             // exclude: /node_modules/, // 排除node_modules代码不编译             include: path.resolve(__dirname, "../src"), // 也可以用包含             loader: "babel-loader",             options: {               cacheDirectory: true, // 开启babel编译缓存               cacheCompression: false, // 缓存文件不要压缩             },           },   // Eslint new ESLintWebpackPlugin({       // 指定检查文件的根目录       context: path.resolve(__dirname, "../src"),       exclude: "node_modules", // 默认值       cache: true, // 开启缓存       // 缓存目录       cacheLocation: path.resolve(         __dirname,         "../node_modules/.cache/.eslintcache"       ),     }) 

打包启用多线程:
开启线程也需要时间,小项目可能提升不明显。
npm i thread-loader -D安装loader,然后配置:

// nodejs核心模块,直接使用 const os = require("os"); const TerserPlugin = require("terser-webpack-plugin"); // webpack自带的js压缩模块  // cpu核数 const threads = os.cpus().length;  // babel使用多线程 {             test: /.js$/,             // exclude: /node_modules/, // 排除node_modules代码不编译             include: path.resolve(__dirname, "../src"), // 也可以用包含             use: [               {                 loader: "thread-loader", // 开启多进程                 options: {                   workers: threads, // 数量                 },               },               {                 loader: "babel-loader",                 options: {                   cacheDirectory: true, // 开启babel编译缓存                 },               },             ],           },  // Eslint使用多线程 new ESLintWebpackPlugin({       // 指定检查文件的根目录       context: path.resolve(__dirname, "../src"),       exclude: "node_modules", // 默认值       cache: true, // 开启缓存       // 缓存目录       cacheLocation: path.resolve(         __dirname,         "../node_modules/.cache/.eslintcache"       ),       threads, // 开启多进程     }),  // js压缩使用多线程 optimization: {     minimize: true,     minimizer: [       // css压缩也可以写到optimization.minimizer里面,效果一样的       new CssMinimizerPlugin(),       // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了       new TerserPlugin({         parallel: threads // 开启多进程       })     ],   }, 

减少的代码体检

Tree Shaking: 默认开启,通常用于描述移除 JavaScript 中的没有使用上的代码。

Babel优化:
@babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。

// 安装 npm i @babel/plugin-transform-runtime -D  // 配置 {                 loader: "babel-loader",                 options: {                   cacheDirectory: true, // 开启babel编译缓存                   cacheCompression: false, // 缓存文件不要压缩                   plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积                 },               }, 

优化代码运行性能

打包代码分块
Preload / prefetch
使用Core-js
使用PWA

从零开始搭建vue-webpack项目

使用的库:
设置环境变量:
https://www.npmjs.com/package/cross-env

// 安装 npm install --save-dev cross-env  // package.json  

vue-loder文档: https://vue-loader.vuejs.org/zh/
安装vue-loader

npm i vue npm install -D vue-loader vue-template-compiler  module: {     rules: [       // ... 其它规则       {         test: /.vue$/,         loader: 'vue-loader'       }     ]   },   plugins: [     // 请确保引入这个插件!     new VueLoaderPlugin()   ] 

样式处理

style-loader 替换为 vue-style-loader,并安装预处理器loader

npm i -D css-loader vue-style-loader less-loader less-loader sass-loader stylus-loader 

设置拓展名自动布局

resolve: {     extensions: [".vue", ".js", ".json"], // 自动补全文件扩展名,让vue可以使用   }, 

Eslint配置指定为vue的

npm i -D @babel/eslint-parser  // .eslintrc.js module.exports = {   root: true,   env: {     node: true,   },   extends: ["plugin:vue/vue3-essential", "eslint:recommended"],   parserOptions: {     parser: "@babel/eslint-parser",   }, }; 

Bable配置

npm i -D @vue/cli-plugin-babel  // babel.config.js module.exports = {   presets: ["@vue/cli-plugin-babel/preset"], }; 

提供对js的变量,解决页面警告

// 解决页面警告 new DefinePlugin({   __VUE_OPTIONS_API__: "true",   __VUE_PROD_DEVTOOLS__: "false", }), 

除了以上vue中专用的配置,然后加上less,scss的loader,把前面的html插件加上去。就是一个基本的vue-cli了。完整的配置可以看最前面的仓库链接。

优化

按需引入第三库

如 elment plus 按需引入可参照其官网进行配置

https://yk2012.github.io/sgg_webpack5/
https://vue-loader.vuejs.org/zh/
https://webpack.docschina.org/

发表评论

相关文章