Proxy Pattern
代理模式的实现与前端应用场景
问题
什么是代理模式?在前端开发中有哪些应用场景?
解答
代理模式为对象提供一个代理,用来控制对这个对象的访问。代理对象和本体对象具有相同的接口,客户端无需区分。
ES6 Proxy 基础用法
// 原始对象
const target = {
name: 'Alice',
age: 25
}
// 创建代理
const proxy = new Proxy(target, {
// 拦截读取操作
get(target, prop) {
console.log(`读取属性: ${prop}`)
return target[prop]
},
// 拦截设置操作
set(target, prop, value) {
console.log(`设置属性: ${prop} = ${value}`)
target[prop] = value
return true
}
})
proxy.name // 读取属性: name -> 'Alice'
proxy.age = 26 // 设置属性: age = 26
虚拟代理:图片懒加载
// 本体:负责设置图片 src
const imageLoader = {
setSrc(img, src) {
img.src = src
}
}
// 代理:先显示 loading,加载完成后替换
const proxyImageLoader = {
setSrc(img, src) {
// 先显示占位图
imageLoader.setSrc(img, '/loading.gif')
// 后台加载真实图片
const realImg = new Image()
realImg.onload = () => {
imageLoader.setSrc(img, src)
}
realImg.src = src
}
}
// 使用代理加载图片
const img = document.querySelector('#myImage')
proxyImageLoader.setSrc(img, 'https://example.com/large-image.jpg')
缓存代理:计算结果缓存
// 耗时计算函数
function heavyCompute(n) {
console.log('执行计算...')
let result = 0
for (let i = 1; i <= n; i++) {
result += i
}
return result
}
// 创建缓存代理
function createCacheProxy(fn) {
const cache = new Map()
return function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) {
console.log('命中缓存')
return cache.get(key)
}
const result = fn.apply(this, args)
cache.set(key, result)
return result
}
}
const cachedCompute = createCacheProxy(heavyCompute)
cachedCompute(10000) // 执行计算... -> 50005000
cachedCompute(10000) // 命中缓存 -> 50005000
保护代理:访问控制
// 用户数据
const user = {
name: 'Alice',
password: 'secret123',
email: 'alice@example.com'
}
// 创建保护代理
const protectedUser = new Proxy(user, {
get(target, prop) {
// 禁止访问敏感字段
if (prop === 'password') {
throw new Error('无权访问密码')
}
return target[prop]
},
set(target, prop, value) {
// 禁止修改某些字段
if (prop === 'name') {
throw new Error('用户名不可修改')
}
target[prop] = value
return true
}
})
protectedUser.email // 'alice@example.com'
protectedUser.password // Error: 无权访问密码
protectedUser.name = 'Bob' // Error: 用户名不可修改
数据响应式(Vue 3 原理简化版)
function reactive(obj) {
return new Proxy(obj, {
get(target, prop) {
// 收集依赖
track(target, prop)
return target[prop]
},
set(target, prop, value) {
target[prop] = value
// 触发更新
trigger(target, prop)
return true
}
})
}
// 简化的依赖收集和触发
const effects = new Map()
function track(target, prop) {
console.log(`收集依赖: ${prop}`)
}
function trigger(target, prop) {
console.log(`触发更新: ${prop}`)
}
const state = reactive({ count: 0 })
state.count // 收集依赖: count
state.count = 1 // 触发更新: count
关键点
- 代理和本体接口一致,对使用者透明
- 虚拟代理延迟创建开销大的对象(懒加载)
- 缓存代理避免重复计算,提升性能
- 保护代理控制访问权限
- ES6 Proxy 可拦截 13 种操作(get、set、has、deleteProperty 等)
目录