前言
前段时间学习了React、Typescript,所以两个一起用试试; 有点遗憾的是Redux还没学,后续学了Redux后才算完整。。。没办法,三分钟热度的我还在学flutter啊。。。
说一下调接口的问题,最近包括上一份工作都遇到过,后端接口收不到参数。。。搞半天,结果是因为他们是从URL获取的请求参数,无论请求方式是否GET,,,所以沟通还是很重要的,如果后端大佬接口测试通过(postman/swagger等),但是页面上调用还是接收不到参数,,,就要去看他们接收参数的方式了,看他们测试的请求request URL就知道了
代码:react-ts
0、创建项目
提前学习TypeScript、React相关用法
安装 create-react-app 脚手架
npm install -g create-react-app
复制代码
创建 React+TypeScript 项目的命令
会自动安装一些typescript相关的东西,还有react的typescript版本,一些库的声明等
create-react-app my-app --scripts-version=react-scripts-ts
复制代码
1、Typescript 校验规则:
注意修改配置需要重启项目才会生效
tslint.json:
{
"extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
"linterOptions": {
"exclude": [
"config/**/*.js",
"node_modules/**/*.ts",
"coverage/lcov-report/*.js"
]
},
"rules": {
"no-debugger": false,
"no-console": false,
//声明interface是否需要首字母为大写的I
"interface-name": false,
//需要按字母顺序引入模块
"ordered-imports": false,
"object-literal-sort-keys": false,
// jsx中匿名函数
"jsx-no-lambda": false
},
"jsRules": {
"no-debugger": false,
"no-console": false,
//声明interface是否需要首字母为大写的I
"interface-name": false,
//需要按字母顺序引入模块
"ordered-imports": false,
"object-literal-sort-keys": false
}
}
复制代码
2、路由
react好像没有命名式路由,只能通过path去跳转,也没有路由元信息meta;有时间看下有没有其他解决方案;
官网文档:react-router
<Route>
有 children/render/component 三个属性决定当前路由渲染什么,exact 为true表示精确匹配路由,否则模糊匹配;<Route component>
,<Route render>
优先级比<Route children>
高<Route component>
: 直接放组件<Route render>
: 直接在这里写 JSX<Route children>
: 与render
类似;可以根据匹配(match
)的url渲染,,, 这个暂时还不知道具体使用场景(可能是nav
菜单栏,根据路由决定激活状态之类吧)
2.1 生成路由
<Switch>
可以看作是单选框的作用,只匹配第一个符合条件的路由; 路由位置<Route>
一般精确路由放前面,模糊路由放后面
// src/router.tsx
import * as React from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';
import Loadable from '@loadable/component';
import PageRoutes from './routes/index';
// 使用 import { lazy } from '@loadable/component';
// lazy()会有警告,跟React.lazy()一样的警告
const App = Loadable(() => import('./App'));
const ErrComp = Loadable(() => import('./views/ErrComp/index'));
// 生成 路由集合
const GetRoutes = () => {
const AppRoute =
<Route
key='app'
path='/'
exact={true}
component={App}
/>;
const ErrRoute =
<Route
key='err404'
exact={true}
path='/err404'
component={ErrComp}
/>;
const NoMatchRoute =
<Route
key='no-match'
component={ErrComp}
/>;
const routes = [AppRoute, ...PageRoutes, ErrRoute, NoMatchRoute];
return (
<Switch>
{routes.map(route => route)}
</Switch>
);
}
export default function Routes() {
return (
<HashRouter>
<GetRoutes />
</HashRouter>
);
}
复制代码
2.2 路由拆分:
-
2.2.1 模块路由
// src/routes/hello.tsx
import { Route } from 'react-router-dom';
import * as React from 'react';
import Loadable from '@loadable/component';
// 有子路由的话暂时这样处理吧,精确的放前面,模糊的放后面
export default [
<Route
key="hello"
exact={true}
path="/hello"
component={Loadable(() => import('../views/Hello/index'))}
/>,
<Route
key="hello_child1"
exact={true}
path="/hello/child1"
component={Loadable(() => import('../views/Hello/hello_child1'))}
/>,
<Route
key="hello_id"
exact={true}
path="/hello/:id"
component={Loadable(() => import('../views/Hello/hello_id'))}
/>
]
复制代码
-
2.2.2 懒加载
- React.lazy()
曾经我在另一个React的项目写的时候是支持 import(`../views/${name}`) 这种写法的。。。不知为何在这里(React + TypeScript)不行
React.lazy(() => import(component)); 复制代码
- @loadable/component
Webpack v4+ and Babel v7+最好使用@loadable/component,而不是react-loadable,具体看 这里;
npm i @loadable/component 复制代码
然后在项目根目录新建一个文件 loadable.d.ts或者统一一个文件.d.ts中写, (如果不提示找不到模块,就不需要自己新建了) 写入声明
declare module '@loadable/component';
使用:
import Loadable from '@loadable/component';
const App = Loadable(() => import('./App'));
正常使用,没有问题!!!
import { lazy } from '@loadable/component';
const App = lazy(() => import('./App'));
可以用的,但是会有警告(跟使用React.lazy()的问题一样):Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it. in Suspense
-
2.2.3 路由集中处理
// src/routes/index.tsx
import hello from './hello';
export default [
...hello
]
复制代码
2.3 withRouter/RouteComponentProps
- 路由组件可以直接获取这些属性(history,location,match等),而非路由组件就必须通过withRouter修饰后才能获取这些属性了
- 目的就是让被修饰的组件可以从props属性中获取history,location,match等方法
有两点要注意:
- RouteComponentProps的合成
- withRouter的写法:
withRouter<thisProps>(Hello as any)
二者的区别也就是 RouteComponentProps的合成 方式不同而已
写法一:
import * as React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface Props extends RouteComponentProps<any> {
name?: string;
}
class Hello extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
// 方法名 需要添加 public/ private / protected
public render() {...}
}
export default withRouter<Props>(Hello as any);
复制代码
写法二:
import * as React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface Props {
name?: string;
}
type thisProps = Props & RouteComponentProps;
class Hello extends React.Component<thisProps, {}> {
constructor(props: thisProps) {
super(props);
}
// 方法名 需要添加 public/ private / protected
public render() {...}
}
export default withRouter<thisProps>(Hello as any);
复制代码
3、项目入口
// src/index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import Routes from './router';
import './index.css';
// import registerServiceWorker from './registerServiceWorker';
const Loading = () => <div>loading...</div>;
ReactDOM.render(
<React.Suspense fallback={Loading}>
<Routes />
</React.Suspense>,
document.getElementById('root') as HTMLElement
);
// registerServiceWorker();
复制代码
4、组件
-
src/App.tsx
// src/App.tsx import * as React from 'react'; import './App.css'; // import logo from './logo.svg'; interface Props { [prop: string]: any } class App extends React.Component<Props, {}> { constructor(props: Props) { super(props); this.state = {}; } public render() { const { history, location } = this.props; return ( <div style={{display: location.pathname === '/' ? '' : 'none'}}> <p>这是app</p> <p> <a href="javascript:;" onClick={() => history.push('/hello')}> go hello </a> </p> <hr /> </div> ); } } export default App; 复制代码
-
src/components/Hello.tsx
// src/components/Hello.tsx import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; export interface Props { from?: string; } export interface States { times: number; } type thisProps = Props & RouteComponentProps; class Hello extends React.Component<thisProps, States> { constructor(props: thisProps) { super(props); this.state = { times: 0 } this.bclick = this.bclick.bind(this); } public bclick() { this.setState({ times: this.state.times+1 }) } // 方法名 需要添加 public/ private / protected public render() { const { from } = this.props; const { times } = this.state; return ( <div> <h4>Hello { from }</h4> 点击了 {times} <p> <button onClick={this.bclick}>点击</button> </p> </div> ) } } export default withRouter<thisProps>(Hello as any); 复制代码