vue特点
- 易用:用起来比较简单
- 灵活:我们可以只使用它的部分功能
- 高效:开发速率很快
渐进式框架
所谓的渐进式框架,就是它有一套完整的体系,我们可以只使用它的某一部分 比如说刚开始我们只使用它的生命周期渲染,然后要开发大型应用的话,就需要用到组件系统,把各个页面切成不同的组件 通过组件化的方式来使用vue,再然后又有了spa单页面应用这个概念,里面又包含了像客户端路由,路由也分为两种,像hash模式和history模式,又因为页面中有大量数据需要管理,就出现了vuex。然后又出现了vue-cli脚手架,进行整合。 这一套就叫做渐进式
数据响应式变化
- vue2.0 Object.definePrope。不支持数组
- vue3.0 Proxy
实现vue2.0中对象的响应式变化
1.创建1.js
// vue特点,如果是对象会使用Object.defineProperty但是它不支持数组
// 数组的话,会把数组方法重写
// 1.先创建一个对象
let obj = {
name: 'dlb',
//让对象的层级深一点
localtion: { x: 100, y: 100 }
}
// 2.让这个obj变成响应式对象,定义一个更新视图的方法
function render () {
console.log('更新视图')
}
// 4.那我们就要观察这个对象,只要这个对象有属性变化了,就要调用render方法
// 5.创建一个观察方法
function observer (obj) {
//里面需要判断入参obj的类型,如果是对象才进行处理
if (typeof obj == 'object') {
// 如果是对象,给obj里面每个属性定义成set/get的方式
// 因为属性可以是多个的,这里需要用forin循环
for (let key in obj) {
// 循环完以后单独写个方法去定义,因为vue里还有一些其他的api
// 比如Vue.$set(),这些 api也可以定义响应式的数据
// 这里面调用定义响应式方法,相当于这里要把obj的属性重新定义给obj
// 把参数传进去
defineReactive(obj, key, obj[key])
}
}
}
//7.创建一个定义set/get的定义响应式方法,三个参数,数据、key、值
function defineReactive (data, key, value) {
// 9.处理深一层的属性,因为value也有可能是一个对象,所以这里也要对vulue进行观察
observer(value)
// 内部用的就是Object.defineProperty,给当前的数据定义要给key,值是对象
/**
* Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
* data 要定义属性的对象。
* key 要定义或修改的属性的名称或 Symbol 。
* obj 要定义或修改的属性描述符。
*/
Object.defineProperty(data, key, {
// 对象里面先加上set和get方法
get () {
// get的时候需要把value返回去
return value
},
set (newValue) {
// set的时候需要修改这个value
// 如果newValue也是一个对象,需要对newValue也进行观察
observer(newValue)
// 还需要判断下新值和旧值不一致才进行处理
if (newValue !== value) {
// 并且在设置之前需要调用更新视图方法
render()
// 把set方法的入参newValue赋给value
value = newValue
}
}
})
}
// 6.观察obj
observer(obj)
// 3.现在要做的一件事就是,把对象某个属性更改掉,就要调用render方法
// 8.试下在这里改
obj.name = 'lb'
obj.name = 'lb2'
// 8.修改深一层的属性
obj.localtion.x = 998
obj.localtion = {
name: 'dlb2',
localtion: { x: 998, y: 889 }
}
// 9.新增属性的处理
// vue的特点,(1)如果给对象新增属性,是不会被监控的
// (2)vue新增属性要使用$set()
obj.a = 100
// 给对象新增参数的两种方式
// 方法一:如果想要给对象增加不存在的属性,就给obj重新赋值一个对象,把原来的覆盖
// 例:obj.localtion = { ...obj.localtion, 新key: 新value }
// 方法二:手动调用$set
// $set的实现, 接受参数对象,key,值,
function $set (data, key, value) {
// 调用响应式代码
defineReactive(data, key, value)
}
// 用的时候,参数:给谁设置、设置那个属性,值
$set(obj, 'a', 998)
console.log(obj.a, 'obj')
- 运行代码,可以看到视图更新了4次
实现vue2.0中数组的响应式变化
// 1.先创建数组
let obj = [1, 2, 3]
// 更新视图方法
function render () {
console.log('更新视图')
}
// 3.定义数组方法集合 删除最后一个元素 删除首个元素 在开头添加一个元素 排序 反转 指定位置添加新项 追加
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push']
// 4.把这几个方法进行重写
// 4.1 先获取到原型上的数组方法
let arrayProto = Array.prototype
// 创建一个自己的原型,并且重写methods方法
let proto = Object.create(arrayProto)
// 4.2 循环methods,对proto的方法进行重写,
// (个人理解,重写的目的是为了能自定义回调,就可以在回调里面执行更新试图方法)
methods.forEach(method => {
//4.3 把方法作为属性重新设置上去,进行重写
proto[method] = function () {
// 使用继承,拿到原生的数组方法,把this传进去,让它不能操作原来的方法,但是能引用到原来的方法
arrayProto[method].call(this, ...arguments)
// 只要重写方法,就调用更新试图方法更新试图
render()
}
})
// 观察者方法
function observer (obj) {
//2. 判断是否为数组,对数组进行处理
if (Array.isArray(obj)) {
// 把重写的方法,挂到原型链上去
obj.__proto__ = proto
return
}
//判断是否为对象,对对象进行处理
if (typeof obj == 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key])
}
}
}
//创建一个定义set/get的定义响应式方法,三个参数,数据、key、值
function defineReactive (data, key, value) {
observer(value)
Object.defineProperty(data, key, {
get () {
return value
},
set (newValue) {
observer(newValue)
if (newValue !== value) {
render()
value = newValue
}
}
})
}
// 观察obj
observer(obj)
// 增加不存在的属性或数组元素
function $set (data, key, value) {
// $set方法下同样要对数组单独处理
if (Array.isArray(data)) {
// 当前用户调用了重写的splice方法,所以会触发更新视图方法
return data.splice(key, 1, value) // 在指定位置添加
}
defineReactive(data, key, value)
}
obj.push(123)
// 必须通过$set的方式来新增元素,或者替换成一个新的数组
$set(obj, 0, 668)
obj[1] = 555 // 错误操作,这样不会更新视图,vue2.0正式版这样操作也不会更新视图
obj.length -- // 错误操作,这样不会更新视图,vue2.0正式版这样操作也不会更新视图
console.log(obj)
实现vue3.0中对象的响应式变化
// Proxy优点
// 1.可以解决数组的问题,更改数组的索引、更改长度,它都可以实现更新视图
// 2.不用分类单独处理对象和数组
// proxy缺点
// 1.兼容性稍微差些
// 使用proxy来实现数据的响应式变化
// 2.定义一个更新视图方法
function render () {
console.log('更新视图')
}
// 1.创建一个对象
let obj = {
name: 'dlb',
age: 25,
age2: { age: 25 },
// 5.加个数组试下效果
arr: [0, 1, 2]
}
// 3.1创建配置对象
let handler = {
// 目标对象 属性
get (target, key) {
// 如果当前的值为对象,且不为null
if (typeof target[key] == 'object' && target[key] !== null) {
// 再把值代理一次,把handler递归一次
return new Proxy(target[key], handler)
}
// return target[key] // 上下两句一个效果
return Reflect.get(target, key)
},
// 目标对象 属性 值 原对象
set (target, key, value, receiver) {
// 在对数组赋值的时候,会修改数组的长度,所以要对长度特殊处理,不更新视图
if (key === 'length') {
return true
}
// 每次设置值的时候更新下视图
render()
return Reflect.set(target, key, value) // 这里需要返回布尔值
}
}
// 3.创建一个代理来操作这个对象,参数一是对象,参数二是处理配置
let proxy = new Proxy(obj, handler)
// 4.需要达到效果,一调用代理后的对象的属性,视图就需要更新
// 4.1 只要一更新代理后的对象属性,就会触发代理的配置内的set方法
//
proxy.age = 997
// 4.2处理多层的情况
proxy.age2.age = 998
// 5.1修改数组的值
proxy.arr[0] = 999
proxy.arr.push(1000)
console.log(proxy)