资讯专栏INFORMATION COLUMN

vue 双向数据绑定的实现学习(一)

anonymoussf / 906人阅读

摘要:双向数据绑定简言之数据动页面动,页面动,数据动典型的应用就是在做表单时候,输入框的内容改动后,跟该输入框的的值改动。看官网上的这个的演示案例双向数据绑定的好处要说出这个好处的时候,也只有在实际场景中才能对应的显示出来。

前言:本系列学习笔记从以下几个点展开

什么是双向数据绑定

双向数据绑定的好处

怎么实现双向数据绑定

实现双向数据数据绑定需要哪些知识点

数据劫持

发布订阅模式

先看看我们要实现的目标是什么,如下动图:

0、什么是双向数据绑定

  单向数据绑定:把Model 绑定到View上,当我们用js修改模型 Model 时候,视图View上对应的内容也会改动,这就是 数据动,页面动 。

  双向数据绑定:简言之 数据动 页面动,页面动,数据动, 典型的应用就是在做表单时候,输入框的内容改动后,跟该输入框的value 的值改动。

 

看vue 官网上的这个V-model 的演示案例:

1、双向数据绑定的好处

  要说出这个好处的时候,也只有在实际场景中才能对应的显示出来。比如我们需要实时显示数据,我们一边说话,一边实时显示我们说的话的文字内容,等等。这让我想起了去年参加云栖大会,台上的大佬一边说话,下面的字幕实时更新。(当然实现这个技术有很多技术点,我们不讨论这个内容,小编也才疏学浅,搞不懂)

以上的都是废话,我们直接看看怎么实现这个双向数据绑定。

一、实现原理

  Vue实现双向数据绑定的原理:数据劫持 + 发布订阅模式(有的也称为观察者模式)

  数据劫持的核心技术: Object.defineProperty()

  vue 3.0 已经用的不是这个技术了,采用是 原生的 Proxy,据说速度能够提升100%,截张尤大的ppt,** 2018-11-21 修改本篇笔记

(香菇,刚研究会一点,就立马变了,这就是前端世界),Proxy 的方式将会在本系列笔记结束后,再记录这个技术点的使用

二、数据劫持的方法 Object.defineProperty()

  先上一个参照代码,它长这个样子:

var book = {
 _year: 2004,
 edition: 1
};
Object.defineProperty(book, "year", {
 get: function(){
   console.log("访问year了,返回_year")
   return this._year;
 },
 set: function(newValue){
   if (newValue > 2004) {
     this._year = newValue;
     console.log("重新设置_year了,并返回edition")
     this.edition += newValue - 2004;
   }
 }
});
book.year = 2005;
alert(book.edition); //2 

---摘自 JavaScript高级程序设计

  Object.defineProperty() 的具体介绍,我们本文不做具体展开,查看我这里的一篇文章,Object.defineProperty。 我们这里先要知道这么一个事情。这个方法要传入三个参数,传入的数据对象data,属性key,描述符对象。其中,描述符(descriptor)对象的属 性必须是:configurable、enumerable、writable 和 value。设置其中的一或多个值,可以修改 对应的特性值。我们需要用的是这访问器属性。当我们在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。上文代码上的 get 方法,在读取属性时调用的函数,set方法,在写入属性时调用的函数。

三、发布订阅者模式

  我画了一个图,来理解这个模式,如下图:
  

代码解释:

    //下面封装一个单例模式,内容是发布订阅模式
let event = {
    eventList: [],
    listener: function (key, fn) {
        if (!this.eventList[key]) { //没有订阅过此类消息,创建一个缓存列表
            this.eventList[key] = [];
        }
        this.eventList[key].push(fn)
    },
    trigger: function () {
        let key = Array.prototype.shift.call(arguments); // marry
        let fns = this.eventList[key];
        if (!fns || fns.length == 0) { //没有订阅 则返回
            return false;
        }
        for (let i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments)
            // 调用 event.listen 里面的 fn 方法,通过apply将当前执行的对象指向当前的this,arguments 传进 fn 函数
        }
    },
    remove: function (key, fn) { // 取消订阅
        let fns = this.eventList[key];
        if (!fns) {
            return false;
        }
        if(!fn) {
            fns && (fns.length = 0)
        } else {
            for (let l = fns.length-1; l>=0; l--) {
                let _fn = fns[l];
                if( _fn === fn) {
                    fns.splice(l, 1)
                }
            }
        }
    },
    install: function (obj) {
        for (let i in this) {
            if (i === "install") {
                return false
            }
            obj[i] = this[i];
        }
    }
}


let testMsg = {}
event.install(testMsg)

// 上面方法 就会将event的方法 浅拷贝给 testMsg, 这样testMsg就有 event的方法和属性
testMsg.listener("rich", fn1 = (name) => {
    console.log(`${name}知道你有钱了`)
})
testMsg.listener("borrowMoney", fn2 =  (name) => {
    console.log(`${name}想问你借钱`)
})

// listen方法将事件 放进队列
// testMsg.remove("rich", fn1) // 取消订阅

// trigger方法,处理事件队列的方法,调用listen的函数的里面的回调函数 fn
testMsg.trigger("rich", "张三")
testMsg.trigger("rich", "张三2")
testMsg.trigger("borrowMoney", "李四")
testMsg.trigger("borrowMoney", "李四2")



// 代码总结:
// 订阅的事件具有对应的key
    // 通过listener方法,将具体的事件队列保存到 eventList ,可以理解为缓存列表也可以是事件队列;
    // 执行trigger 方法,将事件队列拿出来执行调用
    // remove 方法根据对应的key值,删除对应的订阅事件
// 模式总结:封装一个单例event, 执行installEvent方法,将想要event对象拷贝到某个对象中去,
// 发布者trigger方法,监听者listen方法 ,trigger 发布一个东西,listen立马知道你要发布的东西
四、实现分解 主函数入口
function Myvue (options) {
    this.$options = options
    this.$el = document.querySelector(options.el);
    this.$data = options.data;
    Object.keys(this.$data).forEach(key => {
        this.$prop = key;
    })
    this.init()
}
Myvue.prototype.init = function () {
    // 监听数据变化
    observer(this.$data);
            // 获得值
            // let value = this.$data[this.$prop];
            // 不经过模板编译直接 通知订阅者更新dom
            // new Watcher(this,this.$prop,value => {
            //     console.log(`watcher ${this.$prop}的改动,要有动静了`)
            //     this.$el.textContent = value
            // }) 
    //通知模板编译来执行页面上模板变量替换
    new Compile(this)
}
主函数调用

监听器

订阅者

模板编译器

未完待续,错误之处,敬请指出,共同进步!

下一篇 vue 双向数据绑定的实现学习(二)-监听器的实现

文章参考:

  https://www.cnblogs.com/beeve...

  https://github.com/youngwind/...

  https://juejin.im/post/5a9108...

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

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

相关文章

  • Vue双向绑定原理,教你实现双向绑定

    摘要:储存订阅器因为属性被监听,这一步会执行监听器里的方法这一步我们把也给弄了出来,到这一步我们已经实现了一个简单的双向绑定了,我们可以尝试把两者结合起来看下效果。总结本文主要是对双向绑定原理的学习与实现。 当今前端天下以 Angular、React、vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋。 所以我们要时刻保持好奇心,拥抱变化,...

    Labradors 评论0 收藏0
  • 学习MVVM及框架双向绑定笔记

    摘要:的数据劫持版本内部使用了来实现数据与视图的双向绑定,体现在对数据的读写处理过程中。这样就形成了数据的双向绑定。 MVVM由以下三个内容组成 View:视图模板 Model:数据模型 ViewModel:作为桥梁负责沟通View和Model,自动渲染模板 在JQuery时期,如果需要刷新UI时,需要先取到对应的DOM再更新UI,这样数据和业务的逻辑就和页面有强耦合。 在MVVM中,U...

    VioletJack 评论0 收藏0
  • Vue原理】VModel - 白话版

    摘要:执行的时候,会绑定上下文对象为组件实例于是中的就能取到组件实例本身,的代码块顶层作用域就绑定为了组件实例于是内部变量的访问,就会首先访问到组件实例上。其中的获取,就会先从组件实例上获取,相当于。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得...

    keke 评论0 收藏0
  • vue.js源码 - 剖析observer,dep,watch三者关系 如何具体实现数据双向绑定

    摘要:双向数据绑定的核心和基础是其内部真正参与数据双向绑定流程的主要有和基于和发布者订阅者模式,最终实现数据的双向绑定。在这里把双向数据绑定分为两个流程收集依赖流程依赖收集会经过以上流程,最终数组中存放列表,数组中存放列表。 Vue双向数据绑定的核心和基础api是Object.defineProperty,其内部真正参与数据双向绑定流程的主要有Obderver、Dep和Watcher,基于d...

    mo0n1andin 评论0 收藏0
  • 学习Vue.js-Day1

    摘要:学习内容,基本语法和概念,打包工具,实战操作参考文献官网官方资料库全家桶全家桶文档概念前端框架借助可以实现手机开发前端框架是一套构造用户界面的框架,只关于视图层前端的主要工作室跟用户界面打交道,中的,实现界面效果框架是为了提高开发 学习内容 1,Vue基本语法和概念 2, 打包工具 Webpack , Gulp3,实战操作 参考文献:官网: https://cn.vuejs.org...

    Cheriselalala 评论0 收藏0

发表评论

0条评论

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