前言

在最近的Vue Fes大会上,Vue Vapor的作者智子大佬宣布,如果能够得到资金支持,那么Vue Vapor年底就能发布alpha版本了。

智子也需要赚钱养活自己

根据尤大透露,过去一年以来智子接受赞助全职在为Vue Vapor工作。现在智子虽然还有赞助,但不再是全职的了。

也就是说现在智子大佬也需要想办法赚钱养活自己了,所以上周智子发了一个寻找赞助商的帖子。

智子的目标也很简单,能够养活他就行了。并且表示为赞助商服务,开始虽然是封闭开发,最终还是会开源的。

如果不寻求赞助,为了能够养活自己智子就只能去找一份工作了。如果这样Vapor的开发进度可能就会延缓,所以目前来说赞助计划是目前最好的方式了。

现在的Vue Vapor

现在的Vue Vapor主要有三个特点:没有虚拟DOM、高性能、更小的包体积。

没有虚拟DOM:意思很简单,就是在Vue Vapor中已经将虚拟DOM给干掉了。

高性能:因为干掉了虚拟DOM,瓶颈得以突破,所以性能相对提高了很多。

更小的包体积:包体积大小减少了53.3%。

并且Vue Vapor是目前大家所使用的Vue版本的子集

相比目前的Vue功能要少点,支持Composition API以及<script setup>

因为Vapor是目前Vue版本的子集,所以无虚拟DOM的Vapor模式和有虚拟DOM的vDom模式之间是互相兼容的。

在Vapor组件中支持使用vDom模式的组件。同样在vDom组件中也支持使用Vapor模式的组件。并且还支持只使用Vapor模式的情况。

并且Vue生态中的VueUseVue RouterPiniaNuxt组件库等都会支持Vapor。

同样也支持jsx,不过需要引入unplugin-vue-jsx-vapor

Vapor的机制

先看一个普通的操作DOM的例子:

// Initialize
const container = document.createElement('div')
const label = document.createElement('h1')
const button = document.createElement('button')
button.textContent = 'Increase'

button.addEventListener('click', increase)
let count = 0
const render = () => {
  label.textContent = `Count: ${count}`
}
function increase() {
  count++
  render() // Re-render
}
render() // Initial render

document.body.append(container)
container.append(label, button)

在这个例子中主要有两个元素:h1标签和button按钮。

h1标签中渲染了count变量的值。

点击button按钮触发click事件执行increase函数,在函数中首先会执行count++,然后再去执行render函数。

render函数中将h1标签中的值更新为最新值。

上面这个方案有个弊端,每次在click事件的回调中除了常规的执行count++之外,还去手动调用了render函数。

设想一下,如果我们的业务代码里面也这样写,那代码中将会到处都在调用render函数了,这样的代码光想想都头疼。

还好Vue的设计中有个优秀的响应式机制,并且还将响应式的功能抽取成一个单独的包:@vue/reactivity

而Vue Vapor就是基于@vue/reactivity进行开发的,藉此实现当响应式数据改变后会自动更新DOM,无需去手动执行render函数。

使用@vue/reactivity改造后的代码如下:

import { effect, ref } from '@vue/reactivity'

// Initialize
const container = document.createElement('div')
const label = document.createElement('h1')
const button = document.createElement('button')
button.textContent = 'Increase'

button.addEventListener('click', increase)
const count = ref(0)
effect(() => {
  label.textContent = `Count: ${count.value}`
})
function increase() {
  count.value++
}

document.body.append(container)
container.append(label, button)

改造后的代码和原来的代码主要有三个不同:

  • 之前是直接使用let count = 0定义的变量,而改造后使用const count = ref(0)定义的响应式变量。

  • 之前的increase函数中除了执行count++之外,还需要去手动调用render()函数。而在新的代码中只会执行count.value++

  • 移除了render函数,替代他的是effect函数。在effect的回调函数中同样是进行DOM操作更新h1标签中的值。

    effect函数和watchEffect很相似,当回调中的响应式变量改变后就会重新执行回调函数。这里就是当响应式变量count改变后会重新执行回调函数,在回调函数中进行DOM操作更新h1标签中的值。

这也就是Vapor基于@vue/reactivity实现的响应式原理,在这个过程中完全没有虚拟DOM的介入。当响应式变量改变后会执行对应的effect回调函数,在回调函数中直接去更新DOM即可。

看到这里有的小伙伴会有疑问,这个effect函数以及里面操作DOM的代码需要我们自己手写吗?

当然不需要手动去写!!在编译时Vapor会自动生成一个effect回调函数,以及回调函数里面更新DOM的代码。

这个是上面的例子在Vue Vapor SFC Playground上面经过编译后的js代码,如下图:

从上图中可以看到Vapor模式下经过编译后会自动生成一个effect回调函数,并且在回调函数中会去直接操作DOM。

至于编译时是如何生成effect回调函数,需要等Vapor稳定后欧阳会继续写文章讲解。