快来跟我一起学 React(Day4)

简介

上一节我们从 0 开始搭建了一个项目,完成了入口与出口的配置、ts 语法支持、react 基本库的安装、css 样式配置等工作,我们继续上一节的内容。

知识点

  • Eslint(代码质量校验)
  • eslint-webpack-plugin(Eslint webpack 插件)
  • eslint-config-react-app(React 官方 eslint 配置)
  • fork-ts-checker-webpack-plugin(ts 语法校验插件)
  • Optimization(分包优化等)

准备

上一节所有的内容在 dev 分支:https://gitee.com/vv_bug/cus-react-demo/tree/dev,我们重新切一个新分支 dev-v1.0.0

我们安装好依赖,并且用 npm start 命令启动项目:

npm install --registry https://registry.npm.taobao.org && npm start
1-1.png

可以看到,浏览器自动打开了我们项目入口,并且在页面正常显示了 ”hello react111“ ,到这,我们的准备工作算是完成了。

Eslint

ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。

接下来我们来安装一下 ESLint 相关依赖:

安装 ESLint

Eslint 核心 API。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint --registry https://registry.npm.taobao.org

安装 eslint-webpack-plugin

Eslint 在 Webpack 中的插件。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint-webpack-plugin --registry https://registry.npm.taobao.org

安装 eslint-config-react-app

React 官方提供的 Eslint 配置。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint-config-react-app --registry https://registry.npm.taobao.org

安装 @typescript-eslint/eslint-plugin

ESLint 中的 ts 插件。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D @typescript-eslint/eslint-plugin --registry https://registry.npm.taobao.org

安装 @typescript-eslint/parser

ESLint 中的 ts 语法解析器。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D @typescript-eslint/parser --registry https://registry.npm.taobao.org

安装 typescript

ts 语法核心库。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D typescript --registry https://registry.npm.taobao.org

安装 eslint-plugin-import

Eslint 中对 EsModule 导入导出的规范插件。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint-plugin-import --registry https://registry.npm.taobao.org

安装 eslint-plugin-flowtype

Eslint 中对 flow 语法规范插件。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint-plugin-flowtype --registry https://registry.npm.taobao.org

安装 eslint-plugin-jsx-a11y

安装 eslint-plugin-react-hooks

安装 eslint-plugin-react

安装 babel-eslint

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D eslint-plugin-jsx-a11y eslint-plugin-react-hooks  eslint-plugin-react babel-eslint --registry https://registry.npm.taobao.org

总算是安装完毕了,其实这些都是 React 官方要求的一些 Eslint 插件,都是在 eslint-config-react-app 中定义要求的,每一个具体什么功能我就不一一分析了,小伙伴自己去官网查看哦,童鞋们平时也不需要刻意去记这些东西,直接 copy 然后安装即可,ESLint 依赖我们是安装完毕了,下面我们来给项目配置一下 ESLint。

首先找到 webpack 配置文件 webpack.config.js,然后添加 eslint-webpack-plugin

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .target('web')
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => {
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                })
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
        .rule('sass') // sass-loader 相关配置
            .test(/\.(sass|scss)$/) // Sass 和 Scss 文件
            .use('extract-loader') // 提取 css 样式到单独 css 文件
                .loader(require('mini-css-extract-plugin').loader)
                .end()
            .use('css-loader') // 加载 css 模块
                .loader('css-loader')
                .end()
            .use('postcss-loader') // 处理 css 样式
                .loader('postcss-loader')
                .end()
            .use('sass-loader') // Sass 语法转 css 语法
                .loader('sass-loader')
                .end()
            .end()
        .end()
    .plugin('extract-css') // 提取 css 样式到单独 css 文件
        .use(require('mini-css-extract-plugin'), [
            {
                filename: isDev ? 'css/[name].css': 'css/[name].[contenthash].css',
                chunkFilename: isDev ? 'css/[id].css': 'css/[name].[contenthash].css',
            },
        ])
        .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            {
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加载的 chunk
                inject: 'body', // 指定 script 脚本注入的位置为 body
            },
        ])
        .end()
    .plugin('eslint') // 添加 eslint-webpack-plugin 插件
        .use(require('eslint-webpack-plugin'), [
            {
                // Plugin options
                extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
                eslintPath: require.resolve('eslint'),
                failOnError: !isDev,
                context: path.resolve(__dirname, "./src"),
                // ESLint class options
                cwd: __dirname,
                resolvePluginsRelativeTo: __dirname,
            }
        ])
        .end()
    .devServer
        .host('0.0.0.0') // 服务器外部可访问
        .disableHostCheck(true) // 关闭白名单校验
        .contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
        .historyApiFallback({
            disableDotRule: true, // 禁止在链接中使用 "." 符号
            rewrites: [
                { from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
            ],
        })
        .port(8080) // 当前端口号
        .hot(true) // 打开页面热载功能
        .sockPort('location') // 设置成平台自己的端口
        .open(true)
module.exports = config.toConfig();

如果要让 eslint 起作用的话,我们还需要给 eslint 添加一个配置文件 .eslintrc.js

我们在工程目录 cus-react-demo 下创建一个 .eslintrc.js 文件:

touch .eslintrc.js

然后写入以下代码到 .eslintrc.js 文件:

module.exports = {
    env: {
        node: true, // 添加 node 环境
    },
    extends: [
        "react-app", // 继承 react-app 配置
        "react-app/jest" // 继承 react-app/jest 配置
    ],
    rules: {
        // 自定义规则
        semi: [
            // 代码结尾必须使用 “;“ 符号
            'error',
            'always',
        ],
        quotes: [
            // 代码中字符串必须使用 ”” 符号
            'error',
            'double',
        ],
        'no-console': 'error', // 代码中不允许出现 console
    },
};

其实我们需要的只是对 src 目录底下的所有文件做代码质量校验,其它的文件是不需要的。所以我们在工程目录 cus-react-demo 下再创建一个 .eslintignore 文件,列出那些不需要校验的文件列表:

touch .eslintignore

然后写入以下内容到 .eslintignore 文件:

node_modules/*
public/*
dist/*
webpack.config.js
.eslintrc.js

ok!万事都已俱备。

然后我们重新运行项目:

npm start
1-2.png

可以看到,终端中报了一些警告,我们尝试修复一下这些警告。

为了方便,我们在 package.json 文件中声明一个 lint 脚本:

{
  "name": "cus-react-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "rimraf dist && cross-env NODE_ENV=production webpack --mode=production",
    "start": "cross-env NODE_ENV=development webpack serve --mode=development --progress",
    "lint": "eslint  --ext .js,.mjs,.jsx,.ts,.tsx --fix ./src"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0",
    "@webpack-cli/serve": "^1.3.0",
    "autoprefixer": "^9.8.6",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.2.2",
    "babel-preset-react-app": "^10.0.0",
    "cross-env": "^7.0.3",
    "css-loader": "^5.1.3",
    "cssnano": "^4.1.10",
    "eslint": "^7.22.0",
    "eslint-config-react-app": "^6.0.0",
    "eslint-plugin-flowtype": "^5.4.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-webpack-plugin": "^2.5.2",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^1.3.9",
    "postcss-loader": "^5.2.0",
    "sass": "^1.32.8",
    "sass-loader": "^11.0.1",
    "typescript": "^4.2.3",
    "webpack": "^5.26.3",
    "webpack-chain": "^6.5.1",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  },
  "dependencies": {
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.2",
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }
}

然后我们在工程目录 cus-react-demo 执行 npm run lint 脚本去修复当前项目代码:

npm run lint

运行过后,ESLint 会自动帮我们修复掉了一些格式方面的问题。

从这里也可以看出,ESLint 不但可以发现我们代码中的一些问题,还能自动帮我们解决大部分语法、格式等问题。但也很多小伙伴是很反感 ESLint 的,因为他们觉得有了 ESLint 后,代码变得难写多了,但我想说的是 “真的代码难写了?还是你写出来的代码本来就问题呢?”。

ok!警告解决完毕后,我们再次执行 npm start 的时候就会发现,没有错误和警告信息了:

npm start
1-4.png

一般我们在平时开发中,为了更好的显示代码规范错误信息,我们直接利用 webpack.devServeroverlay 选项将 webpack 的报错跟警告显示到页面中去,所以我们修改一下 webpack.config.js 文件:

...
.devServer
        .host("0.0.0.0") // 为了让外部服务访问
        .port(8090) // 当前端口号
        .hot(true) // 热载
        .open(true) // 开启页面
        .overlay({
            warnings: true,
            errors: true
        }) // webpack 错误和警告信息显示到页面
...

比如我们现在项目中有代码不符合我们的代码规范:

1-5.png

可以看到,页面中提示了 ”编译失败“,IDE 提示了 “语句结尾需要添加分号”,终端中照样提示了 “语句结尾需要添加分号”,这样就很好的保持了项目代码风格的一致性,在团队合作中还是很有必要的。

fork-ts-checker-webpack-plugin

校验 ts 语法,检测 ts 语法中的一些报错并且通过 webpack 在终端中打印出来。

我们首先在工程目录 cus-react-demo 下执行以下命令进行安装:

npm install -D fork-ts-checker-webpack-plugin --registry https://registry.npm.taobao.org

安装完毕后,我们在 webpack.config.js 中将其引入:

.plugin('fork-ts-checker') // 配置 fork-ts-checker
        .use(require('fork-ts-checker-webpack-plugin'), [{
            eslint: {
                files: './src/**/*.{ts,tsx,js,jsx}' // required - same as command `eslint ./src/**/*.{ts,tsx,js,jsx} --ext .ts,.tsx,.js,.jsx`
            },
            typescript: {
                extensions: {
                    vue: {
                        enabled: true,
                        compiler: "vue-template-compiler"
                    },
                }
            }
        }])

webpack.config.js 全部配置:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .target('web')
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => {
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                })
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
        .rule('sass') // sass-loader 相关配置
            .test(/\.(sass|scss)$/) // Sass 和 Scss 文件
            .use('extract-loader') // 提取 css 样式到单独 css 文件
                .loader(require('mini-css-extract-plugin').loader)
                .end()
            .use('css-loader') // 加载 css 模块
                .loader('css-loader')
                .end()
            .use('postcss-loader') // 处理 css 样式
                .loader('postcss-loader')
                .end()
            .use('sass-loader') // Sass 语法转 css 语法
                .loader('sass-loader')
                .end()
            .end()
        .end()
    .plugin('extract-css') // 提取 css 样式到单独 css 文件
        .use(require('mini-css-extract-plugin'), [
            {
                filename: isDev ? 'css/[name].css': 'css/[name].[contenthash].css',
                chunkFilename: isDev ? 'css/[id].css': 'css/[name].[contenthash].css',
            },
        ])
        .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            {
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加载的 chunk
                inject: 'body', // 指定 script 脚本注入的位置为 body
            },
        ])
        .end()
    .plugin('eslint') // 添加 eslint-webpack-plugin 插件
        .use(require('eslint-webpack-plugin'), [
            {
                // Plugin options
                extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
                eslintPath: require.resolve('eslint'),
                failOnError: !isDev,
                context: path.resolve(__dirname, "./src"),
                // ESLint class options
                cwd: __dirname,
                resolvePluginsRelativeTo: __dirname,
            }
        ])
        .end()
    .plugin('fork-ts-checker') // 配置 fork-ts-checker
        .use(require('fork-ts-checker-webpack-plugin'), [{
            eslint: {
                files: './src/**/*.{ts,tsx,js,jsx}' // required - same as command `eslint ./src/**/*.{ts,tsx,js,jsx} --ext .ts,.tsx,.js,.jsx`
            },
            typescript: {
                extensions: {
                    vue: {
                        enabled: true,
                        compiler: "vue-template-compiler"
                    },
                }
            }
        }])
        .end()
    .devServer
        .host('0.0.0.0') // 服务器外部可访问
        .disableHostCheck(true) // 关闭白名单校验
        .contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
        .historyApiFallback({
            disableDotRule: true, // 禁止在链接中使用 "." 符号
            rewrites: [
                { from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
            ],
        })
        .port(8080) // 当前端口号
        .hot(true) // 打开页面热载功能
        .sockPort('location') // 设置成平台自己的端口
        .open(true) // 开启页面
        .overlay({
            warnings: true,
            errors: true
        }) // webpack 错误和警告信息显示到页面
module.exports = config.toConfig();

我们来测试一下,我们在 src/main.tsx 文件中添加一些错误的 ts 语法:

1-6.png

可以看到,我们对一个 string 类型的 a 赋值为 number

首先 IDE 直接报错了,说我们不能对一个string 类型的变量赋值 number 类型。

接下来我们运行一下项目,看 webpack 会不会编译通过:

npm start
1-7.png

可以看到,还是三个地方提示我们报错了,所以很好的避免了 ts 语法的报错。

Optimization

minimizer

将生产环境的代码进行压缩,并且去掉代码中的注释:

//----- optimization start-----
config.when(
  !isDev,
  () => {
    // 生成环境的时候
    config.optimization
      .minimize(true) // 打开压缩代码开关
      .minimizer('terser')
      .use(require('terser-webpack-plugin'), [
        {
          extractComments: false, // 去除注释
          terserOptions: {
            output: {
              comments: false, // 去除注释
            },
          },
        },
      ]);
  },
  () => {
    // 开发环境的时候
  }
);
//----- optimization end-------

Devtool

开发环境的时候将 devtool 改成 cheap-module-source-map 加快速度:

//----- optimization start-----
config.when(
  !isDev,
  () => {
    // 生成环境的时候
    config.optimization
      .minimize(true) // 打开压缩代码开关
      .minimizer('terser')
      .use(require('terser-webpack-plugin'), [
        {
          extractComments: false, // 去除注释
          terserOptions: {
            output: {
              comments: false, // 去除注释
            },
          },
        },
      ]);
  },
  () => {
    // 开发环境的时候
    config.devtool('cheap-module-source-map');
  }
);
//----- optimization end-------

分包机制

  1. node_modules 目录下所有的依赖都打包到 chunk-vendor.js 文件,优先级最高。
  2. 将重复次数 >=2 的依赖打包到 chunk-common.js 文件。
  3. webpack runtime 代码从每个 chunk 中抽离,单独打包到 runtime.js 文件。
config.optimization
  .splitChunks({
    cacheGroups: {
      vendors: {
        // 分离入口文件引用 node_modules 的 module(vue、@babel/xxx)
        name: `chunk-vendors`,
        test: /[\\/]node_modules[\\/]/,
        priority: -10,
        chunks: 'initial',
      },
      common: {
        // 分离入口文件引用次数 >=2 的 module
        name: `chunk-common`,
        minChunks: 2,
        priority: -20,
        chunks: 'initial',
        reuseExistingChunk: true,
      },
    },
  })
  .runtimeChunk('single'); // 分离 webpack 的一些帮助函数,比如 webpackJSONP 等等

webpack.config.js 所有配置内容:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .target('web')
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => {
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                })
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
        .rule('sass') // sass-loader 相关配置
            .test(/\.(sass|scss)$/) // Sass 和 Scss 文件
            .use('extract-loader') // 提取 css 样式到单独 css 文件
                .loader(require('mini-css-extract-plugin').loader)
                .end()
            .use('css-loader') // 加载 css 模块
                .loader('css-loader')
                .end()
            .use('postcss-loader') // 处理 css 样式
                .loader('postcss-loader')
                .end()
            .use('sass-loader') // Sass 语法转 css 语法
                .loader('sass-loader')
                .end()
            .end()
        .end()
    .plugin('extract-css') // 提取 css 样式到单独 css 文件
        .use(require('mini-css-extract-plugin'), [
            {
                filename: isDev ? 'css/[name].css': 'css/[name].[contenthash].css',
                chunkFilename: isDev ? 'css/[id].css': 'css/[name].[contenthash].css',
            },
        ])
        .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            {
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加载的 chunk
                inject: 'body', // 指定 script 脚本注入的位置为 body
            },
        ])
        .end()
    .plugin('eslint') // 添加 eslint-webpack-plugin 插件
        .use(require('eslint-webpack-plugin'), [
            {
                // Plugin options
                extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
                eslintPath: require.resolve('eslint'),
                failOnError: !isDev,
                context: path.resolve(__dirname, "./src"),
                // ESLint class options
                cwd: __dirname,
                resolvePluginsRelativeTo: __dirname,
            }
        ])
        .end()
    .plugin('fork-ts-checker') // 配置 fork-ts-checker
        .use(require('fork-ts-checker-webpack-plugin'), [{
            eslint: {
                files: './src/**/*.{ts,tsx,js,jsx}' // required - same as command `eslint ./src/**/*.{ts,tsx,js,jsx} --ext .ts,.tsx,.js,.jsx`
            },
            typescript: {
                extensions: {
                    vue: {
                        enabled: true,
                        compiler: "vue-template-compiler"
                    },
                }
            }
        }])
        .end()
    .devServer
        .host('0.0.0.0') // 服务器外部可访问
        .disableHostCheck(true) // 关闭白名单校验
        .contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
        .historyApiFallback({
            disableDotRule: true, // 禁止在链接中使用 "." 符号
            rewrites: [
                { from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
            ],
        })
        .port(8080) // 当前端口号
        .hot(true) // 打开页面热载功能
        .sockPort('location') // 设置成平台自己的端口
        .open(true) // 开启页面
        .overlay({
            warnings: true,
            errors: true
        }) // webpack 错误和警告信息显示到页面

//----- optimization start-----
config.when(
    !isDev,
    () => {
        // 生成环境的时候
        config.optimization
            .minimize(true) // 打开压缩代码开关
            .minimizer('terser')
            .use(require('terser-webpack-plugin'), [
                {
                    extractComments: false, // 去除注释
                    terserOptions: {
                        output: {
                            comments: false, // 去除注释
                        },
                    },
                },
            ]);
    },
    () => {
        // 开发环境的时候
        config.devtool('cheap-module-source-map');
    }
);
config.optimization
    .splitChunks({
        cacheGroups: {
            vendors: {
                // 分离入口文件引用 node_modules 的 module(vue、@babel/xxx)
                name: `chunk-vendors`,
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                chunks: 'initial',
            },
            common: {
                // 分离入口文件引用次数 >=2 的 module
                name: `chunk-common`,
                minChunks: 2,
                priority: -20,
                chunks: 'initial',
                reuseExistingChunk: true,
            },
        },
    })
    .runtimeChunk('single'); // 分离 webpack 的一些帮助函数,比如 webpackJSONP 等等
//----- optimization end-------
module.exports = config.toConfig();

到这,我们的项目就算是搭建完毕了,可能还需要一些缝缝补补,不过已经可以满足一个企业级的项目标准了,小伙伴自己直接拖到项目中去使用。

总结

我们花了两节课完成了从 0 开始搭建一个企业级 React 的项目,怎么样?是不是感觉还是有点难度的? 正因为 Webpack 的配置太多了,所以 ReactVue 官方都会直接提供一个脚手架,然后通过一些简单的命令把一个配置好的项目直接给你,你不需要进行配置就可以轻松的把一个项目跑起来了,官方提供的项目可以满足大多数公司的一个业务需求,但是如果不能满足业务需求的时候,我们就需要自己具备从 0 搭建一个项目的功底了,这也是大公司必备的一个技能(对 webpack 不熟的小伙伴,强烈推荐去看我课程 《来和 webpack 谈场恋爱吧》:https://www.lanqiao.cn/courses/2893)。

Demo 项目的全部代码地址:https://gitee.com/vv_bug/cus-react-demo/tree/dev-v1.0.0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容