3.0.8-beta3 • Published 4 years ago

enhanced-miniprogram v3.0.8-beta3

Weekly downloads
-
License
MIT
Repository
-
Last release
4 years ago

enhanced-miniprogram {ignore=true}

toc

概述

  1. 微信小程序原生开发增强插件
  2. 规范组件数据结构用以复杂项目
  3. 严格的类型检查(TS)

安装

  1. 在小程序目录下执行 npm i enhanced-miniprogram -S
  2. 构建 npm

增强功能简介

支持响应式数据(基于mobx)

格式: ()=> observableObject.filed

import { EPage } from "EMP"; 
import { observable } from "mobx"; //默认为mobx6
const user = observable({
    name: "zhao",
    age: 20
});
setTimeout(() => {
    user.name = "liu";
    user.age++;
}, 1000);
EPage({
    data: {
        name: user.name, //name字段非响应式写法,不具备响应式
        age: () => user.age //age字段具有响应式 即当user.age改变时,实例自动更新age为最新的user.age
    },
    onload() {
        console.log(this.data.name, this.data.age); //"zhao",20
        setTimeout(() => {
            console.log(this.data.name, this.data.age); //"zhao" ,21
        }, 1000);
    }
});

集成 computed 和 watch

(同 miniprogram-computed 项目地址)

import { EPage, EComponent } from "Enhanced-miniprogram";
interface User {
    name: string;
    age: number;
}
EPage({
    data: {
        user: <User>{ name: "zhao", age: 20 }
    },
    computed: {
        age(data) {
            return data.user.age + 1; //age:21
        }
    },
    watch: {
        "age,user"(valueAge: number, valueUser: User) {},
        "user.**"(newUser) {}, //自动推导newUser的类型为User
        "user.name"(newName) {}, //自动推导newName的类型为string
        age(newAge) {}, //自动推导newAge的类型为number
        user(newUser) {} //自动推导newUser的类型为User
    }
});

全局依赖注入

//inject.js 
import { EPage, EComponent } from "EMP";
//待注入的数据
const globalData = { user: { name: "zhao", age: 20 } };
const themeStore = observable({
    theme: wx.getSystemInfoSync().theme
});
wx.onThemeChange((Res) => {
    themeStore.theme = Res.theme;
});
//待注入的方法
function print(data:string){
  console.log(data)
}
//注入
GlobalInject.inject({
  data: {
    theme: () => themeStore.theme,
    globalData:globalData
  },
  options: {
    addGlobalClass: true,
    multipleSlots: true,
    pureDataPattern: /^_/
  },
  methods:{
    print,
  }
})
//ts开发需写入注入类型如下
// declare module 'EMA' {
//   interface GlobalInjectOptions {
//     data: {
//       theme:"dark" | "light" | undefined,
//       name:string
//     }
//   }
// }

tips 如果您有更好的导入类型方式,PR

严格的类型检查

  1. 字段限制和约束 很多地方都加入了字段限制,比如 Page 配置对象除了规定的字段,不可有额外字段。不可向原生那样方法直接写在配置中,应写在 methods 字段下。 properties 中检查字段,写错,写多都不可以。 即使是继承父类型的子类型也加了字段溢出检查。子类型字段不可以超出继承的父类型字段。 computed 的 data,watch 的字段,this 实例,所有能检查的地方都有严格的类型检查。

  2. 绝对的单向数据流。 例如 this.setData 方法已经重写了类型,约束只能对 data 字段进行 setData,无法对 properties 字段、计算字段、注入字段、behavior 字段、响应式字段 setData, 除非你放弃 ts 的类型检查。

核心思想

组件 TS 类型代替组件文档

在定义自定义组件的时候,把与外部的关联通过类型声明导出(export),即用 ts 类型描述组件文档。在使用组件的时,导入要使用的组件类型(import),就清楚组件的使用规则。这样做的好处是,定义者省得写文档,使用者免去查阅,通过导出类型,即可以明白如何使用组件。

**示例代码1 创建自定义组件sayHello**

```ts
    //components/sayHello.wxml
    <view bindtap="onTap" style="color:{{color}}">按钮<view>
    //components/sayHello.ts
    import {EComponent} from 'Enhanced-miniprogram'
    interface User {name:string,age?:number} //or class User{...}
          const propertiesType =  EComponent({
          properties:{
            /*
              按钮颜色
            */
            color:{
              type:String,
              value:<const>'black' //value必须使用断言 表示默认值
            },
            /*
              做为triggerEvent的detail参数
            */
            user:{
              type:Object as PropType<User>, //可使用PropType类型断言具体类型 or class User
              value:<User>{}
            },
            /*
              组件被点击时触发回调triggerEvent事件
            */
            customEventA:{        //定义自定义事件名称(做为原生triggerEvent事件的第一个参数)
              type:'customEvent', //其实没什么用,主要为了一目了然的看到哪个是自定义事件字段。
              detailType:String,  //定义原生triggerEvent的第二个参数类型
             // options?:{bubbles?: boolean,composed?: boolean,capturePhase?: boolean} 将做为原生triggerEvent的第三个参数
            }
          },
          methods:{
              onTap(){
                const name = this.data.user.name;
                this.customEventA(`欢迎您:${name}`) //好比this.triggerEvent('customEventA',`欢迎您:${name}`,options: 如果上面customEventA中有写options字段,将赋值到这里)
              }
          }
    });

    type DOC = typeof propertiesType
    export {DOC,User} //导出组件外部属性,和内部类型,方便使用者。
    /**导出的类型为
     * type SayHello = {
              properties: {
                  color?: {   //带?表示非必传,
                    type:string,  //类型为string
                    default:"black" //默认值为'black'
                  };
                  user: {                  //不带?表示必传,类型为{name:string,age?:number}即User
                      name: string;
                      age?: number;
                  };
              };
              customEvent: {               //自定义事件字段
                      customEventA: (detail: string) => void;   //告诉调用者,此组件有一个名为customEventA的 自定义事件(tiggerEvent事件),类型为(detail: string) => void
              };
            }
     */
```

tips 除了基本类型外,通过 propType可实现 所有 ts 类型 (联合类型,元组类型,自定义对象 数组...)

 例如   Properties:{
              classA:Class User, //自定义构造函数约束类型。
              classB:{type:Class User,value:<User>{}
              }
        }

示例代码 2 使用自定义组件 sayHello

 //page/index.json
 {
   "usingComponents": {
     "sayHello":"/components/sayHello"
   }
 }
  //Page/index.wxml
   <view>
     <sayHello bind:customEventA="sayHelloCB" />
   <view>
  //page/index.ts
 import {EPage,PageSubData} from  "enhanced-miniprogram"
 import * as SayHello from '/components/SayHello'
 const subCompData = PageSubData<SayHello.Doc>()({//在组件中使用ComponentSubData
       data:{
           color:'red', // 可以不写,因为不是必传字段。
           user:{name:'zhao'}, //必传字段,且类型必须为SayHello.User
           otherField:'错误的字段报错'// 非SayHello类型字段,是要报错的。
       }
 })
 EPage({ //or EComponent
   subComponent:[subCompData], //增加的字段 用于导入子组件数据 可以导入多个
   subCustomEvent:{ //当subComponent中有customEvent字段时,可在此定义接收函数
       sayHelloCB(detail,e){ //ok detail类型为string,e为事件参数,detail === e.detail true
         console.log(detail,e)
       },
       unknownField(detail,e){//报错,因为ts类型检测不到subComponent中传入的子组件类型数据中有此自定义事件字段
       }
   }
 })

更好的细粒度开发

示例

    const subDataA = ComponentSubData<A.Doc>()({...})
    const subDataB = ComponentSubData<B.Doc>()({...})
    const subDataC = ComponentSubData<C.Doc>()({...})
    const subDataD = ComponentSubData<D.Doc>()({//假设D类型为{subName:string,subAge:number,subData:string}
        properties:{
          subData:String //可以继续向上传递需求,好比这写在父组件中,数据实际上是爷爷的。
        },
        data:{
          subAge:10 //给D.Doc中的数据赋值。
        },
        computed:{
           subName(data){
              return data.obj.name //这里是把父组件的obj数据拿过来给子组件name赋值。 
           }
        },
        methods:{
          changeSubAge(newAge){ //只能通过自己的方法改变自身数据
            this.setData({
              subAge:newAge
            })
          }
        }
    })
  interface User = {name:string,age:number}
 const prop =  Ecomponent({
     properties:{
       obj:Object as PropType<User>
     }
     data(){
       return {
         name:this.properties.obj.name
       }
     },
     watch:{
       age(newAgeValue){ 
          this.changeSubAge(newAgeValue)//向改变子组件数据,只能通过调用子组件方法实现,这里直接setData是不可以的。除非你放弃类型约束。

       }
     }
     subComponent:[subDataA,subDataB,subDataC,subDataD]//所有的子类型会注入进来。
   })

    export {prop,User} //注意这个prop中不仅有自身的obj属性,还有子组件向爷爷要的数据subData。

在上面的示例中,可以看出,复杂的页面或者组件引用其他自定义组件时,需要单独建立子组件数据,然后注入到当前页面或组件实例中。

每个子组件的数据是单独存放的,也是自身单独控制的。父组件想要调用更改子组件数据,只能通过调用子组件的方法来修改。

插件提供的 PageSubData 和 CompSubData 基于原生的 Behavior 函数,虽然如此,但插件提供了更严格的书写方式和类型检查。让基于 Behavior 书写子组件数据变为现实。

无所不在的类型检查

小程序提供的 ts 类型库(@types/wechat-miniprogram)只给了一些基础类型提示,且更新速度缓慢。 这也是开发此插件的根本原因之一,enhanced-miniprogram 提供了有些"变态"的类型检查。这里简单描述,后面有详细的阐述。

  1. 重复字段检查 在书写一个组件或页面实例配置的时,如果 properties,data,computed,behaviors 中引入的数据,有重复字段,就会报错(不能将类型 xxx 分配给类型“never”)。

    interface User {
        name: string;
        age: number;
    }
    EComponent({
        properties: {
            age: Number,
            user: {
                type: Object,
                value: <User>{}
            }
        },
        computed: {
            age(data) {
                //报错:“不能将类型 number 分配给类型“never” 因为properties中有age字段了
                return data.user.age;
            }
        }
    });
  2. 字段拼写检查,无效字段检查,多余字段检查

  3. 类型错误检查...

基于原生的好处

  1. 完美兼容 当你用 EPage 或 EComponent 配置实例发现一些插件错误的时候,完全可以转向用原生 Page 或 Component 配置实例,提交 issue,待修复 bug 后再用 EPage 或 EComponent 替换原生配置。
  2. 渐进融合 如何你喜欢这种开发方式,你可以渐进式的把过去的代码逐步替换。简单的复制粘贴即可,不必大量修改代码。

功能详情

EComponent

  1. properties
  • 书写规则

    1. 简写字段 值类型:String | Number | Boolean | Object | Array 返回类型:'' | 0 | false | { k:string : any } | unknown[]

          properties:{
            str:String,
            num:Number,
            bool:Boolean,
            arr:Array,
            obj:Object
          }
    2. 对象字段

      值类型:{ type:String | Number | Boolean | Object | Array, required?:boolean //不写 默认为 false value?:'use assert' } 返回类型:value 字段类型(应使用断言),若未写 value 字段,返回对应 type 值的类型。

      tips 类型断言可以让使用者通过类型看到组件字段的默认值,应在 value 值为 string | number | [] | {} 的情况时使用断言

      interface User{ id:string,age:number}
        properties:{
          str:{
            type:String,
            value:<const>'annil' //返回类型为'annil' 若不写此字段返回类型为'',若不断言返回类型为string,推荐使用断言
          }
          num:{
            type:Number,
            required:true, //不写默认为 false
            value:<const>100 //返回类型为100 若不写此字段返回类型为0,若不断言返回类型为number,推荐使用断言
          }
          bool:{
            type:Boolean,
            required:true,
            value:true //返回类型为true 若不写此字段返回类型为false,boolean类型不必断言
          }
          arr:{
            type:Array,
            required:true,
            value:[1,2,3] //返回类型为number[] 若不写此字段返回类型为unknown[],value值不为空[],可不使用断言,若为[] 应使用断言 (value:<number[]>[])
          }{ [k:string] : any }
          obj:{
            type:Object,
            required:true,
            value:<User>{} //返回类型为User 若不写此字段返回类型为{ [k:string] : any },使用断言因为value值为空{}
          }
      }
    3. 自定义事件字段(类似原生实例中的 triggerEvent)

      值类型:{ type: 'customEvent', detailType: String | Number |Boolean | {k:string:any}, options?: { bubbles?: boolean,composed?: boolean,capturePhase?: boolean} }

      返回类型:{ customEvent:{ fieldA:(detail:对应 detailType,options:对应 options)=>void } }

      tips 此字段是为了让调用者清楚组件向外部 trigger 的自定义事件,detailType 字段定义原生实例 triggerEvent 函数的第二个属性的类型,options 做为原生实例 triggerEvent 函数的第三个参数值,不写子字段默认全为 false。此字段会在实例中建立一个方法,可直接调用,看下方示例。

      import { EComponent } from "Enhanced-miniprogram";
      interface DemoUser {
          name: string;
          age: number;
      }
      EComponent({
          properties: {
              customEventA: {
                  type: "customEvent",
                  detailType: <DemoUser>{}
              },
              customEventB: {
                  type: "customEvent",
                  detailType: <DemoUser>{},
                  options: {
                      //不写此字段,子字段默认为空对象,即子字段值全为 false
                      bubbles: true,
                      composed: true,
                      capturePhase: true
                  }
              }
          },
          methods: {
              onTap() {
                  this.customEventA({ name: "annil", age: 22 });
                  //相当于原生: this.triggerEvent('customEventA',{name: 'annil',age: 22})
                  this.customEventB({ name: "annil", age: 22 });
                  //相当于原生: this.triggerEvent('customEventB',{name: 'annil',age: 22},options: { bubbles: true,composed: true,  capturePhase: true })
              }
          }
      });
  • 字段检测

    js 运行时检测

      import { EComponent } from "Enhanced-miniprogram";
    
       EComponent({
          properties:{
            fieldD:{
              type:String,
              require:true, //报错 require 少s了
              values:'string', //报错 values 多s了
              otherField:'xxx' //报错 多余的字段
            }
            customEventFoo:{
              type: 'customEvent',
              detailType: String
              options?: { bubble?: boolean,compose?: boolean,capturePhase?: boolean},//自定义事件全字段有检测
              otherField:'xxx' //报错 多余的字段
              }
          }
        })
  • 总结示例

    ```ts
    // component/demo.ts
    import { EComponent } from "Enhanced-miniprogram";
    export interface DemoUser {
        name: string;
        age: number;
    }
    const demo = EComponent({
        properties: {
            str: String,
            num: {
                type: Number,
                value: <const>100
            },
            bool: {
                type: Boolean
            },
            arr: {
                type: Array,
                required: false,
                value: <DemoUser[]>[]
            },
            obj: {
                type: Object,
                required: true,
                value: <DemoUser>{}
            },
            customEventFoo: {
                type: 'customEvent',
                detailType:<DemoUser>{} ,
                options: {
                    //不写此字段,子字段默认为空对象,即子字段值全为false
                    bubbles: true,
                    composed: true,
                    capturePhase: true
                }
            }
            },
            methods: {
                onTap() {
                    //调用customEvent事件
                    this.customEventFoo({name: 'annil',age: 22}); //相当于原生: this.triggerEvent('customEventFoo',{name: 'annil',age: 22},options=properties中定义的options)
            }
        }
    });
    export type Demo = keyof demo
      /** Demo 类型等于
        type Demo = {
            properties: {
                str?: "";
                obj: {
                    name: string;
                    age: number;
                };
                num?: 100;
                bool?: false;
                arr?: {
                    name: string;
                    age: number;
                }[];
            };
            customEvent: {
                customEventFoo: (detail: DemoUser, options: {
                    bubbles: true;
                    composed: true;
                    capturePhase: true;
                }) => void;
          };
     ```
  1. data
  • 增加响应式数据

  • 字段重名检测

        {
            properties:{
                strA:String,
            },
            data:{
                strB:'' //ok
                strA:'' //error 不能将类型“string”分配给类型“never”
            }
        }
  1. computed
  • 书写示例

      import { EComponent } from "Enhanced-miniprogram";
      interface User {name:string,age:number}
      EComponent({
          properties:{
            objInProperty:{
              type:Object,
              value:<User>{}
            }
          },
          data:{
            objInData:{name:'zhao'}
          }
          computed:{
              filedAge(data){
                return  data.objInProperty.age
              },
              filedName(data){
                return  data.objInData.name
              },
              fieldInject(data){//参数data中包含注入的数据
                return data.injectObj.xxx
              }
          }
    
      })
  • 类型约束

    1. 必须有返回值
    2. 字段名不能与 properties 和 data 中相同
    3. data 数据为只读
  1. methods
  • 类型约束
    1. 不能与 proerties 中的 customEvent 类型字段重名
    2. 不能定义子组件 customEvent 函数(应定义在 customEvent 字段中)
  1. customEvent
  • 字段描述 新增字段,为应对复杂组件中清晰子组件自定义事件函数位置。 此字段涉及子组件嵌套,建议看完 CompSubData 函数再看。
  • 书写示例

      import { EComponent,CompSubData } from "Enhanced-miniprogram";
      import {subCompA} from '../component/subCompA'
      import {subCompB} from '../component/subCompB'
    
      const subAData = CompSubData<subCompA>(){
           properties:{
             //...
           }
      }
      const subBData = CompSubData<subCompB>(){
           data:{
            //...
           }
      }
      EComponent({
          customEvent:{
            subAcustomEvent(detail,e){
              //detial为子组件传递的数据,e为事件对象
              console.log(detial === e.detail)//true
            },
            subBcustomEvent(detail,e){
              console.log(detial === e.detail)//true
            }
          },
          behaviors:[subAData,subBData]
      })
  1. watch
  1. behaviors
  • 字段描述 同原生,负责传递子组件数据和类型(子组件自定义事件类型也由此传入)
  • 类型约束 类型为 IBehaviors[]

更细粒度书写复杂组件 (基于原生 Behavior)

  1. 先看示例 (后面有解释)

    //页面中引入component/demo.ts组件和component/demo1.ts组件
    import { DemoCompDoc } from "component/demo.ts";
    import { DemoCompDoc } from "component/demo1.ts";
    
    const mainData =
const subDemo = CreateSubCompData(DemoCompDoc, {
    data:{
      str:'zhao'
    },
    computed:{
      arr(data){
        return data.
      }
    }

},mainData);
const sub1 = CreateSubCompData(otherCompDoc1, options);//省略

EPage({
    behavoirs: [subDemo,sub1, sub2, sub3]
});
```

复杂情况下,每个子组件之间可能要有数据交互影响,这个时候需要一个主数据,对主数据有需求的子组件可以引入主数据,(实际上这么做只是为了子组件可以有严格的类型检查)。

如下

```ts
  import { xxxCompDoc1,xxxCompDoc2,xxxCompDoc3} from "path";
  const mainData = CreateMainData({ //只能写数据。
    properties:{},//爷爷实例传递过来的数据,需要满足子组件提交的properties
    data:{},
    computed:{}
  })
  const sub1 = CreateSubCompData(xxxCompDoc1,{
    properties:{}, //把由爷爷决定的数据提交上层
    computed:{},//可由mainData数据得到计算属性。
    watch:{}
    methods:{}, //事件只能修改自身组件数据。
    lifetimes:{
      attached(){
        //this实例只有自身数据和方法
      }
    }
  },mainData)

  const sub2 = CreateSubCompData(xxxCompDoc2,options,mainData)

  const sub3 = CreateSubCompData(xxxCompDoc3,options,mainData)

  EComponent({
    behavoirs:[mainData,sub1,sub2,sub3],
    customEvent:{} //写所有子组件triggerEvent事件。
    methods:{
      //负责整体数据逻辑, 自能修改mainData数据,通过调用各个子组件方法,修改子组件数据。
      //比如当sub1中trigger一个事件时候T,T事件调用sub2中的事件修改sub2的方法。
    },
    lifetimes:{
      attached(){
        //this实例只有main数据 、自身和子组件方法
      }
    }
  })
```

使用示例

  1. 公共数据的注入 (EPage 中默认注入了当前主题的响应式数据 theme)
//inject.js
   import {EPage,EComponent} from 'enhanced-miniprogram'
    //创建响应式数据theme 基于mobx5(proxy)
    const objStore = observable({
        count:0
    })

    setInterval(() => {
      objStore.count++
    }, 1000)

    EPage.inject({
        options: {
           multipleSlots:true,
           pureDataPattern:/^_/,
           //...
        },
        data: {
          //加入globalData数据到每一个页面实例(非响应式)
          globalData: {
            name:'zhao',
            //...
          },
          //响应式数据(格式为函数返回值形式), themeStore.theme发生变换时,自动更新页面数据(即自动setData)
          count:()=>objStore.count
        }
        methods: {
          print(data){ //加入公共方法到每一个页面实例
             console.log(data)
          }
        }
    //ts部分(如使用ts开发,需要按下面写入IInjectPage类型和IInjectComponent,这样可在每个实例下获取注入的数据类型,有更好的方式欢迎提醒)
    declare global {
        interface IInjectPage { //需要注意这里必须是此名称不然无效,不写options类型,因为不需要,无效。
          data: {
            globalData:{
              name:number,
            },
            count:number
            //...
          },
          methods:{
             /** 实例调用时会显示注解
             * @param value 打印输入的字符串
             */
            print:(data:string)=>void
          }
        }
        interface IInjectComponent {//需要注意这里必须是此名称不然无效
          data: {
            //...
          }
          methods: {
            //...
          }
        }
}

// App.js 引入inject.js
  import "./inject"
  App({})
// xxx.ts
  import {EPage} from 'enhanced-miniprogram'
  EPage({
    onload(){
      console.log(this.data) //{globalData:{name:string},theme:Theme,count:number}
      this.print('hello world') // 'hello world'
    }
  })
  1. EPage 的基本使用
//tabbar.ts
import { EPage } from "enhanced-miniprogram";
const User = observable({
    name: "zhao",
    age: 20
});
setInterval(() => {
    User.age++;
}, 1000);

class Cart {
    @observable public goodsName = "苹果";
    @observable public price = 20;
}
const cart = new Cart();
setInterval(() => {
    cart.price++;
}, 1000);
EPage(
    {
        data: {
            UserName: User.name, //不具有响应式
            UserAge: () => User.age, // 响应式数据
            cartGoodsName:cart.goodsName //不具有响应式
            cartPrice:()=>cart.price //响应式数据
        },
        computed: {
            //同miniprogram-computed 见注释
        },
        watch: {
            //同miniprogram-computed 见注释
        },
        lifetimes: {
            attached() {
                //this中可获取注入的方法,this.data中可获取注入的数据
            }
        }
    },
    "tabbar"
); //可选属性,建议同组件名,看CreateComponentData部分的解释
  1. CreateComponentData 当在组件或页面中引入子组件的时,可按如下书写
import {tabbar} from './tabbar.ts'
import { CreateComponentData,EPage } from "enhanced-miniprogram";
//CreateComponentData,创建behavior数据 一参为要创建的组件的文档,
// const tabbarData = CreateComponentData(tabbar,propertyData)
//propertyData中data对象绑定非响应式数据,bindData中绑定响应式数据,customEvent绑定函数。(ts开发,会有严格的类型提示,必选字段必须有)
//字段有前缀是因为在创建tabbar组件时,写入了第二个参数
const tabbarData = CreateComponentData(tabbar,{
    data:{
      tabbar_dataA:Number,//可选属性可不传
    },
    bindData:{
       tabbar_dataB:()=>xxx.field //必选属性必须有
    },
    customEvent:{ //函数运行时,会传入2个参数,1参为子组件调用此函数时的一参,二参为子组件调用此函数时的事件函数
      tabbar_funcA(detial:any,e:WechatMiniprogram.customEvent){}, //可选函数
      tabbar_funcB(detial:string,e:WechatMiniprogram.customEvent){} //必选函数
    }
})
EPage({
  behaviors:[tabbarData] //通过behaviors把多个子组件数据聚合在一起
  onLoad(){
    this.tabbar_funcB('string') //自身调用传递给子组件的函数 参数类型跟随在createComponentData时的定义类型。
  }
})
// EComponent({ 使用方法同上
//     behaviors:[tabbarData]
// })
  1. CreateNativeComponentData

参与贡献

  1. Fork 本仓库
  2. 新建 Feat_xxx 分支
  3. 提交代码
  4. 新建 Pull Request // EComponent 一参同 Component,二参为可选字符串,避免命名冲突。 properties: { dataA: Number, dataB: { type: String, required: true // ts 特性 不写默认 false }, funcA: Function, // ts 特性 简写默认为可选传入属性 funcB: { type: Function, required: true } }, bindData: { //再次强调,响应式数据格式必须为函数返回,且为可观察对象,否则值非自动更新,建议使用 mobx reactiveDataA: () => storeA.fieldX, reactiveDataB: () => storeB.fieldY },
3.0.8-beta3

4 years ago

3.0.8-beta2

4 years ago

3.0.8-beta1

4 years ago

3.0.2

4 years ago

3.0.1

4 years ago

3.0.7

4 years ago

3.0.5

4 years ago

3.0.4-rc

4 years ago

1.0.9

4 years ago

3.0.0

4 years ago

1.0.10

4 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago