2

Re:用webpack从零开始的vue-cli搭建'生活' - 会飞的一棵树

 1 year ago
source link: https://www.cnblogs.com/flytree/p/16483202.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

有了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/
安装:

shell
npm i webpack webpack-cli -D
shell
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配置文件编写:

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

样式资源处理

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

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

module中:

highlighter- Dart
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

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

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

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兼容处理
js
// 安装
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 来控制样式的兼容性的程度:

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

安装插件:

highlighter- CSS
npm i css-minimizer-webpack-plugin -D

webpack配置:

js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

plugins:[
  // css压缩
  new CssMinimizerPlugin(),
]

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

less的使用:

highlighter- JavaScript
// 安装less-loader
npm i less-loader -D

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

scss, sass的使用:

highlighter- JavaScript
// 安装
npm i sass-loader sass -D

// 配置
{
  test: /\.s[ac]ss$/,
  use: ["style-loader", "css-loader", "sass-loader"],
},

点击查看完整配置

cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- JavaScript
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将那俩都内置了,直接配置开启就可。

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

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

highlighter- JavaScript
{
        test: /\.(png|jpe?g|gif|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
          }
        }
},

点击查看完整配置

cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- JavaScript
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"它就会原封不动的输出了。

highlighter- JavaScript
{
        // 处理字体图标或者视频等其它资源
        test: /\.(ttf|woff2?|map4|map3)$/,
        type: "asset/resource",
        generator: {
          filename: "static/media/[hash:8][ext][query]",
        },
      }

点击查看完整配置

cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- JavaScript
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

highlighter- CSS
npm i eslint-webpack-plugin eslint -D

在webpack配置中使用eslint插件

highlighter- JavaScript
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配置文件

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兼容处理

highlighter- CSS
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 语法的预设

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

webpack增加babel

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

html自动导入处理

安装html-webpack-plugin

highlighter- CSS
npm i html-webpack-plugin -D

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

highlighter- JavaScript
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后,在开发时就能自动检测文件变化,并实时编译展示出来了。

js
// 安装
npm i webpack-dev-server -D

// 配置
devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
  },
highlighter- Dart
// 此时不会打包生成文件,都是在内存中进行编译的
npx webpack serve

点击查看完整配置

cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- JavaScript
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,优点:包含行/列映射,缺点:打包编译速度更慢。

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

提升打包速度

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

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

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

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

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

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

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

js
// 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,然后配置:

js
// 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 并且使所有辅助代码从这里引用。

js
// 安装
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

js
// 安装
npm install --save-dev cross-env

// package.json

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

highlighter- JavaScript
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

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

设置拓展名自动布局

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

Eslint配置指定为vue的

highlighter- Java
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配置

highlighter- Java
npm i -D @vue/cli-plugin-babel

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

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

highlighter- Dart
// 解决页面警告
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/

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK