vue3中的数据劫持的最新实现方案的proxy示例详解

 更新时间:2024年11月25日 14:45:10   作者:风清云淡_A  
Vue3中使用Proxy实现数据劫持,解决了Vue2中数组和对象劫持的遗留问题,Proxy可以修改某些操作的默认行为,通过get和set方法实现数据的劫持和保护机制,感兴趣的朋友跟随小编一起看看吧

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的,在大版本v3中彻底重写了这部分,使用了proxy这个数据代理的方式,来修复了v2中对数组和对象的劫持的遗留问题。

proxy是什么

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成对源对象进行一个封装,在操作源对象之前,做了一系列额外的操作,最终返回我们需要的新数据对象。

基础使用

let obj = new Proxy(
  {},
  {
    get(target, prop, receiver) {
      console.log("get", prop);
      if (!target[prop]) target[prop] = 120;
      return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
      console.log("set", prop);
      return Reflect.set(target, prop, value, receiver);
    },
  }
);
obj.count = 1;
obj.count;
obj.count;
obj.count;
console.log(obj.count);
obj.age;
console.log(obj.age);

proxy实例有两个参数,一个是目标对象,一个是操作方法的hash集合

取值函数get,赋值函数set

对特定属性的劫持

const proxyObj = new Proxy(
  { name: "Tom", age: 18 },
  {
    get: function (target, prop) {
      if (prop === "age") return target[prop] - 1;
      return 35;
    },
  }
);
proxyObj.time;
console.log("🚀 ~ proxyObj.time:", proxyObj.time);
proxyObj.age;
console.log("🚀 ~ proxyObj.time:", proxyObj.age);

把实例方法封装在对象内部

const object = {
  name: "Tom",
  age: 18,
  sayHi() {
    console.log("sayHi");
  },
  proxy() {
    return new Proxy(this, {
      get(target, prop) {
        console.log("🚀 ~ get ~ prop:", prop);
        if (prop in target) {
          return Reflect.get(target, prop);
        } else {
          return "no prop";
        }
        return Reflect.get(target, prop);
      },
    });
  },
};
const newProxy = object.proxy();
// newObjj.age;
console.log("🚀 ~ newObjj.age;:", newProxy.age);
console.log("🚀 ~ newObjj.name;:", newProxy.sex);

对数组进行负值索引的操作

function createArray(...elements) {
  let handler = {
    get(target, prop, receiver) {
      let index = Number(prop);
      if (index < 0) {
        prop = String(target.length + index);
      }
      return Reflect.get(target, prop, receiver);
    },
  };
  let target = [];
  target.push(...elements);
  return new Proxy(target, handler);
}
let arr = createArray("a", "b", "c");
arr[-1];
console.log("🚀 ~ arr[-1]:", arr[-1]);
console.log("🚀 ~ arr[-1]:", arr[-2]);
console.log("🚀 ~ arr[-1]:", arr[-3]);

实现数据的链式调用

var double = (n) => n * 2;
var pow = (n) => Math.pow(n, 2);
var reverse = (n) => String(n).split("").reverse().join("");
const pipe = function (value) {
  var funcStack = [];
  var oProxy = new Proxy(
    {},
    {
      get: function (target, key) {
        console.log("🚀 ~ pipe ~ key:", key);
        if (key === "get") {
          return funcStack.reduce(function (val, func) {
            return func(val);
          }, value);
        }
        // 把方法存储到栈中
        funcStack.push(window[key]);
        console.log("🚀 ~ funcStack:", funcStack);
        return oProxy;
      },
    }
  );
  return oProxy;
};
const data1 = pipe(3).double.pow.reverse.get;
console.log("🚀 ~ data:", data1);

注意:三个方法必须是var声明的,let/const都会报错

上面代码设置 Proxy 以后,达到了将函数名链式使用的效果。

利用get拦截,实现一个生成各种 DOM 节点的通用函数dom

const dom = new Proxy(
  {},
  {
    get(target, prop) {
      return function (arrts, ...children) {
        const el = document.createElement(prop);
        for (let prop of Object.keys(arrts)) {
          el.setAttribute(prop, arrts[prop]);
        }
        for (let child of children) {
          console.log("🚀 ~ get ~ child:", child);
          if (typeof child === "string") {
            child = document.createTextNode(child);
          }
          el.appendChild(child);
        }
        return el;
      };
    },
  }
);
const el = dom.div(
  {},
  "Hello, my name is ",
  dom.a({ href: "//example.com" }, "Mark"),
  ". I like:",
  dom.ul(
    {},
    dom.li({}, "The web"),
    dom.li({}, "Food"),
    dom.li({}, "…actually that's it")
  )
);
document.body.appendChild(el);

第三个参数,它总是指向原始的读操作所在的那个对象

const proxy = new Proxy(
  {},
  {
    get: function (target, prop, receiver) {
      console.log("🚀 ~ prop:", prop);
      return receiver;
    },
  }
);
const isSame = proxy.getReceiver === proxy;
console.log("🚀 ~ isSame:", isSame);
const d = Object.create(proxy);
console.log("ddd", d.a === d);

如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性

const target = Object.defineProperties(
  {},
  {
    foo: { value: "bar", enumerable: false, configurable: false },
  }
);
const handler = {
  get(target, prop) {
    return "abc";
  },
};
const proxy2 = new Proxy(target, handler);
proxy2.foo;

上面通过 Proxy 对象访问该属性会报错。

拦截方法的执行

上面的都是object对象的属性进行劫持,也可以作为方法调用时进行劫持。

var target = function () {
  return "I am the target";
};
var handler = {
  apply(target, thisArg, argumentsList) {
    console.log("🚀 ~ apply ~ argumentsList:", argumentsList);
    const res = target();
    console.log("🚀 ~ apply ~ res:", res);
    return "I am the proxy" + " " + argumentsList.join(",");
  },
};
const p = new Proxy(target, handler);
const a = p("a", "b");
console.log("🚀 ~ a:", a);

变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串

function sum(left, right) {
  return left + right;
}
var twice = {
  apply(target, context, args) {
    console.log("🚀 ~ apply ~ context:", context);
    console.log("🚀 ~ apply ~ args:", args);
    return Reflect.apply(target, context, args) * 2;
  },
};
const proxy = new Proxy(sum, twice);
const data = proxy(1, 2);
console.log("🚀 ~ data:", data);
const data2 = proxy.call(null, 2, 5);
console.log("🚀 ~ data2:", data2);
const data3 = proxy.apply(null, [5, 5]);
console.log("🚀 ~ data3:", data3);

当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。

get和set方法,实现内部属性的保护机制

const proxy = new Proxy(
  {},
  {
    get(target, prop) {
      invariant(prop, "get");
      return Reflect.get(target, prop);
    },
    set(target, prop, value) {
      invariant(prop, "set");
      Reflect.set(target, prop, value);
      return true;
    },
  }
);
function invariant(key, action) {
  if (key[0] === "_") {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
// proxy._prop;
proxy._prop = "value";

拦截key in proxy的操作

var target = { _prop: "foo", prop: "foo" };
const proxy = new Proxy(target, {
  has(target, key) {
    if (key[0] === "_") {
      console.log("false");
      return false;
    }
    return key in target;
  },
});
"_prop" in proxy; // false

deleteProperty删除属性的劫持

const handler = {
  construct(target, args) {
    console.log("called: " + args.join(","));
    return new target(...args);
  },
  deleteProperty(target, prop) {
    if (prop === "age") return false;
    delete target[prop];
    return true;
  },
};
const P = new Proxy(function () {}, handler);
const p = new P(10);
P.value;
const p2 = new Proxy(
  {
    age: 20,
    name: "John",
    greet: () => console.log("hello"),
  },
  handler
);
delete p2.age;
delete p2.name;

到此这篇关于vue3中的数据劫持的最新实现方案的proxy的详解的文章就介绍到这了,更多相关vue proxy内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于vue.js实现图片轮播效果

    基于vue.js实现图片轮播效果

    这篇文章主要为大家详细介绍了基于vue.js实现图片轮播效果,vue如何实现轮播图效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • Vue.js弹出模态框组件开发的示例代码

    Vue.js弹出模态框组件开发的示例代码

    本篇文章主要介绍了Vue.js弹出模态框组件开发的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Vue模拟el-table演示插槽用法的示例详解

    Vue模拟el-table演示插槽用法的示例详解

    很多人知道插槽分为三种,但是实际到elementui当中为什么这么用,就一脸懵逼,接下来就跟大家聊一聊插槽在elementui中的应用,并且自己写一个类似el-table的组件,感兴趣的可以了解一下
    2023-05-05
  • Vue如何整合mavon-editor编辑器(markdown编辑和预览)

    Vue如何整合mavon-editor编辑器(markdown编辑和预览)

    这篇文章主要介绍了Vue整合mavon-editor编辑器(markdown编辑和预览)的相关知识,mavon-editor是目前比较主流的markdown编辑器,重点介绍它的使用方法,需要的朋友可以参考下
    2022-10-10
  • vue-router路由跳转问题 replace

    vue-router路由跳转问题 replace

    这篇文章主要介绍了vue-router路由跳转问题 replace,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • Vue3中集成高德地图并实现平移缩放功能

    Vue3中集成高德地图并实现平移缩放功能

    随着前端技术的不断发展,地图应用在我们的项目中越来越常见,本文将介绍如何在Vue3项目中集成高德地图,并通过简单的配置实现地图的平移和缩放功能,需要的朋友可以参考下
    2024-09-09
  • vue-cli webpack配置文件分析

    vue-cli webpack配置文件分析

    这篇文章主要介绍了vue-cli webpack配置文件分析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • 如何用vue2+element-ui实现多行行内表格编辑

    如何用vue2+element-ui实现多行行内表格编辑

    最近开发项目,关于表格的数据操作比较多,这个地方个人觉得比较难搞,特此记录一下,这篇文章主要给大家介绍了关于如何用vue2+element-ui实现多行行内表格编辑的相关资料,需要的朋友可以参考下
    2024-08-08
  • element组件el-date-picker禁用当前时分秒之前的日期时间选择

    element组件el-date-picker禁用当前时分秒之前的日期时间选择

    本文主要介绍了element组件el-date-picker禁用当前时分秒之前的日期时间选择,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 解决vue单页面应用中动态修改title问题

    解决vue单页面应用中动态修改title问题

    这篇文章主要介绍了解决vue单页面应用中动态修改title问题,本文通过示例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06

最新评论