像写 Vue3 一样写小程序
Vue 3 带来了许多令人兴奋的新特性,这其中最令人期待的莫过于 Composition API,它带来了更灵活的代码组织方式,更好的 TS 支持,以及更强大的逻辑复用能力。
如今 Vue 3 已经发布半年有余,各项周边配套也已基本完善,可以说它已经准备好被用于生产了。你或许早已摩拳擦掌跃跃欲试,但作为国内开发者你却可能面临着一个很尴尬的处境,那就是你的主要工作也许是写小程序,这一切令人激动的消息好像与你并没有什么关系。但是如果你能用 Vue 3 的 Composition API 写小程序呢?
介绍
向大家隆重介绍 Vue Mini,它是一个基于 Vue 3 的小程序库,它能让你用 Composition API 写小程序。
Vue Mini 底层直接且仅依赖 Vue 3 的响应式核心 @vue/reactivity,它完整继承了 Vue 3 的响应式数据,并且设计了与 Composition API 一模一样的 API,当然也拥有一模一样的能力。用 Vue Mini 写小程序就像在写 Vue 3 一样。
动机
小程序本就自带一个框架,为什么还要大费周章的移植别的框架呢?
这是因为小程序自身的框架在几年前或许还差强人意,但如今却很难让人满意了。现在 TS 越来越受欢迎,而小程序自身对 TS 的支持却很羸弱。并且当小程序组件变大时,你也不能按逻辑关注点来组织代码,这可能会导致组件难以阅读和理解。最后小程序提供的逻辑复用方案 Behavior 其实就是 Mixin,存在着与 Mixin 相同的限制,例如容易发生冲突,不能接收参数等。
这些问题恰巧也是 Vue 3 添加 Composition API 所解决的问题。所以 Vue Mini 将 Composition API 移植到小程序,从而解决了这些问题。它提供了非常好的 TS 支持,并且有很好的类型推导,很多时候你并不需要手写类型注解。你可以按逻辑关注点来组织代码,让复杂的组件更容易阅读和理解。你也能将任意逻辑提取为一个组合函数,你可以在任何组件中调用组合函数,你也可以向组合函数传递任何参数来改变它的逻辑,这大大提高了抽象逻辑的灵活性。
Dogfooding
Vue Mini 其实已经在我们公司内部孵化一年多的时间了,它已经先后被用于创新项目和正式的生产项目。之所以最近才开始宣传是因为在经过真实生产项目验证之前,我并不能确定它是可行的,所以过去一年我都在 Dogfooding(使用自己的产品)。
非常幸运,在团队小伙伴的信任和支持下,我们用 Vue Mini 写的第一个生产小程序最近已经成功上线了,最新上线的版本一共包含 58 个页面,几乎全都是用 Vue Mini 写的。我现在十分确定 Vue Mini 是可行的,在经过生产项目打磨后,它的质量已经比较可靠了,API 也比较稳定了。我认为现在是时候让更多的人知道它的存在了,它或许也能解决你的一些问题。
如果你对 Vue Mini 感兴趣,想将其引入到你个人或工作的小程序中,一个保险也更具说服力的做法是先在小程序的部分页面或组件中小规模使用,等你或你的小伙伴逐渐信任它之后,再慢慢扩大使用范围。
当然 Vue Mini 还没有经历过大规模的生产项目的考验,你在使用过程中可能仍然会碰到 Bug 或不完善的地方,这时候欢迎提交 Issue。
比较
这里对比一下 Vue Mini 和其他一些小程序框架的区别。
原生语法
小程序原生语法是开发者所能接触到的最底层,这也是写小程序的唯一方法,任何其他方案最终都要通过原生语法才能工作。
几年前小程序原生语法可能还不算太糟糕,但如今 TS 越来越受欢迎,而原生语法对 TS 的支持却很羸弱。并且 React Hooks 横空出世,彻底改变了 UI 逻辑的编写方式,Vue 3 也根据自己的响应式数据提出了 Composition API,有着与 React Hooks 相同的能力。
在这种背景下原生语法已经很难让人满意,Vue Mini 通过将 Vue 3 的 Composition API 引入小程序,从而解决了这些问题。Vue Mini 与原生语法也并不是二选一的关系,它们可以很好的协同工作。
Taro 2 / Rax 编译时
Taro 2 是个很有想象力的方案(Rax 编译时与 Taro 2 基本类似),你可以像写 React 一样写小程序,它通过编译时的静态分析将你的类 React 组件还原成原生语法。可是这其中有个致命的问题,React Render Function / JSX 是极其动态的,而原生语法的 Template 是比较静态的。都知道把静态模版编译为动态渲染函数不难,反过来却难如登天。也就是这个根本原因导致 Taro 2 有很多难以解决的问题,Taro 团队可能也意识到这条路是个死胡同,所以 Taro 3 抛弃了编译时方案。而 Vue Mini 目前只是一个轻量的纯运行时库,它并不依赖任何编译时手段。
Taro 3 / Remax / kbone / Rax 运行时
Taro 3 / Remax / kbone / Rax 运行时,这四个方案的实现细节略有不同,但可以大致归为同一类。
Taro 3 / kbone / Rax 运行时,这三个方案是通过模拟实现 DOM API 来让现有的 Web 框架可以跑在小程序上,最终通过模拟的 DOM API 生成 VDOM,而 Remax 是实现了一个自定义的 react-reconciler 直接承接 React 生成的 VDOM。最终它们都是将 VDOM 作为小程序组件的 data 发送给模版,然后通过模版语法在运行时暴力递归 VDOM 生成最终的 UI 树。
可以了解到这类方案是重运行时的,很暴力,但有效。可是这类方案也是带着原罪出生的,主要问题有两个:一个是这类方案都带有一个很大的运行时,一个 Hello World 小程序,Taro 3 的大小是 243KB,Remax 更是达到了 305KB,而作为对比 Vue Mini 只有 19KB,虽然这样的对比并不严谨,但大致也可以说明问题。另一个问题是这类方案有很大的性能开销,据 Rax 计算它们与原生小程序存在约 40% 的性能差距,而 Vue Mini 完全依赖小程序自己的运行时,只是在响应式数据变化时点对点的更新小程序组件的 data,因此 Vue Mini 有着与原生小程序十分接近的性能表现。
uni-app / mpvue / Megalo
uni-app / mpvue / Megalo 这三个方案也可以大致归为同一类,它们与 Vue Mini 一样都是基于 Vue 的。
但是 Vue Mini 与它们的差别也很明显,它们都是基于 Vue 2 的,而 Vue Mini 是基于 Vue 3 的,Vue 3 不论是响应式数据还是 Composition API 都比 Vue 2 要更加灵活、强大。由于 Vue 2 的限制,它们基本都维护了一份 Vue 2 的 Fork,Vue 3 将自身拆成了几个相对独立的部分,Vue Mini 直接且仅依赖 Vue 3 的响应式核心 @vue/reactivity,这避免了一些不必要的性能开销,也保证了运行时的小巧。
另外在技术偏向上,Vue Mini 与它们的选择也不同,它们是尽量靠近 Vue,努力将 Vue 渲染到小程序,而 Vue Mini 是在原生小程序基础上借助 Vue 做增强,更靠近原生小程序。这有助于减少各种各样的坑,并且能更好更无痛的使用小程序本身的各种 API。
尝试一下
使用以下命令,你可以快速搭起一个基于 Vue Mini 的小程序:
npm install -g sao@beta
# Then
sao vue-mini/template new-miniprogram
最后欢迎分享、转载本文,让更多的人知道 Vue Mini。