@本文章做者 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
不便貼代碼和項目地址