Webpack V5 + React 脚手架的配置 🔥🔥

Webpack V5 + React 脚手架的配置 🔥🔥

新年新气息,万物更新,一切也将不同于往年。

公司目前起了一个新的项目需求,我这边也是考虑了很多库啊或者框架啊之类的,但是呢最终还是觉得不太合适,都太重或者太繁琐了,所以打算自己用webpack搭一个。

基础配置

因为考虑到之后可能会有小伙伴不太了解webpack,所以这边就从最基础的搭建开始说起。这里贴一个webpack的中文文档链接: webpack

一、五大核心概念:

webpack的五大核心概念分别是:

1、Entry

入口起点,告诉 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。默认值是 ./src/index.js,但也可以通过在 “webpack.config.js” 文件中的 entry 属性来配置,可以指定一个(或多个)不同的入口起点,例如

// 单文件入口简写
module.exports = {
  entry: './src/index.js',
};
 
 
// 单文件入口完整写法
module.exports = {
  entry: {
    main: './src/index.js',
  },
};
 
// 多文件入口写法
module.exports = {
  entry: ['./src/index.js', './src/index_2.js'],
  output: {
    filename: 'bundle.js',
  },
};

复制代码

更详细的入口文件配置可参考官网文档:entry-points

2、Output

output 告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。也可通过在 “webpack.config.js” 文件中的 output 属性来配置

const path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
  },
};
 
 
// 多入口的配置
module.exports = {
  entry: {
    app: './src/app.js',
    search: './src/search.js',
  },
  // 写入到:./dist/app.js, ./dist/search.js
  output: {
    filename: '[name].js',
    path: __dirname + '/dist',
  },
};
复制代码

更详细的出口文件配置可参考官网文档:output

3、Loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。loader的基本属性就两个:

  • test:识别出哪些文件会被转换
  • use:定义出在进行转换时,应该使用哪个 loader
// 示例
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 最后执行 style-loader
          { loader: 'style-loader' },
          // 其次执行 css-loader
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          },
          //  首先执行 sass-loader
          { loader: 'sass-loader' }
        ]
      }
    ]
  }
};
复制代码

在这里需要注意的是:module.rules 允许你在 webpack 配置中指定多个 loader。还有 loader 的执行顺序需要注意一下,他是从下到上依次执行的,配置过程中不要写错了。在上面的示例中,从 sass-loader 开始执行,然后继续执行 css-loader,最后以 style-loader 为结束。

更详细的 loader 文件配置可参考官网文档:loaders

4、Plugins

loader 用于转换某些类型的模块,而plugins(插件)则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

插件的使用需要用 require() 引入,然后通过 new 操作符来创建一个实例 最后添加到 plugins 数组中。如下示例:

const HtmlWebpackPlugin = require('html-webpack-plugin'); // html的插件
 
module.exports = {
  module: {
    rules: [{ test: /\.css$/, use: 'css-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
复制代码

上述示例中 html-webpack-plugin 会为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。

更详细的 plugins 文件配置可参考官网文档:plugins

5、Mode

模式,有生产模式(production)和开发模式(development)或 none。设置 mode 参数,可以启用 webpack 内置在相应环境下的优化

// 其默认值为 production
module.exports = {
  mode: 'production',
};
复制代码

更详细的 plugins 文件配置可参考官网文档:plugins

二、安装及简单的配置

在理解了 webpack 的五大核心配置以后,就可以开始着手一些简单的配置了。

1、安装

webpack的安装需要注意的一点就是需要在全局和项目中都同时安装 webpack 和 webpack-cli

npm i webpack webpack-cli -g
npm i webpack webpack-cli -D
复制代码

2、初始化项目

初始化就是常说的项目初始化

npm init
复制代码

3、编译打包

简单的创建一个项目,然后加入各种类型的文件,应用上面说的五大核心打包一个应用。

首先加入webpack支持的js和json文件

image.png

如上图所示:

  • 创建了一个webpack文件夹
  • 在他的基础上初始化了项目:npm init 
  • 创建 src 文件夹,并在他下面创建 入口文件(index.js)和一些模块(module1.js、module2.js、module3.js ......)
  • 创建 json 文件夹,并在他下面创建 json 文件(index.json)
  • 创建 index.html 文件,以备之后引入webpack 构建后的文件
  • 创建 webpack.config.js 文件,用于配置 webpack 的配置。

其次配置默认支持的js和json文件

这边直接贴一下代码

// webpack.config.js
 
const path = require('path');
module.exports = {
  // 入口文件配置
  entry: './src/js/index.js',
  // 出口文件配置
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
  },
};
复制代码

简单说一下,上面的配置文件主要做两件事。1、配置了入口文件地址为:'./src/js/index.js'  2、配置了出口文件的导出为:'dist/main.js'

配置完以后,执行命令:webpack

image.png 如图可以看到在目录中已经生成了构建后的代码:dist/main.js

到此说明构建已经成功,但有一个问题需要注意一下,就是在终端有一个警告,需要处理一下。如图

image.png

这里其实就是提示没有配置 mode,配置完mode以后也就不会再有这个警告了

// webpack.config.js
 
const path = require('path');
module.exports = {
  // 入口文件配置
  entry: './src/js/index.js',
  // 出口文件配置
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
  },
  // 模式选择(默认)
  mode: 'production',
};
复制代码

这样一来我们就把webpack支持的js和json文件做了一个配置,同时用到了五大核心中的三大核心:entry、output、mode。

然后看一下loader

1、less-loader

首先添加一个 .less 文件,如图

image.png

然后在入口文件中引入

image.png

然后打包发现报错了

image.png

报错的因为就是 webpack 不能识别 .less 的文件。那怎么识别呢?这里就需要我们前面说的五大核心中的:loader

在构建的时候,对于webpack不能识别的文件类型,咱们需要使用 loader 告诉 webpack 加载对应的文件,这里需要的是:less-loader

image.png

loader的配置可以根据官网的配置来,但这里需要说明一下,上图左侧的这些loader并不是所有的loader,这里只是webpack官方觉得不错的挑选了几个列在这里以便使用。

但是,根据上面官方给的示例,配置完以后,发现报错了,如下图:

image.png

其实这里是因为,在官方的示例中,他并不是只用了 'less-loader'、他还使用了'style-loader'和'css-loader'。所以这里我们需要把他两也安装上,所以说咱们转less文件,需要安装五个东西:

npm install less less-loader style-loader css-loader -D
复制代码

这里为什么需要安装'style-loader'和'css-loader'咱们写注释说明一下

// 构建 less 文件 说明
module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i, // 匹配 .less 文件
        // 注意之前说过的,loader是从下到上的一个执行顺序
        use: [ // 还有官方的示例 这里是loader 咱们需要改成use
          'style-loader', // 创建style标签,并将js中的css代码添加到style标签中
          'css-loader', // 将css文件以common.js的方式整合到js文件中
          'less-loader', // 将less文件解析成css文件
        ],
      },
    ],
  },
};
复制代码

安装完以后再试:

image.png

上面看到已经编译成功了,那么打开index.html文件看一下,样式是够OK

image.png

得到的结果,页面中间出现了一个黄色的正方形。以上就是less-loader的基本使用

2、ts-loader

ts想要被 webpack 识别,不仅需要借助ts-loader,还需要借助babel-loader,这里直接看根据官网文档来

image.png

但这里需要注意的是,在构建过程中,如果需要支持IE浏览器,需要额外配置:core-js

这里贴一下具体的配置

// 构建 ts 文件 说明
const path = require('path');
module.exports = {
  // 入口文件配置
  entry: './src/js/index.js',
  // 出口文件配置
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
  },
  // 模式选择(默认)
  mode: 'production',
  // loader的配置
  module: {
    rules: [
      // less-loader
      {
        test: /\.less$/, // 匹配 .less 文件
        use: [
          'style-loader',
          'css-loader',
          'less-loader',
        ],
      },
      // ts-loader
      {
        test:/\.ts$/,
        exclude: /node-modules/,
        use: [
          // babel配置
          {
            // 加载器
            loader: "babel-loader",
            // 设置babel
            options: {
              // 预定义的环境设置
              presets:[
                [
                  // 指定环境的插件
                  "@babel/preset-env",
                  // 配置信息
                  {
                    // 要兼容的目标浏览器
                    targets: {
                      "chrome": "58",
                      "ie": "11",
                    },
                    // 指定core.js的版本
                    "corejs": "3",
                    // 使用corejs的方式 “usage” 表示按需加载
                    "useBuiltIns": "usage"
                  }
                ]
              ],
              // 开启babel缓存
              cacheDirectory: true
            }
          },
          'ts-loader'
        ],
      }
    ],
  },
};
复制代码
3、图片的构建

在图片的构建中,以前的webpack4需要用到url-loader 和 file-loader,但在webpack5中确不需要了。

image.png

image.png

上图就是之前webpack4 之前的配置。那为什么webpack5不用这么配置了呢?这里简单说一下:

webpack5 新增 资源模块(Asset Modules )。资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

在 webpack5 之前,通常用 raw-loader 将文件导入为字符串、用 url-loader 将文件作为 data URI 内联到 bundle 中、用 file-loader 将文件发送到输出目录。但现在新增了资源模块后,资源模块通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

看一下webpack4 和 webpack5 的代码的区别:

// webpack4 的使用
module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/i,
        use: 'file-loader'
      },
      {
        test: /\.ico$/i,
        use: 'url-loader'
      },
      {
        test: /\.text$/i,
        use: 'raw-loader'
      },
    ],
  },
};
复制代码
// webpack5 的使用
module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/i,
        use: 'asset/resource'
      },
      {
        test: /\.ico$/i,
        use: 'asset/inline'
      },
      {
        test: /\.text$/i,
        use: 'asset/source'
      },
    ],
  },
};
复制代码

接下来看一下plugins

插件的话简单说两个,之后咱们会用到的,一个是 ‘mini-css-extract-plugin’ ,另一个是 ‘html-webpack-plugin’。他们是用来干啥的呢?下面简单说一下

1、mini-css-extract-plugin

插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

简单贴一下代码

// 安装
npm install --save-dev mini-css-extract-plugin
 
// 使用
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
复制代码

需要注意的是,插件基于 webpack5 的新特性构建,需要 webpack5 才能正常工作。

2、html-webpack-plugin

插件会生成一个 HTML5 文件, 在 body 中使用 script 标签引入你所有 webpack 生成的 bundle。

简单贴一下代码

// 安装
npm install --save-dev html-webpack-plugin
 
 
// 使用
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js',
  },
  plugins: [new HtmlWebpackPlugin()],
};
复制代码

需要注意的是 如果你有多个 webpack 入口,也会在已生成 HTML 文件中的 script 标签内引入

如果在 webpack 的输出中有任何 CSS 资源(例如,使用 mini-css-extract-plugin 提取的 CSS),那么这些资源也会在 HTML 文件 head 元素中的 link 标签内引入

最后来看一下代码规范

代码规范的工具有很多,但怎么选择是个问题,一般我觉得跟着主流的框架走总不会错,就目前vue3和react17用的都是Eslint,所以咱们就以Eslint为例,简单说一下。

这里有个小插曲,当我熟练的打开 webpack去搜索的时候发现没有搜索到 ‘eslint-loader’,我心想难道我记错了,但不可能啊,webpack最熟练地配置当属eslint了啊,我怎么说也算对代码规范有点洁癖的人啊!查阅了一些资料以后发现一个让我很大跌眼镜的事:eslint-loader 被废除了!我艹!!!!

但转念一想,那些废弃的loader都有替代品,eslint-loader应该不会没有。于是又跑webpack5 官网找了找,发现确实有:EslintWebpackPlugin

image.png

没错,上图就是这哥们就是 “eslint-loader” 的接盘侠。那接下来看看这哥们怎么使用。

// 安装
npm install eslint eslint-webpack-plugin --save-dev
 
 
// 使用
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
  // ...
  plugins: [new ESLintPlugin({
    fix: true, // 启用 ESLint 自动修复特性
    extensions: ['js', 'json', 'coffee'], // 需要排查的文件
    exclude: '/node_modules/' // 排除 node_module 的检查
  })],
  // ...
};
复制代码

webpack5 + react 搭建项目脚手架

上面我们说的主要是webpack5 的一些核心概念和基础的搭建。下面正式开始脚手架的搭建。

首先看一下目前我这边搭建好的一个目录

image.png

下面就按照这个一步一步的来搭建一下吧,

一、基础搭建

image.png

如上图所示, 初始化项目和安装react以后,我们打开vscode 开始搭建项目

初始搭建的代码这边我简单贴一下

image.png

如图webpack的基本配置已经OK,代码为:

// webpack.config.base.js
 
const path = require("path");
// 根据相对路径获取绝对路径
const resolvePath = (relativePath) => path.resolve(__dirname, relativePath);
 
// 基础配置
const baseConfig = {
  // 入口文件
  entry: resolvePath("../src/index.tsx"),
  // 出口文件
  output: {
    path: resolvePath("../dist"),
    filename: "[name].bundle.js",
  },
};
module.exports = {
  baseConfig,
};
复制代码

然后对应的开发环境和生产环境的webpack配置代码为:

// webpack.config.dev.js
 
// 合并规则
const { merge } = require("webpack-merge");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境设置:开发环境
  mode: "development",
});
复制代码
// webpack.config.prod.js
 
// 合并规则
const { merge } = require("webpack-merge");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境配置:生产环境
  mode: "production"
});
复制代码
// webpack的入口文件:src/index.tsx
 
import React from "react";
import ReactDOM from "react-dom";
import "./index.less";
import logo from "./assets/logo.svg";
 
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1> Webpack V5 + React </h1>
      </header>
    </div>
  );
}
 
ReactDOM.render(<App />, document.getElementById("root"));
复制代码

// less文件也贴一下吧:src/index.less
 
.App {
  text-align: center;
  .App-logo {
    width: 30vw;
    pointer-events: none;
    margin: 100px auto 10px;
  }
}
 
@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}
 
@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
复制代码

// html模板文件 public/index.html
 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- 配置title的导入 -->
  <title><%=htmlWebpackPlugin.options.title %></title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
复制代码

基本模板搭建搭建完以后,开始安装需要的包

// 1、webpack的安装
yarn add webpack webpack-cli -D
yarn add webpack webpack-cli -g
 
// 2、把打包的文件配置到html模板
yarn add html-webpack-plugin -D
 
 
// 3、入口文件中涉及到了 less 的文件 需要增加配置
yarn add style-loader css-loader less less-loader -D
 
 
// 4、入口文件中涉及到了 ts 的文件 需要增加的babel配置
yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react -D
 
 
// 5、入口文件还涉及 svg 图片,这里不用增加配置文件,webpack 已经内置了他的处理
复制代码

安装完需要配置到基础配置里面

// webpack.config.base.js
 
const path = require("path");
// 根据相对路径获取绝对路径
const resolvePath = (relativePath) => path.resolve(__dirname, relativePath);
// HTML模板
const HtmlWebpackPlugin = require("html-webpack-plugin");
 
// 基础配置
const baseConfig = {
  // 入口文件
  entry: resolvePath("../src/index.tsx"),
  // 出口文件
  output: {
    path: resolvePath("../dist"),
    filename: "[name].bundle.js",
  },
  // 所有loader的配置都在 module.rules 中
  module: {
    rules: [
      // 对css文件的处理
      // use里的loader如果有多个的情况下,切记执行顺序是:从下到上(或者从右到左)
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      // 对less文件的处理
      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          "less-loader",
        ],
      },
      // 对ts|tsx文件的处理
      {
        test: /\.(ts|tsx)$/,
        use: "babel-loader",
      },
      // 对图片的处理
      {
        test: /\.(svg|png|jpg|gif)$/,
        type: "asset/resource",
      },
    ],
  },
  // 插件的处理
  plugins: [
    new HtmlWebpackPlugin({
      // title 配置
      title: "Webpack V5 + React",
      // 模板导入
      template: resolvePath("../public/index.html"),
      // 名称为
      filename: "index.html",
    }),
  ],
};
module.exports = {
  baseConfig,
};
复制代码

同时还需要增加babel的配置


// babel.config.json
{
  "presets": ["@babel/preset-react", "@babel/preset-env"]
}
复制代码

之后配置一下webpack的启动命令: build 和 serve

// package.json
 
{
  "name": "webpack5-react",
  "version": "1.0.0",
  "description": "weback V5 + react ",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./webpack/webpack.config.prod.js",
    "serve": "webpack-dev-server --config ./webpack/webpack.config.dev.js"
  },
  "author": "ndz",
  "license": "MIT",
  "dependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.70.0",
    "webpack-cli": "^4.9.2"
  }
}
复制代码

这里需serve命令时 咱们需要起一个服务需要安装:webpack-dev-server

// 安装webpack服务
yarn add webpack-dev-server -D
 
 
// 刚才还漏了一个配置合并的插件
yarn add webpack-merge -D
复制代码

安装完以后的配置如下


// 合并规则
const { merge } = require("webpack-merge");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境设置:开发环境
  mode: "development",
  // 便于开发调试 这里开启source-map模式
  devtool: "source-map",
  // webpack-dev-server 的一下配置,webpack-dev-server 会提供一个本地服务(serve)
  devServer: {
    // host
    host: "127.0.0.1",
    // 端口
    port: 8000,
    // 热更新
    hot: true,
    // 启动时打开浏览器
    open: true,
  },
});
复制代码

然后启动看一下:


// 启动项目
yarn serve
复制代码

我们可以发现目前所有配置OK 项目启动成功了。

image.png

再看看打包成功与否。

//打包
yarn build
复制代码

image.png

可以看到打包完成以后,咱们的文件目录 dist 已经生成了。

二、优化

接下咱们将针对打包做一下优化

现在咱们的js和css文件是被统一打包在一个文件(dist/main.bundle.js)里面,如图

image.png

那么现在咱们需要单独对css做一些处理,首先安装需要的插件

// css分包处理
yarn add mini-css-extract-plugin -D
 
// css压缩处理
yarn add css-minimizer-webpack-plugin -D
 
// 进一步处理css文件,比如添加浏览器前缀等
yarn add postcss-loader autoprefixer -D
 
// 统一安装
yarn add mini-css-extract-plugin css-minimizer-webpack-plugin postcss-loader autoprefixer -D
复制代码

安装完以后的配置如下:

// 首先是基础配置
const path = require("path");
// 根据相对路径获取绝对路径
const resolvePath = (relativePath) => path.resolve(__dirname, relativePath);
// HTML模板
const HtmlWebpackPlugin = require("html-webpack-plugin");
// css 代码打包分离
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
 
// 基础配置
const baseConfig = {
  // 入口文件
  entry: resolvePath("../src/index.tsx"),
  // 出口文件
  output: {
    path: resolvePath("../dist"),
    filename: "[name].bundle.js",
  },
  // 所有loader的配置都在 module.rules 中
  module: {
    rules: [
      // 对css文件的处理
      // use里的loader如果有多个的情况下,切记执行顺序是:从下到上(或者从右到左)
      // MiniCssExtractPlugin插件和style-loader冲突,所以这里用MiniCssExtractPlugin插件替换了style-loader
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
      },
      // 对less文件的处理
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "less-loader",
        ],
      },
      // 对ts|tsx文件的处理
      {
        test: /\.(ts|tsx)$/,
        use: "babel-loader",
      },
      // 对图片的处理
      {
        test: /\.(svg|png|jpg|gif)$/,
        type: "asset/resource",
      },
    ],
  },
  // 插件的处理
  plugins: [
    new HtmlWebpackPlugin({
      // title 配置
      title: "Webpack V5 + React",
      // 模板导入
      template: resolvePath("../public/index.html"),
      // 名称为
      filename: "index.html",
    }),
    new MiniCssExtractPlugin({
      filename: `[name].[hash:8].css`,
    }),
  ],
};
module.exports = {
  baseConfig,
};
复制代码

这里需要注意的是 postcss-loader 需要添加配置文件

// postcss.config.js
 
// postcss配置文件
module.exports = {
  plugins: {
    autoprefixer: require("autoprefixer"),
  },
};
复制代码

然后css文件的压缩只需要在生产环境配置

// webpack.config.prod.js
 
 
// 合并规则
const { merge } = require("webpack-merge");
// 压缩css文件
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境配置:生产环境
  mode: "production",
  plugins:[
    new CssMinimizerWebpackPlugin()
  ]
});
复制代码

然后build看一下效果

image.png

可以看到 css 文件已经被单独抽取出来了

接下来再做两个配置,一个是错误提示,另一个是清理上一次的打包文件

// 错误提示
yarn add friendly-errors-webpack-plugin -D
 
 
// 清理上一次的打包
yarn add clean-webpack-plugin -D
复制代码

错误提示我们是添加在开发环境的配置中

// webpack.config.dev.js
 
 
// 合并规则
const { merge } = require("webpack-merge");
// 错误提示插件
const FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境设置:开发环境
  mode: "development",
  // 便于开发调试 这里开启source-map模式
  devtool: "source-map",
  // webpack-dev-server 的一下配置,webpack-dev-server 会提供一个本地服务(serve)
  devServer: {
    // host
    host: "127.0.0.1",
    // 端口
    port: 8000,
    // 热更新
    hot: true,
    // 启动时打开浏览器
    open: true,
  },
  // 插件配置
  plugins: [
    new FriendlyErrorsWebpackPlugin(),
  ],
});
复制代码

清理上一次的打包文件是配置在生产环境中

// webpack.config.prod.js
 
 
// 合并规则
const { merge } = require("webpack-merge");
// 清理原来的打包文件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// 压缩css文件
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境配置:生产环境
  mode: "production",
  plugins:[
    new CleanWebpackPlugin(),
    new CssMinimizerWebpackPlugin()
  ]
});
复制代码

到此咱们的基本配置和优化就算完成了。

但......但是,对于我这种有追求的人怎么可能少了代码检查呢?!,所以接下来 咱们继续搞Eslint 的配置。

三、Eslint 配置

这里我就把需要安装的都全部列出来 然后单个解释一下,然后贴配置代码

// webpack5 弃用了eslint-loader 支持了eslint-webpack-plugin
yarn add -D eslint-webpack-plugin
 
// eslint 和 prettier 结合校验
yarn add -D eslint prettier prettier-eslint eslint-config-prettier eslint-plugin-prettier
 
// 一个可扩展的共享配置
yarn add -D eslint-config-airbnb-base
 
// 用于react的eslint规则
yarn add -D eslint-plugin-react
 
// typescript相关规则 详细说明:https://www.npmjs.com/package/@typescript-eslint/parser
yarn add -D typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
 
// 添加一些.eslintrc文件的扩展特性
yarn add -D standard eslint-plugin-promise eslint-plugin-node eslint-plugin-import eslint-plugin-standard eslint-config-standard
复制代码

安装完以后配置如下


// 合并规则
const { merge } = require("webpack-merge");
// 错误提示插件
const FriendlyErrorsWebpackPlugin = require("friendly-errors-webpack-plugin");
// eslint插件
const ESLintPlugin = require("eslint-webpack-plugin");
// 导入基础配置
const { baseConfig } = require("./webpack.config.base");
module.exports = merge(baseConfig, {
  // 环境设置:开发环境
  mode: "development",
  // 便于开发调试 这里开启source-map模式
  devtool:  'source-map',
  // webpack-dev-server 的一下配置,webpack-dev-server 会提供一个本地服务(serve)
  devServer: {
    // host
    host: '127.0.0.1',
    // 端口
    port: 8000,
    // 热更新
    hot: true,
    // 启动时打开浏览器
    open: true,
  },
  // 插件配置
  plugins: [
    new FriendlyErrorsWebpackPlugin(),
    new ESLintPlugin({
      fix: true,
      extensions: ["js", "ts", "tsx", "json"],
      exclude: "/node_modules/",
    })
  ],
});
复制代码

.eslintrc.js文件 的配置如下

// 增加.eslintrc.js文件
 
 
module.exports = {
  // eslint的配置主要走的是:typescript-eslint
  // 详细内容请参阅:https://typescript-eslint.io/
  parser: "@typescript-eslint/parser",
  // 可共享的配置 是一个npm包,输出的是一个配置对象。
  extends: [
    "standard",
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "plugin:import/typescript",
  ],
  // 指定脚本的运行环境。每种环境都有一组特定的预约义全局变量。
  env: {
    browser: true,
    node: true,
    es6: true,
    mocha: true,
    jest: true,
    jasmine: true,
  },
  // 输出的规则
  plugins: ["react", "prettier", "@typescript-eslint"],
  // 为特定类型的文件(ts、tsx)指定处理器。
  overrides: [
    {
      files: ["*.ts", "*.tsx"],
      rules: {
        "@typescript-eslint/no-unused-vars": [1, { args: "none" }],
      },
    },
  ],
  // 规则集,会覆盖extends中的规则
  rules: {
    // 语句强制分号结尾
    semi: [2, "always"],
    // 布尔值类型的 propTypes 的 name 必须为 is 或 has 开头 (off 不强制要求写 propTypes)
    "react/boolean-prop-naming": "off",
    // 一个 defaultProps 必须有对应的 propTypes ()
    "react/default-props-match-prop-types": "off",
    // 组件必须有 displayName 属性 (off 不强制要求写 displayName)
    "react/display-name": "off",
    // 禁止在自定义组件中使用一些指定的 props (off 没必要限制)
    "react/forbid-component-props": "off",
    // 禁止使用一些指定的 elements (off 没必要限制)
    "react/forbid-elements": "off",
    // 禁止使用一些指定的 propTypes (off 不强制要求写 propTypes)
    "react/forbid-prop-types": "off",
    // 禁止直接使用别的组建的 propTypes (off 不强制要求写 propTypes)
    "react/forbid-foreign-prop-types": "off",
    // 禁止使用数组的 index 作为 key (off 不强制要求 太严格了!)
    "react/no-array-index-key": "off",
    // note you must disable the base rule as it can report incorrect errors
    "no-use-before-define": "off",
    "@typescript-eslint/no-use-before-define": ["off"],
    "@typescript-eslint/no-var-requires": 0,
    // 禁止使用 children 做 props
    "react/no-children-prop": "error",
    // 禁止使用 dangerouslySetInnerHTML (off 没必要限制)
    "react/no-danger": "off",
    // 禁止在使用了 dangerouslySetInnerHTML 的组建内添加 children
    "react/no-danger-with-children": "error",
    // 禁止使用已废弃的 api
    "react/no-deprecated": "error",
    // 禁止在 componentDidMount 里面使用 setState (off 同构应用需要在 didMount 里写 setState)
    "react/no-did-mount-set-state": "off",
    // 禁止在 componentDidUpdate 里面使用 setState
    "react/no-did-update-set-state": "error",
    // 禁止直接修改 this.state
    "react/no-direct-mutation-state": "error",
    // 禁止使用 findDOMNode
    "react/no-find-dom-node": "error",
    // 禁止使用 isMounted
    "react/no-is-mounted": "error",
    // 禁止在一个文件创建两个组件
    "react/no-multi-comp": "off",
    // 禁止在 PureComponent 中使用 shouldComponentUpdate
    "react/no-redundant-should-component-update": "error",
    // 禁止使用 ReactDOM.render 的返回值
    "react/no-render-return-value": "error",
    // 禁止使用 setState
    "react/no-set-state": "off",
    // 禁止拼写错误
    "react/no-typos": "error",
    // 禁止使用字符串 ref
    "react/no-string-refs": "error",
    // 禁止在组件的内部存在未转义的 >, ", ' 或 }
    "react/no-unescaped-entities": "error",
    // @fixable 禁止出现 HTML 中的属性,如 class
    "react/no-unknown-property": "error",
    // 禁止出现未使用的 propTypes
    "react/no-unused-prop-types": "off",
    // 定义过的 state 必须使用
    "react/no-unused-state": "off",
    // 禁止在 componentWillUpdate 中使用 setState
    "react/no-will-update-set-state": "error",
    // 必须使用 Class 的形式创建组件
    "react/prefer-es6-class": ["error", "always"],
    // 必须使用 pure function
    "react/prefer-stateless-function": "off",
    // 组件必须写 propTypes
    "react/prop-types": "off",
    // 出现 jsx 的地方必须 import React
    "react/react-in-jsx-scope": "off",
    // 非 required 的 prop 必须有 defaultProps
    "react/require-default-props": "off",
    // 组件必须有 shouldComponentUpdate
    "react/require-optimization": "off",
    // render 方法中必须有返回值
    "react/require-render-return": "error",
    // @fixable 组件内没有 children 时,必须使用自闭和写法
    "react/self-closing-comp": "off",
    // @fixable 组件内方法必须按照一定规则排序
    "react/sort-comp": "off",
    // propTypes 的熟悉必须按照字母排序
    "react/sort-prop-types": "off",
    // HTML 中的自闭和标签禁止有 children
    "react/void-dom-elements-no-children": "error",
    // @fixable 布尔值的属性必须显式的写 someprop={true}
    "react/jsx-boolean-value": "off",
    // @fixable 自闭和标签的反尖括号必须与尖括号的那一行对齐
    "react/jsx-closing-bracket-location": [
      "error",
      {
        nonEmpty: false,
        selfClosing: "line-aligned",
      },
    ],
    // @fixable 结束标签必须与开始标签的那一行对齐
    "react/jsx-closing-tag-location": "off",
    // @fixable 大括号内前后禁止有空格
    "react/jsx-curly-spacing": [
      "error",
      {
        when: "never",
        attributes: {
          allowMultiline: true,
        },
        children: true,
        spacing: {
          objectLiterals: "never",
        },
      },
    ],
    // @fixable props 与 value 之间的等号前后禁止有空格
    "react/jsx-equals-spacing": ["error", "never"],
    // 限制文件后缀
    "react/jsx-filename-extension": "off",
    // @fixable 第一个 prop 必须得换行
    "react/jsx-first-prop-new-line": "off",
    // handler 的名称必须是 onXXX 或 handleXXX
    "react/jsx-handler-names": "off",
    // 数组中的 jsx 必须有 key
    "react/jsx-key": "error",
    // @fixable 限制每行的 props 数量
    "react/jsx-max-props-per-line": "off",
    // jsx 中禁止使用 bind
    "react/jsx-no-bind": "off",
    // 禁止在 jsx 中使用像注释的字符串
    "react/jsx-no-comment-textnodes": "error",
    // 禁止出现重复的 props
    "react/jsx-no-duplicate-props": "error",
    // 禁止在 jsx 中出现字符串
    "react/jsx-no-literals": "off",
    // 禁止使用 target="_blank"
    "react/jsx-no-target-blank": "off",
    // 禁止使用未定义的 jsx elemet
    "react/jsx-no-undef": "error",
    // 禁止使用 pascal 写法的 jsx,比如 <TEST_COMPONENT>
    "react/jsx-pascal-case": "error",
    // @fixable props 必须排好序
    "react/jsx-sort-props": "off",
    // @fixable jsx 的开始和闭合处禁止有空格
    "react/jsx-tag-spacing": [
      "error",
      {
        closingSlash: "never",
        beforeSelfClosing: "always",
        afterOpening: "never",
      },
    ],
    // jsx 文件必须 import React
    "react/jsx-uses-react": "error",
    // 定义了的 jsx element 必须使用
    "react/jsx-uses-vars": "error",
    // @fixable 多行的 jsx 必须有括号包起来
    "react/jsx-wrap-multilines": "off",
    // 消除未使用的变量,函数和函数的参数。
    "no-unused-vars": "off",
    // jsdoc语法检查
    "valid-jsdoc": [
      "error",
      {
        requireReturn: false,
      },
    ],
  },
  // 添加共享设置
  settings: {
    react: {
      version: "detect",
    },
    polyfills: ["fetch", "promises", "url"],
  },
};
复制代码
// 增加 eslint 需要忽略的校验文件:.eslintignore
 
.eslintrc.js
node_modules
dist
复制代码

配置完启动项目,发现确实报了很多错误,咱们恢复一下,如图:

1.gif

这边我是对 vscode 做了配置,结合项目可以做到 保存(Ctrl + S)自动格式化。 关于vscode的配置如下

四、VS Code 配置

首先在vscode中安装Eslint 插件

image.png

然后打开配置文件(VSCode => 首选项 => 设置 => 用户设置 => 打开settings.json)做如下配置:

//对 VSCode settings.json 文件的配置
 
 
    // 保存时开启自动修复 默认只支持 .js 文件
    "eslint.autoFixOnSave": true,
 
    // 为以下的文件类型开启保存自动修复
    "eslint.validate": [
        "javascriptreact",
        "javascript",
        "typescript",
        "html",
        "vue",
        {
          "language": "html",
          "autoFix": true
        },
        {
            "language": "vue",
            "autoFix": true
        },
        {
            "language": "ts",
            "autoFix": true
        },
        {
            "language": "tsx",
            "autoFix": true
        },
    ],
复制代码

到此配置完毕。之后如有更新继续加上

后续补充

一、Resolve 配置

这边在之后的开发中涉及到ts和tsx 文件,webpack默认只会查找js和json,因此不加后缀的情况下是搜索不到的。还有在搜索模块的时候,导入路劲太费劲,所以也可以加一个映射:@ 。


//对 webpack.config.base.js 增加 resolve 配置

......

  // Resolve 配置 Webpack 如何寻找模块所对应的文件
  resolve: {
    // 在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试访问文件是否存在。  resolve.extensions用于配置在尝试过程中用到的后缀列表,默认是:js 和 json
    extensions: [".js", ".ts", ".tsx"],
    // 配置项通过别名来把原导入路径映射成一个新的导入路径
    alias: {
      "@": resolvePath("../src"),
    },
  },

......


复制代码

项目地址:Github

分类:
前端
标签: