amis源码 数据域 数据链解析

注意: 看此篇文章前需要你对react mobx-state-tree有一定了解,如果不了解请先看这篇文章React 之 mobx-state-tree(Redux替代品) 状态管理-CSDN博客

store的定义(mobx)

数据域基本是在store中维护的:
amis-core/src/store文件夹是mobx 定义,这里主要介绍几个比较重要的:

1.node.ts定义了基础model:

其他所有store定义基本都是基于此model定义进行.named, .props, .views, .actions拓展的

export const StoreNode = types
  .model('StoreNode', {  id: types.identifier,  path: '',  storeType: types.string, disposed: false,  parentId: '', childrenIds: types.optional(types.array(types.string), [])  })
  .views(self => { })
  .actions(self=>{ })

2-1.index.ts中:

定义了RendererStore渲染器store的形状 和 addStore、removeStore、get stores来维护组件store:

export const RendererStore = types
  .model('RendererStore', {
    storeType: 'RendererStore'
  }).views(  get stores() {
      return getStores();
    } )
  .actions(self =>({
    addStore(store){ //若是RootStore,创建RootStore定义的model,否则找到对应类型定义的model然后再创建
      if (store.storeType === RootStore.name) {
        return addStore(RootStore.create(store, getEnv(self)));//创建model
      }
      const factory = find( allowedStoreList, item => item.name === store.storeType  )!;
      return addStore(factory.create(store as any, getEnv(self)));//创建model
    }
})

2-2.RendererStore的使用:

amis.embed时会根据amisEnv.session从缓存中获取,缓存中没有则创建新的RendererStore

amis-core/src/index.tsx(创建了RendererStore的mobx树):   

import {RegisterStore, RendererStore} from './store';
render(){
 let store = stores[options.session || 'global'];
 if (!store) {
   store = RendererStore.create({}, options);   //
}
  (window as any).amisStore = store; // 为了方便 debug.  rootStore
  //...省略
}

后续渲染组件Component时,会使用amisStore.addStore()创建组件store。

3.iRenderer.ts

iRenderer基于node.ts基础store拓展而来。增加了data prop和对data的处理action

service.ts、table.ts、combo.ts、list.ts均是由iRenderer.ts拓展而来

crud.ts、form.ts、modal.ts、root.ts均是由service.ts拓展而来

export const iRendererStore = StoreNode.named('iRendererStore')
  .props({
    data: types.optional(types.frozen(), {}),
    pristine: types.optional(types.frozen(), {}),
    action: types.optional(types.frozen(), undefined),
    dialogOpen: false,
    dialogData: types.optional(types.frozen(), undefined),
    drawerOpen: false,
    drawerData: types.optional(types.frozen(), undefined)
  })
  .views(self => ({
    getValueByName(name: string, canAccessSuper: boolean = true) {
      return getVariable(self.data, name, canAccessSuper);
    },
    getPristineValueByName(name: string) {
      return getVariable(self.pristine, name, false);
    }
  }))
  .actions(self => {
    return {
      setTopStore(value: any) {
        top = value;
      },

      initData(data: object = {}, skipSetPristine = false) {
        self.initedAt = Date.now();
        if (self.data.__tag) {
          data = injectObjectChain(data, self.data.__tag);
        }

        !skipSetPristine && (self.pristine = data);
        self.data = data;
      },

      reset() {
        self.data = self.pristine;
      },

      updateData(
        data: object = {},
        tag?: object,
        replace?: boolean,
        concatFields?: string | string[]
      ) {
        if (concatFields) {
          data = concatData(data, self.data, concatFields);
        }

        const prev = self.data;
        let newData;
        if (tag) {
          let proto = createObject((self.data as any).__super || null, {
            ...tag,
            __tag: tag
          });
          newData = createObject(proto, {
            ...(replace ? {} : self.data),
            ...data
          });
        } else {
          newData = extendObject(self.data, data, !replace);
        }

        Object.defineProperty(newData, '__prev', {
          value: {...prev},
          enumerable: false,
          configurable: false,
          writable: false
        });

        self.data = newData;
      },
    };
  });

4.root.ts

基于service.ts的store拓展而来,主要做顶级数据域。

主要在amis-core/src/RootRenderer中进行了初始化顶级数据域topStore(RootStore类型的mobx树),设置amisProps.data 为顶级数据域

export const RootStore = ServiceStore.named('RootStore')
  .props({
    runtimeError: types.frozen(),
    runtimeErrorStack: types.frozen(),
    query: types.frozen()
  })
  .volatile(self => {
    return {
      context: {}
    };
  })
  .views(self => ({
    get downStream() {
      let result = self.data;

      if (self.context || self.query) {
        const chain = extractObjectChain(result);
        self.context && chain.unshift(self.context);
        self.query &&
          chain.splice(chain.length - 1, 0, {
            ...self.query,
            __query: self.query
          });

        result = createObjectFromChain(chain);
      }

      return result;
    }
  }))

@Renderer和@FormItem中storeType属性,会创建新store

@Renderer和@FormItem中指定了storeType的会通过WithStore来创建store和初始化数据域,并通过props传递下store和store.data。

如果没有新的指定了storeType的组件覆盖,那么子组件的props.store和props.data都是复用的父组件

ps:如果指定了isolateScope为true的还会封装一层Scoped : config.component = Scoped(config.component, config.type);

在amis/src/renderers下

像Chart.tsx、CRUD、Dialog、Drawer、Page、App、Service中均指定了storeType:

@Renderer({ type: 'chart', storeType: ServiceStore.name })

@Renderer({ type: 'crud', storeType: CRUDStore.name, isolateScope: true})

@Renderer({ type: 'dialog', storeType: ModalStore.name, storeExtendsData: false, isolateScope: true, shouldSyncSuperStore: ()=>{} })

@Renderer({ type: 'page', storeType: ServiceStore.name, isolateScope: true})

@Renderer({ type: 'app', storeType: AppStore.name })

@Renderer({ type: 'service', storeType: ServiceStore.name, isolateScope: true, storeExtendsData: (props: any) => (props.formStore ? false : true) })

@FormItem({ type: 'combo', storeType: ComboStore.name, ...省略 })

Card、Avatar、input-text 等其他大部分组件则只有type属性,不会创建store

amis-core/src/factory.tsc中对@Renderer装饰器的处理逻辑如下:

判断config.storeType存在,则使用WithStore暴露的HocStoreFactory包装一层进行处理。

amis-core/src/renderers/Item.tsx中@FormItem的处理如下:

判断config.storeType存在,则使用WithStore暴露的HocStoreFactory包装一层进行处理。

amis-core/src/WithStore.tsx:

用于@FormItem和@Renderer中指定了storeType的store(mobx树)创建(大部分都未指定,只有一部分指定了)

同时会props传递data={this.store.data}, store={this.store}, scope={this.store.data}, render={this.renderChild}下去

比如以下俩个例子(Form和Page):

一、Form中FormItem元素渲染 amis-core/src/renderers/Form.tsx:

1.@Renderer指定了storeType

@Renderer({ type: 'form', storeType: FormStore.name, isolateScope: true,  ...省略, shouldSyncSuperStore: ()=>{} })

WithStore会创建新store(FormStore类型的mobx树)并初始化数据域,并通过props传递下去(data={this.store.data},store={this.store},scope={this.store.data},render={this.renderChild})

2-1.Form.tsx中renderChild方法(form.body中formItem渲染)如下:

subProps中包含1.form的data   2. onChange方法(修改form的data)

const { render } = this.props;

const form = this.props.store; //WithStore传递来的当前form store

const subProps = {

    formStore: form, 

    data: store.data, //WithStore传递来的当前form store.data

    onChange: this.handleChange}

return render(`${region ? `${region}/` : ''}${key}`, subSchema, subProps); 

//此render是WithStore.tsx中的renderChild方法: 进行了预处理。

      renderChild(
        region: string,
        node: SchemaNode,
        subProps: {
          data?: object;
          [propName: string]: any;
        } = {}
      ) {
        let {render} = this.props;

        return render(region, node, {
          data: this.store.data,
          dataUpdatedAt: this.store.updatedAt,
          ...subProps,
          scope: this.store.data,
          store: this.store
        });
      }

//此render是SchemaRenderer.tsx中的renderChild方法

2-2.Form.tsx中handleChange核心部分如下:
  handleChange( value: any, name: string, submit: boolean, changePristine = false) {

    const {store, formLazyChange, persistDataKeys} = this.props;

store.changeValue(name, value, changePristine);//WithStore传递来的当前form store

//...省略

 }

3.SchemaRenderer.tsx中renderChild方法如下:

进行预处理向下传递了相关参数 和 subProps.data(即formStore.data) || rest.data(即topStore.downStream), 最终调用了父组件render(即Root.tsx中renderChild方法,最终走<SchemRenderer>识别并渲染FormItem子组件)

4.FormItem中:

FormItem可以调用props.onChange修改form数据域(父组件)的值,通过props.data可以获取form数据域(父组件)

如果没有新的指定了storeType的组件覆盖,那么子组件的props.store和props.data都是复用的父组件

二、Page中body子元素渲染(amis/src/renderers/Page.tsx):

也有onChange,传递给子组件,改变page的数据域。但是subProps 没有传递data数据域

  const subProps = { 

      onAction: this.handleAction,

      onChange: this.handleChange,

      onBulkChange: this.handleBulkChange,

  };

  {(Array.isArray(regions) ? ~regions.indexOf('body') : body)          

      ? render('body', body || '', subProps)

   : null}

  handleChange(  value: any,  name: string,  submit?: boolean,  changePristine?: boolean ) {

    const {store, onChange} = this.props;

    if (typeof name === 'string' && name) {

      store.changeValue(name, value, changePristine);//WithStore传递来的当前page store

    }

    //向上派送

    onChange?.apply(null, arguments); //onChange是父级props传递来的

  }

//此render是WithStore.tsx中的renderChild方法: 进行了预处理。

子组件中的props.data是在WithStore.tsx中render调用时,向下传递的,如下所示。

      renderChild(
        region: string,
        node: SchemaNode,
        subProps: {
          data?: object;
          [propName: string]: any;
        } = {}
      ) {
        let {render} = this.props;

        return render(region, node, {
          data: this.store.data,
          dataUpdatedAt: this.store.updatedAt,
          ...subProps,
          scope: this.store.data,
          store: this.store
        });
      }

//此render是SchemaRenderer.tsx中的renderChild方法

SchemaRenderer.tsx中renderChild方法如下:

Amis数据链实现

数据链的实现主要是通过createObject构建将__proto__指向父组件数据并存到__super上。

createObject做了俩件事

1.用Object.create(superProps)创建一个新对象,将原型(__proto__)指向superProps,同时还附加了__super。

取值时向上通过原型链可以取到所有父级值(还可以通过__super获取父数据域),修改时只修改到当前对象中。

2.将props所有值赋值给新对象。  

Object.keys只能取到props的key(不检测原型链)。   in 判断为true(检测原型链)  hasOwnProperty 为false(不检测原型链)

amis-core/src/utils/utils/object.ts:

// 方便取值的时候能够把上层的取到,但是获取的时候不会全部把所有的数据获取到。

export function createObject(
  superProps?: {[propName: string]: any},
  props?: {[propName: string]: any},
  properties?: any
): object {
  if (superProps && Object.isFrozen(superProps)) {
    superProps = cloneObject(superProps);
  }

  const obj = superProps
    ? Object.create(superProps, {
        ...properties,
        __super: {
          value: superProps,
          writable: false,
          enumerable: false
        }
      })
    : Object.create(Object.prototype, properties);

  props &&
    isObject(props) &&
    Object.keys(props).forEach(key => (obj[key] = props[key]));

  return obj;
}

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李庆政370

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值