# API

# 基础类型

type State = Record<string, any>

type Events = Record<string, (...args: any[]) => void>

type Methods = Record<string, (...args: any[]) => any>

type BlockDeclare = Partial<{ 
  state:  State
  events: Events
  methods: Methods
}>

# createBlock

  • 类型:<T extends BlockDeclare>(name: string) => CreatedBlock<T['state'], T['events'], T['methods'], T['exports']>

  • 说明:创建一个 Block,接收全局唯一的 block 名作为参数,返回一个CreatedBlock实例。

  • 示例:

    import { createBlock } from "@rallie/block";
    
    interface MyBlock {
      state: {
        count: number,
        user: {
          name: string
        }
      },
      events: {
        incrementCount: () => void
      },
      methods: {
        login: () => void,
        logout: () => void
      }
    }
    
    const myBlock = createBlock<MyBlock>("my-block");
    

# CreatedBlock

# name

  • 类型:string
  • 说明: Block 的名称

# state

  • 类型:State
  • 说明: Block 的状态,是只读的,要修改 Block 的状态必须使用setState方法

# methods

  • 类型:Methods

  • 说明:Block 的方法调用器,是只读的,要给 Block 添加方法,必须使用addMethods方法

  • 示例:

    // 添加方法
    block.addMethods({
      login: async () => {
        const success = await requestLogin();
        return success;
      },
    });
    
    // 调用方法
    block.methods.login().then((success) => {
      if (success) {
        alert("Login successfully");
      }
    });
    

# events

  • 类型:Events

  • 说明:Block 的事件调用器,是只读的,要给 Block 添加事件监听,必须使用listenEvents方法

  • 示例:

    // 监听事件
    block.listenEvents({
      themeChange: (themeColor) => {
        doChangeTheme(themeColor);
      },
    });
    
    // 触发事件
    block.events.themeChange("red");
    

# initState

  • 类型:(value: State, isPrivate: boolean = false) => CreatedBlock
  • 说明:初始化 Block 的状态。一个 Block 只能初始化一次状态,多次调用initState会抛出错误。如果希望状态不能被其他 Block 直接修改,第二个参数可以传入true,表示将状态初始化为私有状态。

# setState

  • 类型:((action: string, state: State) => void | Promise<void>) => Promise<void>

  • 说明:修改 Block 的状态,第一个参数是对本次操作的描述

  • 示例:

    // 同步修改状态
    block.setState("add the count synchronously", (state) => {
      state.count++;
    });
    
    // 异步修改状态
    await block.setState("add the count asynchronously", async (state) => {
      const userInfo = await requestLogIn();
      state.user = userInfo;
    });
    

# watchState

  • 类型:((state: State) => any)) => Watcher

  • 说明:监听 Block 的状态变化,基于@vue/reactivityeffect方法实现的。具体使用方式见示例

  • 示例:

    • 链式调用监听状态

      // 监听状态
      const unwatch = block
        .watchState((state) => state.count)
        .do((newCount, oldCount) => {
          console.log(newCount, oldCount);
        });
      
      // 取消监听
      unwatch();
      
    • watchEffect

      // 监听状态
      const watcher = block.watchState((state) => {
        console.log(state.count, state.user);
      });
      
      // 取消监听
      watcher.unwatch();
      

# listenEvents

  • 类型:(events: Partial<Events>) => (eventName?: string) => void
  • 说明:给 Block 添加事件监听,返回值是用来取消事件监听的函数。从监听回调的this上下文中,可以获得事件调用方的block名
  • 示例:
    • 监听事件:
      const cancelEvents = block.listenEvents({
        changeTheme(this: { trigger: string }, themeColor) {
          console.log(`事件调用方是${this.trigger}`)
          doChangeTheme(themeColor);
        },
        changeLanguage: (lang) => {
          doChangeLanguage(lang);
        },
      });
      
    • 取消监听所有事件:
      cancelEvents();
      
    • 取消监听单个事件
      cancelEvents("changeTheme");
      

# addMethods

  • 类型:(methods: Partial<Methods>) => (methodName?: string) => void
  • 说明:给 Block 添加方法,返回值是用来移除方法的函数。从方法回调的this上下文中,可以获得事件调用方的block名
  • 示例:
    • 添加方法:
      const removeMethods = block.addMethods({
        async logIn(this: { trigger: string }) => {
          console.log(`事件调用方是${this.trigger}`)
          await requestLogIn();
        },
        logOut: async () => {
          await requestLogOut();
        },
      });
      
    • 移除所有方法:
      removeMethods();
      
    • 移除单个方法
      removeMethods("logIn");
      

# load

  • 类型:(name: string) => Promise<void>
  • 说明:加载 Block 的资源,参数是要加载的 Block 的名字

# activate

  • 类型:(name: string) => Promise<void>
  • 说明:激活 Block,参数是要激活的 Block 的名字

# connect

  • 类型:<T extends BlockDeclare>(name: string) => ConnectedBlock<T>
  • 说明:连接 Block,接收要连接的 Block 的名字作为唯一参数,返回一个可以使用被连接 Block 的状态,事件,方法的ConnectedBlock
  • 示例:
    interface RemoteBlock {
      state: {
        count: number,
        user: {
          name: string
        }
      },
      events: {
        incrementCount: () => void
      },
      methods: {
        login: () => void,
        logout: () => void
      },
      exports: {
        text: string
      }
    }
    const connectedBlock = block.connect<RemoteBlock>("remote");
    

connect方法将返回一个ConnectedBlock。其状态,事件和方法相关的API是CreatedBlock的API的子集,包括namestateeventsmethodssetStatewatchStatelistenEvents

TIP

如果连接的 Block 将状态声明为私有状态,那么通过ConnectedBlock调用setState更改 Block 的状态将报错

以上属性和方法的使用方式与 CreatedBlock 的使用方式是完全一致的

TIP

对于一个 ConnectedBlock,要使用其状态,调用其方法,导入其暴露的对象,应该保证其状态已经被初始化,方法已经被添加,对象已经被导出。而 connect 操作并不会加载和激活要连接的 Block,因此你应该将要连接的 Block 声明为当前 Block 的关联或依赖,或者手动加载或激活要连接的 Block

# run

  • 类型:(callback: (env: Env) => void | Promise<void>) => Promise<void>
  • 说明:执行传入的回调函数。可以在回调参数中获取当前 Block 的运行环境,详见Env

# Env

Block 的运行环境对象,可以在run方法的回调中获得

# isEntry

  • 类型:boolean
  • 说明:是否是入口环境。如果一个 Block 是应用集群中第一个被创建的应用,则在执行 Block 的run方法时,其回调参数env.isEntrytrue,否则为false

# freeze

  • 类型:() => void
  • 说明:冻结当前运行环境,只有当env.isEntrytrue时,该方法才有效。当运行环境被冻结后,非入口环境的 Block 应用的中间件和配置将不生效

# unfreeze

  • 类型:() => void
  • 说明:解冻当前运行环境,只有当env.isEntrytrue时,该方法才有效。

# use

  • 类型:(middleware: (ctx: Context, next: () => Promise<void>) => void) => void
  • 说明:应用资源加载中间件,使用方式参考中间件,context 的类型为:
    interface Context {
      name: string; // 要加载的Block的名字
      conf: ConfType; // 运行环境的配置,参考env.conf
      loadScript: (
        script: Partial<HTMLScriptElement> | string | HTMLScriptElement
      ) => Promise<void>; // 插入script脚本的方法
      loadLink: (
        link: Partial<HTMLLinkElement> | string | HTMLLinkElement
      ) => Promise<void>; // 插入Link标签的方法
      [other: string]: any; // 中间件可以给context添加方法和属性
    }
    

# conf

  • 类型:
    interface ConfType {
      assets?: Record<
        string,
        {
          js: Array<string>;
          css: Array<string>;
        }
      >;
      [other: string]: any;
    }
    
  • 说明:运行环境的全局配置,可以通过env.config进行配置。其中assets属性是应用集群的资源路径信息,rallie 的洋葱圈中间件模型的最内层中间件会根据 Block 的名字在assets中查找应用资源并加载

# config

  • 类型:(conf: ConfType) => void
  • 说明:对运行环境进行配置。在配置assets时,配置的信息将会追加,而不会覆盖。
    env.config({
      assets: {
        bar: {
          js: ["bar.js"],
        },
      },
    });
    env.config({
      assets: {
        foo: {
          js: ["foo.js"],
        },
      },
    });
    console.log(env.conf.assets);
    /* 打印结果为:
      {
        bar: {
          js: ['bar.js']
        },
        foo: {
          js: ['foo.js']
        }
      }
    */