您现在的位置是:网站首页 > Vue的响应式系统与设计模式文章详情
Vue的响应式系统与设计模式
陈川
【
JavaScript
】
50325人已围观
7801字
Vue的响应式系统原理
Vue的响应式系统是其核心特性之一,通过数据劫持和依赖收集实现自动更新。当数据变化时,依赖该数据的视图会自动重新渲染。这个机制主要基于ES5的Object.defineProperty或ES6的Proxy实现。
// 简单的响应式实现示例
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
dep.depend() // 收集依赖
return val
},
set(newVal) {
val = newVal
dep.notify() // 触发更新
}
})
}
class Dep {
constructor() {
this.subscribers = []
}
depend() {
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target)
}
}
notify() {
this.subscribers.forEach(sub => sub())
}
}
let target = null
function watchEffect(fn) {
target = fn
fn()
target = null
}
观察者模式在响应式中的应用
Vue的响应式系统本质上是观察者模式的实现。每个响应式属性都是一个被观察者(Subject),而依赖这些属性的计算属性、watcher和模板渲染函数都是观察者(Observer)。
// 观察者模式示例
class Subject {
constructor() {
this.observers = []
}
addObserver(observer) {
this.observers.push(observer)
}
notify() {
this.observers.forEach(observer => observer.update())
}
}
class Observer {
update() {
console.log('Observer notified!')
}
}
// Vue中的Watcher就是典型的观察者
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
Dep.target = this
const value = this.getter.call(this.vm, this.vm)
Dep.target = null
return value
}
update() {
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
发布-订阅模式与事件系统
Vue的事件系统($on/$emit)是典型的发布-订阅模式实现。组件间通信的Event Bus也是基于此模式构建。
// 简单的Event Bus实现
class EventBus {
constructor() {
this.events = {}
}
$on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
}
$emit(event, ...args) {
const callbacks = this.events[event]
if (callbacks) {
callbacks.forEach(cb => cb(...args))
}
}
$off(event, callback) {
if (!callback) {
delete this.events[event]
} else {
const callbacks = this.events[event]
if (callbacks) {
this.events[event] = callbacks.filter(cb => cb !== callback)
}
}
}
}
// Vue中使用
const bus = new EventBus()
// 组件A
bus.$on('some-event', payload => {
console.log('Received:', payload)
})
// 组件B
bus.$emit('some-event', { data: 'test' })
代理模式与Vue 3的响应式
Vue 3使用Proxy替代Object.defineProperty实现响应式,这是代理模式的典型应用。Proxy可以拦截对象的各种操作,提供更完善的响应式支持。
// 使用Proxy实现响应式
function reactive(obj) {
const handler = {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(target, key)
return true
}
}
return new Proxy(obj, handler)
}
const targetMap = new WeakMap()
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
let activeEffect = null
function effect(fn) {
activeEffect = fn
fn()
activeEffect = null
}
策略模式与指令系统
Vue的指令系统(v-if, v-for等)采用了策略模式,不同的指令对应不同的处理策略,使得系统易于扩展。
// 简化的指令策略模式实现
const directiveStrategies = {
'text': (el, value) => {
el.textContent = value
},
'show': (el, value) => {
el.style.display = value ? '' : 'none'
}
}
function updateDirective(el, binding) {
const strategy = directiveStrategies[binding.name]
if (strategy) {
strategy(el, binding.value)
}
}
// 使用示例
const el = document.querySelector('#test')
updateDirective(el, { name: 'text', value: 'Hello' })
updateDirective(el, { name: 'show', value: true })
装饰器模式与高阶组件
在Vue中,高阶组件(HOC)和mixin可以看作装饰器模式的应用,它们在不修改原组件的情况下增强组件功能。
// 高阶组件示例
function withLoading(WrappedComponent) {
return {
data() {
return {
loading: false
}
},
methods: {
showLoading() {
this.loading = true
},
hideLoading() {
this.loading = false
}
},
render(h) {
return h(WrappedComponent, {
props: this.$props,
on: this.$listeners,
scopedSlots: this.$scopedSlots
}, [
this.loading ? h('div', { class: 'loading' }, 'Loading...') : null,
this.$slots.default
])
}
}
}
// 使用高阶组件
const EnhancedComponent = withLoading(BaseComponent)
工厂模式与组件创建
Vue的组件系统可以看作工厂模式的应用,通过Vue.component()注册组件,然后通过标签名创建组件实例。
// 简化的组件工厂
class VueComponentFactory {
constructor() {
this.components = {}
}
register(name, component) {
this.components[name] = component
}
create(name, props) {
const component = this.components[name]
if (!component) {
throw new Error(`Unknown component: ${name}`)
}
return new component(props)
}
}
// 使用示例
const factory = new VueComponentFactory()
factory.register('my-button', {
template: '<button @click="handleClick"><slot></slot></button>',
methods: {
handleClick() {
this.$emit('click')
}
}
})
const button = factory.create('my-button', {
onClick: () => console.log('Clicked!')
})
状态模式与Vuex
Vuex作为Vue的状态管理库,其核心概念(State, Mutation, Action)体现了状态模式的思想,将状态变化的行为封装在不同的对象中。
// 简化的Vuex实现
class Store {
constructor(options) {
this.state = reactive(options.state || {})
this._mutations = options.mutations || {}
this._actions = options.actions || {}
this._getters = options.getters || {}
// 处理getters
this.getters = {}
Object.keys(this._getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => this._getters[key](this.state),
enumerable: true
})
})
}
commit(type, payload) {
const mutation = this._mutations[type]
if (mutation) {
mutation(this.state, payload)
}
}
dispatch(type, payload) {
const action = this._actions[type]
if (action) {
return action({
state: this.state,
commit: this.commit.bind(this),
dispatch: this.dispatch.bind(this),
getters: this.getters
}, payload)
}
}
}
// 使用示例
const store = new Store({
state: {
count: 0
},
mutations: {
increment(state, payload) {
state.count += payload
}
},
actions: {
asyncIncrement({ commit }, payload) {
setTimeout(() => {
commit('increment', payload)
}, 1000)
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
组合模式与插槽系统
Vue的插槽系统(slot)体现了组合模式的思想,允许组件以树形结构组合,形成复杂的UI结构。
// 简化的插槽系统实现
class Component {
constructor(options) {
this.template = options.template
this.slots = options.slots || {}
}
render() {
let html = this.template
Object.keys(this.slots).forEach(slotName => {
const slotContent = this.slots[slotName]
html = html.replace(`<slot name="${slotName}"></slot>`, slotContent)
})
return html
}
}
// 使用示例
const parent = new Component({
template: `
<div>
<header><slot name="header"></slot></header>
<main><slot></slot></main>
<footer><slot name="footer"></slot></footer>
</div>
`
})
const child = new Component({
slots: {
header: '<h1>Page Title</h1>',
default: '<p>Main content</p>',
footer: '<p>Copyright 2023</p>'
}
})
console.log(child.render())
上一篇: React中的设计模式实践
下一篇: Angular中的依赖注入模式