渲染前的准备
渲染前的准备,在 Vue 渲染框架下,会先安装所有所支持的默认组件,比如 Mask,Header 等,这些组件支持 XML 默认模版的 Vue 框架下的渲染,详情可见 main.ts 中,maskInstall 与 install,这两个函数将平台内部支持的组件进行了注册,随后将整个 Vue 挂载为运行时 App,随后进行初始化。
渲染的起步
OioProvider 方法是整个应用的入口,我们忽略掉一些配置方
法,将注意力集中到 initializeServices
从名称中我们可以看出来内部保存的都是初始化服务,其中提供了渲染服务等,我们当前使用的是 Vue 框架,所以当前其渲染的 Root 节点为 Vue, 以此,我们视野可以暂时转移到 admin-base
中的 Root.vue
以及 RootWidget
上, 其实现了整个 Vue 框架下的 Root 节点如何渲染,其中定义了多个 widget,比如登陆页,首页,忘记密码已经重置密码等页面, 在本文中我们着重关注渲染首页的能力,RootWidget
将 DefaultMetadataMainViewWidget
作为渲染 Props
中的 page
即首页提供给下层组件使用,
渐入佳境
DefaultMetadataMainViewWidget
从名称中可以看到,其为元数据主视图,主要做两件事,
1:提供 Mask 的渲染
2:提供元数据上下文初始化
该组件主要通过观察路由变化触发上面两个动作,当路由发生变化,该组件 reloadPage
将被调用,reloadPage
方法通过组装路由参数构成一个唯一 key 向后段查询当前路由所对应的渲染信息, 随后将获取到的信息进行处理,初始化,即 元数据上下文初始化
,
在初始化后,会将获取到的数据注入到 MetadataViewWidget
中,
Mask 的渲染
关于 Mask 的渲染,在获取到数据后,将生成 maskTemplate,并将其赋值, DefaultMetadataMainView.vue
文件将渲染该模板,并渲染到页面中
数据的变更
当上面两件任务完成后,将开始主视图的渲染,
上文提到,DefaultMetadataMainViewWidget
只负责 mask 的渲染和上下文的初始化,所以 DefaultMetadataMainViewWidget
通过触发事件的方式来实现主视图的渲染, DefaultMetadataMainViewWidget
将必要信息作为事件参数触发,MultiTabsContainerWidget
接收到 reloadMainViewCallChaining
事件后,开启主视图渲染,
MultiTabsContainerWidget
会刷新运行时上下文,即 refreshRuntimeContext
,该方法将尝试查询并通过 createOrUpdateEnterTab
方法创建 Tab 页,createOrUpdateEnterTab
最终生成一个 MultiTabItem
格式的对象,该对象描述了 Tab 的相关信息,随后调用 createTabContainerWidget
创建 tab 的容器即新建了一个 MultiTabContainerWidget
组件即单个 tab 的容器,随后调用 setActiveTabItem
, 并获取其绑定的 Vue 组件,并将其组件放置在 KeepAlive
内部,触发更新,
主视图的渲染
MultiTabContainerWidget
继承自MetadataViewWidget
,
当 MetadataViewWidget
数据发生变更, 其绑定的 Vue 组件将解析 viewTemplate
, 获取到与该模板 dslNodeType
想匹配的 Vue 组件,当前例子中为 View.vue
,
随后 View.vue
开始渲染,View.vue
文件只是一个纯粹的容器,
当 View.vue
被挂载时,其内部的 template
属性包含了整个页面的描述信息,View.vue
需要做的就是将这个 template
翻译并渲染成 DOM 展现在浏览器上,
渲染整个页面
当 View.vue
被挂载时,其内部的 template
属性包含了整个页面的描述信息,View.vue
主要做了两个事情,一:将 template 中的 widget
转换为组件,二:根据当前的 template
信息生成 slot
信息,
const currentSlots = computed<Slots | undefined>(
() =>
DslRender.fetchVNodeSlots(props.dslDefinition) ||
(Object.keys(context.slots).length ? context.slots : undefined)
);
const renderWidget = createCustomWidget(InternalWidget.View, {
...context.attrs,
type: props.type || viewType.value,
template: props.dslDefinition,
metadataHandle: props.metadataHandle || metadataHandle.value,
rootHandle: props.rootHandle || rootHandle.value,
parentHandle: props.parentHandle || parentHandle.value,
slotName: props.slotName,
inline: inlineProp
} as ViewWidgetProps);
生成这两部分信息后,View.vue
会将这两部分挂载到页面上,这两部分从代码中可以看出,主要靠 fetchVNodeSlots
,createCustomWidget
两个函数,
export function createCustomWidget(
widget: string,
props: CustomWidgetProps
): RenderWidget | undefined
public static fetchVNodeSlots(dsl: DslDefinition | undefined, supportedSlotNames?: string[]): Slots | undefined
从函数名称中我们可以看出,这两个函数一个会去创建 widget
,一个会创建 slot
,这就不得不提到 dsl-render.ts
dsl-render
从名称中我们可以看出 dsl-render.ts
是专门用来渲染 DSL 的,其内部通过 fetchComponent
函数返回不同 dslType
对应的 Vue 组件,通过 fetchVNodeSlots
去构建 Vue 插槽,当组件开始渲染时,UseWidgetTagMixin
将混入各布局组件,通过基类 VueWidget
所提供的 render
能力将自己渲染成为容器,并且将 dslDefinition
转换为 slot
,将两者整合并挂载。此来层层向下进行渲染。渲染完成后Vue组件将触发自身的生命周期钩子,在VueWidget
中,其将Widget
组件自身的生命周期混入Vue生命周期,在Vue组件触发钩子后,将带动Wiget
生命周期钩子的触发。以表格页面为例,将获取 TableView
组件,并渲染至 View.vue
中,随后触发 $$mounted
钩子。
数据的获取
当 TableView
组件被挂载到页面时,触发 mounted
生命周期钩子,并在基类 BaseView
中发出 currentMountedCallChaining
事件, BaseElementViewWidget
监听到后,调用 mountedProcess
,最终调用到 BaseElementListViewWidget
,开始进行数据源请求,fetchData
, 获取到数据后,调用 reloadDataSource
数据的变更
当搜索组件内部数据发生变更,将触发 SearchWidget.ts
中 onSearch 事件,该事件将组装请求体,并且将请求体作为事件参数传递,外层的 SearchView
接收到该事件后会进一步向外传播事件,并最终传递到 TableWidget.ts
,该组件开始 refreshProcess
刷新进程,该方法实际动作基本由基类 BaseElementListViewWidget
提供,
该功能会以新数据向后端进行请求,随后将数据进行存储,触发子组件的更新,其核心逻辑类似于上一点中提到的数据的获取,不同的点在于触发点的区别,一个由mounted
钩子触发,一个则由onSearch
事件触发
Oinone社区 作者:liji2原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/20732.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验