虚拟dom

  1. 用js对象来表示dom树的结构,然后用这个对象来构建一个真正的dom树插入文档中;
  2. 当状态有变时,重新构造一个新的对象树,然后比较新的和旧的树,记录两个数的差异;
  3. 把差异部分应用到真正的dom树上,更新视图。

核心算法实现(diff算法)

  1. 用js对象表示dom树

    var e = {
    tagName: 'ul',
    props: {
    id: 'list'
    },
    children: [
    {tagName: 'li', props: {class: 'li1'}, children: ['item1']},
    {tagName: 'li', props: {class: 'li2'}, children: ['item2']},
    {tagName: 'li', props: {class: 'li3'}, children: [
    {tagName: 'h2', props: {class: 'h'}, children: ['hello qq']}
    ]},
    ]
    }
  2. 把js对象渲染成dom树
    function dom(tagName, props, children){
    function Element(tagName, props, children){
    this.tagName = tagName;
    this.props = props;
    this.children = children;
    }
    Element.prototype.render = function(){
    const el = document.createElement(this.tagName);
    const props = this.props;
    for(let key in props){
    el.setAttribute(key, props[key]);
    }
    const children = this.children || [];
    children.forEach(child => {
    const c = child.tagName ? new Element(child.tagName, child.props, child.children).render() : document.createTextNode(child);
    el.appendChild(c);
    })
    return el;
    }
    return new Element(tagName, props, children);
    }
  3. 比较两个虚拟dom树的差异,同层节点进行比较(时间复杂度O(n));
    对每一个树在深度优先遍历的时候,每遍历到一个节点就把该节点和新的的树进行对比,把差异部分记录到一个对象里面。
    // diff 函数,对比两棵树
    function diff (oldTree, newTree) {
    var index = 0 // 当前节点的标志
    var patches = {} // 用来记录每个节点差异的对象
    dfsWalk(oldTree, newTree, index, patches)
    return patches
    } // 对两棵树进行深度优先遍历
    function dfsWalk (oldNode, newNode, index, patches) {
    // 对比oldNode和newNode的不同,记录下来
    patches[index] = [...] diffChildren(oldNode.children, newNode.children, index, patches)
    } // 遍历子节点
    function diffChildren (oldChildren, newChildren, index, patches) {
    var leftNode = null
    var currentNodeIndex = index
    oldChildren.forEach(function (child, i) {
    var newChild = newChildren[i]
    currentNodeIndex = (leftNode && leftNode.count) // 计算节点的标识
    ? currentNodeIndex + leftNode.count + 1
    : currentNodeIndex + 1
    dfsWalk(child, newChild, currentNodeIndex, patches) // 深度遍历子节点
    leftNode = child
    })
    }
  4. 因为步骤一所构建的 JavaScript 对象树和render出来真正的DOM树的信息、结构是一样的。所以我们可以对那棵DOM树也进行深度优先的遍历,遍历的时候从步骤二生成的paches对象中找出当前遍历的节点差异,然后进行 DOM 操作。
    function patch (node, patches) {
    var walker = {index: 0}
    dfsWalk(node, walker, patches)
    } function dfsWalk (node, walker, patches) {
    var currentPatches = patches[walker.index] // 从patches拿出当前节点的差异 var len = node.childNodes
    ? node.childNodes.length
    : 0
    for (var i = 0; i < len; i++) { // 深度遍历子节点
    var child = node.childNodes[i]
    walker.index++
    dfsWalk(child, walker, patches)
    } if (currentPatches) {
    applyPatches(node, currentPatches) // 对当前节点进行DOM操作
    }
    } function applyPatches (node, currentPatches) {
    currentPatches.forEach(function (currentPatch) {
    switch (currentPatch.type) {
    case REPLACE:
    node.parentNode.replaceChild(currentPatch.node.render(), node)
    break
    case REORDER:
    reorderChildren(node, currentPatch.moves)
    break
    case PROPS:
    setProps(node, currentPatch.props)
    break
    case TEXT:
    node.textContent = currentPatch.content
    break
    default:
    throw new Error('Unknown patch type ' + currentPatch.type)
    }
    })
    }

Vue之MVVM简单实现

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MVVM</title>
</head>
<body>
<div id='app'>
<h2>{{ song }}</h2>
<h3>{{ obj.name }}</h3>
<h3>{{ obj.a }}</h3>
<h3>{{ obj.obj1.name1 }}</h3>
<input type="text" v-model='msg'>
<h3>{{ msg }}</h3>
<h2>{{ total }}</h2>
</div>
<script src="index.js"></script>
<script>
let vm = new Vm({
el: '#app',
data(){
return {
song: 'Time',
obj: {
name: 'xxx',
a: 20,
b: 3,
obj1: {
name1: 'll'
}
},
msg: 'hello'
}
},
computed: {
total() {
return this.obj.a * this.obj.b;
}
},
mounted() {
console.log(this.$el)
console.log('everything is done')
}
})
</script>
</body>
</html>
function Vm(opts = {}){
this.$opts = opts;
let data = this.$data = this.$opts.data(); initComputed.call(this); // 数据监测
observer(data);
for (let key in data) {
Object.defineProperty(this, key, {
configurable: true,
get() {
return this.$data[key];
},
set(newVal) {
this.$data[key] = newVal;
}
});
} // 数据编译
new Compile(opts.el, this);
opts.mounted.call(this);
} function initComputed(){
let vm = this;
let computed = this.$opts.computed;
Object.keys(computed).forEach(key => {
Object.defineProperty(vm, key, {
get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
set(){}
})
})
} function observer(data) {
if(!data || typeof data !== 'object') return;
return new Observer(data);
} function Observer(data) {
let dep = new Dep();
for (let key in data) {
let val = data[key];
observer(val);
Object.defineProperty(data, key, {
configurable: true,
get() {
Dep.target && dep.addSub(Dep.target);
return val;
},
set(newVal) {
if(val === newVal) return;
val = newVal;
observer(newVal);
dep.notify();
}
})
}
} function Compile(el, vm){
vm.$el = document.querySelector(el);
var fragment = document.createDocumentFragment();
var child;
while(child = vm.$el.firstChild) {
fragment.appendChild(child);
}
function replace(fragment){
Array.from(fragment.childNodes).forEach(item => {
let text = item.textContent;
let reg = /\{\{(.*?)\}\}/g;
if(item.nodeType === 3 && reg.test(text)){
// 重点重点重点!!!!!!
// 去掉空格!!!!!!!!
function replaceTxt() {
item.textContent = text.replace(reg, (matched, placeholder) => {
console.log(placeholder); // 匹配到的分组 如:song, album.name, singer...
new Watcher(vm, placeholder.trim(), replaceTxt); // 监听变化,进行匹配替换内容
return placeholder.trim().split('.').reduce((val, key) => {
return val[key];
}, vm);
});
};
replaceTxt();
}
if(item.nodeType === 1){
let itemAttr = item.attributes;
Array.from(itemAttr).forEach(attr => {
let name = attr.name;
let exp = attr.value;
if(name.includes('v-')){
item.value = vm[exp];
}
new Watcher(vm, exp, newVal => {
item.value = newVal;
})
item.addEventListener('input', e => {
vm[exp] = e.target.value;
})
})
}
if(item.childNodes && item.childNodes.length){
replace(item);
}
})
}
replace(fragment);
vm.$el.appendChild(fragment);
} // 发布订阅
function Dep(){
this.subs = [];
}
Dep.prototype = {
addSub(sub){
this.subs.push(sub);
},
notify(){
this.subs.forEach(sub => {
sub.update()
});
}
}
function Watcher(vm, exp, fn){
this.fn = fn;
this.vm = vm;
this.exp = exp;
Dep.target = this;
let arr = exp.split('.');
let val = vm;
arr.forEach(key => {
val = val[key];
});
Dep.target = null;
}
Watcher.prototype.update = function(){
let arr = this.exp.split('.');
let val = this.vm;
arr.forEach(key => {
val = val[key]; // 通过get获取到新的值
});
this.fn(val);
}
// let watcher = new Watcher(() => {
// console.log('watch')
// })
// let dep = new Dep();
// dep.addSub(watcher);
// dep.addSub(watcher);
// dep.notify();

virtual dom & mvvm的更多相关文章

  1. 个人对于Virtual DOM的一些理解

    之前一直认为react的Virtual DOM操作会比传统的操作DOM要快,这其实是错误的,React 从来没有说过 "React 比原生操作 DOM 快".如果没有 Virtua ...

  2. 抛开react,如何理解virtual dom和immutability

    去年以来,React的出现为前端框架设计和编程模式吹来了一阵春风.很多概念,无论是原本已有的.还是由React首先提出的,都因为React的流行而倍受关注,成为大家研究和学习的热点.本篇分享主要就聚焦 ...

  3. 深度剖析:如何实现一个 Virtual DOM 算法

    本文转载自:https://github.com/livoras/blog/issues/13 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步 ...

  4. Virtual DOM 算法

    前端 virtual-dom react.js javascript 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...

  5. virtual dom的实践

    最近基于virtual dom 写了一个小框架-aoy. aoy是一个轻量级的mvvm框架,基于Virtual DOM.虽然现在看起来很单薄,但我做了完善的单元测试,可以放心使用.aoy的原理可以说和 ...

  6. 深度理解 Virtual DOM

    目录: 1 前言 2 技术发展史 3 Virtual DOM 算法 4 Virtual DOM 实现 5 Virtual DOM 树的差异(Diff算法) 6 结语 7 参考链接 1 前言 我会尽量把 ...

  7. Virtual DOM 系列一:认识虚拟DOM

    1. 什么是Virtual DOM? Virtual DOM(虚拟DOM)是指用JS模拟DOM结构.本质上来讲VD是一个JS对象,并且至少包含三个属性:tag(html标签),props(标签的属性, ...

  8. 实现一个 Virtual DOM 算法

    1 前言 本文会在教你怎么用 300~400 行代码实现一个基本的 Virtual DOM 算法,并且尝试尽量把 Virtual DOM 的算法思路阐述清楚.希望在阅读本文后,能让你深入理解 Virt ...

  9. Virtual DOM 虚拟DOM的理解(转)

    作者:戴嘉华 转载请注明出处并保留原文链接( #13 )和作者信息. 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...

随机推荐

  1. 17.如何网页发起QQ聊天

    1.被发起的QQ需要再QQ推广开启QQ在线状态http://shang.qq.com/v3/widget.html 2.html代码 <html> <head></hea ...

  2. Spark2.0机器学习系列之1: 聚类算法(LDA)

    在Spark2.0版本中(不是基于RDD API的MLlib),共有四种聚类方法:      (1)K-means      (2)Latent Dirichlet allocation (LDA)  ...

  3. POJ2318:TOYS(叉积判断点和线段的关系+二分)&&POJ2398Toy Storage

    题目:http://poj.org/problem?id=2318 题意: 给定一个如上的长方形箱子,中间有n条线段,将其分为n+1个区域,给定m个玩具的坐标,统计每个区域中的玩具个数.(其中这些线段 ...

  4. Java中树的存储结构实现

    一.树 树与线性表.栈.队列等线性结构不同,树是一种非线性结构. 一棵树只有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树了,而是多棵树的集合,也被称为森林. 二.树的父节点表示法 树中除 ...

  5. Way to MongoDB

    1.MongoDB术语/概念:database,collection,document,field,indexSQL术语/概念:database,table,row,column,index 2.所有 ...

  6. Redis持久化及复制

    一.持久化的两种方式 1.RDB: RDB是在指定时间间隔内生成数据集的时间点快照(point-in-time snapshot)持久化,它是记录一段时间内的操作,一段时间内操作超过多少次就持久化.默 ...

  7. Linux优雅退出问题

    问题:Springboot框架开发的项目中会内嵌tomcat容器,在杀死进程的时候tomcat为被正常杀死,导致端口未被释放,第二次启动的时候报端口冲突. 先讲一个基本概念:如何在shell中终止一个 ...

  8. hdu4057 Rescue the Rabbit

    地址:http://acm.hdu.edu.cn/showproblem.php?pid=4057 题目: Rescue the Rabbit Time Limit: 20000/10000 MS ( ...

  9. 自从教学弟学会了Python,他每天都爬一些好不正经的图片!

    学弟他作为一个宅男,闲着没事就喜欢用我教他的Python来爬一些 “资源” 套图欣赏,我每次都骂他你总是这样找个女朋友不好点,他不以为然,依旧如故.气的我只好把他写的东西发出来让更多的人来谴责他! 我 ...

  10. 使用Xib创建自定义视图(不是cell)时需要注意的问题

    开发项目过程中,有些地方不免会用到Xib来提高开发效率,如果你的手速够快,写代码建视图,我并不反对这样做.因为我以前也是纯手写代码开发. 进入正题,Xib好用,但是这些下面这些问题需要注意一下. 问题 ...