yuxino / source

🌟 The source code analysis
3 stars 0 forks source link

Snabbdom 了解一下 #5

Open yuxino opened 6 years ago

yuxino commented 6 years ago

Snabbdom 是Vue使用到的virtual DOM库。最近在看Vue的源码所以先学习一下这个库的用法。

Snabbdom给自己的定位是简单,模块化,强大高性能的virtual dom库。

为什么我们需要Virtual DOM

因为Virtual Dom非常棒。它允许我们根据应用程序的状态来表达应用程序的视图。但是现有的解决方案太臃肿、太慢、缺少特性、API偏向OOP或缺少我需要的特性

介绍 Snabbdom

Snabbdom 由极其简单高性能和可扩展的内核(非常小大概只有200 SLOC)组成。它提供了一个模块化体系结构,通过定制模块为扩展提供了丰富的功能。为了保持核心的简单,所有非必要的功能都委托给了模块。

你可以随心所欲地制造你需要的Snabbdom !挑选、选择和定制您想要的功能。或者,您可以只使用默认扩展,获得一个具有高性能、小尺寸和下面列出的所有功能的虚拟DOM库。

使用案例

var snabbdom = require('snabbdom');
var patch = snabbdom.init([ // Init patch function with chosen modules
  require('snabbdom/modules/class').default, // makes it easy to toggle classes
  require('snabbdom/modules/props').default, // for setting properties on DOM elements
  require('snabbdom/modules/style').default, // handles styling on elements with support for animations
  require('snabbdom/modules/eventlisteners').default, // attaches event listeners
]);
var h = require('snabbdom/h').default; // helper function for creating vnodes

var container = document.getElementById('container');

var vnode = h('div#container.two.classes', {on: {click: someFn}}, [
  h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),
  ' and this is just normal text',
  h('a', {props: {href: '/foo'}}, 'I\'ll take you places!')
]);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);

var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [
  h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),
  ' and this is still just normal text',
  h('a', {props: {href: '/bar'}}, 'I\'ll take you places!')
]);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state

核心文档

Snabbdom的核心只提供最基本的功能。它被设计得尽可能简单,同时仍然快速和可扩展。

snabbdom.init

初始化,非常好用。自定义Module,可以拿来处理各种属性。class,style,可以自己扩展。

var patch = snabbdom.init([
  require('snabbdom/modules/class').default,
  require('snabbdom/modules/style').default,
]);

patch

patch 是通过init创建的。接受两个参数。第一个是表示当前视图的DOM元素或vnode。第二个是表示新的,需要更新视图的vnode。

第一个参数可以是VDOM也可以是真实的DOM。如果是真实的DOM的话,调用Patch会把第二个参数的NewVdom创建成真实的DOM。如果第一个参数是VDOM,那么调用的时候会高效的替换掉不同的地方。

oldVdom需要被传进来,因为Snabbdom把需要的信息存在了Vdom里面。这使得实现一个更简单、性能更高的体系结构成为可能。这也避免了创建新的oldVnode树。

patch(oldVnode, newVnode);

snabbdom/h

建议通过h来创建元素。h接收tag或者selector,一个可选的options,可选的数据对象和可选的子字符串或数组。

var h = require('snabbdom/h').default;
var vnode = h('div', {style: {color: '#000'}}, [
  h('h1', 'Headline'),
  h('p', 'A paragraph'),
]);

snabbdom/tovnode

将DOM节点转换为虚拟节点。特别适合预先存在的服务器端生成的内容。

var snabbdom = require('snabbdom')
var patch = snabbdom.init([ // Init patch function with chosen modules
  require('snabbdom/modules/class').default, // makes it easy to toggle classes
  require('snabbdom/modules/props').default, // for setting properties on DOM elements
  require('snabbdom/modules/style').default, // handles styling on elements with support for animations
  require('snabbdom/modules/eventlisteners').default, // attaches event listeners
]);
var h = require('snabbdom/h').default; // helper function for creating vnodes
var toVNode = require('snabbdom/tovnode').default;

var newVNode = h('div', {style: {color: '#000'}}, [
  h('h1', 'Headline'),
  h('p', 'A paragraph'),
]);

patch(toVNode(document.querySelector('.container')), newVNode)

Hook

snabbdom 提供了很多的hook方便我们做自己爱做的事情。

Hook的名字 触发时间 回调参数
pre 开始patch none
init vnode被添加的时候 vnode
create 已经基于vnode创建了一个DOM元素
emptyVnode, vnode
insert 元素被插入到DOM里面 vnode
prepatch patch完成之前 oldVnode, vnode
update 元素被更新的时候 oldVnode, vnode
postpatch patch完成之后 oldVnode, vnode
destroy 元素被直接或间接移除 vnode
remove 元素被直接或间接从DOM移除 vnode, removeCallback
post patch 完成的时候触发 none

使用方法

h('div.row', {
  key: movie.rank,
  hook: {
    insert: (vnode) => { movie.elmHeight = vnode.elm.offsetHeight; }
  }
});

Hook Module

我们可以创建一个Modulel来处理Hook

var myModule = {
  create: function(oldVnode, vnode) {
    // invoked whenever a new virtual node is created
  },
  update: function(oldVnode, vnode) {
    // invoked whenever a virtual node is updated
  }
};

总结

剩下的部分都是在讨论Module的作用,这里我们不再深究,直接看Module源码也大概知道个所以然。大体上我们知道了Snabbdom怎么用和是做什么的已经够了,剩下的是继续研究VUE的代码了。

Vue参照Snabbdom自己实现了一个Patch。所以我们会发现Vue的Vdomw文件夹和Snabbdom基本上是大同小异的。但是会更加复杂添加了更多定制化的功能。


19年2月25日

今天出于好奇又重新看了一下算法。 其实算法的核心就是尽可能的利用相同标签的元素。通过四个索引找到一样的标签,对于正常的情况来说,效果还不错,但是对于极端的情况,乱序的标签,可能效果就不怎么好。很难找到可以重用的。Key的作用主要是位移元素。可以提升一些效率。