技术实践
Vue-Render函数解决实际业务问题
简单的说,在vue中我们使用模板HTML语法来组建页面的,使用render函数我们可以用js语言来构建DOM。因为vue是虚拟DOM,所以在

简单的说,在vue中我们使用模板HTML语法来组建页面的,使用render函数我们可以用js语言来构建DOM。因为vue是虚拟DOM,所以在拿到template模板时也要转译成VNode的函数,而用render函数构建DOM,vue就免去了转译的过程。
当使用render函数描述虚拟DOM时,vue提供一个函数,这个函数是就构建虚拟DOM所需要的工具。官网上给他起了个名字叫createElement。还有约定它的简写叫h
常见模板写法
js
<template>
<div id="app">
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld,
},
};
</script>
render函数
js
<script>
export default {
name: "App",
render(createElement) {
return createElement(
"div",
{
attrs: {
id: "app1",
},
},
"测试Render"
);
},
};
</script>

阔以发现, 根节点app被替换成了app1,所以我们为什么不能将元素绑定在body/html这些元素上就知道了吧,因为它会替换掉页面上的元素
解决实际问题
业务需求:
1、渲染两种类型的的组件
2、相同的类型的input的值,需要更改这个值,其他的同样类型的也需要随之变化
3、下拉框存在多项,选择不同的项,需要重新渲染
4、按照数据的返回的顺序渲染
5、修改的数据,需要修改原数据,保存是一次性提交

数据结构
根据这种框红的条件,去渲染input框
根据框圆圈的,去渲染selse下拉框,下拉值去另一个接口获取

第一种方式,jQuery操作DOM
js
// 创建input标签
const createInput = (item, obj) => {
obj.value = obj.value ? obj.value : '';
obj.tokenref = obj.tokenref ? obj.tokenref : '';
// arrpath="reviewContent[${item.rowIndex}].addCategorys.addCategory[${item.item_index}].adds.add[${obj.add_index}]""
return `<input rowIndex="${obj.rowIndex}" addCategorys='${obj.addCategorys}' rowText="${obj.rowText}" ctokenref="${obj.ctokenref}" tokenref="${obj.tokenref}" value="${obj.value}" type="text" class="tokenrefInput_${obj.tokenref}" onchange="tokenrefInput(this)"/>`
}
// 创建select
const createSelect = (arr, obj) => {
let option = '';
arr.forEach((item, index) => {
if (obj.defaultkey === item.methodkey) {
option += `<option rowIndex="${obj.rowIndex}" addCategorys="${obj.addCategorys}" rowText="${obj.rowText}" value="${item.text}" selected="selected" tag="${obj.tag}" optionkey="${item.methodkey}">${item.text}</option>`
} else {
option += `<option rowIndex="${obj.rowIndex}" addCategorys="${obj.addCategorys}" rowText="${obj.rowText}" value="${item.text}" tag="${obj.tag}" optionkey="${item.methodkey}">${item.text}</option>`
}
})
return `<select onchange="methodkeySelect(this)">${option}</select><br/>`
}
缺点:数据保定在html上,可读性差,数据关联需要去遍历,需要绑定单一事件,要知道是点的那个元素,或者操作的是那个元素,
DOM量大
第二种,render函数
整个业务才需200行左右,数据可控,逻辑清晰,并且能和数据实现双向绑定以及和Vue的实例交互
js
import Vue from "vue";
import { find } from "lodash";
import { IEVersion } from "@/utils/browser";
const time = IEVersion() === -1 ? 200 : 1000;
const getAddTokenrefObj = (arr, tag) => {
let obj = {};
for (let i = 0, len = arr.length; i < len; i++) {
if (arr[i].tokenref === tag) {
obj = arr[i];
break;
}
}
return obj;
};
// 获取类型 根据key
const getMethodgroupsOptions = ({ methodgroups: { methodgroup }, tag }) => {
for (let i = 0, len = methodgroup.length; i < len; i++) {
if (methodgroup[i].key == tag) {
return methodgroup[i];
}
}
};
Vue.component("Description", {
naem: "Description",
props: {
row: Object,
methodgroups: Object,
canModify: Boolean,
},
data() {
return {
title: "",
};
},
methods: {
handSlect({ key, value, addCategoryKey }) {
let arr = find(this.methodgroups.methodgroup, {
key: parseInt(key),
});
if (arr && arr.methoditems.methoditem) {
let { adds, description, wayref, methodkey, text } = find(
arr.methoditems.methoditem,
{
methodkey: value,
}
);
let addCategory = this.row.addCategorys.addCategory[
addCategoryKey
];
this.$emit("handSelectResult");
// this.$forceUpdate() // 强制更新
}
},
},
render: function(createElement, context) {
const addCategory = this.row.addCategorys.addCategory;
const vnodes = [];
if (addCategory.length === 0) {
let elInput = "";
if (this.row._isEdit) {
elInput = createElement("textarea", {
domProps: {
value: this.row.description,
type: "text",
},
attrs: {
class: "vxe-textarea--inner " + this.row.tokenref,
maxlength: 255,
disabled: !this.$store.state.app.canModify,
},
style: {
minHeight: this._height,
},
ref: this.row.tokenref,
on: {
input: (event) => {
this.row.description = event.target.value;
},
focus: (event) => {},
blur: () => {
this.row._isEdit = false;
},
},
});
} else {
elInput = createElement(
"span",
{
style: {
minHeight: "18px",
minWidth: "100%",
display: "inline-block",
},
on: {
click: () => {
this._height =
((this.$el && this.$el.offsetHeight) + 30 ||
100) + "px";
if (!this.canModify) {
this.row._isEdit = true;
this.$nextTick(() => {
setTimeout(() => {
this.$refs[
this.row.tokenref
].focus();
}, time);
});
}
},
},
},
this.row.description
);
}
vnodes.push(elInput);
} else {
addCategory.forEach((item, index) => {
let method = getMethodgroupsOptions({
methodgroups: this.methodgroups,
tag: item.methodkey,
});
const elOptions = [];
if (method) {
method.methoditems.methoditem.forEach((item) => {
const option = createElement(
"option",
{
attrs: {
value: item.methodkey,
addCategoryKey: index,
},
},
item.text
);
elOptions.push(option);
});
const elSelect = createElement(
"select",
{
domProps: {
value: item.defaultkey,
},
attrs: {
key: method.key,
disabled: !this.$store.state.app.canModify,
},
on: {
change: ({ target }) => {
let key = target.getAttribute("key");
let index = target.options[
target.selectedIndex
].getAttribute("addCategoryKey");
this.handSlect({
key,
value: target.value,
addCategoryKey: parseInt(index),
});
},
},
},
elOptions
);
vnodes.push(elSelect);
vnodes.push(createElement("br"));
}
if (item.description) {
let strs = item.description
.split("{")
.map((item) => item.split("}"))
.flat();
strs.forEach((str) => {
let obj = getAddTokenrefObj(item.adds.add, str);
if (obj.key) {
let type = "text";
if (
obj.datatype === "double" ||
obj.datatype === "Integer"
) {
type = "number";
}
const elInput = createElement("input", {
domProps: {
value: obj.value,
type: type,
},
attrs: {
class: "inputa",
maxlength: type === "text" ? 1000 : 11,
alt: obj.value,
disabled: this.canModify,
},
on: {
input: (event) => {
obj.value = event.target.value;
},
},
});
vnodes.push(elInput);
} else {
const elSpan = createElement("span", {}, str);
vnodes.push(elSpan);
}
});
}
});
}
return createElement("div", vnodes);
},
});