react + ts 学习历程
1. 安装初始化一个react + ts 的项目
以前安装的方法:
npm install -g create-react-app
create-react-app my-app --scripts-version=react-scripts-ts
现在安装的方法:
npx create-react-app my-app --typescript
此时的工程结构应如下所示:
my-app/
├─ .gitignore
├─ node_modules/
├─ public/
├─ src/
│ └─ ...
├─ package.json
├─ tsconfig.json
└─ tslint.json
注意:
tsconfig.json
包含了工程里TypeScript特定的选项。tslint.json
保存了要使用的代码检查器的设置,TSLint。package.json
包含了依赖,还有一些命令的快捷方式,如测试命令,预览命令和发布应用的命令。public
包含了静态资源如HTML页面或图片。除了index.html
文件外,其它的文件都可以删除。src
包含了TypeScript和CSS源码。index.tsx
是强制使用的入口文件。
待拓展
2. 增加ignore文件
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.idea
npm-debug.log*
yarn-debug.log*
yarn-error.log*
3. 增加目录结构
增加src目录下的结构
src/
├─ actions/
│ └─index.ts
├─ assets/
│ └─logo.svg
├─ components/
│ └─ Menu.tsx
├─ containers/
│ └─ Menu.ts
├─ interface/
│ └─index.ts
├─ layouts/
├─ PageLayout/
│ └─index.tsx
├─ BasicLayout.tsx
├─ pages/
│ └─ index.tsx
├─ reducers/
│ └─ index.ts
├─ sagas/
│ └─index.ts
├─ services/
│ └─api.ts
├─ utils/
│ └─Logger.ts
├─ webpack/
└─ App.tsx
4. 增加Logger
export interface Logger {
trace(message?: any, ...param: any[]): void;
debug(message?: any, ...param: any[]): void;
info(message?: any, ...param: any[]): void;
warn(message?: any, ...param: any[]): void;
error(message?: any, ...param: any[]): void;
}
class LoggerClass implements Logger {
protected moduleName: string = '';
protected TIMESTAMP: string = 'YYYY-MM-DD HH:mm:ss.sss';
constructor(moduleName: string) {
this.moduleName = moduleName;
}
public trace(message?: any, ...param: any[]): void {
// console.trace(`[${moment().format(this.TIMESTAMP)}] [${this.moduleName}]`, message, ...param);
console.trace(`[${this.moduleName}]`, message, ...param);
}
public debug(message?: any, ...param: any[]): void {
// console.debug(`[${moment().format(this.TIMESTAMP)}] [${this.moduleName}]`, message, ...param);
console.trace(`[${this.moduleName}]`, message, ...param);
}
public info(message?: any, ...param: any[]): void {
// console.info(`[${moment().format(this.TIMESTAMP)}] [${this.moduleName}]`, message, ...param);
console.trace(`[${this.moduleName}]`, message, ...param);
}
public warn(message?: any, ...param: any[]): void {
// console.warn(`[${moment().format(this.TIMESTAMP)}] [${this.moduleName}]`, message, ...param);
console.trace(`[${this.moduleName}]`, message, ...param);
}
public error(message?: any, ...param: any[]): void {
// console.error(`[${moment().format(this.TIMESTAMP)}] [${this.moduleName}]`, message, ...param);
console.trace(`[${this.moduleName}]`, message, ...param);
}
}
export function createLogger(moduleName: string): Logger {
return new LoggerClass(moduleName);
}
export function LoggerDecorator(target: string): any;
export function LoggerDecorator(target: object, propertyKey: string): void;
export function LoggerDecorator(target: any, propertyKey?: any): any {
if (typeof target === "object") {
target[propertyKey] = createLogger(target.constructor.name);
} else {
return function (tar: any, pKey: string): any {
tar[pKey] = createLogger(target);
};
}
}
5. 添加路由
npm install react-router-dom --save
npm install @types/react-router-dom --save-dev
这里为了满足需求,增加一个history插件
npm install --save history
6. 添加老版antd (3.16.3)
npm install antd --save
按需引入组件
npm install react-app-rewired --save-dev
修改package.json的配置
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start --scripts-version react-scripts-ts",
- "build": "react-scripts build",
+ "build": "react-app-rewired build --scripts-version react-scripts-ts",
- "test": "react-scripts test --env=jsdom",
+ "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts-ts",
}
然后在项目根目录创建一个 config-overrides.js
用于修改默认配置。
module.exports = function override(config, env) {
// do stuff with the webpack config...
return config;
};
使用 ts-import-plugin
ts-import-plugin 是一个用于按需加载组件代码和样式的 TypeScript 插件(原理),现在我们尝试安装它并修改 config-overrides.js
文件。
$npm install ts-import-plugin --save-dev
/* config-overrides.js */
const tsImportPluginFactory = require('ts-import-plugin')
const { getLoader } = require("react-app-rewired");
module.exports = function override(config, env) {
const tsLoader = getLoader(
config.module.rules,
rule =>
rule.loader &&
typeof rule.loader === 'string' &&
rule.loader.includes('ts-loader')
);
tsLoader.options = {
getCustomTransformers: () => ({
before: [ tsImportPluginFactory({
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css',
}) ]
})
};
return config;
}
然后移除前面在 src/App.css
里全量添加的 @import '~antd/dist/antd.css';
样式代码,并且按下面的格式引入模块。
import * as React from 'react';
import { Button } from 'antd';
import './App.css';
class App extends React.Component {
render() {
return (
<div className="App">
<Button type="primary">Button</Button>
</div>
);
}
}
export default App;
报错:
Cannt find module react-scripts-ts/config/webpack.config
解决:
使用yarn add react-app-rewired@1.6.2
或npm install react-app-rewired@1.6.2
退回到react-app-rewired的1.x版本的最后一个版本
原因:
This has been caused by recent changes to CRA (2.1.2) where they merged the
webpack.config.dev.js
andwebpack.config.prod.js
into a single filewebpack.config.js
. See #343 and #345 for more details on the change.The adjustment to paths made in
react-app-rewired
in order to continue to be able to be used with CRA looks for areact-scripts
version number greater than or equal to 2.1.2. Thereact-scripts-ts
version number has been higher than that for the last ~18 months, soreact-app-rewired
is treating it according to the merged webpack config instead of the split one.To solve it, you’ll need to install an older version of
react-app-rewired
that doesn’t have this change. I believe the last version ofreact-app-rewired
that will be compatible withreact-scripts-ts
is version1.6.2
. The versions for 2.x have the breaking change to support CRA 2.1.2 and beyond that is misfiring onreact-scripts-ts
.Use
yarn add react-app-rewired@1.6.2
ornpm install react-app-rewired@1.6.2
to step back to the last of the 1.x versions of react-app-rewired - it will install the older version and lock the version number to exactly that version so that it doesn’t get upgraded to a newer version accidentally in future.
参考: https://github.com/timarney/react-app-rewired/issues/358
报错:
The key 'libraryDirectory' is not sorted alphabetically
解决:
config-overrides.js 文件里交换下libraryName和libraryDirectory的位置。
参考: https://github.com/ant-design/ant-design/issues/10533
报错:
interface name must not have an "I" prefix
解决:
根据参考网址添加
"interface-name" : [true, "never-prefix"]
对我的项目而言无效,需要修改成
"interface-name": false
即是在tslint.json的rules中添加
{
"extends": [
"tslint:recommended",
"tslint-react",
"tslint-config-prettier"
],
"linterOptions": {
"exclude": [
"config/**/*.js",
"node_modules/**/*.ts",
"coverage/lcov-report/*.js"
]
},
"rules": {
// 添加这一句
"interface-name": false,
...
}
}
参考: https://github.com/palantir/tslint/issues/3265
安装新版 ant3.20.3
npm install antd --save
全局引用
修改 src/App.tsx
,引入 antd 的按钮组件。
import React, { Component } from 'react';
import Button from 'antd/es/button';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<Button type="primary">Button</Button>
</div>
);
}
}
export default App;
修改 src/App.css
引入 antd 的样式。
@import '~antd/dist/antd.css';
.App {
text-align: center;
}
...
重新启动 yarn start
,现在你应该能看到页面上已经有了 antd 的蓝色按钮组件,接下来就可以继续选用其他组件开发应用了。其他开发流程你可以参考 create-react-app 的官方文档。
高级配置,按需引入
此时我们需要对 create-react-app 的默认配置进行自定义,这里我们使用 react-app-rewired (一个对 create-react-app 进行自定义配置的社区解决方案)。
引入 react-app-rewired 并修改 package.json 里的启动配置。由于新的 react-app-rewired@2.x 版本的关系,你还需要安装 customize-cra。
npm install react-app-rewired customize-cra --save
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
}
然后在项目根目录创建一个 config-overrides.js
用于修改默认配置。
module.exports = function override(config, env) {
// do stuff with the webpack config...
return config;
};
使用 babel-plugin-import
npm install babel-plugin-import --save
babel-plugin-import 是一个用于按需加载组件代码和样式的 babel 插件(原理),现在我们尝试安装它并修改 config-overrides.js
文件。
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css',
}),
);
然后移除前面在 src/App.css
里全量添加的 @import '~antd/dist/antd.css';
样式代码,并且按下面的格式引入模块。
// src/App.tsx
import React, { Component } from 'react';
- import Button from 'antd/es/button';
+ import { Button } from 'antd';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<Button type="primary">Button</Button>
</div>
);
}
}
export default App;
最后重启 yarn start
访问页面,antd 组件的 js 和 css 代码都会按需加载,你在控制台也不会看到这样的警告信息。关于按需加载的原理和其他方式可以阅读这里。
自定义主题 需要安装less
npm install less less-loader --save
按照 配置主题 的要求,自定义主题需要用到 less 变量覆盖功能。我们可以引入 customize-cra
中提供的 less 相关的函数 addLessLoader 来帮助加载 less 样式,同时修改 config-overrides.js
文件如下。
- const { override, fixBabelImports } = require('customize-cra');
+ const { override, fixBabelImports, addLessLoader } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
- style: 'css',
+ style: true,
}),
+ addLessLoader({
+ javascriptEnabled: true,
+ modifyVars: { '@primary-color': '#1DA57A' },
+ }),
);
这里利用了 less-loader 的 modifyVars
来进行主题配置,变量和其他配置方式可以参考 配置主题 文档。
修改后重启 yarn start
,如果看到一个绿色的按钮就说明配置成功了。
7. 安装lodash
npm install lodash --save
npm install @types/lodash --save-dev
8. 安装redux
npm install react-redux --save
npm install redux --save
npm install @types/react-redux --save-dev
9. 安装redux-actions
GitHub地址:https://github.com/redux-utilities/redux-actions
当在开发大型应用的时候,对于大量的action,reducer需要些大量的swich来对action.type进行判断。
redux-actions可以简化这一烦琐的过程,它可以是actionCreator,也可以用来生成reducer,其作用都是用来简化action、reducer。主要函数有createAction、createActions、handleAction、handleActions、combineActions。
npm install redux-actions --save
npm install @types/redux-actions --save-dev
10. 安装humps
用于JavaScript中的字符串和对象键的下划线到驼峰写法的转换器(反之亦然)。
GitHub地址:https://github.com/domchristie/humps
npm install humps --save
npm install @types/humps --save-dev
基本用法:
转换字符串
humps.camelize('hello_world') // 'helloWorld'
humps.decamelize('fooBar') // 'foo_bar'
humps.decamelize('fooBarBaz', { separator: '-' }) // 'foo-bar-baz'
转换对象键
var object = { attr_one: 'foo', attr_two: 'bar' }
humps.camelizeKeys(object); // { attrOne: 'foo', attrTwo: 'bar' }
对象数组也被转换
var array = [{ attr_one: 'foo' }, { attr_one: 'bar' }]
humps.camelizeKeys(array); // [{ attrOne: 'foo' }, { attrOne: 'bar' }]
11. 增加 redux-devtools-extension
Redux DevTools的扩展,更好看的页面
```bash
npm install --save-dev redux-devtools-extension
```
12. 增加action和reducers
13. 增加less
```bash
npm install --save-dev less less-loader
```
14. 增加sass
```bash
$ npm install node-sass --save
```
15. history
history是一个JavaScript库,可以让您轻松地在JavaScript运行的任何地方管理会话历史。history抽象了不同环境中的差异,并提供了一个最小的API,允许您管理历史堆栈、导航、确认导航和会话之间的持久状态。
```bash
npm install --save history
```
16. 增加ts配置文件
tsconfig.json
```json
// https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Compiler%20Options.html
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": false,
"importHelpers": true,
"strictNullChecks": true,
"allowSyntheticDefaultImports":true,
"suppressImplicitAnyIndexErrors": true,
"experimentalDecorators": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"paths": {
"@/*": [
"./src/*"
]
}
},
"exclude": [
"node_modules",
"build",
"scripts",
"acceptance-tests",
"webpack",
"jest"
]
}
```