vue响应式原理学习菜鸟教程下载_响应式入门教程

vue响应式原理学习菜鸟教程下载

前言

提到vue,大家肯定会想到双向数据绑定,数据驱动视图,虚拟DOM,diff算法等等这些概念。在使用vue的时候,会感觉到它的数据双向绑定真的很爽啊。会不会在你用了很长时间后,会好奇到,这个是如何实现的?或者在遇到问题的时候,会不会想到,为啥这个数据并没有响应式的发生改变,视图怎么没有变化…当你抱着这些疑问的时候你肯定会想了解其中的原理了。那么我也是..现在把之前学习和理解的内容整理一下,如果有什么问题,请多多指教~

vue响应式原理学习菜鸟教程下载_响应式入门教程

要了解的核心API

众所周知,是个vue的使用者都知道其响应式数据是结合Object.defineProperty()这个方法实现,那么关于这个方法的使用和作用,请自行了解..一个合格的jser应该都知道的。

要了解的设计模式

核心是观察者模式,数据是我们的被观察者,发生改变的时候,会通知我们所有的观察者。

我们来分析这个图

vue响应式原理学习菜鸟教程下载_响应式入门教程

关于上面这个图,请仔细的看下,从vue官网拔过来的…。主要包括数据变化更新视图,视图变化更新数据。其中主要涉及Observe,Watcher,Dep这三个类,要了解vue的响应式原理,弄清楚这三个类是如何运作的,这样就能够大概了解了。
Observe是数据监听器,其实现方法就是Object.defineProperty。
Watcher是我们所说的观察者。
Dep是可以容纳观察者的一个订阅器,主要收集订阅者,然后在发生变化的时候通知每个订阅者。

实现一个Observe

observe的主要工作是针对vue中的响应式数据属性进行监听,所以通过递归的方法遍历属性。

function observe(data) {
    if(!data || typeof data !== 'object') {
        return
    }
    
    Object.keys(data).map((k) => {
        defineReactive(data, k, data[k])
    })
}

function defineReactive(data, k, val) {
    observe(val)
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            return val
        },
        set: function(newval) {
            val = newval
        }
    })
}

创建一个Dep订阅器

订阅器负责收集观察者,然后在属性变化的时候通知观察者进行更新。

function Dep() {
    this.subs = []
}

Dep.prototype.addSub = function(sub) {
    this.subs.push(sub)
}
Dep.prototype.notify = function() {
    this.subs.forEach(v => {
        v.update()
    })
}

那么什么时候将一个观察者添加到Dep里面呢?设计上是将观察者的添加放在getter里面。

实现一个Watcher

上面说到Watcher是在初始化的时候在getter里面放进订阅器Dep中。那么在Watcher初始化的时候触发getter。那么还有个问题,在getter中是如何获取到Watcher的?这个我们可以暂时缓存的放到Dep的静态属性Dep.target上面。

function Watcher(vm, exp, cb) {
    this.vm = vm
    this.exp = exp
    this.cb = cb
}
Watcher.prototype.update = function() {}
Watcher.prototype.run = function() {
  var value = this.vm.data[this.exp]; 
  var oldVal = this.value; 
  if (value !== oldVal) {      
    this.value = value; 
    this.cb.call(this.vm, value, oldVal);
  }         
}
Watcher.prototype.get = function() {
    Dep.target = this // 在target上进行缓存
    var value = this.vm.data[exp] //触发getter
    Dep.target = null //清空null
    return value
}

这个时候我们还需要修改下Observe:

function defineReactive(data, k, val) {
    observe(val)
    var dep = new Dep(); 
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function() {
            if(Dep.target) {
                dep.addSub(Dep.target)
            }
            return val
        },
        set: function(newval) {
            if(newval === val) {
                return
            }
            val = newval
            dep.notify()
        }
    })
}

看到这里,大致应该都明白如何把Observe,Dep,Watcher如何运作到一起了吧。

如何通过这三者完成一个数据的双向绑定

function MyVue(data, el, exp) {
    this.data = data
    observe(this.data)
    el.innerHTML = this.data[exp]
    new Watcher(this, exp, function(value) {
        el.innerHTML = value
    })
}

优化MyVue,代理data上属性

function MyVue(data, el, exp) {
    this.data = data
    Object.key(data).forEach(function(k) => {
        this.proxyKeys(k)
    })
    observe(data);
    el.innerHTML = this.data[exp];  // 初始化模板数据的值
    new Watcher(this, exp, function (value) {
        el.innerHTML = value;
    });
    return this;
}
MyVue.prototype.proxyKeys = function(k) {
    Object.defineProperty(this, k, {
        enumerable: false,
        configurable: true,
        get: function proxyGetter() {
            return this.data[key];
        },
        set: function proxySetter(newVal) {
            this.data[key] = newVal;
        }
    })
}

实现compile解析dom节点

上面的例子中没有解析DOM节点的操作,只是针对指定dom节点进行内容的替换。那么compile要进行哪写操作呢?

  1. 解析模板指定,并替换模板数据
  2. 将模板指定对应的节点绑定对应的更新函数,并初始化相应的订阅器。
function nodeToFragment(el) {
    var fragment = document.createDocumentFragment()
    var child = el.firstChild
    while(child) {
        fragment.appendChild(child)
        child = el.firstChild
    }
    return fragment
}

接下来需要遍历各个节点,对含有相关指定的节点进行特殊处理。

function compileElement(el) {
    var childNodes = el.childNodes;
    var self = this;
    [].slice.call(childNodes).forEach(function(node) {
        var reg = /\{\{(.*)\}\}/;
        var text = node.textContent;
 
        if (self.isTextNode(node) && reg.test(text)) {  // 判断是否是符合这种形式{{}}的指令
            self.compileText(node, reg.exec(text)[1]);
        }
 
        if (node.childNodes && node.childNodes.length) {
            self.compileElement(node);  // 继续递归遍历子节点
        }
    });
}

function compileText (node, exp) {
    var self = this;
    var initText = this.vm[exp];
    updateText(node, initText);  // 将初始化的数据初始化到视图中
    new Watcher(this.vm, exp, function (value) {  // 生成订阅器并绑定更新函数
        self.updateText(node, value);
    });
},
function updateText (node, value) {
    node.textContent = typeof value == 'undefined' ? '' : value;
}

获取到最外层节点后,调用compileElement函数,对所有子节点进行判断,如果节点是文本节点且匹配{{}}这种形式指令的节点就开始进行编译处理,编译处理首先需要初始化视图数据,对应上面所说的步骤1,接下去需要生成一个并绑定更新函数的订阅器,对应上面所说的步骤2。这样就完成指令的解析、初始化、编译三个过程,一个解析器Compile也就可以正常的工作了。为了将解析器Compile与监听器Observer和订阅者Watcher关联起来,我们需要再修改一下类MySelf函数:

function MyVue(opt) {
    let self = this
    this.vm = this
    this.data = opt.data
    
    Object.key(this.data).forEach(function(k) {
        this.proxyKeys(k)
    })
    observe(this.data)
    new Compile(opt, this.vm)
    return this
}

感觉大致说的差不多了…希望能够对大家有点点启发,谢谢。

原文:https://segmentfault.com/a/1190000021971917

海计划公众号
(0)
上一篇 2020/03/19 07:12
下一篇 2020/03/19 07:12

您可能感兴趣的内容

  • 理解JavaScript中的语法和代码结构菜鸟知识_代码菜鸟教程

    所有编程语言都必须遵守特定的规则才能运行。 确定编程语言的正确结构的这组规则称为语法。 许多编程语言主要由具有语法变化的类似概念组成。在本教程中,我们将介绍JavaScript语法和代码结构的许多规则和约定。功能性和可读性在开始使用JavaScript时,功能性和可读性是关注语法的两个重要原因。有些语法规则是JavaScript功能所必需的。如果不遵循它们,

    2020/03/22
  • 快图网入门基础_免费PNG图片免抠PNG高清背景素材库

    快图网入门基础 官方网址:http://www.kuaipng.com/ 简介描述:免费PNG图片免抠PNG高清背景素材库 快图网是一家致力于为设计师提供免费PNG素材以及背景图库…

    2020/03/06
  • Base64 编码与解码详解小白基础_base64指南教程

    Base64 是基于 64 个可打印字符 A-Z、a-z、0-9、+、/ 来表示二进制数据的表示方法,常用于数据在网络中的传输。本篇将分别介绍其编码、解码以及实际运用。Base64 编码Base64 本质是一种将二进制转为文本的方案。基本规则如下:编码时候选用 64 (大小写英文字母,数字,+ /)个字符以及用作补位的=来表示在编码的时候,将3个字节变为4个

    2020/03/29
  • 基于iScroll实现内容滚动入门指南_滚动指南攻略

    一、iScroll简介iScroll 是一款针对web app使用的滚动控件,它可以模拟原生IOS应用里的滚动列表操作,还可以实现缩放、拉动刷新、精确捕捉元素、自定义滚动条等功能。这里博主使用的版本iScroll4.25,目前最新版本是iScroll5,大家可以去官网下载。 官网地址:http://iscrolljs.com/二、iScroll使用方法1.i

    2020/04/05
  • 女生30 岁转行做前端开发,晚吗?入门百科_前端入门基础教程

    130岁转行做前端程序开发!请把“晚吗”去掉。50多岁大爷都学编程了。你还担心啥?先从年龄上来说,这个年龄进入IT职业,那是相当棒的黄金时间,有目标,有干劲,有新颖的思想,而且仍是女孩子。从女人工作开展上来讨论这个问题,现在好多都在谈论“女人在事业和家庭的抉择”。由于社会形态的改变,现在咱们不难发现女人所承担的社会压力,以及所贡献出的价值是非常巨大的,咱们已

    2020/04/03
  • 小白如何入门编程入门基础知识_编程使用攻略

    本人于2010年开始从事WEB领域相关开发岗位,先后从事过ASP#net、PHP、JAVA、前端、项目管理、技术总监等岗位。参与研发重构互联网产品60多项,领导负责项目12例。职业生涯算是中国互联网中生代开发者中比较常规的路线选择:初级工程师>中级工程师>高级工程师>全栈工程师>项目管理>技术团队管理,曾经也的在A/T等厂子工作过,也完成了基础的财富积累。总

    2020/03/20
  • css3动画的性能优化小白帮助针对移动端卡顿问题_css3动画入门基础教程

    这篇文章主要讲的是怎样制作流畅动画,特别是针对移动端。在这里我首先介绍制作动画的几种方法的优缺点;接着会着重介绍用css3制作动画的注意事项。1、用canvas、css3、jquery制作动画Canvas优点:性能好,强大,支持多数浏览器(除了IE6、IE7、IE8),画出来的图形可以直接保存为 .png 或者 .jpg的图形;缺点:依赖于HTML,只能通过

    2020/04/05
  • bootstrap-tour使用帮助_一个基于Bootstrap的Js用户引导插件

    bootstrap-tour使用帮助 官方网址:http://bootstraptour.com GitHub:https://github.com/sorich87/bootst…

    2020/03/06
  • 如何使用 Set 来提高代码的性能入门百科_性能小白基础

    我确信有很多开发人员坚持使用基本的全局对象:数字,字符串,对象,数组和布尔值。对于许多用例,这些都是需要的。 但是如果想让你的代码尽可能快速和可扩展,那么这些基本类型并不总是足够好。在本文中,我们将讨论JS 中Set对象如何让代码更快— 特别扩展性方便。 Array 和Set工作方式存在大量的交叉。但是使用Set会比Array在代码运行速度更有优势。Set

    2020/03/29
  • 工作了四五年,感觉技术上依旧长进不大基础知识教程_技术小白指南

    技术精进是一个持续增长的过程,而非一朝一夕,即便你在最短时间的掌握了大量的技术点,如何不及时应用到实际问题中,也很容易被遗忘。有朋友会说,我平时也挺努力的,一直不间断的学习,并持续将近 3、4、5 年的时间,依旧精进不大,这是为什么?现在数一数掌握的技术有哪些?一个简单的方式就是把自己的简历找出来,技能掌握那一栏,都填了些什么就是自认掌握的技术点。学习是一个

    2020/03/30
  • tape入门知识像代码一样跑测试

    tape基础入门 官方网址:https://github.com/substack/tape GitHub:https://github.com/substack/tape 简介描…

    2020/03/05
  • 程序员眼中的浏览器是什么样的?IE:有本事你卸了我啊小白帮助_浏览器小白知识

    主流浏览器之争从上个世纪开就开始,已经持续了很长的时间。就在几年前,IE还是最主流的web浏览器。但现在形势完全不同了,人们都在笑话IE,纷纷转向其它浏览器。今天,我向大家分享一下针对IE的搞笑图片,只是逗乐而已,喝杯咖啡,坐下来慢慢享受吧。 如果浏览器是一种枪反射弧有点长…如何区分 HTML 和 HTML5成长的烦恼这么说来,IE浏览器扳回一分!主流浏览

    2020/04/03
  • 前端美小白入门_专注国内外前端设计资讯,前端资源下载

    前端美小白入门 官方网址:https://www.qianduanmei.com/ 简介描述:专注国内外前端设计资讯,前端资源下载 前端美是一个前端妹纸的博客,专注国内外前端设计资…

    2020/03/06
  • ps在线工具【uupoop】入门教程_在线PS图片处理工具_ps精简版

    ps在线工具【uupoop】入门教程 官方网址:https://www.uupoop.com/ 简介描述:在线PS图片处理工具_ps精简版 PS在线图片编辑器是一个专业精简的在线p…

    2020/03/07
  • 行内元素和块级元素之间有什么区别?使用帮助_区别入门知识

    在html+css基础当中,我们往往会遇到块元素和行内元素。在实际开发中,会经常把他们弄混淆,而且很难记住。那么行内元素和块级元素之间有什么区别?行内元素和块级元素的区别行内元素和其他行内元素都会在一条水平线上排列,都是在同一行的;块级元素却总是会在新的一行开始排列,各个块级元素独占一行,垂直向下排列,若想使其水平方向排序,可使用左右浮动(float:lef

    2020/03/20
  • angular-starter新手入门_Angular入门套件

    angular-starter新手入门 GitHub:https://github.com/PatrickJS/angular-starter 简介描述:Angular入门套件 A…

    2020/03/11