一、基础
1、使用 this.$forceUpdate(); 强制更新数据, computed里面的数据不会变化。
2、vue是单项数据流,不要在子组件里面更改父组件的数据,应该通过父组件的属性里面绑定一个函数,在子组件里面调用该函数,把数据传回父组件,然后在父组件里更改数据;
3、vue2.6版本的slot插槽使用和2.5的已经不一样了。但是还是兼容之前的使用方式。
// 2.6版本的
<template v-slot:item="props">
<p>item slot-scope {{ props }}</p>
</template>
// 2.5版本的
<p slot="item" slot-scope="props">item slot-scope {{ props }}</p>
4、vue2.4版本新增了inheritAttrs属性,当父组件传过来的数据,子组件没有在html中使用时,该变量会默认挂载到子组件的元素上,设置inheritAttrs为false时,就不会挂载了。应用场景?
5、子组件发射事件时,可以传递一个函数,在父组件接受到值后再回调。
// 子组件
this.$emit("change", e.target.value, val => {
console.log(val);
});
父组件
handleEventChange(val, callback) {
this.name = val;
callback("hello");
}
6、函数式组件:
functional:true
特征: 无状态、无实例、没有this上下文、无生命周期
7、指令的执行顺序:
创建时:beforeCreate
data
created
beforeMount
render
bind // 组件render后,开始执行指令的bind
mounted
inserted // 组件mounted后,开始执行指令的inserted
更新时:update // 执行指令的update方法
componentUpdated // 执行指令的componentUpdated 方法
beforeUpdate
render
updated
销毁时:beforeDestroy
destroyed
unbind // 执行指令的unbind方法
8、Vue.observable,创建一个响应对象。以前只有当实例被创建时 data 中存在的属性才是响应式的
9、vue响应式原理和依赖收集
function isObject (obj) {
return typeof obj === 'object'
&& !Array.isArray(obj)
&& obj !== null
&& obj !== undefined
}
function observe (obj) {
if (!isObject(obj)) {
throw new TypeError()
}
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
let dep = new Dep()
Object.defineProperty(obj, key, {
get () {
dep.depend()
return internalValue
},
set (newValue) {
const isChanged = internalValue !== newValue
if (isChanged) {
internalValue = newValue
dep.notify()
}
}
})
})
}
window.Dep = class Dep {
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpdate) {
// register the current active update as a subscriber
this.subscribers.add(activeUpdate)
}
}
notify () {
// run all subscriber functions
this.subscribers.forEach(subscriber => subscriber())
}
}
let activeUpdate
function autorun (update) {
function wrappedUpdate () {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
const state = {
count: 0
}
observe(state)
autorun(() => {
console.log(state.count)
})
// should immediately log "count is: 0"
state.count++
二、原理
new Vue 发生了什么
执行了初始化方法_init();
合并参数;
// 各种初始化
vm._self = vm
initLifecycle(vm) // $parent/$root
initEvents(vm) // 自定义事件监听
initRender(vm) // $slots/$createElement
callHook(vm, 'beforeCreate')
// 获取祖辈注入的数据
initInjections(vm) // resolve injections before data/props
initState(vm) // 数据状态初始化:data/props/methods/computed/watch
// 给后代提供数据
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// 选项如果有el,自动执行$mount
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
说一下数据响应式
在new Vue 中执行 initState(vm)的时候,遍历data并做响应式处理,
通过Object.defineProperty添加属性拦截,每一个属性会创建一个Dep,
在get的时候通过闭包的方式把依赖添加到之前创建的dep里面,从而对依赖进行收集,
在set的时候通知会通知各个watcher去更新视图;
keep-alive原理
根据组件id和tag 设置缓存的key值, value值用组件的vnode, 用 activeted deactived 钩子 , 如果组件已经缓存到队列里面了,会先删除缓存里面那个缓存的值,然后再把新的值更新到队列最后面, 有最大的缓存值
$nextTick 原理
vue里面set了一个数据后,不会马上更新视图,而是放到一个watcher里面,nextTick里面的方法也会放到watcher里面,用于created的时候操作dom元素,里面的实现主要有三个promise.then mutationObsive, setTimeout
Vue中的diff原理
Vue的diff算法是平级比较,不考虑跨级别比较的情况。内部采用深度递归的方式+双指针的方式进行比较;
1、先比较是否是相同的节点
2、相同节点比较属性,并复用老节点
3、比较儿子节点,考虑老节点和儿子节点的情况
4、优化比较:头头、尾尾、头尾、尾头
5、比对查找进行复用
三种类型的Watcher对象
Watcher分为三种,按创建的先后顺序为:计算属性的Watcher、用户Watcher(监听器)、渲染Watcher,执行的顺序也是一样的。
三、vue周边
vuex
state: 提供一个响应式数据
getter: 借助vue的计算属性computed来实现缓存
mutation: 更改state方法
action: 触发mutation
module: vue.set动态添加state到响应式数据中
如上图所示:vue-devtool会监测mutation里面的数据改变并做记录,因此只能在mutation里面更改state的数据。
boforeCreate中混入$store的获取方式
问:为什么需要action?
用于异步操作,异步代码放在一个地方,便于复用
vue-router
- hash模式 丑,无法使用锚点定位
history 需要后端配合,ie9不兼容(可使用强制刷新处理)
原理:
vue.util.defineReactive为响应式数据
SPA 缺点
1、不利于seo
2、首屏渲染时间长
nuxt 优点
1、静态站点
2、动态渲染
3、简化配置
ssr 核心原理
无头浏览器渲染