作者:Roby
一、 新建空项目
新建一个项目文件夹例如叫ProjectA,然后在里面新建两个文件夹如下:
• build: 项目的构建目录.
• src: 源码目录.
$ npm init
生成package.json
{
"name": "my-app",
"version": "0.0.1"
}
新建一个 index.html 在 src 文件夹:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>My app</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
这个html模版会被webpack利用来生成构建项目。告诉webpack如何将react注入进来。
二、配置react 和 TS
npm install react react-dom
npm install --save-dev typescript
npm install --save-dev @types/react @types/react-dom
TypeScript通过 tsconfig.json文件来配置 ,新建一个tsconfig.json文件内容如下:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"paths": {
"src/*": ["./src/*"]
}
},
"include": ["src"]
}
typescript仅仅是被用来做类型检查,最终我们用babel做代码转译。所以在ts配置选项里仅设置它的类型检查,不做代码转译。如下:
• lib: 类型检查的库,只检查浏览器 DOM 和最新版本的ECMAscript
• allowJs: 是否允许javascript被编译.
• allowSyntheticDefaultImports: 类型检查允许默认imports 来自于没有默认exports的模块。
• skipLibCheck: 忽视类型检查对文件(*.d.ts)类型.
• esModuleInterop: 与 Babel 的兼容性.
• strict: 设置高级别类型检查. true, 表示项目运行在 strict 严格模式.
• forceConsistentCasingInFileNames: Ensures that the casing of referenced file names is consistent during the type checking process.
• moduleResolution: 用 node解决项目依赖
• resolveJsonModule: 允许模块文件类型为 .json, 对于一些文件配置很有用.
• noEmit: 编译过程是否禁止 TypeScript 生成 js 代码. 这里设置 true 是因为 Babel 帮我们转译 JavaScript 代码.
• jsx: 是否支持 JSX 在 .tsx 文件中.
• paths: 定义包的根路径,ts在索引包的时候就不需要 ../..,可以直接从src/pages/*开始索引,对于复杂关系的索引会很干净。
• include: 需要 TypeScript 去检查的文件目录.我们项目指定 src 文件夹.
增加react根组件
新建一个 index.tsx 文件在 src文件夹. 最终会展示在 index.html里.
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => <h1>My React and TypeScript App!</h1>;
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
我们已经新建了react 项目在 strict mode 下,并且注入到了id 为 root的 div 元素, .
三、配置Babel
我们项目要使用 Babel 去转译react和TS为 JavaScript.
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/plugin-transform-runtime @babel/runtime
• @babel/core: Babel 核心库.
• @babel/preset-env: 环境插件, 可以让最新的 JavaScript 特性运行在老的浏览器上。@babel/preset-react: REACT转译插件, Babel可以将React tsx jsx代码转译为JavaScript.
• @babel/preset-typescript: TS转译插件, Babel 可以将TypeScript 代码转译为 JavaScript.
• @babel/plugin-transform-runtime 和 @babel/runtime: 可以让JS代码使用 async 和await JavaScript 功能.
配置Babel配置文件
Babel 配置文件为 .babelrc. 配置文件告诉Babel使用哪些插件,新建一个如下:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
四、添加linting
用ESLint检查代码格式和语法错误
npm install --save-dev eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/parser @typescript-eslint/eslint-plugin
• eslint: ESLint 核心.
• eslint-plugin-react: React的lint插件.
• eslint-plugin-react-hooks: react hook 的lint 插件.
• @typescript-eslint/parser: 可以检查TypeScript 代码
• @typescript-eslint/eslint-plugin: 标准检查规则对 TypeScript 代码.
配置 .eslintrc.json 文件,生成一个如下:
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "react-hooks"],
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off"
},
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
}
}
五、添加Webpack
Webpack对前端项目内的所有模块(这里的模块指的是各个资源文件,包括.json, .ts, .js, .png, .svg等等)进行打包生成css,转译 ES6 语法的 js, Json和images。
如果想适配更多 ES5 语法就要借助Babel进行转译。
安装webpack
npm install --save-dev webpack webpack-cli @types/webpack
安装webpack server 用来本地启动打包后的项目
npm install --save-dev webpack-dev-server @types/webpack-dev-server
babel-loader, 可以让 Babel 转换 React 和TypeScript 代码为JavaScript.
npm install --save-dev babel-loader
html-webpack-plugin, 用于生成 HTML.
npm install --save-dev html-webpack-plugin
5.1 开发环境配置
webpack配置文件为JS的,安装ts-node包后就可以使用ts配置了,
npm install --save-dev ts-node
通常只需要配置一个webpack.config.ts就够了,然后在里面通过process.env.NODE_ENV标志去判断编译环境,然后通过设置package.json的启动脚本选择运行环境,
...
"scripts": {
"build": "webpack --node-env production",
"start": "webpack serve --node-env development"
},
...
这里配置两个配置文件一个用于开发一个用于生产部署仅做演示。
先在项目根目录建一个开发配置文件 webpack.dev.config.ts ,内容如下:
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import WebpackDevServer from 'webpack-dev-server';
// 这是webpack的一个bug未来可能被修复, webpack的configuration接口为定义devServer
declare module 'webpack' {
interface Configuration {
devServer?: WebpackDevServer.Configuration;
}
}
const config: webpack.Configuration = {
mode: 'development',
output: {
publicPath: '/',
},
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(ts|js)x?$/i,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript',
],
},
},
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
new webpack.HotModuleReplacementPlugin(),
],
devtool: 'inline-source-map',
devServer: {
contentBase: path.join(__dirname, 'build'),
historyApiFallback: true,
port: 4000,
open: true,
hot: true,
},
};
export default config;
• mode 设置是开发还是部署环境. , development设置为部署环境. Webpack 会自动设置 process.env.NODE_ENV 为development 表示会把react的开发相关包 包含在最后的打包里.
• output.publicPath 设置 Webpack app的根路径. 对于 deep 链接在开发服务器上很重要。
• entry 告诉 Webpack从哪里开始对项目进行 bundle. 我们的项目是 index.tsx.
• module 告诉 Webpack不同的 modules 如何处理. 由于 Webpack本身只能转换JS/JSON,所以要使用 babel-loader插件去处理 .js, .ts, 和.tsx 文件.
• resolve.extensions告诉 Webpack 处理包依赖的时候仅查找什么类型文件。
• HtmlWebpackPlugin 生成 HTML 文件. 使用src里的index.html 作为 模版.
• HotModuleReplacementPlugin 和 devServer.hot 允许模块在应用运行时更新,无需重新加载.
• devtool告诉 Webpack 使用 full inline source maps. 在转译JS之前我们可以对原始代码进行调试。
• devServer 配置 Webpack 开发服务器. 告诉 Webpack网络服务器的根目录是build文件夹, 服务器端口 4000. historyApiFallback 必要的 使用深度链接在多页面应用. open 告诉Webpack 服务启动后打开浏览器.
添加一个npm启动脚本在开发模式下
在 package.json 添加执行命令:
...,
"scripts": {
"start": "webpack serve --config webpack.dev.config.ts",
},
...
脚本启动 Webpack 开发服务器. 这用就可以利用 config 里配置好的选项启动服务.
启动运行
npm start
几秒钟后可以在浏览器看页面。
目前Webpack还没有在build文件夹打包任何资源。目前仅仅是把文件加载到webpack dev server 内存里。
现在改动一个 <h1> 标签,看到浏览器自动刷新了。
增加类型检查在 webpack 处理过程
目前 Webpack 还没进行任何类型检查。 可以使用 fork-ts-checker-webpack-plugin 让Webpack 对代码进行类型检查.
安装类型检查包
npm install --save-dev fork-ts-checker-webpack-plugin @types/fork-ts-checker-webpack-plugin
增加到 webpack.dev.config.ts:
...
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
const config: webpack.Configuration = {
...,
plugins: [
...,
new ForkTsCheckerWebpackPlugin({
async: false
}),
],
};
async表示告诉 Webpack 在打包代码前做一下类型检查.
重启应用。
把 index.tsx 的头改一下. 引用一个没有定义的 today
...
const App = () => <h1>My React and TypeScript App!! {today}</h1>;
...
webpack会抛个错误出来。
const App = () => (
<h1>My React and TypeScript App!! {new Date().toLocaleDateString()}</h1>
);
求改后,错误消失。
在webpack 打包过程增加lint检查
目前还没有做任何 linting 检查。 通过 ESLintPlugin插件来做lint检查:
安装lint
npm install --save-dev eslint-webpack-plugin
求改配置 webpack.dev.config.ts:
...
import ESLintPlugin from "eslint-webpack-plugin";
const config: webpack.Configuration = {
...,
plugins: [
...,
new ESLintPlugin({
extensions: ["js", "jsx", "ts", "tsx"],
}),
],
};
extensions 告诉 lint 去检查 TypeScript和 JavaScript 类型文件.
重启应用。
在 index.tsx, 增加一个未使用的变量:
const unused = "something";
webpack 会警告我们有未使用变量。
5.2 生产环境配置
生产环境要去掉开发环境的包。需要重新配置文件,在项目根目录新建一个生产配置文件
webpack.prod.config.ts 如下:
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import ESLintPlugin from 'eslint-webpack-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
const config: webpack.Configuration = {
mode: 'production',
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[contenthash].js',
publicPath: '',
},
module: {
rules: [
{
test: /\.(ts|js)x?$/i,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript',
],
},
},
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
new ForkTsCheckerWebpackPlugin({
async: false,
}),
new ESLintPlugin({
extensions: ['js', 'jsx', 'ts', 'tsx'],
}),
new CleanWebpackPlugin(),
],
};
export default config;
和开发环境的区别是:
• mode 设为production模式. Webpack 会自动设置process.env.NODE_ENV 为production , 这样react中一些开发环境的依赖就不会打包进到生产环境.
• output 告诉Webpack最后的打包文件到哪里. 设为 build 文件夹.使用 [name] 定义JS文件名. 附加一个 [contenthash]内容哈希值,这样当这个文件内容改变得时候浏览器就可以强制更新缓存.
• CleanWebackPlugin build之前清空build文件夹 ,要按个插件.
npm install --save-dev clean-webpack-plugin
设置一个生产 build 脚本
...,
"scripts": {
...,
"build": "webpack --config webpack.prod.config.ts",
},
...
运行npm run build命令进行打包
一会就可以在build文件夹看到打包好的内容。
之后就可以部署应用了。
六、 配置测试
使用jest进行测试。
安装以下测试库
npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event
jestjs 的单元测试库@testing-library/reactReact 的测试库@testing-library/jest-domjest 的dom测试库, 例如测试dom里是否包含某个属性@testing-library/user-event用户事件测试库
继续安装
npm install --save-dev babel-jest identity-obj-proxy
babel-jestjest 测试前用babel对 ts,js 进行编译identity-obj-proxyis a handy library for cases where we import files like CSS modules. If you import styles from ‘styles.css’, then we can configure jest to import ‘identity-obj-proxy’ for *.css, and then when you do styles.container, it will resolve to “container” instead of throwing an exception.
在根目录下新建jest.config.ts配置文件, 配置详见 https://jestjs.io/docs/next/configuration#modulefileextensions-arraystring
// jest.config.ts
import type { Config } from '@jest/types';
// Sync object
const config: Config.InitialOptions = {
globals: { tsConfig: { target: 'es2019' } },
verbose: true,
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
},
moduleNameMapper: {
// Resolve .css and similar files to identity-obj-proxy instead.
'.+\\.(css|styl|less|sass|scss)$': `identity-obj-proxy`,
// Resolve .jpg and similar files to __mocks__/file-mock.js
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `<rootDir>/__mocks__/file-mock.js`,
},
// Tells Jest what folders to ignore for tests
testPathIgnorePatterns: [`node_modules`, `\\.cache`],
testURL: `http://localhost`,
};
export default config;
配置以下测试命令在package.json
"scripts": {
"test": "jest",
...
},
执行 npm test 进行单元测试。
下一篇来介绍如何利用CircleCI 进行CI/CD。