最近看相关报道,vue3.0 开发计划 传送门 [译] 尤雨溪:Vue 3.0 计划 - 掘金
其中,监测机制:
一句话介绍:更加全面、精准、高效;更具可调试性的响应跟踪;以及可用来创建响应式对象的 API。
3.0 将带来一个基于 Proxy 的 observer 实现,它可以提供覆盖语言 (JavaScript——译注) 全范围的响应式能力,消除了当前 Vue 2 系列中基于 Object.defineProperty 所存在的一些局限,如:
对属性的添加、删除动作的监测
对数组基于下标的修改、对于 .length 修改的监测
对 Map、Set、WeakMap 和 WeakSet 的支持
另外这个新的 observer 还有以下特性:
公开的用于创建 observable (即响应式对象——译注) 的 API。这为小型到中型的应用提供了一种轻量级的、极其简单的跨组件状态管理解决方案。(译注:在这之前我们可以通过另外 new Vue({data : {...}}) 来创建这里所谓的 observable;另外,其实 vuex 内部也是用这种方式来实现的)
默认为惰性监测(Lazy Observation)。在 2.x 版本中,任何响应式数据,不管它的大小如何,都会在启动的时候被监测。如果你的数据量很大的话,在应用启动的时候,这就可能造成可观的性能消耗。而在 3.x 版本中,只有应用的初始可见部分所用到的数据会被监测,更不用说这种监测方案本身其实也是更加快的。
更精准的变动通知。举个例子:在 2.x 系列中,通过 Vue.set 强制添加一个新的属性,将导致所有依赖于这个对象的 watch 函数都会被执行一次;而在 3.x 中,只有依赖于这个具体属性的 watch 函数会被通知到。
不可变监测对象(Immutable observable):我们可以创建一个对象的“不可变”版本,以此来阻止对他的修改——包括他的嵌套属性,除非系统内部临时解除了这个限制。这种机制可以用来冻结传递到组件属性上的对象和处在 mutation 范围外的 Vuex 状态树。
更良好的可调试能力:通过使用新增的 renderTracked 和 renderTriggered 钩子,我们可以精确地追踪到一个组件发生重渲染的触发时机和完成时机,及其原因
那么 proxy 到底是什么 该怎么使用呢???
target是指代理的原对象,它是你需要拦截访问的原始对象,它总是作为Proxy构造器的第一个方法,也可以传递到每个trap中。
handler是一个包含你要进行拦截和处理的对象,也就是你拦截了原始对象以后想干什么?主要的代理内容在这里,是作为Proxy构造器的第二个方法参数传统,它是实现Proxy API。
trap用来规定对于指定什么方法进行拦截处理,如果你想拦截get方法的调用,那么你要定义一个get trap。
let obj = { name:"bruce",age:"25"}
let handler = {
//get运算符有两个参数 - 对象本身和被访问的属性。
get:function(target,prop){
console.log('target:'+target+"prop:"+prop);
},
//set操作符有三个参数 - 对象本身,被访问的属性和为该属性设置的值。
set:function(target,prop,value){
if(typeof(value) == 'string'){ //用来作 类型检验
}
console.log('target:'+target+"prop:"+prop+"value:"+value);
}
}
let proxy = new Proxy(obj,handler)
proxy.name //打印 target:[object Object]prop:name
proxy.name = 333 //打印 target:[object Object]prop:namevalue:333
proxy与设计模式
在面向对象的编程中,代理模式的合理使用能够很好的体现下面两条原则:
开放-封闭原则:代理可以随时从程序中去掉,而不用对其他部分的代码进行修改,在实际场景中,随着版本的迭代可能会有多种原因不再需要代理,那么就可以容易的将代理对象换成原对象的调用
Proxy实现前端中3种代理模式的使用场景,分别是:缓存代理、验证代理、实现私有属性。
1.0.0 缓存代理
//计算斐波那契数列 40以上很慢
const getFib = (number) => {
if (number <= 2) {
return 1;
} else {
return getFib(number - 1) + getFib(number - 2);
}
}
//创建缓存代理的工厂函数
const getCacheProxy = (fn, cache = new Map()) => {
return new Proxy(fn, {
apply(target, context, args) {
const argsString = args.join(' ');
console.log(args)
if (cache.has(argsString)) {
console.log(argsString)
// 如果有缓存,直接返回缓存数据
console.log(`输出${args}的缓存结果: ${cache.get(argsString)}`);
return cache.get(argsString);
}
const result = fn(...args);
cache.set(argsString, result);
return result;
}
})
}
//使用
const getFibProxy = getCacheProxy(getFib)
getFibProxy(40); // 102334155
getFibProxy(40); // 输出40的缓存结果: 102334155
1.0.1 验证代理
// 表单对象
const userForm = {
account: '',
password: '',
}
// 验证方法
const validators = {
account(value) {
// account 只允许为中文
const re = /^[\u4e00-\u9fa5]+$/;
return {
valid: re.test(value),
error: '"account" is only allowed to be Chinese'
}
},
password(value) {
// password 的长度应该大于6个字符
return {
valid: value.length >= 6,
error: '"password "should more than 6 character'
}
}
}
// 校验器
const getValidateProxy = (target, validators) => {
return new Proxy(target, {
_validators: validators,
set(target, prop, value) {
if (value === '') {
console.error(`"${
prop}" is not allowed to be empty`);
return target[prop] = false;
}
const validResult = this._validators[prop](value);
if(validResult.valid) {
return Reflect.set(target, prop, value);
} else {
console.error(`${
validResult.error}`);
return target[prop] = false;
}
}
})
}
// 使用
const userFormProxy = getValidateProxy(userForm, validators);
userFormProxy.account = '123'; // "account" is only allowed to be Chinese
userFormProxy.password = 'he'; // "password "should more than 6 character
1.0.2 实现私有属性 (设置访问限制)
function getPrivateProps(obj, filterFunc) {
return new Proxy(obj, {
get(obj, prop) {
if (!filterFunc(prop)) {
let value = Reflect.get(obj, prop);
// 如果是方法, 将this指向修改原对象
if (typeof value === 'function') {
value = value.bind(obj);
}
return value;
}
},
set(obj, prop, value) {
if (filterFunc(prop)) {
throw new TypeError(`Can't set property "${prop}"`);
}
return Reflect.set(obj, prop, value);
},
has(obj, prop) {
return filterFunc(prop) ? false : Reflect.has(obj, prop);
},
ownKeys(obj) {
return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop));
},
getOwnPropertyDescriptor(obj, prop) {
return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);
}
});
}
//因为私有属性 一般以 开头 这里就简单过滤
function propFilter(prop) {
return prop.indexOf('') === 0;
}
参考资料:从ES6重新认识JavaScript设计模式: 代理模式和Proxy_慕课手记
ES6 入门教程
https://www.cnblogs.com/diligenceday/p/5474126.html