浅析Proxy可以优化vue的数据监听机制问题及实现思路


Posted in Javascript onNovember 29, 2018

我们首先来看vue2.x中的实现,为简单起见,我们这里不考虑多级嵌套,也不考虑数组

vue2.x中的实现

其本质是new Watcher(data, key, callback)的方式,而在调用之前是先将data中的所有属性转化成可监听的对象, 其主要就是利用Object.defineProperty,。

class Watcher{
  constructor(data, key, cb){
  }
}
//转换成可监听对象
function observe(data){
  new Observer(data)
}
//修改数据的getter和setter
function defineReactive(obj, key){
  let value = obj[key];
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      return value;
    },
    set(newVal){
      value = newVal
    }
  })
}

Observer的实现很简单

class Observer {
  constructor(data){
    this.walk(data);
  }

  walk(data){
    for(var key in data) {
      // 这里不考虑嵌套的问题,否则的话需要递归调用walk
      defineReactive(data, key)
    }
  }
}

现在怎么将watcher和getter/setter联系起来,vue的方法是添加一个依赖类:Dep

class Watcher{
  constructor(data, key, cb){
    this.cb = cb;
    Dep.target = this; //每次新建watcher的时候讲给target赋值,对target的管理这里简化了vue的实现
    data[key];//调用getter,执行addSub, 将target传入对应的dep; vue的实现本质就是如此
  }
}
class Dep {
  constructor(){
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(){
    this.subs.forEach(sub => sub.cb())
  }
}
function defineReactive(obj, key){
  let value = obj[key];
  let dep = new Dep(); //每一个属性都有一个对应的dep,作为闭包保存
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      dep.addSub(Dep.target)
      Dep.target = null;
      return value;
    },
    set(newVal){
      value = newVal
      dep.notify();
    }
  })
}

以上就是vue的思路,vue3之所以要从新实现,主要有这几个原因:

  1. Object.defineProperty的性能开销。
  2. defineReactive一开始就要对要监听的对象所有属性都执行一遍,因为传统方法要将一个对象转换成可监听对象,只能如此。
  3. 添加删除属性的问题。
  4. 还有一点就是这个模块被耦合到了vue里面,新版本可以单独作为一个库来使用。

然后我们来看看同样的功能采用Proxy会怎样实现。

Proxy的实现

将一个对象转换成Proxy的方式很简单,只需要作为参数传给proxy即可;

class Watcher {
  constructor(proxy, key, cb) {
    this.cb = cb;
    Dep.target = this;
    this.value = proxy[key];
  }
}
class Dep {
  constructor(){
    this.subs = []
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(newVal){
    this.subs.forEach(sub => {
      sub.cb(newVal, sub.value);
      sub.value = newVal;
    })
  }
}
const observe = (obj) => {
  const deps = {};
  return new Proxy(obj, {
    get: function (target, key, receiver) {
      const dep = (deps[key] = deps[key] || new Dep);
      Dep.target && dep.addSub(Dep.target)
      Dep.target = null;
      return Reflect.get(target, key, receiver);
    },
    set: function (target, key, value, receiver) {
      const dep = (deps[key] = deps[key] || new Dep);
      Promise.resolve().then(() => {
        dep.notify(value);
      })
      return Reflect.set(target, key, value, receiver);
    }
  });
}
var state = observe({x:0})
new Watcher(state, 'x', function(n, o){
  console.log(n, o)
});
new Watcher(state, 'y', function(n, o){
  console.log(n, o)
});
state.x = 3;
state.y = 3;

也许一开始我们只关心x和y,那么就不会对其他的属性做相应的处理,除非添加watcher,其他时间target都是null

总结

以上所述是小编给大家介绍的Proxy可以优化vue的数据监听机制问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery 创建Dom元素
May 07 Javascript
js封装的textarea操作方法集合(兼容很好)
Nov 16 Javascript
各浏览器对link标签onload/onreadystatechange事件支持的差异分析
Apr 27 Javascript
小试JQuery的AutoComplete插件
May 04 Javascript
js实现对table动态添加、删除和更新的方法
Feb 10 Javascript
node.js 和HTML5开发本地桌面应用程序
Dec 13 Javascript
vue项目中用cdn优化的方法
Jan 03 Javascript
JS实现可针对算术表达式求值的计算器功能示例
Sep 04 Javascript
JS获取并处理php数组的方法实例分析
Sep 04 Javascript
更强大的vue ssr实现预取数据的方式
Jul 19 Javascript
微信小程序使用自定义组件导航实现当前页面高亮
Jan 02 Javascript
实例分析javascript中的异步
Jun 02 Javascript
vue实现双向绑定和依赖收集遇到的坑
Nov 29 #Javascript
js中this的指向问题归纳总结
Nov 28 #Javascript
基于vue实现移动端圆形旋钮插件效果
Nov 28 #Javascript
VUE2.0 ElementUI2.0表格el-table自适应高度的实现方法
Nov 28 #Javascript
Vue触发式全局组件构建的方法
Nov 28 #Javascript
Vue axios全局拦截 get请求、post请求、配置请求的实例代码
Nov 28 #Javascript
jQuery实现购物车的总价计算和总价传值功能
Nov 28 #jQuery
You might like
用PHP的ob_start() 控制您的浏览器cache
2009/08/03 PHP
centos 5.6 升级php到5.3的方法
2011/05/14 PHP
Codeigniter整合Tank Auth权限类库详解
2014/06/12 PHP
PHP实现根据设备类型自动跳转相应页面的方法
2014/07/24 PHP
PHP判断一个变量是否为整数、正整数的方法示例
2019/09/11 PHP
JS实现图片放大镜效果的方法
2015/02/27 Javascript
JavaScript模拟可展开、拖动与关闭的聊天窗口实例
2015/05/12 Javascript
Vue.js系列之项目结构说明(2)
2017/01/03 Javascript
函数四种调用模式以及其中的this指向
2017/01/16 Javascript
浅谈React Native 中组件的生命周期
2017/09/08 Javascript
jQuery判断网页是否已经滚动到浏览器底部的实现方法
2017/10/27 jQuery
Angular设置别名alias的方法
2018/11/08 Javascript
重学 JS:为啥 await 不能用在 forEach 中详解
2019/04/15 Javascript
微信小程序swiper禁止用户手动滑动代码实例
2019/08/23 Javascript
巧用python和libnmapd,提取Nmap扫描结果
2016/08/23 Python
Python实现针对给定字符串寻找最长非重复子串的方法
2018/04/21 Python
详解如何将python3.6软件的py文件打包成exe程序
2018/10/09 Python
漂亮的Django Markdown富文本app插件的实现
2019/01/02 Python
网易2016研发工程师编程题 奖学金(python)
2019/06/19 Python
python爬虫之爬取百度音乐的实现方法
2019/08/24 Python
解决python 执行sql语句时所传参数含有单引号的问题
2020/06/06 Python
详解Python中Pyyaml模块的使用
2020/10/08 Python
通过Django Admin+HttpRunner1.5.6实现简易接口测试平台
2020/11/11 Python
Python爬虫后获取重定向url的两种方法
2021/01/19 Python
用html5实现语音搜索框的方法
2014/03/18 HTML / CSS
解决HTML5手机端页面缩放的问题
2017/10/27 HTML / CSS
伦敦最著名的老字号百货公司:Selfridges(塞尔福里奇百货)
2016/07/25 全球购物
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
意大利中国电子产品购物网站:Geekmall.com
2019/09/30 全球购物
学生自我鉴定
2013/12/18 职场文书
八年级物理教学反思
2014/01/19 职场文书
购房意向书范本
2014/04/01 职场文书
私人委托书格式
2014/09/10 职场文书
2015年乡镇安全生产工作总结
2015/05/19 职场文书
2016大学生入党积极分子心得体会
2016/01/06 职场文书
公司晚会主持词
2019/04/17 职场文书