用vue3公开的思路从0实现最简化的vue(05-02更新)

名词解释

  1. 浏览器标准:本文中做为es六、css三、h5的统称

咱们为何要学习vue的实现

咱们用vue,是由于它是当前业界最佳的解决方案之一,但前端技术方案迭代及工业标准化发展的浪潮,大几率不会在vue这里到达终点。css

jquery没有死,它的基因已经注入浏览器标准。而webcomponent shadow dom的灵感,一样有受到vue和react的vdom技术的启发。固然vue也在进化,但要知道,vue的竞争对手,并非react,而是浏览器的标准化进程。当webcomponent成为主流,咱们如今津津乐道的vdom技术也就完成了过渡的使命。html

因此咱们要有危机感和好奇心,不能过渡依赖vue。咱们须要搞清楚在Vue的黑盒中,都作了什么magic。同时,咱们也能学习到不少现代软件工程的方法和设计模式,好比tdd、代理模式、观察者模式、封装和解耦的艺术。前端

Vue源码为何难读

  1. 功能繁多

咱们读vue源码的目的,首先是想了解全貌及部分核心feature的实现,好比双向绑定、vnode渲染等。而Vue源码中包含了太多非核心代码,好比keep alive、dynamic component、functional component等,这些并非不重要,仅仅是咱们暂时对它的实现并不太感兴趣,这些代码的干扰会阻碍咱们对核心部分的理解。vue

  1. 向后兼容

Vue源码中包含大量向后兼容代码,但随着浏览器标准从主流框架中吸收精华,以及主流浏览器向浏览器标准的靠拢,vue做为框架所承担的责任会愈来愈小,举个最简单的例子,用ES6的Proxy实现双向绑定,就比Vue2.x中defineProperty的hack作法要简洁不少。而当webcomponent火候成熟,vue甚至连vdom都不用作了。因此vue3的实现必定会比vue2简单,这已是官方肯定的。我判断vue4的实现必定会更精简,而这个趋势一直持续到vue退出历史舞台。node

为何要从0写一个vue

既然咱们要学习vue的实现,而vue的源码又包含太多的噪音,难以梳理。纵观全网,虽然源码分析的文章和教程层出不穷,但思路大多依然埋没在vue代码的复杂细节中。那么,咱们为什么不干掉不感兴趣的功能,不care浏览器兼容性,只用最新的技术,拨开迷雾去写一个最简化的Vue呢?react

此项目将按Vue3.0公开的思路,用测试驱动开发的方法,一步一步写一个最简化的Vue,我会尽可能确保每个commit都容易理解。建议跟随下面步骤作,你将会对Vue有个清晰的理解。jquery

测试驱动开发~why?

软件工程上,按个人理解,彻底自上而下的设计(瀑布模型),已是过期的方法。即便超大型计算机项目,好比操做系统级别的工程,也是宏观自上而下,微观上下结合(敏捷开发)。而TDD是一种上下结合的编程实践,对于每一个模块,首先设计测试用例,再写代码实现出来。有如下好处:css3

  1. 控制质量,便于回归测试,提升开发效率
  2. test case即文档
  3. 粗略的顶层设计后(包括产品设计和技术设计),便可自下而上开始编程,避免过渡设计

步骤

因为tdd具备强大的test case即文档的基因,因此要理解每一步作了什么事情,只须要看对应的test case代码便可。git

下面每步将按diff的形式给出,必要的地方会加comment。大段总体的阐述会写在diff页面的底部评论区,针对某段代码的comment会穿插其中。欢迎留言和提issue。es6

阶段1: Basic

这部分,咱们从0作起,实现一些基础feature,不求作到完善

  1. TDD环境搭建

  2. Data代理

  3. 实现$watch

  4. html原生元素渲染

  5. 支持method

  6. 支持lifecycle

  7. html原生元素事件监听

  8. 实现mvvm

  9. 阶段1成果:Basic Demo

运行npm run test后点击弹出浏览器页面中的DEBUG按钮便可看到效果

阶段2: 完善mvvm

这部分咱们完善mvvm到实现vue2.x的全部feature,并实现vue3.0公开的一个重要feature, 官方说法是"Detection of property addition / deletion",在vue2.x中,咱们须要用$set。

  1. [mvvm]data支持深层object

  2. [mvvm]data中object支持新增属性

  3. [mvvm]data中object支持删除属性

  4. [mvvm]支持array

  5. 阶段2成果:mvvm in depth demo

运行npm run test后点击弹出浏览器页面中的DEBUG按钮便可看到效果

  1. 简单重构和补充注释

整理心情,再出发

阶段3: 支持组件

  1. 支持属性

  2. 实现组件渲染

  3. 实现event&action

阶段4: Computed & watch

  1. 计算属性

  2. 实现watch

  3. 重构:优雅实现代理和依赖收集

  4. 实现$nextTick

  5. 实现Watcher调度

为了解决改变多个data会触发屡次render的问题

var cb = jasmine.createSpy('cb');

var vm = new Vue({
  data () {
    return {
      a:1,
      b:2,
    }
  },
  render (h) {
    cb()
    return h('p', null, this.a + this.b)
  }
}).$mount()

expect(cb).toHaveBeenCalledTimes(1)

vm.a = 10
vm.b = 11
setTimeout(_ => {
  expect(cb).toHaveBeenCalledTimes(2) // change 'a' and 'b' only trigger one render
  done()
})
复制代码

Todo list

  1. Patch

  2. Scoped Slot

补充

virtual dom vs shadow dom

上面对于virtual dom和shadow dom有些混淆,为了避免误导你们,这里推荐两篇文章

  1. develoger.com/shadow-dom-…

  2. svelte.dev/blog/virtua…

因此,将来vue的发展有两种可能:

  1. 可能还会作virtual dom,但virtual dom实际渲染出来会是一个web component,如今vue的scoped css,也将被shadow dom取代。

  2. 但还有一种可能。virtual dom存在的意义是因为考虑到操做dom开销大,因此要merge对dom操做。但操做shadow dom会快不少,全部我对将来virtual dom是否还有必要保持怀疑。

做者其它文章

  1. ES6类继承原理及缺陷