实现vue2.0响应式的基本思路

最近看了vue2.0源码关于响应式的实现,以下博文将通过简单的代码还原vue2.0关于响应式的实现思路。

注意,这里只是实现思路的还原,对于里面各种细节的实现,比如说对象里面数据的操作的监听,以及对象嵌套这些细节本实例都不会涉及到,如果想了解更加细节的实现思路,可以通过阅读源码 observer文件夹以及instance文件夹里面的state文件具体了解。

首先,我们先定义好实现vue对象的结构

class Vue {
    constructor(options) {
        this.$options = options;
        this._data = options.data;
        this.$el = document.querySelector(options.el);
    }
}

第一步:将data下面的属性变为observable

使用Object.defineProperty对数据对象做属性get和set的监听,当有数据读取和赋值操作时则调用节点的指令,这样使用最通用的=等号赋值就可以触发了。

//数据劫持,监控数据变化
function observer(value, cb){
  Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
}

function defineReactive(obj, key, val, cb) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: ()=>{
      return val
    },
    set: newVal => {
      if(newVal === val)
        return
      val = newVal
    }
  })
}

第二步:实现一个消息订阅器

很简单,我们维护一个数组,这个数组,就放订阅者,一旦触发notify,订阅者就调用自己

的update方法

class Dep {
  constructor() {
    this.subs = []
  }
  add(watcher) {
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach((watcher) => watcher.cb())
  }
}

每次set函数,调用的时候,我们触发notify,实现更新

那么问题来了。谁是订阅者。对,是Watcher。。一旦 dep.notify()就遍历订阅者,也就是Watcher,并调用他的update()方法

function defineReactive(obj, key, val, cb) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: ()=>{
      return val
    },
    set: newVal => {
      if(newVal === val)
        return
      val = newVal
      dep.notify()
    }
  })
}

第三步:实现一个 Watcher

Watcher的实现比较简单,其实就是执行数据变化时我们要执行的操作

class Watcher {
  constructor(vm, cb) {
    this.cb = cb
    this.vm = vm
  }
  update(){
    this.run()
  }
  run(){
    this.cb.call(this.vm)
  }
}

第四步:touch拿到依赖

上述三步,我们实现了数据改变可以触发更新,现在问题是我们无法将watcher与我们的数据联系到一起。

我们知道data上的属性设置defineReactive后,修改data 上的值会触发 set。那么我们取data上值是会触发 get了。所以可以利用这一点,先执行以下render函数,就可以知道视图的更新需要哪些数据的支持,并把它记录为数据的订阅者。

function defineReactive(obj, key, val, cb) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: ()=>{
      if(Dep.target){
        dep.add(Dep.target)
      }
      return val
    },
    set: newVal => {
      if(newVal === val)
        return
      val = newVal
      dep.notify()
    }
  })
}

最后我们来看用一个代理实现将我们对data的数据访问绑定在vue对象上

 _proxy(key) {
    const self = this
    Object.defineProperty(self, key, {
      configurable: true,
      enumerable: true,
      get: function proxyGetter () {
        return self._data[key]
      },
      set: function proxySetter (val) {
        self._data[key] = val
      }
    })
}

Object.keys(options.data).forEach(key => this._proxy(key))

下面就是整个实例的完整代码

class Vue {
  constructor(options) {
    this.$options = options;
    this._data = options.data;
    this.$el =document.querySelector(options.el);
    Object.keys(options.data).forEach(key => this._proxy(key))
    observer(options.data)
    watch(this, this._render.bind(this), this._update.bind(this))
  }
  _proxy(key) {
    const self = this
    Object.defineProperty(self, key, {
      configurable: true,
      enumerable: true,
      get: function proxyGetter () {
        return self._data[key]
      },
      set: function proxySetter (val) {
        self._data[key] = val
      }
    })
  }
  _update() {
    console.log("我需要更新");
    this._render.call(this)
  }
  _render() {
    this._bindText();
  }

  _bindText() {
    let textDOMs=this.$el.querySelectorAll(‘[v-text]‘),
    bindText;
    for(let i=0;i<textDOMs.length;i++){
       bindText=textDOMs[i].getAttribute(‘v-text‘);
       let data = this._data[bindText];
       if(data){
          textDOMs[i].innerHTML=data;
       }
    }
  }
}

function observer(value, cb){
  Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
}

function defineReactive(obj, key, val, cb) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: ()=>{
      if(Dep.target){
        dep.add(Dep.target)
      }
      return val
    },
    set: newVal => {
      if(newVal === val)
        return
      val = newVal
      dep.notify()
    }
  })
}
function watch(vm, exp, cb){
  Dep.target = new Watcher(vm,cb);
  return exp()
}

 class Watcher {
  constructor(vm, cb) {
    this.cb = cb
    this.vm = vm
  }
  update(){
    this.run()
  }
  run(){
    this.cb.call(this.vm)
  }
}

class Dep {
  constructor() {
    this.subs = []
  }
  add(watcher) {
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach((watcher) => watcher.cb())
  }
}
Dep.target = null;

var demo = new Vue({
el: ‘#demo‘,
data: {
text: "hello world"
}
})


setTimeout(function(){
demo.text = "hello new world"


}, 1000)

  <body>
   <div id="demo">
       <div v-text="text"></div>
   </div>
  </body>

上面就是整个vue数据驱动部分的整个思路。如果想深入了解更细节的实现,建议深入去看vue这部分的代码。

时间: 2024-10-10 17:44:32

实现vue2.0响应式的基本思路的相关文章

Spring5.0响应式编程入门

引言? 响应式编程是一种面向数据流和变化传播的编程范式.使用它可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播.我们可以使用声明的方式构建应用程序的能力,形成更加敏感和有弹性的应用,所以Spring 5在其核心框架中增加了反应系统,已经开始向声明式编程的范式转变. 响应式编程的优势 提高了代码的可读性,因此开发人员只需要关注定义业务逻辑. 在高并发环境中,可以自然的处理消息. 可以控制生产者和消费者之间的流量,避免内存不足. 对于一个或多个线程,

JuCheap V2.0响应式后台管理系统模板正式发布beta版本

JuCheap V1.* 查看地址: http://blog.csdn.net/allenwdj/article/details/49155339 经过半年的努力,JuCheap后台通用响应式管理后台模板框架,终于有V1.*正式升级到了JuCheap V2.0 beta版本. 首先介绍下JuCheap V2.0的基础技术: 1.相对于V1.*版本的框架,更加的简洁 2.T4模板,一键生成多种类型的文件 3.DI容器,由MEF改成了AutoFac,可选属性注入和构造函数注入两种方式. 4.真正的响

Vue3.0响应式实现

基于Proxy // 弱引用映射表 es6 防止对象不能被回收 let toProxy = new WeakMap(); // 原对象: 代理过得对象 let toRaw = new WeakMap(); // 被代理过的对象: 原对象 // 判断为对象 function isObject(val) { return typeof val === 'object' && val !== null } // 区分改变数组长度还是数值 function hasOwn(target, key)

响应式开发的思路和断点的选择

一般思路有两种, 1. 渐进增强. 即先开发比较低级的版本, 再开发高级的 2. 优雅降级. 与第一种方式相反. 个人倾向第2种, 因为如果迭代比较快的话, 那么往往低级的版本刚开发完成, 可能整个项目已经不存在了, 比较没有意义. 还有另外一种思路是根据先开发大屏幕还是先开发小屏幕来区分. 这个感觉可以具体情况具体分析, 看侧重于哪一种用户. 断点就是媒体查询种设置的宽度, 按屏幕的大小选择 举个栗子, 0--480px 481--800px 801--1400px 1400px+ 小屏幕 中

浅谈Vue响应式(数组变异方法)

很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法. 看来想让女神自己动,关键得用对方法.虽然在官方文档中已经给出了方法,但是在下实在好奇的紧,想要解锁更多姿势的话,那就必须先要深入女神的心,于是乎才有了去探索Vue响应式原理的想法.(如果你愿意一层一层地剥开我的心.你会发现,你会讶异-- 沉迷于鬼哭狼嚎 无法自拔QAQ). 前排提示,Vue的响应式原理主要是使用了ES5的Obj

vue2.0源码分析之理解响应式架构

https://segmentfault.com/a/1190000007334535 分享前啰嗦 我之前介绍过vue1.0如何实现observer和watcher.本想继续写下去,可是vue2.0横空出世..所以   直接看vue2.0吧.这篇文章在公司分享过,终于写出来了.我们采用用最精简的代码,还原vue2.0响应式架构实现   以前写的那篇 vue 源码分析之如何实现 observer 和 watcher可以作为本次分享的参考.   不过不看也没关系,但是最好了解下Object.defi

前端响应式开发

最近在工作中遇到一些让人头疼的问题--多媒体查询,也就是大家所说的响应式布局(多终端适配).在实际的开发过程中,响应式的设计使得多终端的适配变得非常方便,响应式展现的方式,更有一种组装变形金刚的感觉,但也在实际工作中发现了许多问题: 一.开发思维变得复杂 在我们开发页面的时候,思维无法专注于单一的PC端.移动端以及Pad端,开发每一个元素与版块的时候,都需要考虑多终端适配:所以建议大家在接到这一类项目的时候,不要急于去开发,先把所有的终端页面设计图全部浏览的看一遍,不单单光是看,看的过程中脑海里

响应式一些知识

1,什么是媒体查询,媒体查询的优缺点media queries 媒体查询媒体查询表达式,指定媒体类型,根据媒体类型来选择相应的样式,在样式中,选择一种页面的布局以精确地适应不同的设备 我们要做的就是针对不同的浏览器设备大小,编写不同的样式,让浏览器根据不同的窗口尺寸大小,选择不同的样式 IE8 以下不支持媒体查询responsivator 工具通过服务器判断,选择不同的页面,缺点页面风格修改复杂 2, @media 设备类型 only() not() and (),设备二{}<meta name

响应式编程(Reactive Programming)(Rx)介绍

很明显你是有兴趣学习这种被称作响应式编程的新技术才来看这篇文章的. 学习响应式编程是很困难的一个过程,特别是在缺乏优秀资料的前提下.刚开始学习时,我试过去找一些教程,并找到了为数不多的实用教程,但是它们都流于表面,从没有围绕响应式编程构建起一个完整的知识体系.库的文档往往也无法帮助你去了解它的函数.不信的话可以看一下这个: 通过合并元素的指针,将每一个可观察的元素序列放射到一个新的可观察的序列中,然后将多个可观察的序列中的一个转换成一个只从最近的可观察序列中产生值得可观察的序列. 天啊. 我看过