vue全家桶
2020-06-05 14:37:26 5 举报
AI智能生成
前端面试 vue
作者其他创作
大纲/内容
Vue常考基础知识点
生命周期钩子函数
beforeCreate
获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在 initState 中
在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问
在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问
created
可以访问到之前不能访问到的数据,
实例已经完成数据观测, 属性和方法的运算, watch/event事件回调.
但是这时候组件还没被挂载,所以是看不到的
可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom
实例已经完成数据观测, 属性和方法的运算, watch/event事件回调.
但是这时候组件还没被挂载,所以是看不到的
可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom
beforeMount
当前阶段虚拟Dom已经创建完成,即将开始渲染
mounted
将 VDOM 渲染为真实 DOM 并且渲染数据
组件中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。
组件中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。
mounted在挂载完成后发生,
在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作
在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作
beforeUpdate
发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,
你可以在当前阶段进行更改数据,不会造成重渲染
你可以在当前阶段进行更改数据,不会造成重渲染
updated
发生在更新完成之后,当前阶段组件Dom已完成更新。
要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新
要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新
activated
deactivated
deactivated
keep-alive 独有的生命周期
用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,
命中缓存渲染后会执行 actived 钩子函数
命中缓存渲染后会执行 actived 钩子函数
beforeDestroy
移除事件、定时器等等,否则可能会引起内存泄露的问题
然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,
所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数。
然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,
所有子组件都销毁完毕后才会执行根组件的 destroyed 钩子函数。
destroyed
组件生命周期调用顺序
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。
组件的销毁操作是先父后子,销毁完成的顺序是先子后父
组件的销毁操作是先父后子,销毁完成的顺序是先子后父
加载渲染过程
父beforeCreate->父created->父beforeMount->
子beforeCreate->子created->子beforeMount- >子mounted->
父mounted
子beforeCreate->子created->子beforeMount- >子mounted->
父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
组件通信
父子通信
最典型的方法
父->子
props,父组件通过 props 传递数据给子组件
子->父
$on、$emit
子组件通过 emit 发送事件传递数据给父组件
这种父子通信方式也就是典型的单向数据流
$parent 或者 $children
可以通过访问 $parent 或者 $children 对象来访问组件实例中的方法和数据
$listeners 和 .sync
兄弟组件通信
对于这种情况可以通过查找父组件中的子组件实现,也就是 this.$parent.$children,
在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信
在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信
跨多层级组件通信
provide / inject
任意组件
Vuex
Event Bus
就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。
项目比较小时,用这个比较合适
项目比较小时,用这个比较合适
Vue.prototype.$bus = new Vue
mixin 和 mixins 区别
mixin
mixin 用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的。
虽然文档不建议我们在应用中直接使用 mixin,但是如果不滥用的话也是很有帮助的,
比如可以全局混入封装好的 ajax 或者一些工具函数等等。
比如可以全局混入封装好的 ajax 或者一些工具函数等等。
mixins
mixins 应该是我们最常使用的扩展组件的方式了。
如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,
通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。
如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,
通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。
需要注意的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,
并且在遇到同名选项的时候也会有选择性的进行合并
并且在遇到同名选项的时候也会有选择性的进行合并
computed 和 watch 区别
computed 是计算属性,依赖其他属性计算值,
并且 computed 的值有缓存,只有当计算值变化才会返回内容。
并且 computed 的值有缓存,只有当计算值变化才会返回内容。
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
所以一般来说需要依赖别的属性来动态获得值的时候可以使用 computed,
对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch。
对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch。
keep-alive 组件有什么作用
如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。
对于 keep-alive 组件来说,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。
用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
对于 keep-alive 组件来说,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。
用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
v-show 与 v-if 区别
v-show 只是在 display: none 和 display: block 之间切换。
无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。
所以总的来说 v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。
无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。
所以总的来说 v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。
v-if 的话就得说到 Vue 底层的编译了。
当属性初始为 false 时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,
所以总的来说在切换时开销更高,更适合不经常切换的场景
并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。
当属性初始为 false 时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,
所以总的来说在切换时开销更高,更适合不经常切换的场景
并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。
组件中 data 什么时候可以使用对象
为什么把data写成函数
组件复用时所有组件实例都会共享 data,
如果 data 是对象的话,就会造成一个组件修改 data 以后会影响到其他所有组件,
所以需要将 data 写成函数,每次用到就调用一次函数获得新的数据。
如果 data 是对象的话,就会造成一个组件修改 data 以后会影响到其他所有组件,
所以需要将 data 写成函数,每次用到就调用一次函数获得新的数据。
什么时候可以把data写成对象
当我们使用 new Vue() 的方式的时候,无论我们将 data 设置为对象还是函数都是可以的,
因为 new Vue() 的方式是生成一个根组件,该组件不会复用,也就不存在共享 data 的情况了。
因为 new Vue() 的方式是生成一个根组件,该组件不会复用,也就不存在共享 data 的情况了。
Vue 常考进阶知识点
响应式原理
监听数据的 set 和 get 的事件
Vue 内部使用了 Object.defineProperty() 来实现数据响应式,通过这个函数可以监听到 set 和 get 的事件
数据劫持
以上代码简单的实现了如何监听数据的 set 和 get 的事件,但是仅仅如此是不够的,因为自定义的函数一开始是不会执行的。
只有先执行了依赖收集,才能在属性更新的时候派发更新,所以接下来我们需要先触发依赖收集。
只有先执行了依赖收集,才能在属性更新的时候派发更新,所以接下来我们需要先触发依赖收集。
依赖收集
<div>
{{name}}
</div>
{{name}}
</div>
在解析如上模板代码时,遇到 {{name}} 就会进行依赖收集。
实现一个 Dep 类,
用于解耦属性的依赖收集和派发更新操作
用于解耦属性的依赖收集和派发更新操作
以上的代码实现很简单,当需要依赖收集的时候调用 addSub,当需要派发更新的时候调用 notify。
接下来我们先来简单的了解下 Vue 组件挂载时添加响应式的过程。
在组件挂载时,会先对所有需要的属性调用 Object.defineProperty(),然后实例化 Watcher,传入组件更新的回调。
在实例化过程中,会对模板中的属性进行求值,触发依赖收集。
在组件挂载时,会先对所有需要的属性调用 Object.defineProperty(),然后实例化 Watcher,传入组件更新的回调。
在实例化过程中,会对模板中的属性进行求值,触发依赖收集。
触发依赖收集时的操作
因为这一小节主要目的是学习响应式原理的细节,所以接下来的代码会简略的表达触发依赖收集时的操作。
以上就是 Watcher 的简单实现,在执行构造函数的时候将 Dep.target 指向自身,从而使得收集到了对应的 Watcher,
在派发更新的时候取出对应的 Watcher 然后执行 update 函数。
在派发更新的时候取出对应的 Watcher 然后执行 update 函数。
接下来,需要对 defineReactive 函数进行改造,
在自定义函数中添加依赖收集和派发更新相关的代码。
在自定义函数中添加依赖收集和派发更新相关的代码。
以上所有代码实现了一个简易的数据响应式,核心思路就是手动触发一次属性的 getter 来实现依赖收集。
Object.defineProperty 的缺陷
缺陷
通过下标方式修改数组数据或者给对象新增属性并不会触发组件的重新渲染
产生这写缺陷的原因
因为 Object.defineProperty 不能拦截到这些操作,
更精确的来说,对于数组而言,大部分操作都是拦截不到的,只是 Vue 内部通过重写函数的方式解决了这个问题
更精确的来说,对于数组而言,大部分操作都是拦截不到的,只是 Vue 内部通过重写函数的方式解决了这个问题
怎么解决
Vue 提供了一个 API
解决添加对象属性不响应的问题
Vue.set( target, key, value )
解决添加对象属性不响应的问题
Vue.set( target, key, value )
对于数组而言,
Vue 内部重写了以下函数实现派发更新
Vue 内部重写了以下函数实现派发更新
vue2.x中如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。
这样当调用数组api时,可以通知依赖更新。
如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。
这样就实现了监测数组变化。
这样当调用数组api时,可以通知依赖更新。
如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。
这样就实现了监测数组变化。
NextTick 原理分析
nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM。
nextTick主要使用了宏任务和微任务
根据执行环境分别尝试采用
根据执行环境分别尝试采用
- Promise
- MutationObserver
- setImmediate
- 如果以上都不行则采用setTimeout
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列
v-model
语法糖
v-model是v-bind:value和v-on:input的简写,
所以在父组件你完全可以直接写 :value="name", @input="val => name = val"
所以在父组件你完全可以直接写 :value="name", @input="val => name = val"
vue-router
hash模式 和 history模式
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用 window.location.hash 读取。
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
history模式:history采用HTML5的新特性;
且提供了两个新方法: history.pushState(), history.replaceState()可以对浏览器历史记录栈进行修改,
以及popState事件的监听到状态变更
且提供了两个新方法: history.pushState(), history.replaceState()可以对浏览器历史记录栈进行修改,
以及popState事件的监听到状态变更
vue路由的钩子函数
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。
一些需要登录才能调整页面的重定向功能
一些需要登录才能调整页面的重定向功能
- beforeEach主要有3个参数to,from,next。
- to:route即将进入的目标路由对象。
- from:route当前导航正要离开的路由。
- next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳
$route和$router的区别
$route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等
vuex
state:Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。
存放的数据状态,不可以直接修改里面的数据。
存放的数据状态,不可以直接修改里面的数据。
mutations:mutations定义的方法动态修改Vuex 的 store 中的状态或数据
getters:类似vue的计算属性,主要用来过滤一些数据。
action:actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。
view 层通过 store.dispath 来分发 action
MVVM
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。
Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,
数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据
Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,
数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据
diff
Q: Vue2.x和Vue3.x渲染器的diff算法分别说一下
diff算法有以下过程
- 同级比较,再比较子节点
- 先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
- 比较都有子节点的情况(核心diff)
- 递归比较子节点
时间复杂度
O(n)
正常Diff两个树的时间复杂度是O(n^3),但实际情况下我们很少会进行跨层级的移动DOM,
所以Vue将Diff进行了优化,从O(n^3) -> O(n),
只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
所以Vue将Diff进行了优化,从O(n^3) -> O(n),
只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
Vue2的核心Diff算法
采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。
相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅
相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅
虚拟Dom
以及
key属性的作用
以及
key属性的作用
Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。
是对真实DOM的一层抽象
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段
是对真实DOM的一层抽象
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段
key的作用是尽可能的复用 DOM 元素
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。
key也就是children中节点的唯一标识
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。
key也就是children中节点的唯一标识
Vue complier 实现
Vue complier 是将 template 转化成一个 render 字符串
步骤
- parse 过程,将 template 利用正则转化成AST 抽象语法树。
- optimize 过程,标记静态节点,后 diff 过程跳过静态节点,提升性能。
- generate 过程,生成 render 字符串
vue & react
vue的优点是什么
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
- 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写
0 条评论
下一页