Vue中的渲染函数(render函数)
节点、树以及虚拟 DOM
-
当浏览器读到一些DOM结构时,会建立一个“DOM节点”树来保持追踪所有内容,如同你会画一张家谱树来追踪家庭成员的发展一样。
-
Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。
-
createElement 会返回一个虚拟节点 (virtual node)”,也常简写它为“VNode”。
-
“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。
-
虚拟DOM所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。
-
每个DOM元素或组件都对应一个VNode对象。
渲染函数
- 通过createElement函数来创建虚拟节点 (virtual node),用来渲染要渲染的节点。
- 需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。
- 可以解决有些场景中用template实现起来代码冗余,重复模板比较多时。
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => {
console.log(h);
//ƒ (a, b, c, d) { return createElement(vm, a, b, c, d, true); }
return h(App);
}
}).$mount("#app");
ƒ (a, b, c, d) { return createElement(vm, a, b, c, d, true); }
- render函数:createElement是render函数的参数,它本身也是个函数。
使用render函数的写一个to do list(Vue 的模板实际上被编译成了渲染函数)
import Vue from "vue";
const AddHeros = {
props: {
heros: {
type: Array,
default: () => []
},
add: {
type: Function
}
},
data() {
return {
name: "",
job: ""
};
},
render(createElement) {
return createElement(
"div",
{
style: {
display: "flex",
"align-items": "center",
justifyContent: "space-between",
marginBottom: "5px"
}
},
[
createElement("span", {}, [
createElement("span", "英雄名:"),
createElement("input", {
attrs: {
placeholder: "请输入英雄名"
},
style: {
width: "150px"
},
on: {
input: e => {
this.name = e.target.value;
}
}
})
]),
createElement("span", {}, [
createElement("span", "职业:"),
createElement("input", {
attrs: {
placeholder: "请输入英雄职业"
},
style: {
width: "150px"
},
on: {
input: e => {
this.job = e.target.value;
}
}
})
]),
createElement(
"button",
{
on: {
click: () => {
let id = ++this.$props.heros[
this.$props.heros.length - 1
].id;
this.$props.add({
id: id,
name: this.name,
job: this.job
});
debugger;
this.name = "";
this.job = "";
}
}
},
"添加"
)
]
);
}
};
// 函数式组件
const HerosList = {
functional: true,
render: function(createElement, context) {
const HerosItem = context.data.props.heros.map(item => {
return createElement(
"li",
{
style: {
display: "flex",
"align-items": "center",
justifyContent: "space-between",
padding: "0 10px",
height: "40px",
border: "1px solid #ccc"
}
},
[
createElement(
"div",
{
style: {
width: "70px"
}
},
item.name
),
createElement("div", {}, item.job),
createElement(
"div",
{
style: {
width: "90px",
display: "flex",
"align-items": "center",
justifyContent: "space-between",
cursor: "pointer"
}
},
[
createElement(
"button",
{
on: {
click: () => {
context.data.on.edit();
}
}
},
"修改"
),
createElement(
"button",
{
on: {
click: () => {
context.data.nativeOn.del(item.id);
}
}
},
"删除"
)
]
)
]
);
});
return createElement(
"ul",
{
style: {
"list-style": "none",
width: "100%",
padding: 0,
margin: 0
}
},
HerosItem
);
}
};
const HerosListWrep = {
name: "HerosListWrep",
data() {
return {
heros: []
};
},
created() {
// 初始英雄列表
this.heros = this.HEROS;
},
computed: {
HEROS() {
const heros = [
{ id: "1", name: "鲁班七号", job: "射手" },
{ id: "2", name: "铠", job: "战士" },
{ id: "3", name: "小乔", job: "法师" },
{ id: "4", name: "刘禅", job: "辅助" },
{ id: "5", name: "孙悟空", job: "刺客" }
];
return heros;
}
},
methods: {
// 添加英雄
add(herosItem) {
this.heros.push(herosItem);
}
},
render(createElement) {
return createElement(
"div",
{
style: {
margin: "0 auto",
width: "500px",
paddingTop: "100px"
}
},
[
createElement(AddHeros, {
props: {
heros: this.heros,
add: this.add
},
nativeOn: {
click: () => {
console.log(
"仅用于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。"
);
}
}
}),
createElement(HerosList, {
props: {
heros: this.heros
},
on: {
edit: () => {
console.log("编辑");
}
},
nativeOn: {
del: id => {
let arr = this.heros.filter(item => item.id != id);
this.heros = arr;
}
}
})
]
);
}
};
new Vue({
render(h) {
return h(
"div",
{
attrs: {
id: "app"
}
},
[h(HerosListWrep)]
);
}
}).$mount("#app");
函数式组件参考: https://cn.vuejs.org/v2/guide/render-function.html#函数式组件
开发工具