技术实践

Vue-Render函数解决实际业务问题

简单的说,在vue中我们使用模板HTML语法来组建页面的,使用render函数我们可以用js语言来构建DOM。因为vue是虚拟DOM,所以在

羊先生的头像
羊先生2021.04.01 · 3 分钟阅读 · 86 阅读
Vue-Render函数解决实际业务问题封面
文章正文还需 3 分钟

简单的说,在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>
微信图片_20210401200903.png

阔以发现, 根节点app被替换成了app1,所以我们为什么不能将元素绑定在body/html这些元素上就知道了吧,因为它会替换掉页面上的元素

解决实际问题

业务需求:
1、渲染两种类型的的组件
2、相同的类型的input的值,需要更改这个值,其他的同样类型的也需要随之变化
3、下拉框存在多项,选择不同的项,需要重新渲染
4、按照数据的返回的顺序渲染
5、修改的数据,需要修改原数据,保存是一次性提交

image.png

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

image.png

第一种方式,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);
    },
});