资讯专栏INFORMATION COLUMN

用JavaScript自己写MVVM前端框架-VM实现篇

VincentFF / 1203人阅读

摘要:关于前端框架大家都有了解,或多或少的使用过,比如,,等等。那么你是否也想自己手写一个的前端框架呢,我们从入手,手把手教你写基于的前端框架,在整个编写的过程中,希望大家学习更多,理解更多。本节我们以打包工具结合转换插件实现数据的抽象。

关于MVVM前端框架大家都有了解,或多或少的使用过,比如Angular,React,VUE等等。那么你是否也想自己手写一个MVVM的前端框架呢,我们从Virtual DOM入手,手把手教你写基于Virtual DOM的前端框架,在整个编写的过程中,希望大家学习更多,理解更多。
Github代码: https://github.com/chalecao/v...

章节1: 认识DOM与VirtualDOM

真实的DOM是网页上的文档对象模型,由一个个HTML元素节点构成的树形结构。

如图中所示,我们用JS创建出来的节点就是虚拟节点,Virtual node,当然由这些虚拟节点vd构成的树形结构就称为虚拟DOM,Virtual DOM。我们本节课介绍的就是要如何创建这样的虚拟DOM。

章节2: 如何构建VirtualDOM

首先我们需要分析一个node节点的构成,比如他的节点类型type,节点属性的集合props,子元素的集合。这样我们就可以抽象一个数据模型来表示这个节点。虚拟DOM是由许多虚拟节点按照层级结构组合起来的,那么我们实现虚拟节点的数据模型抽象之后,就可以构建虚拟DOM的数据模型抽象。

手工实现DOM模型构建不太合理,我们可以借助JSX的工具来完成这个转换。本节我们以rollup打包工具结合babel转换插件实现数据的抽象。具体代码配置参考:github中package.json配置和rollup.config.js

const vdom = (
    
hello
)

上面我们定义的vdom片段采用JSX处理器处理后如下面代码:

/* fed123.com */
"use strict";

var vdom = vnode(
    "div",
    { id: "_Q5", style: "border: 1px solid red;" },
    vnode(
        "div",
        { style: "text-align: center; margin: 36px auto 18px; width: 160px; line-height: 0;" },
        vnode("img", { src: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_160x56dp.png", height: "56", style: "border: none; margin: 8px 0px;" }),
        "google"
    )
);

是不是很好理解,JSX编译后会自动根据定义好的语法格式提取出元素的类型和属性和子元素,并填入vnode方法中,我们只需要实现vnode方法就可以。我们可以编写vnode方法用于构建虚拟节点的模型,编写createElement方法用于根据vnode模型创建元素。并且把vnode的子元素追加到父元素上,形成树形层级结构。

function vnode(type, props, ...children) {
    return { type, props, children };
  }

function createElement(node) {
    if (typeof node === "string") {
        return document.createTextNode(node);
    }
    const $el = document.createElement(node.type);
    node.children
        .map(createElement)
        .forEach($el.appendChild.bind($el));
    return $el;
}
document.body.appendChild(createElement(vdom));

这样我们就完成了虚拟节点vnode和虚拟vDOM的构建。

章节3: Diff VirtualDOM 与Update DOM

如图展示了最简单的一层DOM的结构变化,无非也就这么几种:增加元素节点、修改节点,删除节点。我们可以基于DOM API来实现这些基本的操作,代码如下:

function updateElement($parent, newnode, oldnode) {
    var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

    if (!newnode) {
        $parent.removeChild($parent.childNodes[index]);
    } else if (!oldnode) {
        $parent.appendChild(createElement(newnode));
    } else if (isChange(newnode, oldnode)) {
        $parent.replaceChild(createElement(newnode), $parent.childNodes[index]);
    } else if (newnode.type) {
        var newL = newnode.children.length;
        var oldL = oldnode.children.length;
        for (var i = 0; i < newL || i < oldL; i++) {
            updateElement($parent.childNodes[index], newnode.children[i], oldnode.children[i], i);
        }
    }
}

上面的代码中我们实际上是把diff VirtualDOM 和update vdom放在一起处理了,采用了深度优先遍历的算法,从根节点优先查到子节点,判断子节点是否变化,有变化就进行变更处理,然后再回到上级节点。

章节4: 处理DOM属性和事件绑定
{
  type: “div”,
  props: {“style”: ”…”}, 
  children: [
      {type: “img”, props: {“src”: ”…”}
]}

上面我们抽取的vnode的模型中已经把props拿出来了,我们这里需要把这些样式设置到对应元素上就好了。我们先看下元素的属性变化有哪几种情况:

如上,元素属性可以增加可以减少,我们通过DOM API实现属性的更新操作,代码如下:

//handle props

function setProp($el, name, value) {
    if (typeof value == "boolean") {
        handleBoolProp($el, name, value);
    } else {
        $el.setAttribute(name, value);
    }
}
function handleBoolProp($el, name, value) {
    if (!!value) {
        $el.setAttribute(name, value);
        $el[name] = !!value;
    } else {
        $el[name] = !!value;
    }
}
function removeProp($el, name, value) {
    if (typeof value == "boolean") {
        $el[name] = false;
    }
    $el.removeAttribute(name, value);
}
function updateProp($el, name, newvalue, oldValue) {
    if (!newvalue) {
        removeProp($el, name, oldValue);
    } else if (!oldValue || newvalue != oldValue) {
        setProp($el, name, newvalue);
    }
}
function updateProps($el) {
    var newprops = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    var oldProps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

    var _props = Object.assign({}, newprops, oldProps);
    Object.keys(_props).forEach(function (key) {
        updateProp($el, key, newprops[key], oldProps[key]);
    });
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107714.html

相关文章

  • JavaScript 数据绑定实现一个简单的 MVVM

    摘要:和刷新函数是一对多的关系,即一个可以有任意多个处理它的回调函数刷新函数,比如和两个指令共用一个数据模型字段。添加数据订阅实现方式为建立缓存回调函数的数组缓存回调函数当数据模型的字段发生改变时,就会触发缓存数组中订阅了的所有回调。 MVVM 是 Web 前端一种非常流行的开发模式,利用 MVVM 可以使我们的代码更专注于处理业务逻辑而不是去关心 DOM 操作。目前著名的 MVVM 框架有...

    hzx 评论0 收藏0
  • 基于Vue的MVVM学习笔记

    摘要:发布订阅现在每个人应该都用微信吧,一个人可以关注多个公众号,多个人可以同时关注相同的公众号。公众号每周都会更新内容,并推送给我们,把写好的文章在微信管理平台更新就好了,点击推送,就相当于发布。 什么是MVVM MVVM——Model-View-ViewModle的缩写,MVC设计模式的改进版。Model是我们应用中的数据模型,View是我们的UI层,通过ViewModle,可以把我们M...

    Alan 评论0 收藏0
  • 【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理

    摘要:模块则负责维护,以及各个模块间的调度思考题了解了的实现机制,你能否自己动手也试着用百来行代码实现一个库呢好了本教程第一部分设计篇就写到这里,具体请移步下一篇教学向行代码教你实现一个低配版的库代码篇我会用给出一版实现。 适读人群 本文适合对MVVM有一定了解(如有主流框架ng,vue等使用经验配合本文服用则效果更佳),虽然会用这类框架,但是对框架底层核心实现又不太清楚,或者能说出个所以然...

    selfimpr 评论0 收藏0
  • JavaScript 进阶之深入理解数据双向绑定

    摘要:当我们的视图和数据任何一方发生变化的时候,我们希望能够通知对方也更新,这就是所谓的数据双向绑定。返回值返回传入函数的对象,即第一个参数该方法重点是描述,对象里目前存在的属性描述符有两种主要形式数据描述符和存取描述符。 前言 谈起当前前端最热门的 js 框架,必少不了 Vue、React、Angular,对于大多数人来说,我们更多的是在使用框架,对于框架解决痛点背后使用的基本原理往往关注...

    sarva 评论0 收藏0
  • React 可视化开发工具 Shadow Widget 非正经入门(之一:React 三宗罪)

    摘要:前言非正经入门是相对正经入门而言的。不过不要紧,正式学习仍需回到正经入门的方式。快速入门建议先学会用拼文写文档注册一个账号,把库到自己名下,然后用这个库写自己的博客,参见这份介绍。会用拼文写文章,相当于开发已入门三分之一了。 本系列博文从 Shadow Widget 作者的视角,解释该框架的设计要点,既作为用户手册的补充,也从更本质角度帮助大家理解 Shadow Widget 为什么这...

    dongxiawu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<