概述
我们知道,Vue中模板从渲染到展示过程大致是这样一个流程:
模板(template) ---》 AST ---》渲染函数 ---》VNode ---》 Render Tree(虚拟DOM) ---》Layout ---》 Painting ---》Show
也就是说:
- Vue.js在将模板字符串转换为JavaScript渲染函数 时,会生成 的一个内部数据结构称为AST ,这个AST表示了模板的结构,包括元素、属性、子元素等信息。
- Vue.js的编译器会遍历AST,将其转换为渲染函数。这个过程包括静态提升(hoisting static content)、代码生成等优化步骤。
- 当Vue的渲染函数执行时,它会将元素转换一个个的VNode,然后这些VNode组合在一起就形成了虚拟DOM
- 然后经过diff算法过后就能更新到真实的DOM上了。
认识渲染函数 h
Vue推荐在绝大数情况下使用模板来创建你的HTML,然后一些特殊的场景,你真的需要JavaScript的完全编程的能力,这个时候你可以使用渲染函数,它比模板更接近编译器。
上面说了,我们之前编写的template 中的HTML 最终也是使用渲染函数生成对应的VNode; 那么,如果你想充分的利用JavaScript的编程能力,我们可以自己来编写createVNode函数,生成对应的VNode
那么我们应该怎么来做呢? --- 使用h()函数:
- h() 函数是一个用于创建vnode的一个函数
- 其实更准确的命名应该是createVNode() 函数,但是为了简便,在Vue中将之简化为h() 函数
h函数的使用
h()函数如何使用呢?它接受三个参数,如下图所示:
注意事项:
- 如果没有props ,那么通常可以将children作为第二个参数传入
- 如果会产生歧义 ,可以将null作为第二个参数传入 ,将children作为第三个参数传入
h函数可以在两个地方使用:
- render函数选项中;
- setup 函数选项中(setup本身需要是一个函数类型,函数再返回h函数创建的VNode)
也就是说,我们现在要做什么工作呢?再啰嗦一遍,我们上面说了,我们编写的template最终会被渲染函数转换为VNode,而现在呢我们想充分利用JS的能力,所以我们不使用template了,自己来编写html内容,这样没有中间商赚差价,渲染函数一步到位我们编写的内容转换为VNode。
文字描述可能还是有些抽象,接下来我们一起来看几个例子,加深一下理解吧!
在render函数选项中使用
example1.vue
javascript
<script>
import { h } from 'vue'
export default {
render() {
return h("div", { className: "app" }, [
h("h2", { className: "title" }, "我是标题"),
h("p", { className: "content" }, "我是内容, 哈哈哈"),
])
}
}
</script>
<style scoped>
</style>
上面代码的运行结果和下面代码的运行结果在Vue中是一样的 example2.vue
javascript
<template>
<div class="app">
<h2 class="title">我是标题</h2>
<p class="content">我是内容, 哈哈哈</p>
</div>
</template>
<script>
</script>
<style scoped>
</style>
在setup函数选项中使用
再来一个稍微复杂点的例子: 实现计数器功能 + 复用别的组件 Home.vue
javascript
<template>
<div class="home">
<h2>home Page</h2>
</div>
</template>
<script setup>
</script>
<style lang="less" scoped>
</style>
App.vue
javascript
<script>
import { h } from 'vue'
import Home from "./Home.vue"
export default {
data() {
return {
counter: 0
}
},
render() {
return h("div", { className: "app" }, [
h("h2", null, `当前计数: ${this.counter}`),
h("button", { onClick: this.increment }, "+1"),
h("button", { onClick: this.decrement }, "-1"),
h(Home)
])
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
}
</script>
<style scoped>
</style>
那么在Vue3的 <script setup>
语法糖中又该如何使用呢? 有 亿 点点奇怪,先看代码吧 App.vue
javascript
<template>
<render/>
<h2 class="">内容</h2>
</template>
<script setup>
import { ref, h } from 'vue';
import Home from './Home.vue'
const counter = ref(0)
const increment = () => {
counter.value++
}
const decrement = () => {
counter.value--
}
const render = () => h("div", { className: "app" }, [
h("h2", null, `当前计数: ${counter.value}`),
h("button", { onClick: increment }, "+1"),
h("button", { onClick: decrement }, "-1"),
h(Home)
])
</script>
<style scoped>
</style>
也就是说,在 <script setup>
语法糖中我们不能直接返回渲染函数,否则会报错(大家自己尝试一下就知道了)。我们应该将渲染函数当成箭头函数的返回值返回,并且还要在template中使用才行。。。嗯??? 不知道大家到这里有没有一种感觉,这不是脱裤子放 P 吗!绕一圈又回去了
我们本来在template中的写法多么简单,这还整的越来越复杂了,如果我们想完全的利用JS的能力,为什么要这样写呢?为什么不使用JSX呢! 哈哈是的呢,我们平常确定是不会这样写的,但是,这不是没办法吗,面试官想让我们这样哈哈哈。但是 u1s1,理解h不是也使得我们提升了一些吗
至于JSX,相信写过React的同学一定不陌生,和JS不能说是十分相似,只能说是一模一样(还是不一样的哈,JSX比JS的范围要大的)。Vue中也可以使用 JSX,不过我们一般也不会使用。毕竟一个框架有一个框架的特点吗。不过看Vue官方的动向似乎有点向JSX倾斜,我们拭目以待吧~
总结
本篇文章我们讲述了Vue文件从编写到显示到页面上的整个过程,并且详细解说了渲染函数的执行流程,相信大家看过以后对整个流程一定会有更加深刻的了解。我们下次再见吧~