React結合TypeScript和Mobx初體驗

圖片描述

爲何要使用TypeScript

偵測錯誤

經過靜態類型檢測能夠儘早檢測出程序中隱藏的的邏輯錯誤,對於JavaScript動態的弱類型語言,雖然靈活性高,可是對於初學者來講,若是不熟悉JavaScript內部的語言機制,很容易形成隱藏的事故。可是經過TypeScript的靜態類型檢測能夠規避這些問題,由於其可以約束變量產生的類型。結合IDE編輯器能夠推導變量對應的類型以及內部的結構,提升代碼的健壯性和可維護性。css

抽象

類型系統可以強化規範編程,TypeScript提供定義接口。在開發大型複雜的應用軟件時十分重要,一個系統模塊能夠抽象的看作一個TypeScript定義的接口。讓設計脫離實現,最終體現出一種 IDL(接口定義語言,Interface Define Language),讓程序設計迴歸本質。前端

文檔

TypeScript能夠自動根據類型標註生成文檔,對於簡單的功能實現都不須要編寫註釋。react

爲何要使用Mobx

MobX 和 Redux 的比較

先要明白 mobx 和 redux 的定位是不一樣的。redux 管理的是 (STORE -> VIEW -> ACTION) 的整個閉環,而 mobx 只關心 STORE -> VIEW 的部分。webpack

Redux優缺點:git

  • 數據流流動很天然,由於任何 dispatch 都會觸發廣播,依據對象引用是否變化來控制更新粒度。
  • 經過充分利用時間回溯的特徵,能夠加強業務的可預測性與錯誤定位能力。
  • 時間回溯代價高,由於每次都要更新引用,除非增長代碼複雜度,或使用 immutable。
  • 時間回溯的另外一個代價是 action 與 reducer 徹底脫節,緣由是可回溯必然不能保證引用關係。
  • 引入中間件,解決異步帶來的反作用,業務邏輯或多或少參雜着 magic。
  • 靈活利用中間件,能夠經過約定完成許多複雜的工做。
  • 對 typescript 支持困難。

Mobx優缺點:github

  • 數據流流動不天然,只有用到的數據纔會引起綁定,局部精確更新,但避免了粒度控制煩惱。
  • 沒有時間回溯能力,由於數據只有一份引用。自始至終一份引用,不須要 immutable,也沒有複製對象的額外開銷。
  • 數據流動由函數調用一鼓作氣,便於調試。
  • 業務開發不是腦力活,而是體力活,少一些 magic,多一些效率。
  • 因爲沒有 magic,因此沒有中間件機制,無法經過 magic 加快工做效率(這裏 magic 是指 action 分發到 reducer 的過程)。
  • 完美支持 typescript。

SO: 前端數據流不太複雜的狀況,使用 Mobx,由於更加清晰,也便於維護;若是前端數據流極度複雜,建議謹慎使用 Redux,經過中間件減緩巨大業務複雜度web

使用Create-React-App來創建TypeScript的環境

npm i -g create-react-app
create-react-app tinylog-ui --scripts-version=react-scripts-ts
cd tinylog-ui/
npm start
npm run eject

TPS: 最後一個命令使用eject將全部內建的配置暴露出來typescript

經過create-react-app能夠很方便地對整個項目完成環境初始化,若是願意折騰TypeScript和webpack的環境能夠試試,這裏忽略webpack和TypeScript的環境搭建過程,而是使用create-react-app來實現環境搭建。npm

加入React-Router

單頁應用怎麼能夠沒有前端路由呢,因此咱們要加入React-Rotuer, 這裏使用的React-Router的版本是v4.2.0編程

路由配置使用姿式

對於React-Router,這裏使用到的模塊有Router, Route, Switch

React Router 是創建在 history 之上的。 簡而言之,一個 history 知道如何去監聽瀏覽器地址欄的變化, 並解析這個 URL 轉化爲 location 對象, 而後 router 使用它匹配到路由,最後正確地渲染對應的組件。

代碼以下:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router';
import { createBrowserHistory } from 'history';
import registerServiceWorker from './registerServiceWorker';
import { Root } from './containers/Root';
import './index.css';
import Container from './containers/Container';
import SignIn from './containers/Auth/signIn';
import SignUp from './containers/Auth/signUp';

const history = createBrowserHistory();

ReactDOM.render(
  <Root>
    <Router history={history}>
      <Switch>
        <Route
          path="/signIn"
          component={SignIn}
        />
        <Route
          path="/signUp"
          component={SignUp}
        />
        <Route
          path="/"
          component={Container}
        />
      </Switch>
    </Router>
  </Root>,
  document.getElementById('root') as HTMLElement
);
registerServiceWorker();

頁面的編寫

這裏描述一寫Container這個組件的編寫

import * as React from 'react';
import Header from '../../layout/Header';
import { IAuth } from '../../interfaces';
import { Route, Switch } from 'react-router';
import App from '../App';
import Website from '../Website';

// 這部分是坑點,一開始不知道配置,後發現react-rotuer的4.0版本下須要配置prop的接口
interface Container extends RouteComponentProps<{}> {
}

class Container extends React.Component<Container, {}> {
  render () {
    return (
      <div>
        <Header {...this.props} />
        <Switch>
          <Route path="/website" component={Website}/>
          <Route  path="/" component={App}/>
        </Switch>
      </div>
    )
  }
}

export default Container;

這樣,當咱們訪問url爲'/'的時候,默認會進入Container,其中Container裏面是一層子頁面,會匹配url,若是url爲'/website', 則進入Website頁面,若爲'/',則進入App頁面。

具體關於React-Router的使用請閱讀React-Router文檔

加入Mobx

npm i mobx react-mobx mobx-react-router -S

從新修改index.tsx的入口配置

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router';
import { createBrowserHistory } from 'history';
import { useStrict } from 'mobx';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
// 定義須要使用到的store來進行數據狀態的管理
import { 
  TokenStore, 
  AuthStore, 
  HostStore, 
  OverViewStore,
  AssetsStore,
  CommonDataStore,
  PageStore,
  RealTimeStore  
} from './stores';
import registerServiceWorker from './registerServiceWorker';
import { Root } from './containers/Root';
import './index.css';
import Container from './containers/Container';
import SignIn from './containers/Auth/signIn';
import SignUp from './containers/Auth/signUp';
// 引入Echarts
import './macarons';
import 'echarts/map/js/world';

// 開啓mobx的嚴格模式,規範數據修改操做只能在action中進行
useStrict(true);

const browserHistory = createBrowserHistory();
const routerStore =  new RouterStore();
// 同步路由與mobx的數據狀態
const history = syncHistoryWithStore(browserHistory, routerStore);
const rootStore = {
  token: new TokenStore(),
  auth: new AuthStore(),
  host: new HostStore(),
  overview: new OverViewStore(),
  assets: new AssetsStore(),
  commmon: new CommonDataStore(),
  page: new PageStore(),
  realtime: new RealTimeStore(),
  router: routerStore
};

ReactDOM.render(
  <Provider {...rootStore}>
    <Root>
      <Router history={history}>
        <Switch>
          <Route
            path="/signIn"
            component={SignIn}
          />
          <Route
            path="/signUp"
            component={SignUp}
          />
          <Route
            path="/"
            component={Container}
          />
        </Switch>
      </Router>
    </Root>
  </Provider>,
  document.getElementById('root') as HTMLElement
);
registerServiceWorker();

Container容器的修改

import * as React from 'react';
import Header from '../../layout/Header';
import { IAuth } from '../../interfaces';
import { Route, Switch } from 'react-router';
// 使用inject和observer來進行數據監聽和數據依賴聲明
import { inject, observer } from 'mobx-react';
import App from '../App';
import Website from '../Website';

interface Container extends IAuth {
}

@inject('router', 'auth')
@observer
class Container extends React.Component<Container, {}> {
  render () {
    return (
      <div>
        <Header {...this.props} />
        <Switch>
          <Route path="/website" component={Website}/>
          <Route  path="/" component={App}/>
        </Switch>
      </div>
    )
  }
}

export default Container;
@observable 能夠在實例字段和屬性 getter 上使用。 對於對象的哪部分須要成爲可觀察的,@observable 提供了細粒度的控制。

@inject 至關於Provider 的高階組件。能夠用來從 React 的context中挑選 store 做爲 prop 傳遞給目標組件

組件的接口定義

import { RouteComponentProps } from 'react-router';
import {
  RouterStore,
  AuthStore
} from '../stores';

export interface IBase extends RouteComponentProps<{}> {
  router: RouterStore;
}

export interface IAuth extends IBase {
  auth: AuthStore;
}

Store的配置

先看一下RouterStore:

import { History } from 'history';
import { RouterStore as BaseRouterStore, syncHistoryWithStore } from 'mobx-react-router';

// 路由狀態同步
class RouterStore extends BaseRouterStore {
  public history;
  constructor(history?: History) {
    super();
    if (history) {
      this.history = syncHistoryWithStore(history, this);
    }
  }
}

export default RouterStore;

而後是AuthStore:

import { ISignIn, ISignUp } from './../interfaces/index';
import { observable, action } from 'mobx';
import api from '../api/auth'; 
import { IUser } from '../models';

// 登陸註冊狀態
class AuthStore {
  @observable token;
  @observable id;
  @observable email;
  constructor () {
    this.id = '';
    this.token = '';
    this.email = '';
  }
  setLocalStorage ({ id, token, email }: IUser) {
    localStorage.setItem('id', id);
    localStorage.setItem('token', token);
    localStorage.setItem('email', email);
  }
  clearStorage () {
    localStorage.clear();
  }
  @action async signIn (data: ISignIn) {
    try {
      const { data: res } = await api.signIn(data);
      this.id = res.data.id;
      this.token = res.data.token;
      this.email = res.data.email;
      this.setLocalStorage({
        id: this.id,
        token: this.token,
        email: this.email
      });
      return res;
    } catch (error) {
      return error;
    }
  }
  
  @action async signUp (data: ISignUp) {
    try {
      const { data: res } = await api.signUp(data);
      this.id = res.data.id;
      this.token = res.data.token;
      this.email = res.data.email;
      this.setLocalStorage({
        id: this.id,
        token: this.token,
        email: this.email
      });
      return res;
    } catch (error) {
      return error;
    }
  }

  @action signOut () {
    this.id = '';
    this.token = '';
    this.email = '';
    this.clearStorage()
  }
}

export default AuthStore;

Auth是用於網站的登陸註冊事件以及對應的Token的數據狀態保存,登陸註冊事件的接口請求等操做。

具體的有關Mobx的用法請閱讀Mobx文檔

目錄結構

app
├── api             後端提供的接口數據請求
├── components      編寫的可複用組件
├── config          側邊欄以及導航欄配置
├── constants       常量編寫
├── interfaces      接口編寫
├── layout          佈局外框
├── stores          mobx的數據狀態管理
├── index.css       全局樣式
├── index.tsx       頁面入口
├── reset.css       瀏覽器重置樣式

本項目使用了Ant-Design來做爲依賴的組件庫,具體怎麼使用以及配置請參考Ant-Design

到這裏其實以及完成對React下TypeScript結合React-Router和Mobx的配置。具體的業務模塊如何編寫有興趣能夠參閱項目tinylog-ui

我的表達能力有限,沒法描述得太清晰,請見諒!

相關文章
相關標籤/搜索