mini-vue开发准备工作
数据驱动
数据响应式、双向绑定、数据驱动
数据响应式
数据模型仅仅是普通的javascript对象,而当我们修改数据时,视图会进行更新,避免了繁琐的DOM操作,提高开发效率。
双向绑定
数据改变,视图改变,视图改变,数据也随之改变
我们可以使用v-model在表单元素上创建双向数据绑定
数据驱动时Vue最独特的特性之一
开发过程中仅需要关注数据本身,不需要关心数据是如何渲染到视图
vue2.x响应式原理
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data
选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty
把这些 property 全部转为 getter/setter。Object.defineProperty
是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
下面我们来演示下Object.defineProperty如何使用
<template> <div id='app'>hello</div> </template> <script> // 模拟vue中的data let data = { msg: 'hello' } // 模拟vue实例 let vm = {} // 数据劫持: 当访问或者设置vm中的成员的时候,做一些干预操作 Object.defineProperty(vm, 'msg', { // 可枚举 enumerable: true, // 可配置 configurable: true, // 取值操作 get() { console.log('get:', data.msg) return data.msg }, // 设置值操作 set(newValue) { if(newValue === data.msg) { return } data.msg = newValue document.querySelector('#app').textContent = data.msg } }) </script> 复制代码
上述代码我们演示了单个属性的转化方法,如果多个属性的话即需要循环的给属性添加defineProperty
即可,这块不做演示了
vue3.x响应式原理
vue3.x中使用proxy来进行监听对象,而不是属性,此外proxy IE不支持。
下面我们通过代码来演示下proxy
// 模拟vue中的data let data = { msg: 'hello' } // 模拟vue实例 let vm = new Proxy(data, { // 当访问vm对象的时候执行get方法 get(target, key) { return target(key) }, // 当设置vm的成员是会执行set方法 set(target, key, newValue) { if(target[key] === newValue) { return } target[key] = newValue document.querySelector('#app').textContent = target[key] } }) </script> 复制代码
发布订阅模式
我们假定存在一个‘信号中心’,某个任务执行完成,就向信号中心发布(publish)一个信号,其他任务可以向信号中心‘订阅(subscibe)’这个信号,从而知道什么时候自己可以开始执行,这就叫做‘发布/订阅模式’(publish-subscribe pattern)
我们用兄弟组件通信过程来演示下发布订阅模式
// eventBus.js // 事件中心 let eventBus = new Vue(); // ComponentA.vue // 发布者 addTodo: function() { // 发布消息 eventBus.$emit('add-todo', {text: '发布新消息了'}) } // ComponentB.vue // 订阅者 create: function() { //订阅消息 eventBus.$on('add-todo', () => {}) } 复制代码
下面我们来模拟下vue自定义事件
let vm = new Vue(); vm.$on('dataChange', () => { console.log('dataChange') }) vm.$emit('dataChange') 复制代码
class EventEmitter { constructor() { this.subs = Object.create(null); } $on(eventType, handler) { this.subs[eventType] = this.subs[eventType] || []; this.subs[eventType].push(handler) } $emit(eventType) { if(this.subs[eventType] && Array.isArray(this.subs[eventType])) { this.subs[eventType].forEach(handler => { handler() }) } } } 复制代码
观察者模式
观察者(订阅者)--Watcher
update() 当事件发生时,具体要做的事情
目标(发布者)--Dep
subs数组 存储所有观察者
addSub() 添加观察者
notify() 当事件发生时,调用所有观察者的update方法
没有事件中心
下面我们用一段代码演示观察者模式
// 发布者-目标 class Dep { constructor() { this.subs = [] } addSub(sub) { if(sub && sub.update) { this.subs.push(sub) } } notify() { this.subs.forEach(sub => { sub.update() }) } } // 订阅者 Watcher class Watcher() { update() { console.log('update') } } 复制代码
总结:
观察者模式是有具体的目标调度,比如事件触发,Dep就会去调用观察者方法,所以观察者模式的订阅者与发布者之间时存在依赖的
发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。
作者:真不想写代码
链接:https://juejin.cn/post/7025533146456129550