母版-布局-DSL 渲染基础(v4)

概述

不论是母版布局还是DSL,我们统一使用XML进行定义,可以更好的提供结构化表述。

参考文档:

下面文档中未介绍到的Mask母版Layout布局,可以去数据库中base库的表base_layout_definitionbase_mask_definitiontemplate字段查看

母版

确定了主题、非主内容分发区域所使用组件和主内容分发区域联动方式的页面配置。

母版内容分为主内容分发区域与非主内容分发区域。非主内容分发区域一般包含顶部栏、底部栏和侧边栏。侧边栏可以放置菜单,菜单与主内容分发区域内容进行联动。

image.png

默认母板

<mask>
    <multi-tabs />
    <header>
        <widget widget="app-switcher" />
        <block>
            <widget widget="notification" />
            <widget widget="divider" />
            <widget widget="language" />
            <widget widget="divider" />
            <widget widget="user" />
        </block>
    </header>
    <container>
        <sidebar>
            <widget widget="nav-menu" height="100%" />
        </sidebar>
        <content>
            <breadcrumb />
            <block width="100%">
                <widget width="100%" widget="main-view" />
            </block>
        </content>
    </container>
</mask>

该模板中包含了如下几个组件:

  • mask:母版根标签
  • multi-tabs:多选项卡
  • header:顶部栏
  • container:容器
  • sldebar:侧边栏
    • nav-menu:导航菜单
  • content:主内容
    • breadcrumb:面包屑
    • block:div块
    • main-view:主视图;用于渲染布局和DSL等相关内容;

母版将整个页面的大体框架进行了描述,接下来将主要介绍布局和DSL是如何在main-view中进行渲染的。关于自定义母版组件的相关内容 点击查看

布局

布局是将页面拆分成一个一个的小单元,按照从上到下、从左到右进行顺序排列

布局主要用于控制页面中元素的展示的相对位置,原则上不建议将元数据相关内容在布局中进行使用,可最大化布局的利用率。

默认表格视图(TABLE)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <xslot name="actions" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>

该模板中包含了如下几个组件:

  • view:视图;用于定义当前视图类型,不同的视图类型会有不同的数据交互,以及渲染不同的组件。
  • pack:容器类型相关组件。
  • element:元素组件;包含各种各样的组件,根据组件实现有不同的作用。
  • xslot:DSL插槽;用于将DSL中定义的模板分别插入到对应的槽中;

特别的,任何XML标签上的slot属性都具备DSL插槽的全部能力。当学习完DSL相关内容后,我们将会对DSL插槽有比较清晰的理解。

PS:在下面的内容中,将使用该布局进行描述。

DSL

准备工作

为了方便描述DSL和元数据之间的关系,我们需要先定义一个简单模型,这个模型里面包含字段和动作。这些通常是服务端定义的。(对服务端不感兴趣的同学可以跳过代码部分)

DemoModel.java
@Model.model(DemoModel.MODEL_MODEL)
@Model(displayName = "演示模型", labelFields = {"name"})
public class DemoModel extends IdModel {

    private static final long serialVersionUID = -7211802945795866154L;

    public static final String MODEL_MODEL = "demo.DemoModel";

    @Field(displayName = "名称")
    private String name;

    @Field(displayName = "是否启用")
    private Boolean isEnabled;
}
DemoModelAction.java
@Model.model(DemoModel.MODEL_MODEL)
@UxRouteButton(
        action = @UxAction(name = "redirectCreatePage", displayName = "创建", contextType = ActionContextTypeEnum.CONTEXT_FREE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
@UxRouteButton(
        action = @UxAction(name = "redirectUpdatePage", displayName = "编辑", contextType = ActionContextTypeEnum.SINGLE),
        value = @UxRoute(model = DemoModel.MODEL_MODEL, viewName = "演示模型form"))
public class DemoModelAction {

    @Action(displayName = "启用")
    public DemoModel enable(DemoModel data) {
        data.setIsEnabled(true);
        data.updateById();
        return data;
    }

    @Action(displayName = "禁用")
    public DemoModel disable(DemoModel data) {
        data.setIsEnabled(false);
        data.updateById();
        return data;
    }
}

上面的java代码定义了演示模型字段动作

  • 字段 field
    • id:ID 整数 Integer
    • name:名称 字符串 String
    • isEnabled:是否启用 布尔 Boolean
  • 动作 action
    • redirectCreatePage:创建;目标视图为"演示模型form" 跳转动作 ViewAction
    • redirectUpdatePage:编辑;目标视图为"演示模型form" 跳转动作 ViewAction
    • enable:启用 提交动作 ServerAction
    • disable:禁用 提交动作 ServerAction

根据上面的元数据内容,我们可以定义一个如下所示的DSL模板,来使用所有的元数据将其渲染到页面中:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

该模板中包含的标签含义:

  • view:视图;与布局模板中的view标签作用一致。
  • template:向DSL插槽中插入一个模板片段
  • field:字段元数据标签;data属性与元数据中字段API名称(field)对应。
  • action:动作元数据标签;name属性与元数据中动作API名称(name)对应。

DSL模板本质上是对布局模板的补充,它使用布局模板中已经定义好的DSL插槽,对其进行相应规则的合并替换,最终构成一个可被执行的视图模板再经过客户端渲染展示给用户。单独解释DSL模板的结构化含义是完全没有意义的。

接下来,我们会分别定义不同的DSL模板,并结合默认表格视图的布局模板进行详细说明。

模板编译

服务端模板编译基础

进入页面时,客户端将通过加载跳转动作(ViewActionLoad)接口获取对应的布局模板和DSL模板,得到经过服务端编译JSON数据,类似于如下数据结构:(DSL模板的JSON结构)

{
    "dslNodeType": "VIEW",
    "type": "TABLE",
    ......,
    "widgets": [
        {
            "dslNodeType": "TEMPLATE",
            "slot": "search",
            "widgets": [
                ......
            ]
        },
        {
            "dslNodeType": "TEMPLATE",
            "slot": "actions",
            "widgets": [
                ......
            ]
        }
    ]
}

服务端编译过程可以简单的理解为以下几个内容:(实际要复杂很多,在这里我们仅需要关注最终拿到的JSON结果即可)

  • XML转换为JSON结构
  • XML标签转换为dslNodeType属性,XML子标签转换为widgets属性,其他属性保持不变
  • field标签(字段)action标签(动作)对应的元数据补充完整。如上所示,我们仅定义了字段的data属性,返回的JSON中将包含字段动作的全部元数据。字段会补充字段类型、显示名称、是否存储等元数据属性,动作会补充动作类型、显示名称等元数据属性。

示例模板

表格视图-布局模板
<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <xslot name="actions" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <xslot name="fields" />
            <element widget="rowActions" slot="rowActions" />
        </element>
    </pack>
</view>
表格视图-DSL模板
<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="search">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="actions">
        <action name="redirectCreatePage" />
    </template>
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

以上所示的两个模板(布局模板DSL模板)会作为布局和DSL的模板合并相关内容介绍的演示模板。更多的布局模板点击查看

布局和DSL的模板合并

当我们从服务端获取了布局模板DSL模板对应的JSON后,客户端将对这两个JSON进行合并处理。

合并操作主要是将DSL模板中定义的根标签下定义的所有template标签,向布局模板中定义的DSL插槽进行属性合并和子节点替换。

标准合并

将上述两个模板进行合并后,将得到如下结果:(为了方便观察结构,将使用XML结构进行解释,实际运行时为JSON结构)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search">
                <!-- slot="search" -->
                <field data="id" invisible="true" />
                <field data="name" />
                <field data="isEnabled" />
            </element>
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar">
            <!-- slot="actions" -->
            <action name="redirectCreatePage" />
        </element>
        <element widget="table" slot="table">
            <element widget="expandColumn" slot="expandRow" />
            <!-- slot="fields" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,DSL模板定义的template标签下的内容被合并到了布局模板的对应位置中。接下来就可以进行模板渲染操作了。

子标签替换和属性合并

一般的,我们将处理布局模板中任何标签上的slot属性,以及xslot标签。

遵循就近原则,模板合并将取路径最短的模板进行替换和插入。

当我们需要设置表格所有字段都允许排序时,我们可以使用sortable="true"属性,设置在widget="table"组件上。

使用slot="fields"插槽是无法向widget="table"组件添加额外属性的,此时我们需要使用slot="table"这个插槽来实现我们的需求。

一个错误的DSL模板可能被定义成如下内容:(下面的示例舍弃了部分插槽内容)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
        </element>
    </pack>
</view>

由此可见,我们优先使用了widget="table"上的slot="table"属性作为插槽,将原本定义在widget="table"下的所有节点全部换成了在DSL模板中定义的内容,并且成功的添加了sortable="true"这一属性。

但是,由于子标签被完全替换了,因此丢弃了widget="rowActions"组件,导致slot="rowActions"中定义的内容,找不到对应的插槽,这部分DSL模板内容将被丢弃。

反向合并

由于上面的合并操作丢弃了widget="rowActions"组件,但我们的本意并不是想要丢弃这个组件,而是仍然希望能以某种规则将这部分内容继续保留。为了解决这个问题,我们提供了反向合并操作,但前提是,DOM结构必须是同一层级

基于这一特性,我们将上述模板稍加修改:(将slot="rowActions"放在slot="table"下面)

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,原本定义在布局模板上的widget="rowActions"被找回来了,并且正确填充了定义在DSL模板中的内容。

不足的是,由于widget="table"下面的所有内容都被替换了一次,我们无法确定原本的widget="rowActions"组件应该如何正确放置,因此使用了DSL模板中定义的位置。

这可能会带来一些弊端,比如:将某一元素必须被放在第一个节点处。针对这种情况,布局模板本来是可以做到限定作用的,但由于使用了更大范围的插槽,使得这一特性丢失。

但也带来了一些灵活性,比如:大多数情况下,某一元素需要放在第一个节点处,但在特殊情况下,该元素需要放在最后一个节点处。

我们希望插槽可以设计的尽可能的灵活,以满足各种不同场景的需求。

属性插槽

当我们希望使用属性合并,但不希望替换子标签时,我们可以使用属性插槽来解决这个问题。

当我们希望向widget="group"组件上添加title="这是一个示例标题"属性时,我们可以使用slot="tableGroup"插槽。

我们可以使用如下所示的方式来定义DSL模板:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
        <template slot="rowActions">
            <action name="redirectUpdatePage" />
            <action name="enable" />
            <action name="disable" />
        </template>
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由此可见,当DSL模板中的template标签下没有任何子标签时,我们仅会处理属性合并,但不会处理在原布局模板下定义的子标签。

根据这一特性,我们也可以定义这样的DSL模板来实现上面给widget="table"添加sortable="true"的需求:

<view type="TABLE" title="演示表格" name="演示模型table" model="demo.DemoModel">
    <template slot="tableGroup" title="这是一个示例标题" />
    <template slot="table" sortable="true" />
    <template slot="fields">
        <field data="id" invisible="true" />
        <field data="name" />
        <field data="isEnabled" />
    </template>
    <template slot="rowActions">
        <action name="redirectUpdatePage" />
        <action name="enable" />
        <action name="disable" />
    </template>
</view>

在使用相同的布局模板情况下,合并后的结果为:(与之前的合并结果完全一致)

<view type="TABLE">
    <pack widget="group">
        <view type="SEARCH">
            <element widget="search" slot="search" />
        </view>
    </pack>
    <pack widget="group" slot="tableGroup" title="这是一个示例标题">
        <element widget="actionBar" slot="actionBar" />
        <element widget="table" slot="table" sortable="true">
            <!-- slot="table" -->
            <field data="id" invisible="true" />
            <field data="name" />
            <field data="isEnabled" />
            <element widget="rowActions" slot="rowActions">
                <!-- slot="rowActions" -->
                <action name="redirectUpdatePage" />
                <action name="enable" />
                <action name="disable" />
            </element>
        </element>
    </pack>
</view>

由于属性插槽本身没有子标签,因此我们建议将所有的属性插槽配置在view标签子标签的最前面进行统一定义。

模板渲染

模板渲染是将一个包含dslNodeType属性的对象,生成对应框架的VDom对象,通过该VDom对象执行一段创建ts组件的逻辑,并通过ts组件渲染该组件对应的VDom对象

例如:使用Vue框架时,该VDom对象通常为VNode

(以下内容均围绕Vue框架进行描述,其他框架基本一致)

为了将模板渲染尽可能的接近第三方框架的渲染逻辑,我们采用了Tag(Vue组件) - Class Component(ts) - Component(Vue组件)这样的三层结构进行模板渲染的实现。

  • Tag(Vue组件):使用resolveComponent方法,将一个包含dslNodeType属性的对象,通过既定规则进行解析,获取一个Vue组件作为入口。主要处理模板标签和Vue组件之间的对应关系。
  • Class Component(ts):使用ts面向对象的特性,可以对组件逻辑进行重载、继承等处理,增加组件的灵活性。
  • Component(Vue组件):主要实现数据渲染。

具体的渲染逻辑不在此处进行介绍,该篇文章主要是为了让读者对模板以及整体框架有一个基本的概念,使用这套模板渲染的最终目的是为了让组件复用率更高,开发更加灵活。

Oinone社区 作者:oinone原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/23.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(1)
oinone的头像oinone
上一篇 2023年6月20日 pm4:07
下一篇 2023年11月2日 pm1:58

相关推荐

  • oio-input 输入框

    代码演示 <oio-input v-model:value="value"></oio-input> API Input 参数 说明 类型 默认值 版本 addonAfter 带标签的 input,设置后置标签 string|slot addonBefore 带标签的 input,设置前置标签 string|slot allowClear 可以点击清除图标删除内容 boolean defaultValue 输入框默认内容 string disabled 是否禁用状态,默认为 false boolean false maxlength 最大长度 number prefix 带有前缀图标的 input slot showCount 是否展示字数 boolean false suffix 带有后缀图标的 input slot type 声明 input 类型,同原生 input 标签的 type 属性,见:MDN(请直接使用 <a-textarea /> 代替 type="textarea")。 string text value(v-model:value) 输入框内容 string Input 事件 事件名称 说明 回调参数 update:value 输入框内容变化时的回调 function(e) pressEnter 按下回车的回调 function(e) Input.Search 代码演示 <oio-input-search v-model:value="value"></oio-input-search> Input.Search 事件 事件名称 说明 回调参数 search 点击搜索或按下回车键时的回调 function(value, event) 其余属性和 Input 一致。

    2023年12月18日
    2.3K00
  • 如何在表格的字段内添加动作

    介绍 在日常的业务中,我们经常需要在表格内直接点击动作完成一些操作,而不是只能在操作栏中,例如:订单的表格内点击商品名称或者里面的按钮跳转到商品详情页面,这里我们将带来大家来通过自定义表格字段来实现这个功能。 1.编写表格字段组件 组件ts文件TableBtnFieldWidget.ts import { ActionWidget, ActiveRecordExtendKeys, BaseFieldWidget, BaseListView, ModelFieldType, queryDslWidget, queryRowActionBar, RowContext, SPI, TableStringFieldWidget, BaseElementListViewWidget, ViewType, Widget } from '@kunlun/dependencies'; import { createVNode, VNode } from 'vue'; import { toString } from 'lodash-es'; import TableBtnField from './TableBtnField.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: ViewType.Table, ttype: [ModelFieldType.String, ModelFieldType.Text], // widget: 'StringLink', // 以下3行配置代码测试用,生产建议在界面设计器自定义组件,widget填自定义组件的api名称 model: 'resource.k2.Model0000000109', name: 'name' }) ) export class TableBtnFieldWidget extends TableStringFieldWidget { @Widget.Reactive() private get triggerActionLabel() { // 界面设计器组件内设计该属性 return this.getDsl().triggerActionLabel || '更新'; } private getTriggerAction() { return this.model.modelActions.find((a) => a.label === this.triggerActionLabel); } private getTriggerActionWidget(widgetHandle: string, draftId: string, triggerActionLabel: string): ActionWidget | undefined { const listView = Widget.select(widgetHandle) as unknown as BaseListView; const listWidget = queryDslWidget(listView?.getChildrenInstance(), BaseElementListViewWidget); if (!listWidget) { return undefined; } const rowActionBar = queryRowActionBar(listWidget.getChildrenInstance(), draftId); const actionWidget = rowActionBar?.getChildrenInstance().find((a) => (a as ActionWidget).action.label === triggerActionLabel); return actionWidget as ActionWidget; } protected clickAction(context: RowContext) { const draftId = context.data[ActiveRecordExtendKeys.DRAFT_ID] as unknown as string; const actionWidget = this.getTriggerActionWidget(this.getRootHandle()!, draftId, this.triggerActionLabel); if (!actionWidget) { console.error('未找到action', this.triggerActionLabel); return; } actionWidget.click(); } @Widget.Method() public renderDefaultSlot(context: RowContext): VNode[] | string { const value = toString(this.compute(context)); if (value) { return [ createVNode(TableBtnField,…

    2024年5月16日
    1.4K00
  • 表格字段配置Switch开关

    在业务开发中,我们经常会遇到表格中有个Switch开关组件: 那么如何通过界面设计器配置这个组件呢,下面让我们一起来学习下吧。 设计布尔类型的组件 1: 首先在界面设计器的组件区域添加一个组件。 2: 我们给这个组件添加一个元件,元件的配置必须跟下面的一致 3: 给元件添加属性配置 拖拽一个单行文本字段, 字段编码必须是truthyAction,代表的是该字段为true的时候要执行的动作 再拖拽一个单行文本字段, 字段编码必须是falsyAction,代表的是该字段为false的时候要执行的动作 4: 发布元件,然后再去对应的界面设计器页面,将对应的字段切换成刚刚设计的组件 5: 发布界面设计器,染红我们可以在运行时的页面看到效果 当switch切换的时候,会执行对应的action

    2023年11月21日
    1.9K00
  • 从一个方法开始:浅析页面渲染流程

    渲染前的准备 渲染前的准备,在 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[]):…

    2025年3月19日
    60900
  • 如何通过浏览器开发者工具提高调试效率

    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.5K00

Leave a Reply

登录后才能评论