UNPKG

@v4fire/client

Version:

V4Fire client core library

194 lines (153 loc) 4.37 kB
/*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ import * as init from 'core/component/construct'; import type { VNode } from 'core/component/engines'; import type { RenderContext } from 'core/component/render'; import { $$ } from 'core/component/functional/const'; import type { ComponentField, ComponentInterface } from 'core/component/interface'; import type { FlyweightVNode } from 'core/component/functional/interface'; /** * Initializes a component from the specified VNode. * This function provides life-cycle hooks, adds classes and event listeners, etc. * * @param vnode * @param component - component instance * @param renderCtx - render context */ export function initComponentVNode( vnode: VNode, component: ComponentInterface, renderCtx: RenderContext ): FlyweightVNode { const {unsafe} = component, {data} = renderCtx; const flyweightVNode = Object.assign(vnode, {fakeInstance: component}); component.$renderEngine.patchVNode(flyweightVNode, component, renderCtx); // Attach component event listeners if (data.on != null) { for (let o = data.on, keys = Object.keys(o), i = 0; i < keys.length; i++) { const key = keys[i], fns = Array.concat([], o[key]); for (let i = 0; i < fns.length; i++) { const fn = fns[i]; if (Object.isFunction(fn)) { unsafe.$on(key, fn); } } } } const originalOnBindHook = unsafe.onBindHook; unsafe.onBindHook = onBindHook; init.createdState(component); return flyweightVNode; function onBindHook(): void { const el = component.$el; if (el == null) { unsafe.onUnbindHook(); return; } let oldCtx = el[$$.component]; // The situation when we have an old context of the same component on the same node: // we need to merge the old state with a new if (oldCtx != null) { if (oldCtx === component) { return; } if (component.componentName !== oldCtx.componentName) { oldCtx = undefined; delete el[$$.component]; } } if (oldCtx != null) { oldCtx.$componentId = component.componentId; // Destroy the old component oldCtx.onUnbindHook(); const props = component.$props, oldProps = oldCtx.$props, linkedFields = <Dictionary<string>>{}; // Merge prop values for (let keys = Object.keys(oldProps), i = 0; i < keys.length; i++) { const key = keys[i], linked = oldCtx.$syncLinkCache.get(key); if (linked != null) { for (let keys = Object.keys(linked), i = 0; i < keys.length; i++) { const link = linked[keys[i]]; if (link != null) { linkedFields[link.path] = key; } } } } // Merge field/system field values { const list = [ oldCtx.meta.systemFields, oldCtx.meta.fields ]; for (let i = 0; i < list.length; i++) { const obj = list[i], keys = Object.keys(obj); for (let j = 0; j < keys.length; j++) { const key = keys[j], field = <CanUndef<ComponentField>>obj[key]; if (field == null) { continue; } const link = linkedFields[key]; const val = component[key], oldVal = oldCtx[key]; const needMerge = unsafe.$modifiedFields[key] !== true && ( Object.isFunction(field.unique) ? !Object.isTruly(field.unique(component.unsafe, oldCtx)) : !field.unique ) && !Object.fastCompare(val, oldVal) && ( link == null || Object.fastCompare(props[link], oldProps[link]) ); if (needMerge) { if (Object.isTruly(field.merge)) { if (field.merge === true) { let newVal = oldVal; if (Object.isPlainObject(val) || Object.isPlainObject(oldVal)) { newVal = {...<object>val, ...oldVal}; } else if (Object.isArray(val) || Object.isArray(oldVal)) { newVal = Object.assign([], val, oldVal); } component[key] = newVal; } else if (Object.isFunction(field.merge)) { field.merge(component.unsafe, oldCtx, key, link); } } else { component[key] = oldCtx[key]; } } } } } } el[$$.component] = unsafe; originalOnBindHook.call(unsafe); } }