文章内容输出来源:拉勾教育前端高薪训练营
包含数据响应式、双向绑定、数据驱动三种
语法:Object.defineProperty(obj, prop, descriptor)
descriptor:
vue中使用Object.defineProperty劫持多个数据
function proxyData (data) {
Object.keys(data).forEach(key => {
Object.defineProperty(vm, key, {
enumerable: true,
configurable: true,
get () {
console.log('get:', key, data[key])
return data[key]
},
set (newVal) {
console.log('set:', key, newVal)
if (newVal === data[key]) return
data[key] = newVal
document.querySelector('#app').textContent = data[key]
},
})
})
}
语法:const p = new Proxy(target, handler)
返回值是一个对象,其结构为: {“proxy”: proxy, “revoke”: revoke}
一旦某个代理对象被撤销,它将变得几乎完全不可调用,在它身上执行任何的可代理操作都会抛出 TypeError 异常
当读取代理对象的原型时,该方法就会被调用,Object.getPrototypeOf()方法返回指定对象的原型
Object.getPrototypeOf(obj)
主要用来拦截 Object.setPrototypeOf(),设置一个指定的对象的原型到另一个对象或null
Object.setPrototypeOf(obj, prototype)
用于拦截对对象的Object.isExtensible(),判断一个对象是否是可扩展的
Object.isExtensible(obj)
对Object.preventExtensions()的拦截,让一个对象变的不可扩展,也就是永远不能再添加新的属性
Object.preventExtensions(obj)
Object.getOwnPropertyDescriptor()的钩子,返回指定对象上一个自有属性对应的属性描述符(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
Object.getOwnPropertyDescriptor(obj)
拦截对对象的 Object.defineProperty()操作,直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
针对in操作符的代理方法,可以看作是针对in操作的钩子,指定的属性在指定的对象或其原型链中,则in运算符返回true
in(prop in object)
拦截对象的读取属性操作
设置属性值操作的捕获器
用于拦截对对象属性的 delete 操作
用于拦截对对象属性的 delete 操作
用于拦截函数的调用
用于拦截new 操作符. 为了使new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即new target必须是有效的)
vue中使用Proxy劫持多个数据
let vm = new Proxy(data, {
get (target, prop) {
console.log('get, prop:', prop, data[prop])
return target[prop]
},
set (target, prop, newVal) {
console.log('set, prop:', prop, newVal)
if (newVal === target[prop]) return
target[prop] = newVal
document.querySelector('#app').textContent = target[prop]
},
})
Vue主要使用了两种设计模式
观察者模式:由具体的目标调度,比如当事件触发,Dep发布者就会去调用Watcher观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的
发布订阅模式:由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在
存在三个端:订阅着、发布者、信号中心
订阅者订阅某个任务,发布者在执行完某个任务后,向信号中心发布一个信号,信号中心将任务执行结果发送给订阅者
// 事件中心eventBus.js
let eventBus = new Vue()
// 订阅者ComponentA.vue
create () {
eventBus.$on('dataChange', val => {
console.log(val)
})
}
// 发布者ComponentB.vue
dataChange () {
eventBus.$emit('dataChange', 'data has changed')
}
class eventBus {
constructor () {
this.subs = {}
}
// 注册事件
$on (eventType, callback) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(callback)
}
// 触发事件
$emit (eventType) {
if (this.subs[eventType]) {
this.subs[eventType].forEach(callback => {
callback()
})
}
}
}
观察者模式没有事件中心
class Dep {
constructor () {
this.subs = []
}
addSub (sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
notify () {
this.subs.forEach(sub => {
sub.update()
})
}
}
class Watcher {
update () {
console.log('update')
}
}
Vue类
功能点
constructor params参数
方法
class Vue {
constructor (options) {
// 将选项数据保存在对应数据上
this.$options = options
this.$data = options.data
this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
// 把data中的属性注入到Vue实例,转换成getter/setter
this._proxyData(this.$data)
// 调用observer对象,监听数据变化
new Observer(this.$data)
// 调用compiler解析指令/差值表达式
new Compiler(this)
}
_proxyData (data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get () {
return data[key]
},
set (newVal) {
if (newVal === data[key]) return
data[key] = newVal
}
})
})
}
}
Observer类
功能点
constructor params参数
方法
class Observer {
constructor (data) {
this.walk(data)
}
walk (data) {
if (!data || typeof data !== 'object') return
Object.keys(data).forEach(prop => {
this.defineReactive(data, prop, data[prop])
})
}
// 此处传递value值,而不用obj[prop]获取的原因是,调用obj[prop]时会触发Vue实例中的getter属性,而Vue实例中的getter又会调用Observer实例中的getter,会出现死循环造成堆栈溢出
defineReactive (obj, prop, value) {
const self = this
// 生成Dep收集依赖,并发送通知
const dep = new Dep()
// 如果value是一个object,其内部的属性也需要转换成getter/setter
self.walk(value)
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
get () {
// 收集依赖
Dep.target && dep.addsub(Dep.target)
return value
},
set (newVal) {
if (newVal === value) return
value = newVal
// 如果newVal是一个object,其内部的属性也需要转换成getter/setter
self.walk(newVal)
// 发送通知
dep.notify()
}
})
}
}
Compiler类
功能点
constructor params参数
方法
nodeType
class Compiler {
constructor (vm) {
this.el = vm.$el
this.vm = vm
this.compile(this.el)
}
compile (el) {
const childNodes = el.childNodes
Array.from(childNodes).forEach(node => {
if (this.isTextNode(node)) {
this.compileText(node)
}
if (this.isElementNode(node)) {
this.compileElement(node)
}
// 处理深层次子节点
if (node.childNodes && node.childNodes.length) {
this.compile(node)
}
})
}
compileElement(node) {
const { attributes } = node
Array.from(attributes).forEach(attr => {
let attrName = attr.name
if (this.isDirective(attrName)) {
attrName = attrName.substr(2)
const { value } = attr
this.update(node, value, attrName)
}
})
}
update (node, prop, attrName) {
const updateFn = this[`${attrName}Updater`]
updateFn && updateFn.call(this, node, this.vm[prop], prop)
}
// v-text
textUpdater (node, value, prop) {
node.textContent = value
// 创建watcher对象监听数据改变
new Watcher(this.vm, prop, newValue => {
node.textContent = newValue
})
}
// v-model
modelUpdater (node, value, prop) {
node.value = value
// 创建watcher对象监听数据改变
new Watcher(this.vm, prop, newValue => {
node.value = newValue
})
// 实现v-model双向绑定
node.addEventListener('input', () => {
this.vm[prop] = node.value
})
}
compileText(node) {
// 匹配{{}}
const reg = /\{\{(.+?)\}\}/
let value = node.textContent
if (reg.test(value)) {
// 获取正则中的第一个分组
const prop = RegExp.$1.trim()
node.textContent = value.replace(reg, this.vm[prop])
// 创建watcher对象监听数据改变
new Watcher(this.vm, prop, newValue => {
node.textContent = newValue
})
}
}
isDirective(attrName) {
return attrName.startsWith('v-')
}
isTextNode(node) {
return node.nodeType === 3
}
isElementNode(node) {
return node.nodeType === 1
}
}
Dep类
功能点
constructor params参数
方法
class Dep {
constructor () {
this.subs = []
}
addSub (sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
notify () {
this.subs.forEach(sub => {
sub.update()
})
}
}
Watcher类
功能点
constructor params参数
方法
class Watcher {
constructor (vm, prop, callback) {
this.vm = vm
this.prop = prop
this.callback = callback
// 把watcher对象记录到Dep类的静态属性target
Dep.target = this
// 访问vm[prop]的get方法把watcher添加到dep
this.oldValue = vm[prop]
// 重置Dep.target
Dep.target = null
}
update () {
const newValue = this.vm[this.prop]
if (this.oldValue === newValue) return
this.callback(newValue)
}
}