代码地址:
目标:
- 模拟一个最小版本的vue
- 响应式原理在面试常常问到的问题
- 学习别人优秀的经验,转换成自己的经验
- 实际项目中出问题的原理层面的解决
- 为学习vue源码做铺垫
1.数据驱动
数据响应式
:数据响应式中的【数据】指的是数据模型,基于vue开发的时候,我们的数据模型就是普通的javascript对象。
数据响应式的核心是
:当我们改变数据的时候,试图会进行更新,避免了频繁的dom操作,提高开发效率。
双向绑定
:当数据发生改变,视图同时会发生更新;视图改变,数据也会发生改变。
在双向绑定中包含了数据响应式。双向绑定一般指可以和用户交互的表单元素。我们可以使用v-model在表单元素上创建双向数据绑定。
数据驱动
:就是一个开发的过程,开发过程中仅需要关注数据本身,不需要关心数据是如何渲染到视图。
现在主流的mvvm都帮我们实现了,数据响应式和双向绑定。
是vue开发过程中最独特的特性之一。
2.响应式核心原理 vue2.x
响应式核心原理
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty
把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
- 演示Object.defineProperty如何使用 - 单个数据
Object.defineProperty()参数:第一个参数:对象,第二个参数:向对象中添加的属性,第三个参数:属性描述符,给当前的属性添加了enumerable,configurable属性。和一些方法,get和set方法,其实就是getter访问器和setter设置器。在get方法:是访问数据。在set中是更新数据。
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>defineProperty</title>
</head>
<body>
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello'
}
// 模拟 Vue 的实例
let vm = {
}
// 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
Object.defineProperty(vm, 'msg', {
// 可枚举(可遍历)
enumerable: true,
// 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
configurable: true,
// 当获取值的时候执行
get () {
console.log('get: ', data.msg)
return data.msg
},
// 当设置值的时候执行
set (newValue) {
console.log('set: ', newValue)
if (newValue === data.msg) {
return
}
data.msg = newValue
// 数据更改,更新 DOM 的值
document.querySelector('#app').textContent = data.msg
}
})
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>
</body>
</html>
演示Object.defineProperty如何使用 - 多个数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>defineProperty 多个成员</title>
</head>
<body>
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello',
count: 10
}
// 模拟 Vue 的实例
let vm = {
}
proxyData(data)
function proxyData(data) {
// 遍历 data 对象的所有属性
Object.keys(data).forEach(key => {
// 把 data 中的属性,转换成 vm 的 setter/setter
Object.defineProperty(vm, key, {
enumerable: true,
configurable: true,
get () {
console.log('get: ', key, data[key])
return data[key]
},
set (newValue) {
console.log('set: ', key, newValue)
if (newValue === data[key]) {
return
}
data[key] = newValue
// 数据更改,更新 DOM 的值
document.querySelector('#app').textContent = data[key]
}
})
})
}
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>
</body>
</html>
3.响应式核心原理 vue3.x
数据劫持使用的是:Proxy
Proxy:是直接监听对象而非属性,因此再把多个对象转换成getter和setter的时候不需要循环。这个对象是es6中新增的。ie不支持,性能由浏览器优化。
Proxy的使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Proxy</title>
</head>
<body>
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello',
count: 0
}
/**
Proxy是一个构造函数,因此通过new Proxy创建一个代理对象,当我们想要访问data中的属性的时候,我们使用vm这个代理对象来访问,
例如想要获取msg,就是vm.msg。当我们使用vm.msg的时候会触发代理中的get方法,当我们给msg赋值的时候会触发set方法。
Proxy有2个参数:
第一个参数:我们要代理的对象,
第二个参数:是一个对象,这个对象中的成员是执行代理行为时候的函数,也就是:当我们要访问vm.msg的时候就会触发get方法,当我们给msg赋值的时候会触发set方法。
get:get方法跟之前的方法不同,需要传递参数,get方法中需要传递target和key。target就是代理的目标对象当前的是data,key:是我们要访问的哪个属性,这2个参数我们不需要关系如何实现的,这个是由系统帮我们实现的。只需要传递target,和key。返回的target[key]就是我们获取到的值。
set方法有3个参数:
target:我们代理的目标对象,
key:我们设置的属性,例如:msg,count。
newValue:新设置的值。
Proxy的好处
1.性能好,因为浏览器做了优化
2.不用循环
*/
// 模拟 Vue 实例
let vm = new Proxy(data, {
// 执行代理行为的函数
// 当访问 vm 的成员会执行
get (target, key) {
console.log('get, key: ', key, target[key])
return target[key]
},
// 当设置 vm 的成员会执行
set (target, key, newValue) {
console.log('set, key: ', key, newValue)
if (target[key] === newValue) {
return
}
target[key] = newValue
document.querySelector('#app').textContent = target[key]
}
})
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>
</body>
</html>
4.发布订阅模式
vue中的自定义事件事件模式,nodejs中的事件都是基于发布订阅模式的。
订阅者,发布者,信号中心
我们假设,存在一个信号中心,某个任务执行完成,就向信号中心发布(publish)一个信号,其他任务可以向信号中心订阅(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫发布订阅模式。
发布订阅模式概念:
举例:假设你是学生的家长,学校每次考完试,都要获取小孩子的成绩,现在假设可以到孩子所在的班级去订阅孩子的成绩,一旦考试成绩出来,相当于触发了一个事件,就会由班级的老师把孩子的成绩以短信的形式发送给家长,不需要天天再问孩子成绩出来没。
在这个案例中:家长就是事件的订阅者,老师就是事件的发布者,孩子所在的班级可以假设为事件的中心,家长订阅班级中孩子的考试成绩,老师发布班级中孩子的考试成绩。
vue自定义事件是基于发布订阅模式
注册事件:注册事件的时候并不执行。eventType: 事件类型,handler:处理函数
触发事件:触发事件。eventType:事件类型
- vue自定义事件:
<!DOCTYPE html>
<html lang&