Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack + React 单页实践#4:引入 TypeScript #11

Open
S-T-D opened this issue Mar 14, 2021 · 3 comments
Open

webpack + React 单页实践#4:引入 TypeScript #11

S-T-D opened this issue Mar 14, 2021 · 3 comments

Comments

@S-T-D
Copy link
Owner

S-T-D commented Mar 14, 2021

目前 TypeScript 基本上是前端项目的标配了,所以这一篇介绍如何在项目内集成 TypeScript。

选择编译器

要在项目内使用 TypeScript,肯定得选择编译器和编译方式。

鉴于我们已经在项目内使用了 Babel,并且 TypeScript 官方也推荐使用 Babel,所以我们直接选用 @babel/preset-typescript 进行编译。同时,只使用一个编译工具也使项目配置更加容易,不用维护多个工具的配置文件。

关于 @babel/preset-typescript 的由来,可以参见 [译] TypeScript 牵手 Babel:一场美丽的婚姻

 

安装

A. 安装编译所需依赖:

yarn add @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread -D

@babel/preset-typescriptBabel 编译 TypeScript 所需的预设,@babel/plugin-proposal-class-properties@babel/plugin-proposal-object-rest-spread 是用于编译尚在提议阶段的语法,具体介绍可点击下面的链接查看。

 

B. 安装类型检查所需依赖:

yarn add @types/react @types/react-dom

这两个包是对于 reactreact-dom 的类型声明,如果不安装,编辑器会有相应的类型缺失的提示,类似的如果我们使用其他的库,比如说 lodash,可以查看是否有对应的 @types/loadsh 包,如果没有就需要自己写类型声明文件。

 

C.修改 webpack.config.js

const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.tsx',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    devServer: {
        contentBase: './dist',
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
    module: {
        rules: [
            {
                test: /\.(ts|js)x?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
            },
        ],
    },
};

相对于之前的配置文件,entry 字段做了修改,添加了 extensions 字段,用于 import 的时候不用写文件的扩展名称。

test 字段做了修改,目前表示匹配 .ts.tsx.js.jsx 文件。

删除了 use 字段,使用了 loader 字段,将 loader 的配置移到了专门的配置文件。

 

D. 配置 babel-loader

新增 .babelrc 文件

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-typescript"
        "@babel/preset-react",
    ],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-object-rest-spread"
    ]
}

.babelrc 文件是 babel 的配置文件,用于配置其所使用的插件和预设。

 

E. 配置 TypeScript 的编译选项

新增 tsconfig.json 文件

{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "noFallthroughCasesInSwitch": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "moduleResolution": "node",
        "esModuleInterop": true,
        "noUnusedLocals": true,
        "noImplicitAny": true,
        "target": "ESNext",
        "module": "ESNext",
        "strict": true,
        "jsx": "react-jsx",
        "noEmit": true,
        "allowJs": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "dist"
    ]
}

tsconfig.json 的配置项特别多,全部配置的解释可以查看这个文档

  • allowJs

    默认为 false,表示是否允许使用 .js 文件,设置为 true 可以让 .js.ts 共存,方便对旧项目进行逐步迁移。

 

F. 修改代码

index.js 重命名为 index.tsx,内容不变

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

app.js 重命名为 app.tsx,修改内容为如下

import React from 'react';

const App = () => {
    const title: string = 'A beautiful page!';

    return (
        <h2>{title}</h2>
    )
}

export default App;

 

G. 查看效果

执行 yarn dev,效果如下

 

删除

写过 React 项目的都知道,只要在文件内使用了 jsx,那文件必须得引入 React, 也就是 import React from 'react'

每个文件都要导入一遍,就显得非常啰嗦和繁琐。

A. 为什么只要使用了 jsx,就要导入 React 呢?

因为浏览器并不能直接理解 jsx 代码,在编译打包的时候,jsx 代码实际上编译成了函数调用的代码,比如

import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

|| 编
|| 译
v 成

import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}

所以,React 必须导入到当前作用域。

 

B. React 17 之后,可以不用再导入 React

注意:TypeScript 从 4.1 开始支持。

修改 .babelrc,给 @babel/preset-react 添加配置

{
    "presets": [
        "@babel/preset-env",
        [
            "@babel/preset-react", 
            {
                "runtime": "automatic"
            }
        ],
        "@babel/preset-typescript"
    ],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-object-rest-spread"
    ]
}

修改 tsconfig.js,将 jsx 的值从 react 修改为 react-jsx

最后删除代码中的 import React from 'react',会发现 vscode 自身的 TypeScript 语法检查没有报错,项目也照常运行。

开启这个特性后,上面同样的源代码会被编译成:

// 这行代码由 babel 等编译器自动插入,不要手动改添加!!!
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

实际上,同样是被编译成函数调用,只是编译器默认帮我们做了这件事,并且新增了运行时函数 jsx 提供给编译器使用。

对于这个特性,具体介绍参见 介绍全新的 JSX 转换

 

添加类型检查

目前为止,项目已经可以编译运行 TypeScript 代码了,但是还没有类型检查的功能。

因为 Babel 只负责对代码进行编译,并不会进行类型检查,参考 Using Babel with TypeScript

虽然现在的编辑器比如 vscode 自带有类型检查功能,但是有一定局限性,只能在代码编写阶段给我们提示,如果想要在代码提交前以及持续集成的时候进行检查,那必须得添加专门的工具和检查配置。

A. 使用 TypeScript 进行代码检查

首先,安装 TypeScript
yarn add typescript -D

然后添加检查命令到 package.jsonscripts 字段下。

"scripts": {
    "type-check": "tsc",
    "build": "webpack",
    "dev": "webpack serve"
},

然后,手动在 app.tsx 文件添加一个错误,测试代码检查是否生效。

const App = () => {
    const title: string = 'A beautiful page!';
    // 声明 desc,但不使用
    const desc = 'Beautiful';
    return (
        <h2>{title}</h2>
    )
}

export default App;

因为我们在 tsconfig.json 中设置了 noUnusedLocalstrue,所以理论上,上面添加的代码不能通过代码检查。

运行 yarn type-check,结果如下

可以看到,代码检查命令生效了。

yarn type-check 命令同样使用的是根目录下的 tsconfig.js 配置文件。

 

B. git commit 前自动检查修改的代码文件

上面添加的命令需要我们手动运行,在团队协作的时候,一般都会在代码提交前自动运行代码检查,强制对每个修改的文件进行检查,检查通过后 git commit 命令才会真正开始执行,否则代码将会一直处于暂存区,不会进入代码库。

为了达到这个效果,我们需要安装两个工具:huskylint-staged

  • husky:用于操作 git hook,我们可以使用这个工具非常方便地编写自定义命令和脚本,husky 会在我们指定的时机执行。目前,husky 的最新版本是 5,但是这里我们使用 4,github 地址

  • lint-staged:用于对处于暂存区的代码执行各种操作,github 地址

首先安装依赖

yarn add [email protected] lint-staged -D

修改 package.json

{
    // ...
    "scripts": {
        "type-check": "tsc",
        "type-check-pre-commit": "bash -c tsc",
        "build": "webpack",
        "dev": "webpack serve"
    },
    "husky": {
        "hooks": {
            "pre-commit": "lint-staged"
        }
    },
    "lint-staged": {
        "*.{ts,tsx}": [
            "yarn type-check-pre-commit"
        ]
    },
    // ...
}

我们新增了命令 type-check-pre-commit

所以,假如我们修改了代码,通过 git add . 将代码添加到暂存区

然后执行 git commit -m "fix bugs" 的时候,就会触发上面添加的 pre-commit 钩子

从而会执行 lint-staged 命令

然后 lint-staged 会将本次修改的 .ts.tsx 文件筛选出来作为参数传递给 type-check-pre-commit 命令执行

最后就会使用 tsc 对其进行检查,如果检查失败就会终止 commit 命令

修改代码再次 commit 直至检查通过后,commit 才会成功,代码才会真正进入代码库

 

至于上面为什么会写成 bash -c tsc,可以参考这个 issue,主要是为了在自动执行 type-check-pre-commit 命令的时候,能够让 ts 编译器使用项目根目录下的 tsconfig.json 文件。

 

代码地址

webpack-react-spa-template-project

 

参考资料

@S-T-D S-T-D changed the title 4. webpack + React 单页实践#3:引入 TypeScript 4. webpack + React 单页实践#4:引入 TypeScript Mar 14, 2021
@loveyunk
Copy link

lint-staged bash -c tsc 好像不太好使,依然会检查所有的代码,并不是 staged 的

@S-T-D
Copy link
Owner Author

S-T-D commented Apr 26, 2021

lint-staged bash -c tsc 好像不太好使,依然会检查所有的代码,并不是 staged 的

我这里可以,估计是环境差异,看看 lint-staged/lint-staged#468 这个有答案不

@loveyunk
Copy link

不知道,后来我用这个包来做的 https://github.com/gustavopch/tsc-files

@S-T-D S-T-D changed the title 4. webpack + React 单页实践#4:引入 TypeScript webpack + React 单页实践#4:引入 TypeScript May 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants