import { HookType, Options } from "../types/component"; import defineReactive from "../common/define-reactive"; import * as Component from "../types/component"; import factory from "../common/factory"; import { genSerialNumber } from "../common/util"; import Context from "../common/context"; import { DATA_UID } from "../constant/context"; import nextTick from "../common/next-tick"; type CreateCallback = (el: HTMLElement | undefined) => void; export default abstract class BaseComponentRender { // 当前页面的状态 public $status: HookType = HookType.beforeCreate; // 渲染当前页面的容器 public $el: HTMLElement | undefined; // 页面参数 public $options: Options; protected $index: number; public $parent: BaseComponentRender; public $children: Array; public $context: Context; public exportJson: () => Options; /** * 创建组件对象 * @param callback */ protected abstract create(callback: CreateCallback): void; /** * 渲染 */ protected abstract render(): void; constructor(context: Context, options: Options, parent: BaseComponentRender, index?: number) { options.uid = options.uid || genSerialNumber(); this.$context = context; this.$options = this.defineReactive(options); this.$parent = parent; this.$index = index || parent.size(); this.$children = []; this.exportJson = () => { return options; }; this.init(); } /** * 触发当前页面的生命周期 * @param type */ private invokeHook(type: HookType) { this.$status = type; this.$context.invokeMixin(type); if (type in this && typeof (this as any)[type] === "function") { (this as any)[type].call(this); } } /** * 触发自定义扩展组件的事件 * @param type * @param params */ private invokeEvent(type: string, ...params: any[]) { // TODO: 执行component.events 下的方法 } private editorAdaptor(el: HTMLElement) { if (this.$context.$mode === "edit") { el.setAttribute(DATA_UID, this.$options.uid); } } /** * 组件初始化 */ private init() { // 加载进度 let loadingProgressFn = this.$context.createLoading(this.$options.uid); this.invokeHook(HookType.beforeCreate); this.create((el: HTMLElement | undefined) => { this.$el = el; // 处理编辑器的逻辑 this.$el && this.editorAdaptor(this.$el); this.invokeHook(HookType.created); let promiseList: Array> = []; // 依次构建内部组件 (this.$options.children || []).forEach((options: Component.Options, index) => { promiseList.push(factory.createComponent(this.$context, options, this, index)); }); // 子组件都创建了,执行后续逻辑 Promise.all(promiseList).then(() => { // 触发beforeMount this.invokeHook(HookType.beforeMount); // 渲染逻辑 this.render(); // 触发render this.invokeHook(HookType.render); // 挂载元素 this.$parent.insertBefore(this, this.$index); Promise.resolve().then(() => { loadingProgressFn(); // 触发mounted this.invokeHook(HookType.mounted); }); }); }); } /** * 设置响应式参数 * @param options */ protected defineReactive(options: Options) { const keys = ["attribute", "style", "props", "source"]; const defineAttributeReactive = (target: any) => { return defineReactive(target, { set: (target: any, key: PropertyKey, value: any, receiver: any) => { if (this.$status === HookType.beforeCreate || key === "children") { return; } // 拦截重新对keys中的属性直接赋值 if (keys.indexOf(key as any) > -1) { Reflect.set(target, key, defineAttributeReactive(value), receiver); } this.invokeHook(HookType.beforeUpdate); nextTick(this); } }); }; let $options = defineAttributeReactive(options); // keys中的属性对象也需要设置为响应式 keys.forEach((key) => { $options[key] = defineAttributeReactive($options[key]); }); return $options; } /** * 更新 */ public update() { this.render(); // todo: 增加对component.watch的执行 this.invokeHook(HookType.updated); } /** * children数量 */ public size() { return this.$children.length; } /** * 添加子节点 * @param component * @param index */ public insertBefore(component: BaseComponentRender, index?: number) { if (this.$options.slot !== true) { this.$parent && this.$parent.insertBefore(component, index); return; } index = index || 0; if (index > this.$children.length - 1) { this.$children.push(component); this.$el && this.$el.appendChild(component.$el as HTMLElement); } else { this.$el && this.$el.insertBefore(component.$el as HTMLElement, this.$children[index].$el!); this.$children.splice(index, 0, component); } } /** * 删除当前组件 */ public remove() { this.invokeHook(HookType.beforeDestroy); // 触发所有节点的删除逻辑 this.$children.forEach((child) => { child.remove(); }); // 从父级中移除引用 Promise.resolve().then(() => { this.$parent.$children = this.$parent.$children.filter((child) => child !== this); }); this.$children = []; this.invokeHook(HookType.destroyed); if (this.$el) { this.$el.remove(); } } /** * 强制刷新 */ public forceUpdate() { this.render(); this.invokeHook(HookType.render); } /** * 触发事件 * @param type * @param params */ public emit(type: string, ...params: any[]) { this.invokeEvent(type, ...params); // 事件逐级冒泡 if (this.$parent && this.$parent instanceof BaseComponentRender) { this.$parent.emit(type, ...params); } } }