@本文章做者 Ruoduan.cn 转载请注明出处html
公司中台系统要新增公司项目 —— IM即时聊天 的后台管理,由本人全程负责,前端技术选型 想采用目前比较新比较hot的技术 —— reactHook + react-Router5 + Typescript + (mobx | useContext + useState), 至于括号内的稍后解答,我的以为能够必定程度上替代 mobx 和 redux 的状态管理前端
本文将会从 Typescript,React-Hook,reactHook.TSX(实践)这三个方面来叙述node
Typescriptreact
React-Hook ⭐️jquery
Hook 与 mobx redux 等,实现本身状态管理ios
React 全家桶的东西不详细讲解带过 ...git
好了,下面让咱们开始吧github
nodejs
环境需具有 Not
下载连接typescript
咱们采用脚手架create-react-app 直接搭建react-typescript项目环境npm
版本要求 nodejs 8+ , Yarn 0.25+
npx create-react-app my-app --typescript
npm init react-app my-app --typescript
yarn create react-app my-app --typescript
复制代码
./
/src
./react-app-env.d.ts // 全局声明文件
tsconfig.json // typescript 配置文件
复制代码
tsconfig.json详解 这里例举🌰几个 :
{
"compilerOptions": {
"experimentalDecorators": true,
"target": "es5", //语言编译目标
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react" // 组件语法环境
},
"include": [ //编译目标目录
"src",
]
}
复制代码
jQuery
在typescript中:$('#dom');
// or
jQuery('#dom');
复制代码
// ERROR: Cannot find name 'jQuery'. 报错了,由于ts编译器并不认识$()
or jQuery
咱们须要全局定义一下这个变量 用declare修饰符 🌰
declare var jQuery: (selector: string) => any;
jQuery('#foo');
复制代码
这样咱们就能够快乐的使用jquery了
可是前人种树后人乘凉 不少库 社区已经给我定义好了 咱们至于要安装就能够了
yarn add @types/jquery --save-dev
咱们在后期项目中还会使用到 @types 类型安装
到这里 咱们已经初步了解 Typescript 的项目结构了 正如它官网说的 它是js 的超集,渐进式语言,咱们能够直接撸,它兼容js目前大部分新语法,而且对其增长类型
安装 antd, react-router-dom, 。。。
yarn add antd react-router-dom
这里就不一一例举🌰了
注意⚠️: react-router-dom 须要安装@types哦 yarn add @types/react-router-dom --save-dev
下面让个人步入重点~
咱们知道 react中分为2中组件,一种为类组件
,一种为函数组件
区别
区别 | class | func |
---|---|---|
定义方式 | class | func |
状态 | 有(state) | 无 |
this | 有 | 无 |
声明周期 | 有 | 无 |
简洁度 | 普通 | 较为简洁 |
经过比较咱们能够看到 传统的函数组件没有state
and 生命周期
因此在以前的开发中 函数式组件一般做为 一些 简单的组件 补充
而react-hook 可让咱们在 函数式组件中使用 state 组件状态管理,还能够实现声明周期 —— 反作用,并且还极大的简化了代码,让代码更加简洁明了易于理解
咱们看下面2段代码
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
);
}
复制代码
等价于
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div>
);
}
}
复制代码
useState
使用数组的解构语法来 定义了2个变量count, setCount
而且传入了一个初始值 0,并且在下面的使用中咱们摆脱了恶心的this
直接可使用state
count
的赋值函数 至关于this.setState()
Effect Hook 可让你在函数组件中执行反作用操做
看下面的代码
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 在组件中更新页面的title
useEffect(() => {
// 设置页面title
document.title = `数字 ${count} `;
});
return (
<div> <p>数字 {count}</p> <button onClick={() => setCount(count + 1)}> + 1 </button> </div>
);
}
复制代码
等价于class
lass Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `数字 ${this.state.count} `;
}
componentDidUpdate() {
document.title = `数字 ${this.state.count} `;
}
render() {
return (
<div> <p>数字 {this.state.count} </p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> + 1 </button> </div>
);
}
}
复制代码
咱们能够看到其中的区别,在class组件中 咱们重复的定义了生命周期 componentDidUpdate,componentDidMount
稍微分析下 咱们使用class的state中的数据是为了什么?
咱们知道定义在class
组件中state
中的数据咱们会在数据更新后从新渲染dom树
useEffect 在执行 DOM 更新以后调用它。在这个 effect 中,咱们设置了 document 的 title 属性,不过咱们也能够执行数据获取或调用其余命令式的 API。
注意⚠️:默认状况下,它在第一次渲染以后和每次更新以后都会执行。(咱们稍后会谈到如何控制它。)
其实useEffect作的很简单
就是告诉 React 组件须要在渲染后执行某些操做,而后每次渲染后都会执行
下面咱们介绍用useEffect
实现声明周期的 componentDidMount
和 componentDidUpdate
首先在咱们的函数组件中是能够定义多个useState和useEffect的 他们会依顺序执行的
写在前面useEffect的返回值决定这个反作用的类型
和销毁
例如咱们要在页面挂载是时请求数据且渲染到页面上
let [list, setList] = useEffect([])
// 获取数据
const getList = async () => {
let res = await axios('xxx')
}
// 只挂载一次的反作用函数
useEffect(() => {
getList()
}, [])
复制代码
仍是上面那个栗子 咱们须要实时更新mobx中订阅的store中的数据到页面上
let [list, setList] = useEffect([])
let [data, setData] = useEffect(store.rows)
// 获取数据
const getList = async () => {
let res = await axios('xxx')
}
// 获取store中数据
const getStore = () => {
@observer
let res = store.getxxxxx
... 此处省略若干字
setData(res)
}
// 只挂载一次的反作用函数
useEffect(() => {
getList()
}, [])
// 需清除的反作用函数
useEffect(() =>
(()=>
getStore
)()
)
复制代码
到这里咱们已经基本了解 reactHook 的基本使用。下面将介绍一个基于ReactHook-useContext 来实现一个简单的Redux 状态管理
Redux
基于 前面的 useReducer,和 createContext, useContext
咱们先来看下 额外hook useReducer
:
const [state, dispatch] = useReducer(reducer, initialArg, init);
复制代码
它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(是否是有点熟悉,用过redux 的童鞋~)
import * as React from 'react'
const { useContext, useReducer, createContext } = React
// 根据action.reduce返回状态
function reducerInAction(state, action) {
if (typeof action.reducer == 'function') {
return action.reducer(state)
}
return state
}
// 处理store数据
export default function createStore(params) {
const { _State = {}, reducer
} = {
...params,
reducer: reducerInAction
}
// 由createContext 来进行状态管理数据分发
const Appcontext = createContext()
const upReducer = (lastState, action) => {
//更新数据
let netxState = reducer(lastState, action)
store._state = netxState
return netxState
}
const store = {
_state: _State,
dispatch: undefined,
getState: () => {
return store._state
},
useContext: () => {
return useContext(Appcontext)
}
}
// 处理数据后有返回
const Provider = props => {
const [state, dispatch] = useReducer(upReducer, _State)
if (!store.dispatch) {
store.dispatch = async (action) => {
if (typeof action === 'function') {
await action(dispatch, store.getState())
} else {
dispatch(action)
}
}
}
return <Appcontext.Provider {...props} value={state} /> } return { Provider, store } } 复制代码
import * as React from 'react'
import HooksRedux from '@/HooksRedux'
const {
Provider,
store
} = _HooksRedux({
State: { name: '小明', age: '18' }
// 请求
const Add_action = () => {
return {
type: 'ADD',
reducer(state) {
return {
...state,
age: state.age + 1
}
}
}
复制代码
ps:以上就是basic of redux
,可是在个人实际项目中使用的仍是mobx
,他的设计模式,和面向对象的写法也比较舒服. 可参考
Any
—— anyscriptps: 这固然是不可取的咯~,可是咱们在快速开发过程当中 咱们有时候一个
泛型
|interface
是不肯定的咱们能够暂时先用any
而后后期更改
例如咱们要经过props 向子组件传递一个对象,可是这个对象是接口数据,且如今还不能肯定 对象里面的数据类型和属性 咱们能够暂时用 any代替,我通常会在这个地方,打上TODO any Type
,方便之后处理。
咱们能够看到这样的代码:
const app: React.FC = () => {
...
}
复制代码
FC = Functional Component SFC = Stateless Functional Component (已弃用)
它是对函数组件的声明,咱们看一下的Type type React.FC<P = {}> = React.FunctionComponent<P>
在有的时候你不加也没什么问题,建议是加上,这样方便告诉Typescript你的函数组件
jsDoc
是一个插件 它用于在编辑器|IDE 中给func 添加格式化的注释, 在Typescript中咱们使用强大的vscode interface是很是重要的,它用于描述个人接口,对象 ...
这里咱们要注意的是 jsDoc !== interface
interface 写得好 让咱们的代码很健壮
例以下面这段代码
let _userList: Array<object> = []
let [userList, setUserList] = useState(_userList)
let _userList: object[] = []
let [userList, setUserList] = useState(_userList)
复制代码
使用这2种方式均可以,但并不代编它们 ===
泛型 有着更为强大的用法
function createArray<T>(length: number, value: T): Array<any> ... function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
复制代码
function getLength(something: string | number): number {
return something.length;
}
复制代码
咱们应该严格遵循此语法,来进行书写✍️,没有返回 void
使用 ESlite 来规范咱们书写的代码
公司项目Private
不便贴代码和项目地址