七天接手react專案 系列 —— 尾篇(antd 和 mobx)

彭加李發表於2022-03-27

其他章節請看:

七天接手react專案 系列

尾篇

前面我們依次學習了 react 基礎知識react 腳手架建立專案react 路由,已經花費了不少時間,但距離接手 spug_web 專案還有一些困難。

package.json 作為專案的核心,從中我們能大概知曉此專案到底用了哪些技術,所以筆者決定從其入手。

spug_web/package.json

為什麼沒有看見 reduxmobx 是什麼?antd 是什麼,react-app-rewired@babel/plugin-proposal-decorators又有什麼作用?—— 本篇都能找到答案

// spug_web/package.json

{
  "name": "spug_web",
  "version": "3.0.0",
  "private": true,
  "dependencies": {
    "@ant-design/icons": "^4.3.0",
    "ace-builds": "^1.4.13",
    "antd": "^4.10.3",
    "axios": "^0.21.0",
    "bizcharts": "^3.5.9",
    "history": "^4.10.1",
    "lodash": "^4.17.19",
    "mobx": "^5.15.6",
    "mobx-react": "^6.3.0",
    "moment": "^2.24.0",
    "react": "^16.13.1",
    "react-ace": "^9.5.0",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.3",
    "xterm": "^4.6.0",
    "xterm-addon-fit": "^0.5.0"
  },
  "scripts": {
    "start": "react-app-rewired start",
    "build": "GENERATE_SOURCEMAP=false react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/plugin-proposal-decorators": "^7.10.5",
    "customize-cra": "^1.0.0",
    "less": "^3.12.2",
    "less-loader": "^7.1.0",
    "react-app-rewired": "^2.1.6",
    "http-proxy-middleware": "0.19.2"
  }
}

antd

antd 是基於 Ant Design 設計體系的 React UI 元件庫,主要用於研發企業級中後臺產品。

Tip:類似的 ui 庫有:element-uimaterial-ui(國外比較流行)、vant-ui(移動端)

安裝

react-cli-demo> npm i antd

added 58 packages, and audited 1479 packages in 27s

171 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

使用

ui 庫使用起來通常比較簡單,找到需要的示例,比如按button、日期、彈框等,複製貼上即可。

進入官網的 元件 頁面:

button

以 button 為例,比如我需要使用第一個按鈕(”Primary Button“):

antd-button.png

找到對應的程式碼片段即可:

import { Button } from 'antd';
+ import 'antd/dist/antd.css'

export default function App() {
    return (
        <div className="App">
            <Button type="primary">Primary Button</Button>
        </div >
    );
}

:需要引入 antd 的樣式檔案 antd.css

倘若需要 button 更為詳細的介紹,可以看底部的 api。就像這樣:

antd-button-api.png

icon

語義化的向量圖形。使用圖示元件,你需要安裝 @ant-design/icons 圖示元件包 —— 官網-Icon圖示

react-cli-demo> npm i @ant-design/icons

up to date, audited 1479 packages in 6s

171 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

Tip:以前 icon 是整合在 antd 中的。有些人可能無需使用圖示,所以後來就獨立了出來。

按需引入 css

我們或許只使用 button 元件,卻將 antd 的所有元件的樣式都引入進來(gzipped 後一共大約 60kb):

import { Button } from 'antd';
+ import 'antd/dist/antd.css'

下面我們要做的就是是:按需引入 css。

Tip:類似這種需求,由於高度整合,所以怎麼做通常不能自己去像,需要看對應的文件。

:目前 antd 文件是 4.x,我們可以看 3.x 的文件,因為舊的內容或許更詳細、亦或不同。

安裝依賴包用於對 create-react-app 的預設配置進行自定義:

  • 由於需要對 create-react-app 的預設配置進行自定義,這裡我們使用 react-app-rewired (一個對 create-react-app 進行自定義配置的社群解決方案)
  • 由於新的 react-app-rewired@2.x 版本的關係,你還需要安裝 customize-cra
react-cli-demo> npm i -D react-app-rewired customize-cra

added 4 packages, and audited 1483 packages in 15s

171 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

安裝依賴包用於按需載入元件樣式:

  • babel-plugin-import 是一個用於按需載入元件程式碼和樣式的 babel 外掛
react-cli-demo> npm i babel-plugin-import

added 1 package, and audited 1484 packages in 4s

171 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

修改 scripts:

  • 通過 react-app-rewired 來啟動、打包和測試
/* 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 用於修改預設配置:

// config-overrides.js
const { override, fixBabelImports } = require('customize-cra');

module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
    }),
);

移出手動引入的 antd 樣式:

- import 'antd/dist/antd.css'

重啟後發現按鈕樣式正常。

Tip:更詳細介紹請看 高階配置

主題色

antd 預設主題色是支付寶的藍色。

按照配置主題的要求,自定義主題需要用到 less 變數覆蓋功能:

react-cli-demo> npm i -D less less-loader

added 15 packages, and audited 1499 packages in 1m

173 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

修改 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' },
+ }),
);

報錯如下:

Compiled with problems:X

ERROR in ./node_modules/antd/es/button/style/index.less (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[1].oneOf[9].use[3]!./node_modules/less-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[4]!./node_modules/antd/es/button/style/index.less)

Module build failed (from ./node_modules/less-loader/dist/cjs.js):
ValidationError: Invalid options object. Less Loader has been initialized using an options object that does not match the API schema.
// options 具有未知屬性“modifyVars”。 這些屬性有效
 - options has an unknown property 'modifyVars'. These properties are valid:
   object { lessOptions?, additionalData?, sourceMap?, webpackImporter?, implementation? }
    at validate (react-cli-demo\node_modules\schema-utils\dist\validate.js:105:11)
    at Object.getOptions (react-cli-demo\node_modules\webpack\lib\NormalModule.js:580:19)
    at Object.lessLoader (react-cli-demo\node_modules\less

根據網上資料嘗試修改如下:

const { override, fixBabelImports, addLessLoader } = require('customize-cra');

module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: true,
    }),
    addLessLoader({
        lessOptions: {
            javascriptEnabled: true,
            modifyVars: { '@primary-color': '#1DA57A' },
        }
    }),
);

報錯資訊變成:

Compiled with problems:X

ERROR in ./node_modules/antd/es/button/style/index.less (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[1].oneOf[9].use[3]!./node_modules/less-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[9].use[4]!./node_modules/antd/es/button/style/index.less)

Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'plugins'. These properties are valid:
   object { postcssOptions?, execute?, sourceMap?, implementation? }
    at validate (react-cli-demo\node_modules\schema-utils\dist\validate.js:105:11)
    at Object.getOptions (react-cli-demo\node_modules\webpack\lib\NormalModule.js:580:19)
    at Object.loader (react-cli-demo\node_modules\postcss-loader\dist\index.js:40:24)

Tip:時間有限且不緊急,故筆者決定暫放。

mobx

簡單、可擴充套件的狀態管理。—— npm-mobx

Tip:文件可參考 印象中文-mobxgithub-mobx-api

spug_web 狀態管理沒有使用 redux 而是使用 mobx。以下擷取自 alarm 路由元件:

// spug_web/src/pages/alarm/alarm/index.js

/**
 * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
 * Copyright (c) <spug.dev@gmail.com>
 * Released under the AGPL-3.0 License.
 */
import React from 'react';
import { observer } from 'mobx-react';
import { SyncOutlined } from '@ant-design/icons';
import { Input, Button } from 'antd';
import { SearchForm, AuthDiv, Breadcrumb } from 'components';
import ComTable from './Table';
import store from './store';

export default observer(function () {
  return (
    <AuthDiv auth="alarm.alarm.view">
      <Breadcrumb>
        <Breadcrumb.Item>首頁</Breadcrumb.Item>
        <Breadcrumb.Item>報警中心</Breadcrumb.Item>
        <Breadcrumb.Item>報警歷史</Breadcrumb.Item>
      </Breadcrumb>
      <SearchForm>
        <SearchForm.Item span={8} title="任務名稱">
          <Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="請輸入"/>
        </SearchForm.Item>
        <SearchForm.Item span={8}>
          <Button type="primary" icon={<SyncOutlined/>} onClick={store.fetchRecords}>重新整理</Button>
        </SearchForm.Item>
      </SearchForm>
      <ComTable/>
    </AuthDiv>
  )
})
// spug_web/src/pages/alarm/alarm/store.js

/**
 * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
 * Copyright (c) <spug.dev@gmail.com>
 * Released under the AGPL-3.0 License.
 */
import { observable, computed } from 'mobx';
import http from 'libs/http';

class Store {
  @observable records = [];
  @observable isFetching = false;

  @observable f_name;
  @observable f_status = '';

  @computed get dataSource() {
    let records = this.records;
    if (this.f_name) records = records.filter(x => x.name.toLowerCase().includes(this.f_name.toLowerCase()));
    if (this.f_status) records = records.filter(x => x.status === this.f_status);
    return records
  }

  fetchRecords = () => {
    this.isFetching = true;
    http.get('/api/alarm/alarm/')
      .then(res => this.records = res)
      .finally(() => this.isFetching = false)
  };
}

export default new Store()

主要用到 mobx-reactmobx 兩個包。

具體用法請看下文。

準備環境

:本小節有點麻煩,主要是用於讓專案支援裝飾器語法。但是 mobx 不使用裝飾器語法照常能工作。

spug_web 中有個依賴包 @babel/plugin-proposal-decorators,用於支援 es6 中裝飾器的語法。就像這樣:

// spug_web/src/pages/alarm/alarm/store.js
class Store {
  @observable records = [];
  @observable isFetching = false;

  @observable f_name;
  @observable f_status = '';
  ...

首先安裝 mobxmobx-react 依賴包:

react-cli-demo> npm i mobx mobx-react

added 3 packages, and audited 1502 packages in 7s

176 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
    "mobx": "^6.5.0",
    "mobx-react": "^7.3.0",

:最初打算安裝和 spug_web 相同的版本,但提示安裝失敗:

    "mobx": "^5.15.6",
    "mobx-react": "^6.3.0",

倘若將 src/index.js 替換成下面程式碼,頁面顯示 App 10 則說明環境準備就緒。

先替換內容啟動:

// react-cli-demo/src/index.js (完整程式碼)
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, } from "react-router-dom";

import { observable } from 'mobx';

class Store {
  @observable count = 10
}

class App extends React.Component {
  render() {
    const { store } = this.props
    return (
      <div>
        App {store.count}
      </div>
    )
  }
}

ReactDOM.render(
  <Router>
    <App store={new Store()} />
  </Router>,
  document.getElementById('root')
);

Tip:為方便演示,未將 App 元件抽離出單獨檔案。

Compiled with problems:X

ERROR in ./src/index.js

Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: react-cli-demo\src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3):

   6 |
   7 | class Store {
>  8 |   @observable count = 10
     |   ^
   9 | }
  10 |
  11 | class App extends React.Component {

安裝 @babel/plugin-proposal-decorators:

react-cli-demo> npm i -D @babel/plugin-proposal-decorators@7

changed 1 package, and audited 1502 packages in 6s

176 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (6 moderate, 2 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

參考 spug_web 配置這個包:

// config-overrides.js
const { override, addDecoratorsLegacy, fixBabelImports } = require('customize-cra');
module.exports = override(
  + addDecoratorsLegacy(),
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
    }),
);

重啟後發現頁面能顯示App 10,但頁面蒙版上報錯如下:

Compiled with problems:X

ERROR

src\index.js
  Line 8:2:  Parsing error: This experimental syntax requires enabling one of the following parser plugin(s): "decorators-legacy", "decorators". (8:2)
  
  第 8:2 行:解析錯誤:此實驗性語法需要啟用以下解析器外掛之一:“decorators-legacy”、“decorators”。 (8:2)

vscode 中有兩個錯誤提示,其中之一就有上述錯誤:

mobx1.png

新建 .babelrc 檔案

// react-cli-demo\.babelrc
{
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true
            }
        ]
    ]
}

Tip:參考 @babel/plugin-proposal-decorators

接著對 vscode 進行設定:

  • 設定 -> 搜尋 experimentalDecorators -> 打上勾

mobx2.png

重啟 vscode,再次重啟服務。如果幸運的話,一切都應該就正常了。

裝飾器

裝飾器是處理類的函式。

Tip:參考 @babel/plugin-proposal-decorators

簡單的類裝飾器

請看示例:

// src/decorators-test.js
@fn
class Dog {}

function fn(target) {
    target.aName = 'pjl'
}

// pjl
console.log(Dog.aName)

Dog 類被 fn 裝飾後,增加了一個類的屬性 aName

Tipsrc/index.js 中引入此檔案。就像這樣:import './decorators-test'

類裝飾器

裝飾器函式可以通過函式返回;裝飾器也可以多個一起使用。就像這樣:

@fn
@fn2(18)
class Dog {}

function fn(target) {
  target.aName = 'pjl'
}

// 返回一個裝飾器
function fn2(value) {
  return function (target) {
    target.age = value
  }
}

console.log(Dog.aName) // pjl
console.log(Dog.age)   // 18
給例項新增屬性

裝飾器可以給例項新增屬性。就像這樣:

@fn3
class Dog {}

function fn3(target) {
  target.prototype.c = 'cc'
}

console.log(new Dog().c) // cc
Class 成員裝飾器

下面定義了一個只讀裝飾器:

class Dog {
    @readOnly aName = 'pjl'
}

// target 目標類的 prototype
// key 類成員的名字
function readOnly(target, key, descriptor) {
    descriptor.writable = false
}

let dog = new Dog()

console.log(dog.aName) // pjl

// Uncaught TypeError: Cannot assign to read only property 'aName' of object '#<Dog>'
dog.aName = 'li'

類似的有“Class function decorator”,就像這樣:

class C {
  @enumerable(false)
  method() {}
}

function enumerable(value) {
  return function(target, key, descriptor) {
    descriptor.enumerable = value;
    return descriptor;
  };
}

第一個示例-自增

在”準備環境“章節中我們從 mobx 中讀取了數字 10,現在我們要增加一個按鈕,每點選一次,數字能自增1。請看實現:

// src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, } from "react-router-dom";

import { observable, action, makeObservable, computed } from 'mobx';
import { observer } from 'mobx-react';

class Store {
  constructor() {
    makeObservable(this, {
      // 將 count 轉為可觀察的
      count: observable,
      // action,動作,用於更改狀態(count)
      increment: action,
      // 將 total 標記為計算屬性
      total: computed
    })
  }
  count = 10
  increment() {
    console.log('increment')
    this.count++
  }
  get total() {
    console.log('total')
    return this.count * 10
  }
}

// react 與 mobox 的橋樑
// 倘若註釋掉 @observer,點選”自增1“,頁面內容總是顯示 `App 10`,儘管 increment 方法也被執行。
@observer
class App extends React.Component {
  render() {
    const { store } = this.props
    return (
      <div>
        App {store.count}
        <button onClick={() => store.increment()}>自增1</button>
        <p>total: {store.total}</p>
        <p>total: {store.total}</p>
      </div>
    )
  }
}

ReactDOM.render(
  <Router>
    <App store={new Store()} />
  </Router>,
  document.getElementById('root')
);

頁面顯示:

App 10 自增1

total: 100

total: 100

控制檯輸出一條:total

observable 可觀察的

observable 定義了一個儲存狀態的可跟蹤欄位。

倘若將上述例子中的 count: observable 註釋,也就是說 count 不再是一個可跟蹤的欄位,接下來無論怎麼點選”自增1“按鈕,頁面內容也只會顯示”App 10“。

action

action 將方法標記為將修改狀態的操作。

倘若將上述例子中的 increment: action 註釋,再次點選”自增1“按鈕,頁面內容會變成”App 11“,但控制檯有警告:

[MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: Store@1.count

[MobX] 由於啟用了嚴格模式,因此不允許在不使用 action 的情況下更改(觀察到的)可觀察值。 嘗試修改:Store@1.count

提醒我們要使用 action 來更改可觀察值。

configure

我們可以通過 configure 來關閉提示 action 中的警告,但通常不推薦

import { configure } from 'mobx'

configure({
  enforceActions: 'never'
})

computed 計算屬性

computed 標記了一個 getter,它將從狀態中獲取新的事實並快取其輸出。

Tip:類似 vue 中的計算屬性,也有快取。

上述例子初始化頁面只輸出一次 total,儘管我們讀取了 2 次。倘若我們註釋掉 total: computed 這行,也就是取消 total 為計算屬性,頁面在初始化時就會輸出 2 次 total

this 為 undefined

上述例子將替換 button 這行程式碼,然後點選”自增1“按鈕,控制檯報錯如下:

increment() {
  console.log('increment')
  this.count++
}

- <button onClick={() => store.increment()}>自增1</button>
+ <button onClick={store.increment}>自增1</button>
Uncaught TypeError: Cannot read properties of undefined (reading 'count')

這是因為 increment() 中 this 是 undefined。可以通過以下方式解決:

  • 使用 action.bound
makeObservable(this, {
    count: observable,
  - increment: action,
  + increment: action.bound,
    total: computed
  })
  • 藉助箭頭函式:
increment() {
  console.log('increment')
  this.count++
}

改為

increment = () => {
  console.log('increment')
  this.count++
}

makeAutoObservable

與使用 makeObservable 相比,makeAutoObservable 函式可以更緊湊且更易於維護,因為不必明確提及新成員。

所以上述例子的 constructor 部分可以改為:

constructor() {
    // 引數1,讓哪個物件變成可觀察的
    // 引數2,排除變成可觀察的屬性或方法
    // 引數3,這裡是自動繫結 this
+   makeAutoObservable(this, {}, { autoBind: true })

-   makeObservable(this, {
-     count: observable,
-     increment: action,
-     total: computed
-   })
}

Tip:更多介紹請參考 makeautoobservable

監聽屬性

autorun

autorun 接收一個函式,該函式總是立即被觸發一次。並自動訂閱函式中可觀察屬性,一旦某屬性變化,則該函式會再次被執行。

請看示例:

...
import { autorun } from 'mobx'

class Store {
  constructor() {
    makeObservable(this, {
      count: observable,
      age: observable,
    })

    setTimeout(() => {
      this.count++
      this.age++
    }, 1000)
    autorun(() => {
      console.log(this.count, this.age);
    })
  }
  count = 10
  age = 1
}

控制檯輸出:

// 總會執行一次
10 1
// 過一秒
11 1
11 2

預設會執行一次。由於訂閱了兩個可觀察屬性,所以只要可觀察屬性被修改,函式就會被執行。

倘若註釋 age: observable,輸出:

10 1
11 1
reaction

reactionautorun 的變種,對於如何追蹤 observable 賦予了更細粒度的控制

  • 與 autorun 不同,初始化時不會執行
  • 可以接收兩個函式作為引數
  • 引數1的返回值作為第二個引數的輸入

重寫上述例子,將 autorun 替換成 reaction:

import { reaction } from 'mobx'

class Store {
  constructor() {
    makeObservable(this, {
      count: observable,
      age: observable,
    })

    setTimeout(() => {
      this.count++
      this.age++
    }, 1000)

    reaction(
      () => ({
        count: this.count,
        age: this.age
      }),
      // oldVal 是上一次的值
      (val, oldVal) => {
        console.log(val, oldVal)
      }
    )
  }
  count = 10
  age = 1
}
// 一秒後
{count: 11, age: 1} {count: 10, age: 1}
{count: 11, age: 2} {count: 11, age: 1}

倘若註釋 age: observable,輸出:

{count: 11, age: 1} {count: 10, age: 1}

非同步處理

比如點選”自增1“按鈕,一秒後修改狀態,我們假如這樣寫:

increment() {
    setTimeout(() => {
      this.count++
    }, 1000)
}

控制檯會報警告:

[MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: Store@1.count

一種方式是將 imcrement() 函式拆分成兩個方法,就像這樣:

increment() {
    this.count++
}

asyncIncrement() {
    setTimeout(() => {
      this.increment()
    }, 1000)
}

<button onClick={() => store.asyncIncrement()}>自增1</button>

更好的方式是使用 runInAction

import { runInAction } from 'mobx'

increment() {
    setTimeout(() => {
      runInAction(() => {
        this.count++
      })
    }, 1000)
}

顧名思義,函式在 action 中執行。

新的開始

以下是剩餘一些包的作用:

  • ace-builds,Ace 是一個用 JavaScript 編寫的程式碼編輯器。

  • bizcharts,阿里通用圖表元件庫,致力於打造企業中後臺高效、專業、便捷的資料視覺化解決方案,基於 G2與G2Plot封裝的React圖表庫,已經歷阿里複雜業務場景長達三年的洗禮,在靈活性、易用性、豐富度上滿足常規圖表和高度自定義圖表的業務實現

  • lodash,提供模組化、效能和附加功能的現代 JavaScript 實用程式庫。

  • moment,用於解析、驗證、操作和格式化日期的 JavaScript 日期庫。

  • react-ace,Ace 的一組 react 元件

import Editor from 'react-ace';
  • xterm,Xterm.js 是一個用 TypeScript 編寫的前端元件,它允許應用程式在瀏覽器中將功能齊全的終端帶給使用者。它被 VS Code、Hyper 和 Theia 等流行專案使用。

至此,我們基本具備接手 spug_web 這個 react 前端專案的能力,但是,react 的旅途才剛剛開始。

其他章節請看:

七天接手react專案 系列

相關文章