从一个方法开始:浅析页面渲染流程

渲染前的准备

渲染前的准备,在 Vue 渲染框架下,会先安装所有所支持的默认组件,比如 Mask,Header 等,这些组件支持 XML 默认模版的 Vue 框架下的渲染,详情可见 main.ts 中,maskInstall 与 install,这两个函数将平台内部支持的组件进行了注册,随后将整个 Vue 挂载为运行时 App,随后进行初始化。

渲染的起步

OioProvider 方法是整个应用的入口,我们忽略掉一些配置方
法,将注意力集中到 initializeServices 从名称中我们可以看出来内部保存的都是初始化服务,其中提供了渲染服务等,我们当前使用的是 Vue 框架,所以当前其渲染的 Root 节点为 Vue, 以此,我们视野可以暂时转移到 admin-base中的 Root.vue以及 RootWidget上, 其实现了整个 Vue 框架下的 Root 节点如何渲染,其中定义了多个 widget,比如登陆页,首页,忘记密码已经重置密码等页面, 在本文中我们着重关注渲染首页的能力,
RootWidgetDefaultMetadataMainViewWidget作为渲染 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低代码应用平台体验

(0)
liji2的头像liji2数式员工
上一篇 2025年3月19日 am10:54
下一篇 2025年3月21日 am10:28

相关推荐

  • oio-empty-data 空数据状态

    何时使用 当目前没有数据时,用于显式的用户提示。 初始化场景时的引导创建流程。 API 参数 说明 类型 默认值 版本 description 自定义描述内容 string | v-slot – image 设置显示图片,为 string 时表示自定义图片地址 string | v-slot false imageStyle 图片样式 CSSProperties –

    2023年12月18日
    1.3K00
  • 表格主题配置(v4)

    TableThemeConfig /** * 表格主题配置 */ export interface TableThemeConfig { border: boolean | string; stripe: boolean; isCurrent: boolean; isHover: boolean; /** * 表格列主题配置 */ column: Partial<TableColumnThemeConfig>; } /** * 表格列主题配置 */ export interface TableColumnThemeConfig { /** * <h3>最小宽度</h3> * <ul> * <li>boolean: enabled column width auto compute</li> * <li>number: using css width (default: px)</li> * <li>string: using css width</li> * <li> * object: auto compute width for label by default function * <ul> * <li>min: min min width (default: 120)</li> * <li>max: max min width (default: 432)</li> * <li>chineseWidth: chinese width (default: 14 -> fontSize: 14px)</li> * <li>otherWidth: non chinese width (default: 9 -> fontSize: 14px)</li> * <li>sortableFixWidth: sortable handle width (default: 40)</li> * <li>nonSortableFixWidth: non sortable fix width (default: 22)</li> * </ul> * </li> * <li>function: auto compute width for label by function</li> * </ul> */ minWidth: boolean | number | string | Partial<TableColumnMinWidthComputeConfig> | TableColumnMinWidthComputeFunction; /** * 操作列 */ operation: { /** * 宽度 (default: 165) */ width?: number | string; /** * 最小宽度 (default: 120) */ minWidth?: number | string; }; } export interface TableColumnMinWidthComputeConfig { min: number;…

    2023年11月6日
    1.4K00
  • 自定义字段的数据联动

    某种情况下,开发人员期望自定以的字段发生变化后,需要修改其他的字段,这篇文章从两个维度来讲解如果处理数据的联动 界面设计器配置 1: 在界面设计器页面中的的组件区域找到自定义的字段,设计元件 2: 在模型区域,搜索提交方式,如果找到了,就把该字段拖拽进来, 如果找不到,就在元件中的组件区域,拖拽一个文本字段,按照下面的配置进行配置,然后保存 图一是找到了对应的字段图二是找不到对应的字段 【图一】 【图二】 图二的字段编码必须是constructDataTrigger 3: 从模型区搜索联动函数,将其拖拽进来 3: 从模型区搜索提交数据,将其拖拽进来4: 从模型区搜索提交字段,将其拖拽进来 5: 发布 (记得刷新页面哦) 最后再到对应的设计器页面,选中该字段,进行配置 提交方式为blur或者change , 需要开发者手动调用该方法 this.blur()或者this.change(value) // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // this.change(val) // this.blur() } } 联动函数就是要调用的后端函数 提交数据分为:变更字段 -> 发生变化后的字段当前视图字段 -> 当前视图所有的字段指定字段 -> 指定字段,如果配置的指定字段,那么提交字段的配置就要输入对应的字段 代码配置 平台也支持通过代码的方式修改字段 // 字段对应的ts文件 class MyField extends FormFieldWidget { onChangeValue(val) { // 修改字段本身的值 this.change(val) // 修改其他字段的值 this.formData.otherField = 'value' this.reloadFormData$.subject.next(true); } }

    2023年11月9日
    2.0K00
  • 如何通过浏览器开发者工具提高调试效率

    1.通过vue devtool查看vue组件和oinone组件的信息 平台字段、动作、视图组件都是以Widget结尾的,可以通过关键字快速找到组件 安装vue devtool插件 chrome安装最新版的vue devtool插件 谷歌应用商店插件地址,隐藏窗口需要在扩展程序的详情页额外设置才能使用该插件 安装好插件后,可以通过插件选中html页面中的元素查看相关信息 相关特性了解 组件自动创建的vue组件以组件的class类名命名,据此特性可以得出自定义组件的时候,可继承该命名的父类 一般oinone的组件以Widget后缀命名,也推荐大家也以此为后缀,方便在vue调试面板快速看出哪些是oinone组件 选中oinone组件后可以在右侧面板看到相关的组件信息,部分属性介绍 template属性为dsl的配置 activeRecords、formData、rootData、parentData、dataSource等属性为常用数据属性 action为动作的元数据 field为字段的元数据 2.直接在浏览器开发者工具的源码处调试源码 打开浏览器开发者工具,进入标签页源代码/来源(英文版为Source),win系统用快捷键ctrl+O,mac系统用快捷键cmd+O, 可以打开文件搜索面板,通过关键字搜索文件名找到代码文件,可直接在里面debug调试或者查看执行逻辑

    2024年9月9日
    1.7K00
  • 自定义表格支持合并或列、表头分组

    本文将讲解如何通过自定义实现表格支持单元格合并和表头分组。 点击下载对应的代码 在学习该文章之前,你需要先了解: 1: 自定义视图2: 自定义视图、字段只修改 UI,不修改数据和逻辑3: 自定义视图动态渲染界面设计器配置的视图、动作 1. 自定义 widget 创建自定义的 MergeTableWidget,用于支持合并单元格和表头分组。 // MergeTableWidget.ts import { BaseElementWidget, SPI, ViewType, TableWidget, Widget, DslRender } from '@kunlun/dependencies'; import MergeTable from './MergeTable.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MergeTableWidget' }) ) export class MergeTableWidget extends TableWidget { public initialize(props) { super.initialize(props); this.setComponent(MergeTable); return this; } /** * 表格展示字段 */ @Widget.Reactive() public get currentModelFields() { return this.metadataRuntimeContext.model.modelFields.filter((f) => !f.invisible); } /** * 渲染行内动作VNode */ @Widget.Method() protected renderRowActionVNodes() { const table = this.metadataRuntimeContext.viewDsl!; const rowAction = table?.widgets.find((w) => w.slot === 'rowActions'); if (rowAction) { return rowAction.widgets.map((w) => DslRender.render(w)); } return null; } } 2. 创建对应的 Vue 组件 定义一个支持合并单元格与表头分组的 Vue 组件。 <!– MergeTable.vue –> <template> <vxe-table border height="500" :column-config="{ resizable: true }" :merge-cells="mergeCells" :data="showDataSource" @checkbox-change="checkboxChange" @checkbox-all="checkedAllChange" > <vxe-column type="checkbox" width="50"></vxe-column> <!– 渲染界面设计器配置的字段 –> <vxe-column v-for="field in currentModelFields" :key="field.name" :field="field.name" :title="field.label" ></vxe-column> <!– 表头分组 https://vxetable.cn/v4.6/#/table/base/group –> <vxe-colgroup title="更多信息"> <vxe-column field="role" title="Role"></vxe-column> <vxe-colgroup title="详细信息"> <vxe-column field="sex" title="Sex"></vxe-column> <vxe-column field="age" title="Age"></vxe-column> </vxe-colgroup> </vxe-colgroup> <vxe-column title="操作" width="120"> <template #default="{ row, $rowIndex }"> <!– 渲染界面设计器配置的行内动作 –> <row-action-render :renderRowActionVNodes="renderRowActionVNodes" :row="row" :rowIndex="$rowIndex" :parentHandle="currentHandle" ></row-action-render> </template> </vxe-column> </vxe-table> <!– 分页 –> <oio-pagination :pageSizeOptions="pageSizeOptions" :currentPage="pagination.current"…

    2025年1月9日
    1.9K00

Leave a Reply

登录后才能评论