「使用 webpack 5 从0到1搭建React + TypeScript 项目环境」1. React 与 TypeScript 集成
本篇文章会带大家使用 webpack 5
集成 React
与TypeScript
,同时为了提高我们的代码质量,我们会在构建中添加类型检查和代码规范校验。
创建项目结构
首先我们新建一个项目,结构如下:
由于很多配置在「生产环境」和「开发环境」中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等。所以这里我们分开发环境和生产环境,让打包更灵活。
webpack.config.common.js
(开发环境和生产环境的共同配置)webpack.config.dev.js
(开发环境配置)webpack.config.prod.js
(生产环境配置)webpack.config.js
(对不同环境下的配置文件进行整合)
初始内容
webpack.config.common.js
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
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
module.exports = { output: { filename: 'scripts/[name].[contenthash].js' }, module: { rules: [ ] }, plugins: [ ], mode: 'production', }
webpack.config.js
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
<!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
构建过程中的模板文件。目的是告诉 Webpack
将 React
代码注入到 id="root"
的 div
元素中,并在 HTML
中自动引入打包好的 JavaScript
和 CSS
。
npm 脚本
每次打包或启动服务时,都需要在命令行里输入一长串的命令。配置 npm
脚本来简化命令行的输入,这时可以省略 npx
:
// 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
及其对应的类型库:
yarn add react react-dom yarn add @types/react @types/react-dom -D
在src/index.tsx
来编写 React
组件,此代码将会被展示到index.html
文件id="root"
的 div
元素下:
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」
在项目中,我们需要使用 Babel
将 React
和 TypeScript
代码转换为 JavaScript
。接下来我们安装一些 Babel
工具
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
通知Babel
将React
和TypeScript
代码转换为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
配置,在根目录创建此文件并加入以下内容
{ "presets": [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ], "plugins": [ [ "@babel/plugin-transform-runtime", { "regenerator": true } ] ] }
上面的配置是告诉 Babel
使用哪些插件
或者也可以直接写在webpack.config.common.js
的rules
中:
{ 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
自带的工具来自动化生成它。
npx tsc --init
我们发现生成了一个tsconfig.json
,里面注释掉了绝大多数配置。现在,根据我们想要的效果来打开对应的配置。
{ "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
」
yarn add webpack webpack-cli webpack-dev-server -D
在 src/index.tsx
中添加:
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
修改如下:
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-plugin
让 webpack
构建过程支持类型检查。这意味着 webpack
会通知我们任何类型相关的错误。
首先我们先安装依赖:
yarn add fork-ts-checker-webpack-plugin -D
在 webpack.config.dev.js
中:
引用第三方库:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
在插件配置中使用该插件:
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
进行代码规范校验。
下面我们来安装相应的依赖:
yarn add eslint-webpack-plugin eslint -D
配置eslint
,只需要在根目录下添加一个.eslintrc
文件(或者.eslintrc.json, .js
等)。当然,我们可以使用eslint
工具来自动生成它:
npx eslint --init
并生成了一个配置文件(.eslintrc.json
),这样我们就完成了eslint
的基本规则配置。
在 webpack.config.dev.js
中:
引用第三方库:
const ESLintPlugin = require('eslint-webpack-plugin');
在插件配置中使用该插件:
plugins: [ ..., new ESLintPlugin({ extensions: ["js", "jsx", "ts", "tsx"], }), ],
在 src/index.tsx
中,添加一个未使用的变量:
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
文件夹:
最后
至此我们已经集成了 React
与TypeScript
,下一篇文章是 「「使用 webpack 5 从0到1搭建React + TypeScript 项目环境」2. 集成 css、less 与 sass」