IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vue3源码学习(2)--手写vue2/vue3响应式系统的简单实现-以及二者数据劫持的区别 -> 正文阅读

[JavaScript知识库]vue3源码学习(2)--手写vue2/vue3响应式系统的简单实现-以及二者数据劫持的区别

续上文:
Vue3源码学习之旅(1)–自己实现一个简单的渲染系统-render/h/patch函数

依赖收集系统

// 收集依赖
/**
 * 收集依赖的类
 */
class Dep {
  constructor() {
    // 发布订阅模式
    // 订阅者 将其都加入进来
    this.subscribers = new Set();
  }
  // 收集副作用
  // addEffect(effect) {
  //   this.subscribers.add(effect);
  // }

  /* 自动收集副作用 */
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect)
    }
  }

  // 通知,进行回调执行
  notify() {
    this.subscribers.forEach(effect => {
      effect();
    })
  }
}

let activeEffect = null;
/**
 * 帮助收集依赖
 * @param {*} effect 数据发送改变后要执行的副作用函数
 */
function watchEffect(effect) {
  activeEffect = effect;
  // 用原始数据执行一次
  effect()
  activeEffect = null;
}
/* 
  Map: {key:value} key: 字符串 value任意
  WeakMap: key 是一个对象,弱引用  value任意
*/
const targetMap = new WeakMap();

/**
 * 
 * @param {*} target 目标对象
 * @param {*} key 目标对象的属性
 */
function getDep(target, key) {
  // 1. 根据目标对象 取出对应的Map对象
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  // 取出具体的依赖对象
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Dep();
    depsMap.set(key, dep);
  }
  return dep;
}

数据劫持

响应式系统Vue2实现

**
 * vue2对raw进行数据劫持
 * @param {*} raw 原始数据
 */
function reactive(raw) {
  Object.keys(raw).forEach(key => {
    // 收集依赖
    const dep = getDep(raw, key);
    let value = raw[key];
    // 劫持的对象,对象的属性,属性的描述
    Object.defineProperty(raw, key, {
      // 获取数据 会执行get函数
      get() {
        // 添加依赖
        dep.depend()
        return value;
      },
      // 设置数据
      set(newValue) {
        if (value !== newValue) {
          value = newValue;
          dep.notify();
        }
      }
    })
  })
  return raw;
}

响应式系统Vue3实现

**
 * vue3对raw进行数据劫持
 * @param {*} raw 原始数据
 */
function reactive(raw) {
  // 劫持的对象 对代理对象的数据劫持
  return new Proxy(raw, {
    // 获取数据时调用
    // target 参数 就是 我们劫持的对象 raw
    get(target, key) {
      const dep = getDep(target, key);
      dep.depend();
      // 这里没有考虑属性值还是对象的情况
      return target[key];
    },
    // 设置数据时调用
    set(target, key, newValue) {
      const dep = getDep(target, key);
      target[key] = newValue;
      dep.notify();
    }
  });
}

响应式数据的测试

// ----------------测试----------------------

// 数据
const info = reactive({ name: '毛毛', age: 21 })
// 会自动收集依赖
watchEffect(() => {
  console.log(info.age * 2);
})
// 数据发送改变 自动执行副作用函数
info.age++;

let obj  = reactive({name:'hah ',counter:1});
watchEffect(()=>{
  console.log("name:",obj.name);
})
watchEffect(()=>{
  console.log("counter:",obj.counter);
})
obj.counter++

为什么Vue3选择Proxy呢?

Object.definedProperty 是劫持对象的属性时,如果新增元素:

那么Vue2需要再次 调用definedProperty,而 Proxy 劫持的是整个对象,不需要做特殊处理;

修改对象的不同:

  • 使用 defineProperty 时,我们修改原来的 obj 对象就可以触发拦截;
  • 而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截;

Proxy 能观察的类型比 defineProperty 更丰富

  • has:in操作符的捕获器;
  • deleteProperty:delete 操作符的捕捉器;
  • 等等其他操作;

框架外层API设计

这样我们就知道了,从框架的层面来说,我们需要 有两部分内容:

  • createApp用于创建一个app对象;

  • 该app对象有一个mount方法,可以将根组件挂 载到某一个dom元素上;

/**
 * 
 * @param {*} rootComponent 根组件(元素)
 * @returns 
 */
function createApp(rootComponent) {
  return {
    mount(selector) {
      const container = document.querySelector(selector);
      // 是否挂载过
      let isMounted = false;
      // 数据未更新前的vnode
      let oldVNode = null;
      watchEffect(() => {
        if (!isMounted) {
          // 没有挂载过
          //  进行挂载
          oldVNode = rootComponent.render();
          mount(oldVNode, container);
          isMounted = true;
        } else {
          // 已经挂载过
          // 进行patch比对
          const newVNode = rootComponent.render()
          patch(oldVNode, newVNode);
          // 保存这次新的vnode
          oldVNode = newVNode;
        }
      })
    }
  }
}

测试

// 根组件
    const App = {
      data: reactive({
        counter: 0
      }),
      render() {
        return h("div", { style: "color:red" }, [
          h("h2", null, `当前计数:${this.data.counter}`),
          h("button", {
            onClick: () => {
              this.data.counter++
            }
          }, "+1")
        ])
      }
    }
    // 挂载根组件
    const app = createApp(App)
    app.mount("#app")
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:46:11  更:2021-08-27 11:46:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 5:34:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计