「使用 webpack5从0到1搭建React+TypeScript 项目环境」1. React 与 TypeScript 集成

2022-10-24 18:30:48 浏览数 (1)

「使用 webpack 5 从0到1搭建React TypeScript 项目环境」1. React 与 TypeScript 集成

本篇文章会带大家使用 webpack 5集成 ReactTypeScript,同时为了提高我们的代码质量,我们会在构建中添加类型检查和代码规范校验。

创建项目结构

首先我们新建一个项目,结构如下:

在这里插入图片描述

由于很多配置在「生产环境」「开发环境」中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等。所以这里我们分开发环境和生产环境,让打包更灵活。

  • webpack.config.common.js (开发环境和生产环境的共同配置)
  • webpack.config.dev.js(开发环境配置)
  • webpack.config.prod.js (生产环境配置)
  • webpack.config.js (对不同环境下的配置文件进行整合)

初始内容

webpack.config.common.js

代码语言:javascript复制
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // webpack 配置
  // 入口起点
  entry: './src/index.tsx',
  // 输出
  output: {
    // 输出文件名
    filename: 'scripts/[name].[contenthash].js',
    // 输出路径
    path: resolve(__dirname, '../dist'),
    clean: true, // 打包前清理 dist 文件夹
  },
  // loader 配置
  module: {
    rules: [
      
    ]
  },
  resolve: {
    extensions: [ '.tsx', '.ts', '.jsx', '.js'], 
  },
  // plugins 的配置
  plugins: [
    // 功能:默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
    new HtmlWebpackPlugin({
      // 增加一个配置
      // 复制 '../index.html' 文件,并自动引入打包输出的所有资源(js/css)
      template: '../index.html',
      // 压缩html资源
      // minify: {
      //   collapseWhitespace: true, //去空格
      //   removeComments: true // 去注释
      // }
    }),
  ],
}

「【注】」 插件html-webpack-plugin 需要我们通过 yarn add html-webpack-plugin -D 下载,详情请看 html-webpack-plugin

webpack.config.dev.js

代码语言:javascript复制
const Package = require('../package.json')

const proxy = Package.proxy ?? {} // 获取 package.json 中的 代理配置

module.exports = {
  module: {
    rules: [
      
    ]
  },
  devtool: 'eval-cheap-module-source-map',
  mode: 'development',
  devServer: {
    static: '../dist', // 将 ../dist 目录下的文件作为 web 服务的根目录。
    compress: true,
    port : 3000, // 设置端口号
    open : true, // 自动打开本地默认浏览器
    hot: true, // 开启热更新
    proxy,
    historyApiFallback: true
  }
}

为了我们开发调式,这里设置了devtool: 'eval-cheap-module-source-map',关于 sourcemap的详情请看:一文了解source-map

compress 是否选择开启gzips压缩功能,对应静态资源请求的响应头里的 Content-Encoding: gzip

historyApiFallback :如果我们的应用是个 SPA (单页面应用),当路由到/some时(可以直接在地址栏里输入),会发现此时刷新页面后,控制台会报错:GET http://localhost:3000/some 404 (Not Found)。此时打network,刷新并查看,就会发现问题所在———浏览器把这个路由当作了静态资源地址去请求,然而我们并没有打包出/some这样的资源,所以这个访问无疑是404的。如何解决它?这种时候,我们可以通过配置来提供页面代替任何404的静态资源响应。详情请看:DevServer | webpack 中文文档 (docschina.org)

proxy 开启代理:

  • 我们打包出的 js bundle 里有时会含有一些对特定接口的网络请求(ajax/fetch).要注意,此时客户端地址是在 http://localhost:3000/ 下,假设我们的接口来自http://localhost:4001/ ,那么毫无疑问,此时控制台里会报错并提示你跨域。如何解决这个问题?在开发环境下,我们可以使用devServer自带的proxy功能: proxy: { "/api": "http://localhost:4001" } 详情请看:DevServer | webpack 中文文档 (docschina.org)

webpack.config.prod.js

代码语言:javascript复制
module.exports = {
  output: {
    filename: 'scripts/[name].[contenthash].js'
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [
      
  ],
  mode: 'production',

}

webpack.config.js

代码语言:javascript复制
const { merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common')
const productionConfig = require('./webpack.config.prod')
const developmentConfig = require('./webpack.config.dev')

module.exports = (env) => {
  switch(true) {
    case env.development:
      return merge(commonConfig, developmentConfig)

    case env.production:
      return merge(commonConfig, productionConfig)

    defult:
      return new Error('No matching configuration was found')
  }
}
  • 我们使用一个名为 webpack-merge 的工具。详情请看:survivejs/webpack-merge: Merge designed for webpack (github.com)
  • webpack 命令行 环境配置 的 --env 参数,可以允许你传入任意数量的环境变量。而在 webpack.config.js中可以访问到这些环境变量。例如, --env production。对于我们的 webpack配置,有一个必须要修改之处。通常, module.exports指向配置对象。要使用 env 变量,你必须将 module.exports转换成一个函数:

index.html

代码语言:javascript复制
<!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>React App</title>
</head>
<body>
  <div id='root'></div>
</body>
</html>

这个 HTML 文件是Webpack 构建过程中的模板文件。目的是告诉 WebpackReact代码注入到 id="root"div 元素中,并在 HTML 中自动引入打包好的 JavaScriptCSS

npm 脚本

每次打包或启动服务时,都需要在命令行里输入一长串的命令。配置 npm 脚本来简化命令行的输入,这时可以省略 npx

代码语言:javascript复制
// package.json
{
  ...,
  "scripts": {
    "start": "webpack serve -c ./config/webpack.config.js --env development",
    "build": "webpack -c ./config/webpack.config.js --env production"
  },
}

配置 React 和 TypeScript环境

安装 React 及其对应的类型库:

代码语言:javascript复制
yarn add react react-dom
yarn add @types/react @types/react-dom -D

src/index.tsx 来编写 React 组件,此代码将会被展示到index.html 文件id="root"div 元素下:

代码语言:javascript复制
import React from "react";
import ReactDOM from "react-dom";

const App = () => <h1>Hello World!</h1>;

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

「添加 Babel」

在项目中,我们需要使用 BabelReactTypeScript 代码转换为 JavaScript。接下来我们安装一些 Babel 工具

代码语言:javascript复制
yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/plugin-transform-runtime @babel/runtime typescript -D
  • babel-loader通知 BabelReactTypeScript代码转换为 JavaScript
  • @babel/core: Babel 核心库
  • @babel/preset-env:让我们可以在不支持 JavaScript 最新特性的浏览器中使用 ES6 语法
  • @babel/preset-react:将 React代码转换为 JavaScript
  • @babel/preset-typescript:将 TypeScript 代码转换为 JavaScript
  • @babel/plugin-transform-runtime@babel/runtime:支持在低版本浏览器使用 ES6 语法,如 async/await

「Babel 配置」

我们通过.babelrc文件来进行 Babel 配置,在根目录创建此文件并加入以下内容

代码语言:javascript复制
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ]
  ]
}

上面的配置是告诉 Babel 使用哪些插件

或者也可以直接写在webpack.config.common.jsrules中:

代码语言:javascript复制
{
  test: /.(ts|js)x?$/i,
  exclude: /node_modules/,
  use: {
    loader: "babel-loader",
    options: {
      presets: [
        "@babel/preset-env",
        "@babel/preset-react",
        "@babel/preset-typescript",
      ],
      plugins: [
        [
          "@babel/plugin-transform-runtime",
          {
            regenerator: true,
          },
        ],
      ],
    },
  },
}

相应的我们还需要配置 tsconfig.json

我们可以用ts自带的工具来自动化生成它。

代码语言:javascript复制
npx tsc --init

我们发现生成了一个tsconfig.json,里面注释掉了绝大多数配置。现在,根据我们想要的效果来打开对应的配置。

代码语言:javascript复制
{
  "compilerOptions": {
    "target": "es2016",                                
    "jsx": "preserve",                                /* Specify what JSX code is generated. */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "rootDir": "./src/",                                  /* Specify the root folder within your source files. */
    "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    "outDir": "./dist/",                                   /* Specify an output folder for all emitted files. */
    "removeComments": true,                           /* Disable emitting comments. */
    "esModuleInterop": true,                            
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

启动开发环境

「安装 webpack

代码语言:javascript复制
yarn add webpack webpack-cli webpack-dev-server -D

src/index.tsx 中添加:

代码语言:javascript复制
import React from "react";
import ReactDOM from "react-dom";

const App = () => <h1>Hello World!</h1>;

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

通过yarn start启动开发环境,,然后我们在浏览器中访问http://localhost:3000,可以看到:

在这里插入图片描述

在 webpack 构建过程中添加类型检查

当我们把 src/index.tsx 修改如下:

代码语言:javascript复制
import React from "react";
import ReactDOM from "react-dom";

const App = () => {
  console.log(a);
  return (
    <h1>Hello World!</h1>
  )
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

我们发现 webpack 还是能正常构建:

在这里插入图片描述

但是因为我们输出了一个为声明的变量a,所以浏览器的控制台上会报错:

在这里插入图片描述

为了开发的时候方便,我们希望在 webpack 构建过程中就能发现错误,我们可以使用fork-ts-checker-webpack-pluginwebpack 构建过程支持类型检查。这意味着 webpack 会通知我们任何类型相关的错误。

首先我们先安装依赖:

代码语言:javascript复制
yarn add fork-ts-checker-webpack-plugin -D

webpack.config.dev.js中:

引用第三方库:

代码语言:javascript复制
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

在插件配置中使用该插件:

代码语言:javascript复制
plugins: [
    ...,
    new ForkTsCheckerWebpackPlugin({
     async: false
 }),
],

我们使用 async 标志来告诉 webpack 等待代码的类型检查结束,然后才提交代码进行编译。

接下来我们重新启动:

可以看到控制台报错:

在这里插入图片描述

同时,在浏览器中访问http://localhost:3000,可以看到:

在这里插入图片描述

如果想了解更多 fork-ts-checker-webpack-plugin 的相关配置,请看TypeStrong/fork-ts-checker-webpack-plugin:在单独的进程上运行typescript类型检查器的Webpack插件。(github.com)

在 webpack 构建过程中添加代码规范校验

webpack构建流程不会执行代码规范校验。我们可以使用ESLintPlugin来使 webpack构建过程能够使用 ESLint 进行代码规范校验。

下面我们来安装相应的依赖:

代码语言:javascript复制
yarn add eslint-webpack-plugin eslint -D

配置eslint,只需要在根目录下添加一个.eslintrc文件(或者.eslintrc.json, .js等)。当然,我们可以使用eslint工具来自动生成它:

代码语言:javascript复制
npx eslint --init

在这里插入图片描述

并生成了一个配置文件(.eslintrc.json),这样我们就完成了eslint的基本规则配置。

webpack.config.dev.js中:

引用第三方库:

代码语言:javascript复制
const ESLintPlugin = require('eslint-webpack-plugin');

在插件配置中使用该插件:

代码语言:javascript复制
plugins: [
    ...,
    new ESLintPlugin({
      extensions: ["js", "jsx", "ts", "tsx"],
    }),
],

src/index.tsx 中,添加一个未使用的变量:

代码语言:javascript复制
import React from "react";
import ReactDOM from "react-dom";

const App = () => {
  const a = '';
  // console.log(a);
  return (
    <h1>Hello World!</h1>
  )
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

输入 yarn start 构建,可以看到警告:

在这里插入图片描述

启动生存环境打包

输入 yarn build 可以进行生产环境打包,我们可以看到输出了一个 dist 文件夹:

在这里插入图片描述

最后

至此我们已经集成了 ReactTypeScript,下一篇文章是 「「使用 webpack 5 从0到1搭建React TypeScript 项目环境」2. 集成 css、less 与 sass」

0 人点赞