首页 > 编程语言 >vue3源码-一、响应式原理reactive的实现

vue3源码-一、响应式原理reactive的实现

时间:2023-04-29 21:11:42浏览次数:35  
标签:set const target reactive 源码 key vue3 return

reactive的实现

  1. 使用:

    使用reactive()函数创建一个响应式对象。

    import { reactive } from 'vue'
    
    export default {
      // `setup` 是一个专门用于组合式 API 的特殊钩子函数
      setup() {
        const state = reactive({ count: 0 })
    
        // 暴露 state 到模板
        return {
          state
        }
      }
    }
    
    <div>{{ state.count }}</div>
    
  2. 手写源码

    这里有四个方法需要实现包括reactive, shallowReactive, readonly, shallowReadonly

    这个四个方法的实现:

    export function reactive(target) {
      return createReactiveObject(target, false, mutabelHandlers);
    }
    
    export function shallowReactive(target) {
      return createReactiveObject(target, false, shallowReactiveHandlers);
    }
    
    export function readonly(target) {
      return createReactiveObject(target, true, readonlyHandlers);
    }
    
    export function shallowReadonly(target) {
      return createReactiveObject(target, true, shallowReadonlyHandlers);
    }
    

    这四个方法同一调用一个创建函数,不同的是根据功能给出相应的参数。

    其中target是劫持的对象,第二个参数是是否是仅读的,第三个是处理的handler

    四个handler实现:

    import {
      extend,
      hasChanged,
      hasOwn,
      isAarray,
      isIntegerKey,
      isObject,
    } from "@vue/shared";
    import { reactive, readonly } from "./reactive";
    import { track, trigger } from "./effect";
    // 实现new Proxy(target,handler)
    // 是不是仅读的,仅读属性set时会报异常
    // 是不是深度的
    function createGetters(isReadonly = false, shallow = false) {
      return function get(target, key, receiver) {
        // proxy + reflect
        // 后续object方法会被迁移到Reflect Reflect.getPropertyeof()
        // 以前target[key] = value 方法设置值可能会失败,并不会报异常,也没有返回值标识
        // Reflect方法具备返回值
        //   reflect使用可以不使用 proxy es6
        const res = Reflect.get(target, key, receiver);
    
        if (!isReadonly) {
          //   收集依赖
          track(target, TrackOpTypes.GET, key);
        }
        if (shallow) {
          return res;
        }
        if (isObject(res)) {
          // vue2是一上来就递归,vue3是当取值的时候会进行代理,vue3的代理模式是懒代理
          return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
      };
    } // 拦截获取功能
    function createSetter(shallow = false) {
      return function set(target, key, value, receiver) {
        const oldValue = target[key];
        let hadKey =
          isAarray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key);
        const res = Reflect.set(target, key, value, receiver);
        // s数据更新时视图更新, 通知对应属性的effect重新执行
    
        if (!hadKey) {
          // 新增
          trigger(target, TriggerOrTypes.ADD, key, value);
        } else if (hasChanged(oldValue, value)) {
          // 修改
          trigger(target, TriggerOrTypes.SET, key, value, oldValue);
        }
    
        return res;
      };
    } // 拦截设置功能
    
    const get = createGetters();
    const shallowGet = createGetters(false, true);
    const readonlyGet = createGetters(true);
    const shallowReadonlyGet = createGetters(true, true);
    
    const set = createSetter();
    const shallowSet = createSetter(true);
    // reactive的
    export const mutabelHandlers = {
      get, // get只是获取,无特殊参数
      set: set, // set需要,一样的
    };
    export const shallowReactiveHandlers = {
      get: shallowGet, // 浅的的需要给浅的参数
      set: shallowSet, // 需要给一个浅的的参数
    };
    
    let readonlyObj = {
      // 只读如果取值,就报错
      set: (target, key) => {
        console.warn(`set on key ${key} failed`);
      },
    };
    
    export const readonlyHandlers = extend(
      // 只读get访问只读的,set报错
      {
        get: readonlyGet,
      },
      readonlyObj
    );
    export const shallowReadonlyHandlers = extend(
      //同上
      {
        get: shallowReadonlyGet,
      },
      readonlyGet
    );
    

    目的是重写相应的getset方法。其实使用的都是工厂函数。

    然后就要创建响应式数据,就要根据不同的参数调用createReactiveObject方法

    const reactiveMap = new WeakMap(); // 会自动垃圾回收,存储的key只能是对象
    const readonlyMap = new WeakMap(); //
    export function createReactiveObject(target, isReadonly, baseHandlers) {
      // 如果目标不是对象,没法拦截了,reactive这个api只能拦截对象
      if (!isObject(target)) {
        return target;
      }
      // 如果某个对象被代理过了,就不要再次代理
      const proxyMap = isReadonly ? readonlyMap : reactiveMap;
      const existProxy = proxyMap.get(target);
      if (existProxy) {
        return existProxy;
      }
      const proxy = new Proxy(target, baseHandlers);
      proxyMap.set(target, proxy);
      return proxy;
    }
    

    使用的也是工厂函数实现代理的,数据代理的。

    因此这里的数据响应式处理使用的是Proxy,其中这里还对对象是否代理进行了处理,使用的是WeakMap。这样就可以做到了响应式原理。

标签:set,const,target,reactive,源码,key,vue3,return
From: https://www.cnblogs.com/dgqp/p/17364484.html

相关文章

  • 使用 ChatGPT 生成 Vue3 响应式导航栏组件
    下面是ChartGPT生成的Vue3响应式导航栏组件。经过简单的调试。基本可实现描述的要求。Nav.vue<template><navclass="nav-container"><divclass="logo-container"><imgsrc="your-logo-here.svg"alt="logo"/></......
  • 第四篇:白话tornado源码之褪去模板外衣的前戏
    原笔记博客链接:https://www.cnblogs.com/wupeiqi/p/4592637.html 执行字符串表示的函数,并为该函数提供全局变量本篇的内容从题目中就可以看出来,就是为之后剖析tornado模板做准备,也是由于该知识点使用的巧妙,所有就单独用一篇来介绍了。废话不多说,直接上代码:#!u......
  • vue2源码-十七、Vue组件间传值的方式及之间区别
    Vue组件间传值的方式及之间区别通过props传递:父组件传递数据给子组件使用//chilid,vueprops:{//字符串形式name:String//接收的类型参数//对象形式age:{type:Number,//接收的类型为数值defaule:18,//默认值为18r......
  • 深入探讨源码--ArrayList
    持续推送技术干货目录深入探讨源码之ArrayListArrayList类图ArrayList的数据结构ArrayList的关键属性ArrayList构造方法ArrayList常用方法add方法ArrayList中的fast-fail机制add(i,o)方法set(i,o)方法get(i)方法remove(index)方法remove(Object)方法clear方法indexOf(o)方法深......
  • Java重写源码中的方法
    重写步骤:1.找到你所要重写的方法的所在类,查看其中的路径;2.在我们的src目录下新建一个同包名同类名的类;3.将jar包中的重写方法所在类的所有代码复制到我们新建的同包名同类名的类中;4.在我们新建的同包名同类名的类中修改对应的方法中的代码,注意要保持方法中的参数不要发生改变,也......
  • vue3 ts 网易云信 未读数 手动设置已读已废弃
    vue3ts网易云信未读数//未读数清空$uikit.resetSessionUnread(store.sessionId.value);调用接口nim.resetSessionUnread('sessionId')重置会话未读数。将此会话未读数置为0,之后收到消息重新计算未读数。调用接口nim.setCurrSession('sessionId')设置当前会话。将此会......
  • vue3 获取asset文件夹下所有资源文件列表
     参考链接:https://www.jianshu.com/p/0f4386d19c07importpathfrom"path"; constgetLayerBgs=function(){ constimgs:any=[]; //获取所有背景图层 //读取文件的路径是否遍历文件的子目录匹配文件正则表达式 constfiles=require.context("@/a......
  • vue3 + ts + vite 封装 request
    npmiaxios目录 request.ts (直接复制可用)importaxiosfrom"axios";import{showMessage}from"./status";//引入状态码文件import{ElMessage}from"element-plus";//引入el提示框,这个项目里用什么组件库这里引什么//设置接口超时时间axios.defa......
  • Spring源码分析之BeanFactory
    概述以XmlBeanFactory为例分析Xml描述的Bean被Reasource加载到内存,先解析为Document对象,再解析为BeanDefinition注册到BeanDefinitionRegistry,再通过BeanFactory创建名词解释Resource是Spring对资源的抽象,主要是用来读取文件输入流Document是java本身的API进行解析的,得到......
  • vue3自定义指令实现el-select下拉加载更多
    1.新建js文件exportdefault(app)=>{app.directive('loadmore',{beforeMount(el,binding){constelement=el.querySelector('.t-select__dropdown');element.addEventListener('scroll',()=>{co......