如何通过低代码实现动态渲染并提高页面性能

前言

  • 常网IT戳我呀!
  • 常网IT源码上线啦!
  • 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
  • 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
  • 最近在做低代码平台相关的,想分享一些自己在项目中遇到的场景。

鲁迅曾经说过:中国人的性情总是喜欢调和折中的。
如果你说这屋子太暗,要在这里开一个天窗,大家一定是不允许的。但如果你主张拆掉屋顶,他们就会来调和,反而同意拆掉天窗。

车队民工拿不到工资,刀检察院门口闹一下,市里就给垫款了;
小韩见义勇为,学校装聋作哑,韩母把事情闹大了,学校立刻就破案了;
冷眼的村民,看到小王的妻子跳楼后,终于敢站出来作证了;

如何通过低代码实现动态渲染并提高页面性能

仔细看下去,相信能收获一些干货滴~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

一、问题剖析

那是一个倾盆大雨的早上,花瓣随风雨落在我的肩膀上,是五颜六色的花朵。

我轻轻抚摸着他,随后拨开第一朵花瓣,她不爱我,该怎么办。

花瓣似乎在说:爱她。

拨开第二朵,她对我已经没有那种感觉了。

花瓣似乎在说:那就去爱她。

拨开第三朵,你还没有理解,我是说他对我已经没有了爱的感觉。

花瓣似乎在说:就是因为她对你已经没有了爱的感觉,所以才要去爱她。

正当我沉迷于幻想中,实习生小白🙋喊道:前端大佬,有一个需求。

我说🙋🏻‍♂️:什么时候要?

小白🙋:别问,问就是明天!

🙋🏻‍♂️真是的,慌慌张张的,说吧~

需求是这样子的:

现在我们现在的A页面,北京那边也想要看到这个页面,且有一些搜索条件、表格字段要隐藏掉。

如果是刚刚毕业时的我,拿到这个需求,不管三七二十一,先把A页面拷贝一份出来,给北京那边用。

这样子缺点是:要维护两套,尽管他们有相似处达到80%。

如今的我,想的是:把A页面做成动态渲染。

二、实现思路

一个根据配置渲染搜索条件、表格的组件。

init重置搜索条件

搜索条件,传入的是一个数组:内有一个输入框。

需要先进行深拷贝 JSON.parse一层,在用户点击重置的时候,将最初的值赋值与他。

但使用 $set将整个数组替换无效,直接赋值无效,因为是数组内结构太深,所以用key刷新组件。

<search :key="searchPanelKey" />

searchPanelKey: 0

mounted() {
  this.searchFieldsData = this.searchFields;
  // 先将旧的值保存起来,方便后续重置
  this.searchFieldsDataOld = JSON.parse(JSON.stringify(this.searchFields));
},

// 重置
init() {
  this.searchPanelKey++;
  this.searchFieldsData = JSON.parse(JSON.stringify(this.searchFieldsDataOld));
},

class

该组件因为是公共组件,所以内置了一个class类方便管理该组件。

Render内提供一个class,暴露出多个方法,给父组件使用:

  • set:根据prop设置searchFieldsData的属性
  • get:根据prop获取searchFieldsData
  • getObj:searchFieldsData数组转为对象{ prop: value }
  • init:初始化

set方法是使用 $set来更新值,但抽离出js文件,$set是vue文件独享,所以需要 new Class的时候传入。

三、table表格组件

核心功能还是table组件,内含复杂的功能。

支持多级表头。(这个后续会补充文章来写,挺精彩的)

操作按钮

可以支持鼠标移入显示title,使用el-tooltip组件。

如何通过低代码实现动态渲染并提高页面性能

el-tooltip这里有个坑。

最初的代码:大概的意思,真实不是这样

<el-tooltip :content="btn.title">
  <el-button  v-if="isShow">{{ btn.label }}</el-button>
</el-tooltip>

按钮是每一行都会显示的,然后我第一行显示按钮A,第二行不显示按钮A。

此时第一行鼠标移入按钮,不会显示title,但我明明已经设置了。

排查到的原因: 因为第二行按钮被隐藏了,所以el-tooltip也会不生效,会影响到第二行。

解决方案:将v-if不要写在按钮身上,需要写到el-tooltip身上就正常,可看下面的代码。

怎么控制按钮的显隐

我们知道,我们会依赖于接口返回的字段状态来控制按钮的显隐或者样式。

那我们需要配置接口的参数,如:interface:{ name: “张三” },就是name为张三的这条数据才显示按钮。

v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"

getInterface介绍一下v-if中的这个方法,下面的computed有写。

将传入的interface对象,看看是不是全等,且的关系,判断是不是都命中,如果是,就认为显示,否则则不显示。(这种情况就是有传interface)


又有一种情况,你怎么配置,name != '张三'、又或者 age != 30 || name == '张三' 呢?

getInterface方法只是判断等于的情况,且是every全等的。

当然也是可以配置,比如:传入

{
  prop: "name",
  value:"张三",
  operation: "notEqual",	// notEqual:不等于;equal:等于
  link: "some"	// some:或;every:且
}

以这种形式也可以。

当然有另外一种方法二:

就是不配置interface对象,在外面接受一个isShow方法,该方法接受接口返回的数据,返回布尔类型

那么你就可以愉快的写:

`isShow(row) =>{ row.name=='张三' || row.age != 30 }`

一种是用对象,另外一种是方法,两者都可支持

按钮的样式style

根据接口返回的数据的状态来显示style,比如:name为张三这条数据,按钮显示禁用;其他的状态就正常。

<el-button :style=" getInterface(btn, scope) ? btn.style : ''" type="text" >

这样子也是可以的。

方法二:但后面想了一下,我们可以传入两组按钮。两个按钮的配置是一样的,只不过按钮1显示样式1;按钮2显示样式2,只要有传style就渲染:

<el-button :style=" btn.style " type="text" >

鼠标移入提示

其实和按钮的style一样,有传就显示。

content默认传入title,disabled的话,如果没传title就给其不显示,传了title就显示

<el-tooltip
  v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"
  :content="btn.title"
  :disabled="!btn.title"
>

代码如下

<el-table-column v-if="columns.handle" v-bind="columns.handle">
  <template slot-scope="scope">
    <template v-for="(btn, btnIndex) in columns.handle.btn">
      <el-tooltip
        v-if="(btn.interface ? getInterface(btn, scope) : true) && (!btn.isShow ? true : btn.isShow(scope.row))"
        :key="btnIndex"
        effect="dark"
        :content="btn.title"
        :disabled="!btn.title"
        placement="top"
      >
        <el-button :style="btn.style" type="text" @click="$emit('btnClick', btn, scope.row)">{{ btn.label }}</el-button>
      </el-tooltip>
    </template>
  </template>
</el-table-column>

computed: {
    getInterface() {
      return (btn, scope) => {
        if (getVal(btn, "interface")) {
          let bool = [];
          for (const key in btn.interface) {
            if (Object.hasOwnProperty.call(btn.interface, key)) {
              const element = btn.interface[key];
              bool.push(scope.row[key] == element);
            }
          }
          return bool.every((e) => e);
        } else {
          return false;
        }
      };
    },
  },

表格内容

需求:可以支持显示显示icon,鼠标移入可显示。

如何通过低代码实现动态渲染并提高页面性能

配置:表头添加一个icon,鼠标移上去显示(后续文章补充)

但是之前只是支持:通过动态组件渲染icon(组件名字),利用v-html解析实现换行和颜色,鼠标移入也可显示文本。

之前代码如下

<!-- v-bind绑定属性(传入对象) -->
<component :is="item.name" v-bind="item.attribute">
  <template slot="content">
    <span v-html="item.attribute.content"></span>
  </template>
  <span v-html="item.ele"></span>
</component>

现在需求提升:内含【详情】按钮,点击需要跳转路由。而且还需要根据接口返回的id跳转路由。

那我在想:怎么从外面传进来参数,或者从里面拿到外面的参数?

如果不依赖于外部参数,这样子按照原本的渲染逻辑是可以的。

item.attribute.content = `数量。<br /><span style="color: #409EFF;" onclick="console.log(11)">详情</span>
 `

但根据id路由跳转,我尝试在 onclick内打印this,是他本身,于是我想着外部传入id绑定在span上,然后内部通过this.getAttribute(‘ids’)可以获取到ids的值,进行路由跳转。

item.attribute.content = `数量。<br /><span ids="从外部循环改ids传入的" style="color: #409EFF;" onclick="console.log(11)">详情</span>
`

相对来说是比较麻烦的一种方式。

还有另外一种方法是:从外部传入。

可以看到content从字符串的形式,变成函数,传入scope, coloumnHeader参数,内部接收可随意配置。

至于路由跳转,这里$emit('componentClick')把事件传出去,给父组件传递一个事件,父组件可进行路由跳转操作。

# 动态组件DynamicComponents
<component :is="item.name" v-bind="item.attribute">
  <template slot="content">
    <span v-if="item.attribute.contentSlot" v-html="item.attribute.content(scope, coloumnHeader)" @click="$emit('componentClick',scope, coloumnHeader)"></span>
  </template>
  <span v-html="item.ele"></span>
</component>

这里scope.row.name也可以使用接口的值渲染。

item.attribute.content : (scope, coloumnHeader) => {
  return `填报单位:已收到指标任务总和的单位${scope.row.name}数量。<br /><br />
  <span class="detailed-link" style="cursor: pointer;color: #409EFF;">详情</span>`;
}
             `

需求提升:想可以配置两个的。

如何通过低代码实现动态渲染并提高页面性能

比如:一个icon,一个标签

你看,像这种标签,我们name传入el-tag利用动态组件即可渲染出。

那我们需要循环渲染。

循环 coloumnHeader.tempBody数组。

我们注意到DynamicComponents组件的v-if接收interface方法,可以根据接口的数据的状态来控制其显隐。

<template v-if="getVal(coloumnHeader, 'tempBody', 'length')">
  <template v-for="(tempBody, tempBodyIndex) in coloumnHeader.tempBody">
  	// 这个就是我们上面写的动态组件
    <DynamicComponents
      v-if="getVal(tempBody, 'show') && (tempBody.interface ? tempBody.interface(scope, coloumnHeader) : true ) "
      :key="tempBodyIndex"
      :item="getVal(tempBody)"
      :scope="scope"
      :coloumnHeader="coloumnHeader"
      @componentClick="$emit('componentClick')"
    />
  </template>
</template>

使用:

// 配置表格内容
tempBody: [
// icon
{
  show: true,
  position: "left",
  name: "el-tooltip",
  ele: '<i class="el-icon-warning" style="cursor: pointer;margin-right: 5px;color: #E6A23C;"></i>',
  // 满足这种条件下,才显示
  interface:(val) =>{
     return val.row.name != '示例'
  },
  attribute: {
    placement: "top",
    effect: "dark",
    contentSlot: (scope, coloumnHeader) => {
      return `填${scope.row.name}数量。<br /><br />
      <span class="detailed-link" style="cursor: pointer;color: #409EFF;" onclick="console.log(11)">详情</span>
      数量。`;
    },
  },
},
// 标签
{
  show: true,
  position: "left",
  name: "el-tag",
  ele: "标签",
  attribute: {
    type: "success",
    style: "margin-left:20px;",
    effect: "plain",
  },
},
],

后记

这种类似的场景一般用于可视化低代码平台中。

会根据配置来动态渲染。

做好扩展性,遵守开闭原则。

当然,我们还要考虑安全性防XSS注入,防止外部嵌入,导致安全问题。

如果有其他更好的方法也欢迎评论区见,这里提供的只是诸多方法之一。

最后,祝君能拿下满意的offer。

我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车

👍 如果对您有帮助,您的点赞是我前进的润滑剂。

以往推荐

靓仔,说一下keep-alive缓存组件后怎么更新及原理?

面试官问我watch和computed的区别以及选择?

面试官问我new Vue阶段做了什么?

前端仔,快把dist部署到Nginx上

多图详解,一次性啃懂原型链(上万字)

Vue-Cli3搭建组件库

Vue实现动态路由(和面试官吹项目亮点)

项目中你不知道的Axios骚操作(手写核心原理、兼容性)

VuePress搭建项目组件文档

原文链接

juejin.cn/spost/73401…

原文链接:https://juejin.cn/post/7340191765699264527 作者:Dignity_呱

(0)
上一篇 2024年2月28日 上午10:53
下一篇 2024年2月28日 上午11:03

相关推荐

发表回复

登录后才能评论