1、vue的生命周期钩子函数:
,
钩子函数 | 触发的行为 | 在此阶段可以做的事情 |
beforeCreadted | vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。 | 加loading事件 |
created | vue实例的数据对象data有了,$el还没有 结束loading、 | 请求数据为mounted渲染做准备 |
beforeMount | vue实例的$el和data都初始化了,但还是虚拟的dom,节点具体的data.filter还未替换。 | |
mounted | vue实例挂载完成,data.filter成功渲染 | 配合路由钩子使用 |
beforeUpdate | data更新时触发 | |
updated | data更新时触发 数据更新时,做一些处理(此处也可以用watch进行观测) | |
beforeDestroy | beforeDestroy 组件销毁时触发 |
数据更新时,做一些处理(此处也可以用watch进行观测) |
destroyed | 组件销毁时触发,vue实例解除了事件监听以及和dom的绑定(无响应了),但DOM节点依旧存在 组件销毁时进行提示 | 组件销毁时进行提示 |
计算属性是自动监听依赖值的变化,从而动态返回内容,监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情。
所以区别来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch,用反或混用虽然可行,但都是不正确的用法。
说出一下区别会加分
computed 是一个对象时,它有哪些选项?
computed 和 methods 有什么区别?
computed 是否能依赖其它组件的数据?
watch 是一个对象时,它有哪些选项?
总结
当有一些数据需要随着另外一些数据变化时,建议使用computed。
当有一个通用的响应数据变化的时候,要执行一些业务逻辑或异步操作的时候建议使用watcher
为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
一般有两种
<router-link to=‘home‘> router-link
标签会渲染为<a>
标签,咋填template中的跳转都是这种;router.push(‘/home‘)
这个问题几乎是面试必问的,回答也是有深有浅。基本上要知道核心的 API 是通过 Object.defineProperty()
来劫持各个属性的 setter / getter
,在数据变动时发布消息给订阅者,触发相应的监听回调,这也是为什么 Vue.js 2.x 不支持 IE8 的原因(IE 8 不支持此 API,且无法通过 polyfill 实现)。
MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel
Object.defineProperty(obj, ‘property‘,{})
来定义属性,在定义时传入一些参数,包括set()
和get()
函数,分别在设置和获取该对象的该属性时调用执行。function MVVM(option = {}) {
this.$option = option
let data = this._data = this.$option.data
// 对data进行数据劫持
observe(data)
// 将数据代理到this中,在this中对this._data操作
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get () {
return this._data[key]
},
set (newVal) {
this._data[key] = newVal
}
})
}
}
function Observe(data) { // 这里写逻辑,方便递归
for (let key in data) { // 把data上的属性通过defineProperty定义
let val = data[key]
observe(val) // 递归
Object.defineProperty(data, key, {
enumerable: true,
get () { // 获取该属性
return val
},
set (newVal) { // 更改这个属性
if (val === newVal) return // 值不改变
val = newVal
observe(newVal)
}
})
}
}
// 给对象增加 Object.defineProperty
function observe(data) {
if(typeof data != "object") return
return new Observe(data)
}
从代码看出,this
代理了this._data
(被劫持的数据),所以我们在用this.未定义属性
时不会加上setter
和getter
,应该去报错
function VM(options = {}) { // MVVM框架核心类
this.$options = options
let data = this._data = this.$options.data
observe(data) // 对data进行数据劫持
for (let key in data) { // 将数据代理到this中,在this中对this._data操作
Object.defineProperty(this, key, {
enumerable: true,
get () {
return this._data[key]
},
set (newVal) {
this._data[key] = newVal
}
})
}
new Compile(options.el, this) // 编译页面
}
function Observe(data) { // 这里写逻辑,方便递归
for (let key in data) { // 把data上的属性通过defineProperty定义
let val = data[key]
observe(val) // 递归
Object.defineProperty(data, key, {
enumerable: true,
get () { // 获取该属性
return val
},
set (newVal) { // 更改这个属性
if (val === newVal) return // 值不改变
val = newVal
observe(newVal)
}
})
}
}
function observe(data) { // 给对象增加 Object.defineProperty
if(typeof data != "object") return
return new Observe(data)
}
function Compile (el, vm) {
vm.$el = document.querySelector(el) // 获取到dom容器
// 新建一个文档碎片,将容器中的dom放入到文档碎片中(内存中),操作文档碎片编译(高效)
let fragment = document.createDocumentFragment()
while(child = vm.$el.firstChild) {
fragment.appendChild(child)
}
replace(fragment)
function replace (fragment) {
Array.from(fragment.childNodes).forEach(node => {
let text = node.textContent
let reg = /\{\{(.*)\}\}/
if (node.nodeType === 3 && reg.test(text)) {
// 处理模板 文本节点
let arr = RegExp.$1.split(".")
let val = vm
arr.forEach(key => { // 取到符合条件的值,这样便于取到嵌套值 {{a.a}}
val