您现在的位置是:网站首页 > Vue的响应式系统与设计模式文章详情

Vue的响应式系统与设计模式

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())

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步