Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

一次简单的Virtual DOM实验 #88

Open
FrankKai opened this issue Aug 6, 2018 · 6 comments
Open

一次简单的Virtual DOM实验 #88

FrankKai opened this issue Aug 6, 2018 · 6 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Aug 6, 2018

这次实验基于Matt-Esch提供的实现了Virtual DOM算法的virtual dom 模型库,主要目的在于对Virtual DOM有一个粗略的认识。
基础认识:

  • Virtual DOM的目的在于加速rerender

主要内容:

  • element creation
  • diff computation
  • patch operations
@FrankKai
Copy link
Owner Author

FrankKai commented Aug 7, 2018

动机

手动的DOM操作是很麻烦的,如果在对之前的DOM状态进行追踪就更是难上加难。解决这个问题的一个办法:写代码,就像无论什么时候状态发生变化,重新创建整个DOM一样。当然,如果每次应用状态更新的时候,你真正的重新创建了整个DOM,你的app将会非常非常慢,而且你的当前输入框将会失去焦点。

virtual-dom是一个模块集,这个模块集声明式代表应用中的DOM节点。所以与在应用状态更新时更新DOM不同,你可以简单地创建一个虚拟树或者VTree,这个树可以有和你预期一样的DOM状态。virtual-dom会在不重复创建DOM节点的情况下,明确指出如何去构建DOM树。

virtual-dom允许你在状态发生变化时升级view,它升级view的方式是创建一个完整的view的VTree,然后按照你描述的为DOM打补丁。这样将保持手动的DOM操作和之前您的应用程序代码的状态跟踪,为Web应用程序提升干净和可维护的呈现逻辑。

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 7, 2018

var h = require('virtual-dom/h');
var diff = require('virtual-dom/diff');
var patch = require('virtual-dom/patch');
var createElement = require('virtual-dom/create-element');

// 1:创建一个函数,它用来声明DOM的外观
function render(count)  {
    return h('div', {
        style: {
            textAlign: 'center',
            lineHeight: (100 + count) + 'px',
            border: '1px solid red',
            width: (100 + count) + 'px',
            height: (100 + count) + 'px'
        }
    }, [String(count)]);
}

// 2: 初始化文档
var count = 0;      // 我们需要一些app数据,这里我们只存一个count值

var tree = render(count);               // 我们需要一个初始化树
var rootNode = createElement(tree);     // 创建一个初始化的根DOM节点,并且将树绑定上去
document.body.appendChild(rootNode);    // 将这个绑定了树的DOM根节点,添加到当前的文档中

// 3: 升级DOM树的逻辑
setInterval(function () {
      count++;

      var newTree = render(count);
      var patches = diff(tree, newTree);
      rootNode = patch(rootNode, patches);
      tree = newTree; //这里是为了做下一次DOM更新
}, 1000);

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 7, 2018

DOM model

virtual-dom 暴露了一个代表DOM节点的对象集。"Document Object Model Model"也许看起来有些奇怪,但是它就是这么回事。**它的意思是:原生的Javascript 树状结构,代表了原生的DOM节点树。**这也就是VTree。

我们可以使用一种冗余的方式去直接使用对象创建VTree,或者我们可以使用更多的简洁的virtual-hyperscript。

例子-使用对象直接创建VTree

var VNode = require('virtual-dom/vnode/vnode');
var VText = require('virtual-dom/vnode/vtext');

function render(data) {
    return new VNode('div', {
        className: "greeting"
    }, [
        new VText("Hello " + String(data.name))
    ]);
}

module.exports = render;

例子-使用virtual-hyperscript创建VTree

var h = require('virtual-dom/h');
function render(data) {
    return h('.greeting', ['Hello ' + data.name]);
}
module.exports = render;

DOM模型的设计初衷是为了高效创建和读取表单。我们不创建真是的DOM树的原因在于,创建DOM节点并且读取节点的属性十一个非常昂贵的操作,我们应该避免这样做。读取一些DOM节点属性甚至可能导致一些副作用,所以基于真实的DOM节点去重新创建整个DOM数据结构,对于高性能的渲染是不合适的,而且也不容易推理。

VTree的设计初衷,是为了保证数据结构不变性。当它实际上可变时,你可以重写在多个地方重用节点,重用我们暴露出去的接收VTree作为参数但是不会改变树的函数。我们可以冻结模型中的对象,但是不能提高效率。

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 9, 2018

Element creation

createElement(tree:VTree) -> DOMNode

鉴于我们已经创建了VTree,因此我们需要一些方式去将这个虚拟树转换成一个真实的DOM树。这个函数是由create-element.js。第一次渲染的时候,我们将传递完整的VTree给create-element函数去创建等效的DOM节点。

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 9, 2018

Diff computation

diff(previous:VTree, current:VTree) -> PatchObject

虚拟DOM的主要动机在于:允许我们写出与之前状态完全独立的代码。所以当我们的应用树状态改变的时候,我们将生成一个新的VTree。diff函数创建了一个DOM补丁集合,这个集合基于当前的VTree和之前的VTree,并且更新之前的VTree去匹配当前的VTree。

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 9, 2018

Patch operations

patch(rootNode:DOMNode, patches:PatchObject) -> DOMNode newRootNode

一旦我们计算出补丁集并且应用到DOM上,我们需要一个函数去应用这些补丁。这由patch函数去做。有一个DOM根节点和一个DOM补丁集,patch函数将更新DOM。应用补丁到DOM后,DOM就和VTree表现一致了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant