1.课程大纲 #

1.1 mobx实战篇 #

1.2 mobx源码篇 #

2.Mobx #

2.1 安装 #

pnpm create vite
pnpm install @babel/core @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
pnpm install mobx mobx-react

2.2 vite.config.ts #

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
  plugins: [react({
    babel: {
      plugins: [
        ["@babel/plugin-proposal-decorators", { legacy: true }],
        ["@babel/plugin-proposal-class-properties", { loose: true }],
      ],
    },
  })]
})

2.3 jsconfig.json #

jsconfig.json

{
    "compilerOptions": {
        "experimentalDecorators": true
    }
}

2.4 src\main.tsx #

src\main.tsx

import {observable} from 'mobx';
console.log(observable);

3.mobx #

3.1 创建可观察对象 #

3.2 observable #

import {observable,reaction} from 'mobx';
let obj = {name:'1'}
let proxyObj = observable(obj);
console.log(proxyObj);

3.3 reactions #

3.4 Autorun #

import {observable,autorun} from 'mobx';
let obj = {name:'1'}
let proxyObj = observable(obj);
autorun(()=>{
    console.log(proxyObj.name);
})
proxyObj.name = '2';

3.5 makeObservable #

import {observable,makeObservable,autorun} from 'mobx';
class Doubler {
    value
    constructor(value) {
        makeObservable(this, {
            value: observable,
        })
        this.value = value
    }
}
const doubler = new Doubler(1);
autorun(()=>{
    console.log(doubler.value);
});
doubler.value = 2;

3.6 computed #

+import {observable,makeObservable,autorun,computed} from 'mobx';
class Doubler {
    value
    constructor(value) {
        makeObservable(this, {
            value: observable,
+           double: computed,
        })
        this.value = value
    }
+   get double() {
+       return this.value * 2
+   }
}
const doubler = new Doubler(1);
autorun(()=>{
    console.log(doubler.value);
+   console.log(doubler.double);
});
doubler.value = 2;

3.7 action #

+import {observable,makeObservable,autorun,computed,action} from 'mobx';
class Doubler {
    value
    constructor(value) {
        makeObservable(this, {
            value: observable,
            double: computed,
+           increment: action
        })
        this.value = value
    }
    get double() {
        return this.value * 2
    }
+   increment() {
+       this.value++
+       this.value++
+   }
}
const doubler = new Doubler(1);
autorun(()=>{
    console.log(doubler.value);
    console.log(doubler.double);
});
+doubler.increment();

3.8 flow #

+import {observable,makeObservable,autorun,computed,flow,action} from 'mobx';
class Doubler {
    value
    constructor(value) {
        makeObservable(this, {
            value: observable,
            double: computed,
            increment: action,
+           fetch: flow
        })
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
+   *fetch() {
+       const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
+       this.value = response;
+   }
}
const doubler = new Doubler(1);
autorun(()=>{
    console.log(doubler.value);
    console.log(doubler.double);
});
doubler.increment();
+doubler.fetch();

3.9 bound #

import {observable,makeObservable,autorun,computed,flow,action} from 'mobx';
class Doubler {
    value
    constructor(value) {
        makeObservable(this, {
            value: observable,
            double: computed,
+           increment: action.bound,
+           fetch: flow.bound
        })
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
    *fetch() {
        const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
        this.value = response;
    }
}
const doubler = new Doubler(1);
autorun(()=>{
    console.log(doubler.value);
    console.log(doubler.double);
});
+const increment = doubler.increment;
+increment();
+const fetch = doubler.fetch;
+fetch();

3.10 makeAutoObservable #

+import {observable,makeObservable,autorun,computed,flow,action,makeAutoObservable} from 'mobx';
class Doubler {
+   PI=3.14
    value
    constructor(value) {
+       makeAutoObservable(this,{PI:false},{autoBind:true})
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
    *fetch() {
        const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
        this.value = response;
    }
}
const doubler = new Doubler(1);
+autorun(()=>{
+    console.log(doubler.PI);
+});
autorun(()=>{
    console.log(doubler.value);
    console.log(doubler.double);
});
const increment = doubler.increment;
increment();
const fetch = doubler.fetch;
fetch();
+doubler.PI=3.15;

3.11 Reaction #

+import {makeAutoObservable,reaction} from 'mobx';
class Doubler {
    PI=3.14
    value
    constructor(value) {
        makeAutoObservable(this,{PI:false},{autoBind:true})
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
    *fetch() {
        const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
        this.value = response;
    }
}
const doubler = new Doubler(1);
+reaction(
+    () => doubler.value,
+    value => {
+        console.log('value',value);
+    }
+)
+doubler.value=2;

3.12 When #

+import {makeAutoObservable,reaction,when} from 'mobx';
class Doubler {
    PI=3.14
    value
    constructor(value) {
        makeAutoObservable(this,{PI:false},{autoBind:true})
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
    *fetch() {
        const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
        this.value = response;
    }
}
const doubler = new Doubler(1);
+when(
+    () => doubler.value === 3,
+    () => {
+        console.log('value',doubler.value);
+    }
+)
+doubler.value++;
+doubler.value++;
+doubler.value++;

3.13 runInAction #

import {makeAutoObservable,reaction,when,autorun,runInAction} from 'mobx';
class Doubler {
    PI=3.14
    value
    constructor(value) {
        makeAutoObservable(this,{PI:false},{autoBind:true})
        this.value = value
    }
    get double() {
        return this.value * 2
    }
    increment() {
        this.value++
        this.value++
    }
    *fetch() {
        const response = yield new Promise((resolve)=>setTimeout(()=>resolve(5),1000))
        this.value = response;
    }
}
const doubler = new Doubler(1);
+autorun(()=>console.log(doubler.value));
+runInAction(()=>{
+    doubler.value++;
+    doubler.value++;
+    doubler.value++;
+});

4.mobx-react #

4.1 observer #

4.1.1 main.jsx #

src\main.jsx

import { createRoot } from "react-dom/client";
import Counter from "./Counter";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<Counter/>);

4.1.2 observer #

src\Counter.jsx

import {makeAutoObservable} from 'mobx';
import {observer} from 'mobx-react';
class Store {
    number=1
    constructor(){
        makeAutoObservable(this,{},{autoBind:true});
    }
    add(){
        this.number++;
    }
}
let store=new Store();
export default observer(function () {
    return (
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    )
});

4.2 observer class #

4.2.1 Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {observer} from 'mobx-react';
class Store {
    number=1
    constructor(){
        makeAutoObservable(this,{},{autoBind:true});
    }
    add(){
        this.number++;
    }
}
let store=new Store();
+@observer
+export default class Counter extends React.Component{
  render(){
    return (
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    )
  }
+}

4.3 Observer #

4.3.1 Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
+import {observer,Observer} from 'mobx-react';
class Store {
    number=1
    constructor(){
        makeAutoObservable(this,{},{autoBind:true});
    }
    add(){
        this.number++;
    }
}
let store=new Store();
export default function () {
    return (
+       <Observer>
            {
                ()=>(
                  <>
                    <p>{store.number}</p>
                    <button onClick={store.add}>+</button>
                  </>
                )
            }
+      </Observer>
    )
}

4.4 useObserver #

4.4.1 Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {observer,Observer,useObserver} from 'mobx-react';
class Store {
    number=1
    constructor(){
        makeAutoObservable(this,{},{autoBind:true});
    }
    add(){
        this.number++;
    }
}
let store=new Store();
export default function () {
+    return useObserver(()=>(
        <>
          <p>{store.number}</p>
          <button onClick={store.add}>+</button>
        </>
+     ));
}

4.5 useLocalObservable #

4.5.1 Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {observer,Observer,useObserver,useLocalObservable} from 'mobx-react';
export default function () {
+   const store = useLocalObservable(()=>({
+       number:1,
+       add(){
+           this.number++;
+       }
+   }));
    return useObserver(()=>(
        <>
          <p>{store.number}</p>
          <button onClick={store.add}>+</button>
        </>
      ));
}

4.6 todos #

4.6.1 main.jsx #

import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App/>);

4.6.2 App.jsx #

src\App.jsx

import React from "react";
import store from "./store";
import StoreContext from './context';
import Todos from './Todos';
import User from './User';
const App = () => {
  return (
    <StoreContext.Provider value={store}>
      <User />
      <hr />
      <Todos/>
    </StoreContext.Provider>
  );
};
export default App;

4.6.3 context.jsx #

src\context.jsx

import React from "react";
const StoreContext = React.createContext();
export default StoreContext;

4.6.4 User.jsx #

src\User.jsx

import { useContext,useRef } from 'react';
import StoreContext from './context';
import { observer } from "mobx-react";
const User = observer(function () {
  const { userStore } = useContext(StoreContext);
  const ref = useRef(null);
  return (
    <div>
     {userStore.isLogin?(
      <>
      {userStore.username}<button onClick={() => userStore.logout()}>退出</button>
      </>
     ):(
      <>
      <input ref={ref} type="text" />
      <button onClick={() => userStore.login(ref.current.value)}>登录</button>
      </>
     )}
    </div>
  );
});
export default User;

4.6.5 Todos.jsx #

src\Todos.jsx

import { useContext ,useRef} from 'react';
import StoreContext from './context';
import { observer } from "mobx-react";
import {TodoStore} from './store';
const Todo = observer(function ({todo}) {
  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => todo.toggle()}
      />
      {todo.text}
    </li>
  );
});

const TodoList = observer(function () {
  const { todoStore } = useContext(StoreContext);
  return (
    <div>
      <ul>
        {todoStore.list.map((todo, index) => (
          <Todo todo={todo} key={index} />
        ))}
      </ul>
    </div>
  );
});

const TodoLeft = observer(function () {
  const { todoStore } = useContext(StoreContext);
  return <>未完成: {todoStore.unCompletedCount}</>;
});

const AddTodo = observer(function AddTodo() {
  const { todoStore } = useContext(StoreContext);
  const ref = useRef(null);
  return (
    <>
      <input ref={ref} type="text" />
      <button
        onClick={() => {
          const item = new TodoStore(ref.current.value);
          todoStore.add(item);
          ref.current.value = "";
        }}
      >新增</button>
    </>
  );
});
export default observer(function () {
  return (
    <>
      <AddTodo />
      <TodoList />
      <TodoLeft />
    </>
  );
});

4.6.6 user\index.jsx #

src\store\user\index.jsx

import { makeAutoObservable } from "mobx";
class UserStore {
  username='';
  constructor() {
    makeAutoObservable(this, {}, { autoBind: true });
  }
  get isLogin() {
    return this.username.length > 0;
  }
  login(username) {
    this.username = username;
  }
  logout() {
    this.username = "";
  }
}
const userStore = new UserStore();
export default userStore;

4.6.7 todos\index.jsx #

src\store\todos\index.jsx

import { makeAutoObservable } from "mobx";
class TodoStore {
  list = [];
  get unCompletedCount() {
    return this.list.filter((todo) => !todo.completed).length;
  }
  constructor() {
    makeAutoObservable(this,{},{autoBind: true});
  }
  add(todo) {
    this.list.push(todo);
  }
}
const todoStore = new TodoStore();
export default todoStore;

4.6.8 todo.jsx #

src\store\todos\todo.jsx

import { makeAutoObservable } from "mobx";
export class TodoStore {
  text = "";
  completed = false;
  constructor(text) {
    makeAutoObservable(
      this,{},{autoBind: true}
    );
    this.text = text;
  }
  toggle() {
    this.completed = !this.completed;
  }
}

4.6.9 store\index.jsx #

src\store\index.jsx

import todoStore from "./todos";
import userStore from "./user";
const store = { todoStore, userStore };
export { TodoStore } from "./todos/todo";
export default store;