文章目录
- vue
- 1. computed和watch的区别
- 2. watch如何监听多个值
- 3. v-for和v-if可以一起使用吗
- 4. 为何v-for 中要用key
- 5. vue中如何获取DOM元素
- 6. 什么是作用域插槽
- 7. vue组件如何通讯
- 8. 描述Vue 组件生命周期
- 9. ajax请求应该放在哪个生命周期
- 10.何时需要使用beforeDestrory
- 11.==Vue常见性能优化方式==
- 12. vue为什么要使用虚拟dom
- 13. 请用vnode描述一个 DOM结构
- 14. 对MVVM的理解
- 15. 为何组件data必须是一个函数?
- 16. Vue为何是异步渲染,$nextTick有何用?
- 17. 描述组件渲染和更新的过程
- 18. Vuex中action和mutatiion有何区别
- 19. 路由模式hash和history的区别
- vue3
vue
1. computed和watch的区别
- 功能:computed是计算属性;watch是监听一个值的变化,然后执行对应的回调。
- 缓存:computed有缓存,只有依赖的数据发生改变,才会重新进行计算
- 第一次:computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听(immediate属性)
- 场景:一个属性受多个属性影响用computed(如:商品总价);一个数据影响多个数据用watch(如:币种变化引起单价、总价金额变化)
2. watch如何监听多个值
目的:A、B、C任何一个值发生变化,都会触发函数回调
实现方式:computed
结合watch
computed: {
loadingShow() {
const { A, B, C } = this;
return { A, B, C };
},
},
watch: {
loadingShow: {
handler(newValue) {
if (newValue.A && newValue.B && newValue.C) {
...
}
},
deep: true,
immediate: true,
},
},
3. v-for和v-if可以一起使用吗
- 不能一起使用
- v-for比v-if的计算优先级高一些,同时使用会先遍历数组的每一项,然后再判断v-if,产生不必要的性能开销。
- 解决方法:在外层套一个
<template>
来写v-if
4. 为何v-for 中要用key
- 组件更新时,diff 算法需要通过tag和key 来判断,是否是 sameNode,以此来找出 最小更新范围
- 目的:减少渲染次数,提升渲染性能
- 注意:最好不要用index (删除中间节点,后续节点的key都会发生改变,都要重新渲染)
5. vue中如何获取DOM元素
用refs
- 给元素添加
ref
属性 :<ul ref="ul1"> ... </ul>
- 获取元素
this.$refs.xxx
addItem() { // 获取 DOM 元素 const ulElem = this.$refs.ul1 console.log( ulElem.childNodes.length ) }
6. 什么是作用域插槽
意义:父组件可以使用子组件的数据(延伸了子组件数据的作用范围)
白话:子组件<slot>
里有自己的data,可以用作用域插槽把data传给使用slot的地方
7. vue组件如何通讯
1.父子通讯 props
/$emit
- 父->子:子组件通过
props
接收父组件传来的数据。 - 子->父:子组件可以通过
$emit
自定义事件的方式向父组件传递值,父组件需要监听该事件来进行接收子组件传来的值。
2.父子通讯ref
/refs
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据- 可以直接修改子组件的data
// 父组件调用
<template>
<child ref="child"></component-a>
</template>
<script>
import child from './child.vue'
export default {
components: { child },
mounted () {
console.log(this.$refs.child.name); // JavaScript
this.$refs.child.sayHello(); // hello
}
}
</script>
3.父子通讯$parent
/ $children
this.$parent
:访问父实例,值为对象this.$parent.msg;
this.$children
:访问子实例,值为数组,注意 $children 并不保证顺序,也不是响应式的this.$children[0].messageA
4.祖先后代通讯provide
/ inject
- 祖先组件通过
provide
选项来提供数据,后代组件通过inject
选项来开始使用这些数据(可以通过inject直接访问其两个层次以上的数据) - provide和inject不是响应式的,除非传入的是一个响应式对象
- 用法与props相似
- 祖先组件
<template> <div> <son></son> </div> </template> <script> export default { provide: { name: 'Tom' } }
- 后代组件
<template> <div> {{name}} </div> </template> <script> export default { name: 'grandson', inject: [name] } </script>
5.全局:事件总线
- 全局定义:
Vue.prototype.$eventBus = new Vue();
- 事件处理:
this.$eventBus.$on
、this.$eventBus.$off
、this.$eventBus.$emit
6.全局:vuex
它采用集中式存储管理应用的所有组件的状态,解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题
7.本地存储:localStorage
/ sessionStorage
- 容量:5MB左右
- 存储/更新数据:
localStorage.setItem('key', 'value');
- 获取数据:
localStorage.getItem('person');
- 删除:
localStorage.removeItem('key');
- 清空:
localStorage.clear()
8. 描述Vue 组件生命周期
beforeCreate
、created
(data已经初始化,dom还未挂载,可发异步请求)beforeMount
、mounted
(dom树挂载完成,可进行dom操作)beforeUpdate
、updated
beforeDestroy
、destroyed
- 注:被 keep-alive 缓存的组件会多两个生命周期
activated
、deactivated
9. ajax请求应该放在哪个生命周期
答:放mounted
中。 — DOM渲染完成后,触发ajax请求,再把请求得到的数据渲染上。
原因
- JS是单线程的,ajax异步获取数据
- 在created的时候,视图中的dom并没有被渲染出来,所以此时如果直接去操作dom节点,无法找到相关元素。
- 在mounted中,由于此时的dom元素已经渲染出来了,所以可以直接使用dom节点。
一般情况下,都放在mounted中,保证逻辑的统一性。因为生命周期是同步执行的,ajax是异步执行的。
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放在created中。
10.何时需要使用beforeDestrory
- 解绑自定义事件
this.$eventBus.$off
- 清除定时器
- 解绑自定义的 DOM事件,如window scrroll等
11.Vue常见性能优化方式
vue层面的优化
- 合理使用
v-show
和v-if
- 合理使用
computed
— 有缓存,可提高计算性能 v-for
中加key (便于diff算法进行优化),以及避免和v-if同时使用 (如果一起使用,每次v-for时v-if都要判断一遍,性能浪费)- 自定义事件、DOM事件及时销毁-- 不销毁可能会导致内存泄漏(页面越来越卡)
- 合理使用异步组件(异步加载加载大组件、路由异步加载)--
import
- 合理使用
keep-alive
(不需要重复渲染的地方,就缓存下来) - vue2中的data层级不要太深 — 对data做响应式监听时,深度监听需要一次性遍历完全,层级深会导致递归次数多
通用优化
- 图片懒加载
- 使用缓存(localstorage、sessionstorage)
- 静态资源使用CDN内容分发网络,加快用户访问速度,减轻服务器压力
- 雪碧图
webpack层面的优化
- 压缩代码(mode:production自动开启代码压缩)
- 分割代码,提取公共文件,防止主文件过于庞大,导致首屏加载过慢。
■ 分割代码块splitChunks;chunks块引用HtmlWebpackPlugin - 打包代码时,加上 hash 戳,有利于命中浏览器缓存
- 小图片用base64产出,减少资源请求
12. vue为什么要使用虚拟dom
- 通过用JS模拟DOM结构,进行计算和对比 (diff),找出最小更新范围。(只重新渲染变更部分)
- vdom的核心:diff算法
13. 请用vnode描述一个 DOM结构
vnode组成:标签–tag,属性部分(样式/事件)-- props,子元素-- children
14. 对MVVM的理解
- Model:是指数据模型,用纯JavaScript对象表示
- View:是视图层,也就是用户界面。
- ViewModel:把
Model
和View
关联起来,负责把Model
的数据同步到View
显示出来,还负责把View
的修改同步回Model
。
15. 为何组件data必须是一个函数?
- 这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱;
- 如果写成对象形式,由于Object是引用数据类型,每个组件的data 都是内存的同一个地址,一个地方的数据改变了其地方的数据也会改变。
16. Vue为何是异步渲染,$nextTick有何用?
异步渲染
- vue采用异步渲染,是为了提高渲染性能。
- Vue会在本轮数据更新后,再去异步更新视图,这样可以合并data修改
$nextTick
$nextTick
在DOM更新完之后,触发回调 ,所以,$nextTick
里可以获取最新DOM节点
17. 描述组件渲染和更新的过程
18. Vuex中action和mutatiion有何区别
- action 可以处理异步,mutation 不可以
- mutation 一般做原子操作(每次只做一个操作),action 可以整合多个mutation
19. 路由模式hash和history的区别
hash模式:
- 地址中永远带着#号,不美观 。
- 实现原理:监听
onhashchange
事件 - 兼容性较好。
history模式:
- 地址干净,美观 。
- 实现原理:通过调用
history.pushState
方法,并且监听window.onpopstate
事件来实现的 - 兼容性和hash模式相比略差。
- 需要服务端做额外的配置,解决
刷新页面
服务端404的问题。
vue3
1.Vue3 比Vue2有什么优势?
- 性能提升
- 使用
Proxy
代替defineProperty
实现响应式 - 重写虚拟DOM的实现和
Tree-Shaking
- 使用
- 更好的ts 支持 – vue2是js开发的,vue3是ts开发的
- 新特性
- Composition API
- 新的内置组件:Fragment 、Teleport、Suspense…
- 更好的逻辑抽离hook(对比mixin)
2. vue2的mixin和vue3的hooks比较
mixin的一些缺点:
- 变量来源不明确(隐式传入),不利于阅读
- 多mixin可能会造成命名冲突
- mixin和组件可能出现多对多的关系,复杂度较高
hooks:
- 变量和方法皆是显式传入,解决了来源不明问题。
- 可以用解构赋值的内部命名方式,解决命名冲突的问题(只要是显式传值,就不用担心命名冲突的问题)
3. Vue3为何比 Vue2快?
-
Proxy响应式
-
PatchFlag 静态标记
- 编译模板时,给动态节点做标记 ,区分不同节点类型
- 目的:优化diff算法(静态节点不用比,比较动态节点的PatchFlag)
-
hoistStatic 静态提升
将静态节点的定义,提升到父作用域,缓存起来。重新渲染时,静态节点不用重新定义 -
cacheHandler 缓存事件
-
SSR优化
静态节点直接输出,绕过了vdom;动态节点,还是需要动态渲染 -
tree-shaking
编译时,根据不同的情况,引入不同的API – 不会全部引用
4. 描述Vue3生命周期
Options API
- beforeCreate、created
- beforeMount、mounted
- beforeUpdate、updated
beforeUnmount
、unmouted
- 注:被 keep-alive 缓存的组件会多两个生命周期activated、deactivated
Composition API
- 其他生命周期函数放setup里,没有
beforeCreate
和created
- setup执行时机位于
beforeCreate
之前,所以在setup中不使用this(可通过getCurrentInstance
获取当前实例)
5. Composition API对比Options API
Options API
- 随着组件功能的增大,组件的阅读和理解难度会增加,需要反复横跳
Composition API
- 通过组合的方式,把零散在各个data,methods的代码,重新组合,一个功能的代码都放在一起维护,并且这些代码可以单独拆分成函数 。
- 一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)
6. vue3升级了哪些功能
- 多事件处理
<button @click="one($event),two($event)">submit</button>
- Fragment
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个
Fragment
虚拟元素中
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个
- Teleport 远距离传送
- 能够将我们的
组件html结构
移动到指定位置的技术。-- 例如弹窗
- 能够将我们的
- 移除.sync,可以用 v-model替代
- .sync是父子组件 数据双向传递的关键字
- 可改为
<ChildComponent v-model:title="pageTitle" />
- Suspense
<suspense>
组件有两个插槽。它们都只接收一个直接子节点。default
插槽里的节点会没有展示出来时,就展示fallback
插槽里的节点。- 使用场景:等待异步组件时展示loading
7.vue2和vue3分别是怎样实现响应式的?
vue2.x的响应式
-
实现原理:
-
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', { get () {}, set () {} })
-
-
存在问题:
- 新增属性、删除属性, 界面不会更新。 – 通过调用
this.$set()
、this.$delete()
解决 - 直接通过下标修改数组, 界面不会自动更新。-- 调用
this.$set()
或者用splice()
- 新增属性、删除属性, 界面不会更新。 – 通过调用
Vue3.0的响应式
- 实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。MDN Proxy
-
通过Reflect(反射): 对源对象的属性进行操作。MDN Reflect
new Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom'
-
- 深度监听:什么时候用什么时候递归,vue2是在一开始就一次性递归完
- 基本类型数据的响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 - 补充:
window.Proxy
和window.Reflect
是 window 身上内置的一个构造函数 – 不用引入,直接就可以使用
8. watch 和watchEffect的区别是什么?
- 两者都可监听data属性变化
watch
需要明确监听哪个属性;watchEffect
会根据其中的属性,自动监听其变化watch
默认初始化时不执行(immediate: false);watchEffect
初始化时一定会执行(为了收集要监听的数据)
9. setup 中如何获取组件实例?
- 在
setup
和其他Composition APl
中没this
- 可通过
getCurrentInstance
获取当前实例 (注意,在setup中是undefined,要放在setup里的其它生命周期中) - 注:若使用
Options API
可照常使用this