Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
Y
yifu-study-front-share
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
CI / CD
CI / CD
Pipelines
Schedules
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
yifu-study
front
yifu-study-front-share
Commits
7a158021
Commit
7a158021
authored
Mar 29, 2022
by
zhangfangli
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
浅谈Proxy
parent
852793e5
Pipeline
#15455
canceled with stage
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
198 additions
and
0 deletions
+198
-0
浅谈Proxy.md
source/_posts/浅谈Proxy.md
+198
-0
No files found.
source/_posts/浅谈Proxy.md
0 → 100644
View file @
7a158021
# Vue3使用的proxy是什么?
众做周知Vue2的双向绑定原理是利用了ES5的一个API
`Object.definePropert()`
对数据进行劫持,结合发布订阅模式的方式来实现的。
而Vue3 中则是使用了ES6的Proxy 对数据代理。
## Vue2 的Object.definePropert()
1.
定义:
Object.definePropert()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
例子:
```
jsx
const
object1
=
{};
Object
.
defineProperty
(
object1
,
'property1'
,
{
value
:
42
,
writable
:
false
});
object1
.
property1
=
77
;
console
.
log
(
object1
.
property1
);
//打印出来的一人是42
```
2.
语法:
```
jsx
Object
.
defineProperty
(
obj
,
prop
,
descriptor
)
// obj 要定义属性的对象
// 要定义或修改的属性的名称或 Symbol
//descriptor 要定义或修改的属性描述符
```
返回值:被传递给函数的对象。
3.
如何实现响应式的?
通过
`defineProperty`
两个属性,get及set
-
get
属性的getter函数,当访问改属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承管理,this不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。
-
set
属性的setter函数,当属性值被修改时,会调用此函数。改方法接受一个参数(也就是被赋的新值),会传入赋值时的this对象。默认undefined.
简单的例子: 监听person 的 namep变化
```
jsx
let
person
=
{}
let
name
=
'张小张'
Object
.
defineProperty
(
person
,
'namep'
,{
// 默认是不可枚举的(for in打印不出来) 可以使用:enumerable:true
// 默认不可修改,可:wirtable:true
//默认不可删除 可:configurable:true
get
(){
console
.
log
(
'触发了get的方法'
);
return
name
},
set
(
val
){
console
.
log
(
'触发了set的方法'
);
name
=
val
}
})
// 读取对象的属性
console
.
log
(
person
.
namep
);
// 触发了get的方法 张小张
// 修改name,再次读取
name
=
'王小王'
console
.
log
(
person
.
namep
);
// 触发了get的方法 王小王
// 修改对象
person
.
namep
=
'李小里'
console
.
log
(
person
.
namep
);
// 触发了set的方法 触发了get的方法 李小里
```
如果监听多个属性的变化,这里就要用到
`Object.keys(obj)`
进行遍历等,以及深度监听时又要借助递归来实现。
而在监听数组方面
```
jsx
let
arr
=
[
1
,
2
,
3
,
4
,
5
]
let
obj
=
{}
Object
.
defineProperty
(
obj
,
'arr'
,{
get
(){
console
.
log
(
'get'
);
return
arr
},
set
(
val
){
console
.
log
(
'set'
,
val
);
arr
=
val
}
})
console
.
log
(
obj
.
arr
);
// get [1,2,3,4,5]
obj
.
arr
=
[
2
,
3
,
4
]
// set [2,3,4]
obj
.
arr
.
push
(
7
)
// get 但是监听不到下面的了
```
由此看出:数组的一些方法,例如push等,get是监听不到的。
通过索引访问或者修改数组中已经存在的元素,是可以触达get和set,但是对于通过push、unshift增加的元素,会增加索引,这种情况无法监听,通过pop或shift删除元素,会更新索引,也会触发。
在vue2 中,是通过重写Array原型上的方法解决这个问题。
但是其实依然会有很多问题,类似给对象新增一个属性时,也需要手动监听,所以在vue2中给数组或者对象新增属性时,使用this.$set 或者vue.Set()
## Vue3的Proxy
Es6的proxy正好可以解决以上的问题。
1.
定义:
**Proxy**
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
其实就是在对目标对象的操作之前提供了拦截,可以对外界的操作进行过滤和改写,修改某些操作的默认行为,这样我们可以不直接操作对象本身,而是通过操作对象的代理对象来间接来操作对象,达到预期的目的。
2.
语法:
```
jsx
const
p
=
new
Proxy
(
target
,
handler
)
//`target` 要使用 `Proxy` 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
// `handler` 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 `p` 的行为。
```
简单的例子:
```
jsx
// 定义一个需要代理的对象
let
person
=
{
name
:
'张小张'
,
age
:
18
}
//定义handler 对象
let
handler
=
{
get
(
target
,
key
){
// target就是代理的对象,key 是属性值
return
key
in
target
?
target
[
key
]:
'没有这个属性'
},
set
(
target
,
key
,
val
){
// val 是新值
target
[
key
]
=
val
return
true
}
}
let
proxyObj
=
new
Proxy
(
person
,
handler
)
console
.
log
(
proxyObj
.
name
)
// 张小张
console
.
log
(
proxyObj
.
sex
)
// 没有这个属性
proxyObj
.
age
=
19
console
.
log
(
proxyObj
.
age
)
//19
```
这里注意一下:set()方法必须返回一个布尔值,返回
`true`
代表属性设置成功,- 在严格模式下,如果
`set()`
方法返回
`false`
,那么会抛出一个异常。
3.
Reflect:是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers 的方法相同。
为操作对象而提供的新API,将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法。 如果出错将返回false。
所以上面的例子改一下:
```
jsx
// 定义一个需要代理的对象
let
person
=
{
name
:
'张小张'
,
age
:
18
}
//定义handler 对象
let
handler
=
{
get
(
target
,
key
){
// target就是代理的对象,key 是属性值
console
.
log
(
Reflect
.
get
(
target
,
key
));
return
Reflect
.
get
(
target
,
key
)?
Reflect
.
get
(
target
,
key
):
'没有这个属性'
},
set
(
target
,
key
,
val
){
// val 是新值
Reflect
.
set
(
target
,
key
,
val
)
return
true
}
}
let
proxyObj
=
new
Proxy
(
person
,
handler
)
console
.
log
(
proxyObj
.
name
)
// 张小张 张小张
console
.
log
(
proxyObj
.
sex
)
// undefined 没有这个属性
proxyObj
.
age
=
22
console
.
log
(
proxyObj
.
age
)
//22
```
用这个例子和上面的例子作比较发现:Proxy代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定。
不光如此,对于上面遇到的数组方法、对象新增属性等都可以得到解决。
4.
Proxy 的拦截:
除了上面用到的set()和get(),Proxy支持的拦截操作一共有13种。具体信息可以查看MDN(
[
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy
]
-
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy
[
'foo'
]
。
-
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy
[
'foo'
]
= v,返回一个布尔值。
-
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
-
deleteProperty(target, propKey):拦截delete proxy
[
propKey
]
的操作,返回一个布尔值。
-
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
-
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
-
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
-
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
-
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
-
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
-
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
-
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
-
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
5.
Proxy 总结:
Proxy相比Object.defineProperty,可以直接监听整个对象而非属性,也可以直接监听数组的变化,并且提供13中拦截方法。但是缺点是不兼容IE,存在浏览器兼容问题,也没有polyfill(指的是一个代码块。这个代码块向开发者提供了一种技术, 这种技术可以让浏览器提供原生支持,抹平不同浏览器对API兼容性的差异)。Object.defineProperty支持IE9。
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment