vue数据初始化
vue的基层响应式的逻辑是通过Object.defineProperty()函数控制的,先来说一说该方法的知识点:
1. Object.defineProperty()
1.1 对象的定义与赋值
经常使用的定义与赋值方法obj.prop =value 或者obj['prop']=value
let obj = {}
obj.name = 'jack'
obj['name'] = 'tom'
1.2 Object.defineProperty()语法说明
Object.defineProperty(obj, prop, desc)
作用:该方法的作用就是在对象上定义一个新属性或者修改一个已有的属性。
参数:
- obj 需要定义属性的当前对象
- prop 当前需要定义的属性名
- desc 属性描述符
1.3 属性的特性以及内部属性
javacript 有三种类型的属性
- 命名数据属性:拥有一个确定的值的属性。这也是最常见的属性
var flight = {
airline: "Oceanic",
number: 815,
departure: {
IATA: "SYD",
time: "2004-09-22 14:55",
city: "Sydney"
},
arrival: {
IATA: "LAX",
time: "2004-09-23 10:42",
city: "Los Angeles"
}
};
- 命名访问器属性:通过
getter 和setter 进行读取和赋值的属性, 就跟java类的set, get方法一样
var people = {
name: "lyx",
get value() {
return "uuuuu";
},
set value(value) {
console.log("==========")
this.name = value;
}
};
console.log(people.name);
console.log(people.value);
people.value = "12344";
console.log(people.name);
- 内部属性:由JavaScript引擎内部使用的属性,不能通过JavaScript代码直接访问到,不过可以通过一些方法间接的读取和设置。比如,每个对象都有一个内部属性
[[Prototype]] ,你不能直接访问这个属性,但可以通过Object.getPrototypeOf() 方法间接的读取到它的值。
1.4 属性描述符
Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符和存取描述符
1.4.1 数据描述符
value: props的值
writable : 是否可写
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'jack',
writable: false
})
console.log(Person.name)
Person.name = 'tom'
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'jack',
writable: true
})
console.log(Person.name)
Person.name = 'tom'
console.log(Person.name)
一些描述符的默认值:这是不仅数据描述还有存取描述符
属性名 | 默认值 | 属性描述 |
---|
value | undefined | props的值 | get | undefined | 赋值的方法 | set | undefined | 取值的方法 | writable | false | 是否可写 | enumerable | false | 属性是否会出现在for in 或者 Object.keys()的遍历中 | configurable | false | 属性是否配置,以及可否删除 |
1.4.2 存取描述符 getter setter
let Person = {}
let temp = 'bobo'
Object.defineProperty(Person, 'name', {
get() {
return temp
},
set(val) {
temp = val
}
})
console.log(Person.name)
Person.name = 'tom'
console.log(Person.name)
1.4.3 存取描述符和数据描述符都具有下面的描述符
- configrable 描述属性是否配置,以及可否删除
- enumerable 描述属性是否会出现在 for in 或者 Object.keys() 遍历中
当 writable 为true , configurable 为false时 ,可以重新定义props
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose',
configurable: false,
writable: true
})
Object.defineProperty(Person, 'name', {
value: 'jack'
})
console.log(Person.name)
当 writable 为false configurable 为false时 ,不可以重新定义props
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose',
configurable: false,
writable: false
})
Object.defineProperty(Person, 'name', {
value: 'jack'
})
console.log(Person.name)
当 writable 为false configurable 为true 时 , 可以通过在原型链上重新定义props
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose',
configurable: true,
writable: false
})
Object.defineProperty(Person, 'name', {
value: 'jack'
})
console.log(Person.name)
还有一点:configurable 为 false 时不可以删除对象上的该属性
writable | configurable | 讲义 |
---|
true | false | props可以重新定义,不能删除(configurable的不能删除属性的定义好使,但是不能重新定义props被忽略了) | false | false | props不能重新定义也不能删除 | false | true | props可以重新定义,也可以被删除 |
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose',
configurable: false,
writable: true
})
delete Person.name
1.4.4 enumerable 代码片段分析
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose',
enumerable: false
})
Person.age = '23'
console.log(Person)
for (let item in Person) {
console.log(item)
}
属性的enumerable描述符为false时不能被遍历
Tips: 数据的定义方法的区别
涨知识
let Person = {}
Person.render = 'male'
Object.defineProperty(Person, 'render', {
value: 'male',
writable: true,
configurable: true,
enumerable: true
})
let Person = {}
Object.defineProperty(Person, 'render', {
value = 'female'
})
Object.defineProperty(Person, 'render', {
value: 'female',
writable: false,
configurable: false,
enumerable: false
})
1.4.5 不变性
1.4.5.1 对象常量
常量属性等价于 不可修改,不可重新定义或者删除 writable false, configurable false
1.4.5.2 禁止扩展 Object.preventExtensions()
禁止对象添加新属性并且保留已有属性 — Object.preventExtensions()
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'rose'
})
Object.preventExtensions(Person)
Person.age = '23'
console.log(Person)
1.4.5.3 密封 Object.seal()
Object.seal() = Object.preventExtension() + configurable: false
除了可以修改对象的已有属性的值,不能新添属性,也不能删除属性。
1.4.5.4 冻结 Object.freeze()
Object.freeze() = Object.seal() + writable: false
对冻结的对象不能删除,不能新添属性,也不能修改属性的值。
这个方法是你可以应用在对象上级别最高的不可变性,它会禁止对于对象本身及其任意直接属性的修改(但是这个对象引用的其他对象是不受影响的)。你可以深度冻结一个对象,具体方法为,首先这个对象上调用Object.freeze()然后遍历它引用的所有对象,并在这些对象上调用Object.freeze()。但是一定要小心,因为这么做有可能会无意中冻结其他共享对象。
2. Vue变化侦测
Vue的数据侦测是通过对数据变化的属性的拦截进行的;
2.1 Object的变化侦测
vue的数据驱动视图的首要关键点在于我们如何知道数据发生了变化。
对于对象而言,JS提供了Object.defineProperty方法,可以通过该方法的get给对象取值,通过set给对象赋值,同样通过set方法知道数据发生了变化。
let car = {}
let val = 3000
Object.defineProperty(car, 'price', {
enumerable: true,
configurable: true,
get(){
console.log('price属性被读取了')
return val
},
set(newVal){
console.log('price属性被修改了')
val = newVal
}
})
3. vue生命周期
从图中我们可以看到,Vue 实例的生命周期大致可分为4个阶段:
- 初始化阶段:为
Vue 实例上初始化一些属性,事件以及响应式数据; - 模板编译阶段:将模板编译成渲染函数;
- 挂载阶段:将实例挂载到指定的
DOM 上,即将模板渲染到真实DOM 中; - 销毁阶段:将实例自身从父组件中删除,并取消依赖追踪及事件监听器;
3.1 初始化阶段 new Vue
Vue实例的生命周期分为上面的四个阶段,其中初始化阶段基本就做两部分事情
- 创建一个实例 new Vue()
- 对创建的实例对象初始化一些事件,属性,响应式数据等;
3.1.1 new Vue()
创建一个实例Vue
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
Vue类的构造函数调用了init函数:
this._init(options)
调用原型上的_init(options) 方法并把用户所写的选项options 传入。那这个_init 方法是从哪来的呢?在Vue 类定义的下面还有几行代码,其中之一就是:
initMixin(Vue)
initMixin的源码如下
export function initMixin (Vue) {
Vue.prototype._init = function (options) {
const vm = this
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
除了调用初始化函数来进行相关数据的初始化之外,还在合适的时机调用了callHook函数来触发生命周期的钩子 ----->在所有的初始化工作都完成以后,最后判断用户是否传入了el选项如果传入了则调用$mount 函数进入模板编译与挂载阶段,如果没有传入el 选项,则不进入下一个生命周期阶段,需要用户手动执行vm.$mount 方法才进入下一个生命周期阶段。
4.总结
Vue生命周期顺序:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
Vue生命周期分为四个阶段:
Vue初始化阶段中类的执行顺序:
new Vue
- _init(options)
- initLifecycle
- initEvents
- initRender
- callHook(vm, ‘beforeCreate’)
- initInjections
- initState
- initProps
- initMethods
- initData
- initComputed
- initWatch
- initProvide
- callHook(vm, ‘created’)
因此,一个组件中各个属性的执行循序为:
- inject
- props
- method
- data
- computed
- watch
- provide
|