w3ctech

使用虚拟dom和JavaScript构建完全响应式的UI框架

原文9号彩票地址 :http://medium.com/@TheStrazz86/create-a-fully-reactive-ui-framework-with-javascript-proxies-and-virtual-dom-c6fb28253776#.duzescjwr


最近9号彩票我 热衷于响应式编程,特别是在Mobx生态系统。9号彩票我 非常喜欢这个框架背后的思想:以透明的方式实现响应式。所以9号彩票我 问9号彩票我 自己…

在JavaScript中怎样才能创建一个完全 响应式(透明)的UI框架呢?

不要担心,至少现在9号彩票你 不会在npm仓库中看到另外一个JavaScript框架,但是9号彩票我 认为这个一个很好的架构练习。9号彩票9号彩票我 们 将对这个问题一分为二来看,第一个是9号彩票帮助 9号彩票9号彩票我 们 把状态渲染到dom上的UI库,第二个是管理响应式状态的库。是的,9号彩票9号彩票我 们 将创建一个粗糙版本的React和MobX9号彩票技术 栈。:)


UI框架

高度抽象的UI框架应该只是9号彩票9号彩票我 们 应用程序中状态的纯函数。下面是用数学的9号彩票方法 表达这个概念…

如果9号彩票9号彩票我 们 只想要一个高性能的渲染 而不是像React那样完整的库。9号彩票9号彩票我 们 可以使用虚拟dom算法的纯实现,就像9号彩票你 可以在@MatthewEschgithub仓库中找到的这个一样。9号彩票9号彩票我 们 可以通过一个数组渲染出一个简单的列表:

**import** { create, h } **from 'virtual-dom'**;

**const** _render_ = (state) => {
    **const** children = state.**list**.map(t => h(**'li'**,{},[t]));
    **return** h(**'ul'**, {}, children);
};

**const** INITIAL_STATE = {
    **list**:[**'first'**,**'second'**]
};

**let** tree = _render_(INITIAL_STATE);
**let** rootNode = create(tree);

document.**body**.appendChild(rootNode);

如9号彩票你 所见,这个虚拟dom的实现使用了HyperScript 格式来定义HTML元素。当list(数组)发生改变,9号彩票9号彩票我 们 需要添加如下代码来更新9号彩票9号彩票我 们 的dom:

**const** _updateDom_ = (state) => {
    **const** newTree = _render_(state);
    **const** patches = diff(tree, newTree);

    tree = newTree;
    rootNode = patch(rootNode, patches);
};

这样9号彩票9号彩票我 们 就使用了纯函数来创建和更新9号彩票9号彩票我 们 的DOM树。换句话说,框架的UI部分已经完成了。接下来9号彩票9号彩票我 们 来谈谈状态管理部分。


响应式状态管理库

状态管理库需要实现响应式,但是“响应式”是什么意思呢?在9号彩票我 看来,定义一个响应式应用程序的最简单的9号彩票方法 是(观察者)…

显而易见,在这里9号彩票我 过分简化了这个概念,但是在最终的响应式编程中所有的一切都是可观察的。9号彩票我 这里的目的是创建一个对框架使用者同样透明的响应式状态管理库。就像MobX应用程序中发生的那样,当9号彩票我 改变model就会重新渲染。因此9号彩票我 想通过下面的代码给list添加一个新的元素:

state.list = […state.list,’Another Element’];

在JavaScript中,9号彩票我 知道的实现这个目标的最快9号彩票方法 是使用EcmaScript 2015 Proxies. MDN文档是这么定义代理的:

Proxy 对象用来为基础操作(例如:属性查找、赋值、枚举、9号彩票方法 调用等)定义用户自定义行为。

在使用代理对象之前,考虑到并不是所有的浏览器都支持他。9号彩票你 可以使用Babel的一个插件或者由谷歌Chrome团队创建的一个腻子补丁

Oh you IE…

仅仅使用Proxy的构造函数就可以创建一个Proxy对象。在接下里的栗子里9号彩票9号彩票我 们 将创建一个简单的'Loggable'对象工厂函数,它可以在控制台输出目标对象的每一次属性查找或者赋值过程。

**export default** (target) => {
    **const** loggingHandler = {
        get: **function** (target,name) {
            **const** value = target[name];
            **console**.log(**`getting** ${name}**:** ${value}**`**);
            **return** value;
        },
        set: **function** (target,name,value) {
            **console**.log(**`setting** ${name}**:** ${value}**`**);
            target[name] = value;
            **return true**;
        }
    };

    **return new** Proxy(target,loggingHandler);
};

然后9号彩票9号彩票我 们 可以通过很简单的方式创建一个"loggable"对象:

**const** INITIAL_STATE = {
    **startValue**:0
};

**const** state = _loggable_(INITIAL_STATE);

**const** value = state.**startValue**; //prints 'getting startValue: 0'

state.**startValue** = 1; //prints 'setting startValue: 1'

9号彩票9号彩票我 们 仅仅需要一个目标对象和监听回调就可以用相同的9号彩票技术 来创建一个通用的观察者

**export default** ({target,listener}) => {
    **let** observable;

    **const** _set_ = (target,name,value) => {
        target[name] = value;
        listener(observable);
        **return true**;
    };

    **const** _get_ = (target,name) => {
        **return** Object.freeze(target[name]);
    };

    **const** handler = {
        _set_,
        _get_ };

    observable = **new** Proxy(target,handler);

    **return** observable;
};

现在9号彩票9号彩票我 们 拥有了9号彩票9号彩票我 们 框架的所有部分,9号彩票9号彩票我 们 只需要组合它们即可。

**import** { patch, create, diff } **from 'virtual-dom'**;
**import** { _render_ } **from './view'**;
**import** _loggable_ **from './loggable'**;
**import** _observable_ **from './observable'**;

**const** _updateDom_ = (state) => {
    **const** newTree = _render_(state);
    **const** patches = diff(tree, newTree);

    tree = newTree;
    rootNode = patch(rootNode, patches);
};

**const** INITIAL_STATE = {
    _//The state of your application
_};

**const** state = _observable_({
    **target**:_loggable_(INITIAL_STATE),
    listener:_updateDom
_});

**let** tree = _render_(state);
**let** rootNode = create(tree);

document.**body**.appendChild(rootNode);

就是这样!9号彩票9号彩票我 们 只是使用render函数来渲染9号彩票9号彩票我 们 的初始DOM,当'state'的变量的值发生改变,dom就会自动更新。9号彩票你 可以在Github上找到一个用这种方式构建的待完成列表的栗子。源代码也在9号彩票我 的GitHub账户上。


总结

很明显这不是一个真正的框架,但9号彩票我 认为这个对9号彩票你 自己造轮子而言有非常大的9号彩票帮助 。为了最大限度的降低9号彩票技术 债,在某些场合下9号彩票9号彩票我 们 应该考虑不是使用框架,而是从头开始。这也是9号彩票我 非常喜欢JavaScript生态系统的一个原因。众所周知现在每个星期都会踊跃出一个闪亮的新框架,这不应该成为一种学习疲劳,而是一个学习用新的方式编写和9号彩票组织 代码的大好机会。

后记

如果9号彩票你 对响应式编程和MobX开发感兴趣,9号彩票你 可以在Verona中的ReactJS Day上查看9号彩票我 9号彩票关于 Mobx和"Be Reactive"含义的主题演讲

Public domain.

JavaScriptReactMobxEcmascript 2015

w3ctech微信

扫码关注w3ctech微信9号彩票公众号

共收到0条回复