1. 关于 Vue 生命周期
- 什么是 Vue 生命周期
Vue 实例从创建到销毁的过程就是生命周期。
也就是从开始创建、初始化数据、编译模板、挂在 dom -> 渲染、更新 -> 渲染、卸载等一系列过程
- Vue生命周期的作用是什么
生命周期中有多个事件钩子,让我们在控制整个 Vue 实例的过程时更容易形成好的逻辑
- Vue生命周期总共有几个阶段
8个阶段:创建前/后 beforeCreated / created、载入前/后 beforeMount / mounted、更新前/后beforeUpdate / beforeUpdate、销毁前/后 beforeDestroy / destroyed
- 第一次页面加载会触发哪几个钩子
第一次加载会触发 beforeCreate、created、beforeMount、mounted
- 如果有嵌套组件,父子组件的生命周期的执行顺序是什么?
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
- 在什么阶段才能访问操作DOM?
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
2. Vue 数据双向绑定的实现原理 / 响应式原理
- Vue2.0
Vue 双向数据绑定是通过 Object.defineProperty() 方法 数据劫持 结合 发布订阅模式 Watcher 的方式来实现的。
Object.defineProperty(obj, prop, descriptor)
// 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
// obj (要定义其上属性的对象) prop (要定义或修改的属性) descriptor (具体的改变方法)
Vue 利⽤ Object.defineProperty 创建⼀个 observe 来劫持监听所有的属性,把这些属性全部转为 getter 和 setter 。
Vue 中每个组件实例都会对应⼀个 watcher 实例,它会在组件渲染的过程中把使⽤过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher ,从⽽使它关联的组件重新渲染。
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
- Vue3.x响应式数据原理
Vue3.x改⽤ Proxy 替代Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达13种拦截⽅法。并且作为新标准将受到浏览器⼚商重点持续的性能优化。
Proxy 只会代理对象的第⼀层,Vue3 判断当前Reflect.get 的返回值是否为Object,如果是则再通过 reactive ⽅法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次 get/set,我们可以判断 key 是否为当前被代理对象 target ⾃身属性,也可以判断旧值与新值是否相等,只有满⾜以上两个条件之⼀时,才有可能执⾏ trigger。
- Object.defineProperty 与 Proxy
- Object.defineProperty 只能劫持对象的属性,需要遍历对象的每个属性。Proxy 是直接代理对象。
- Object.defineProperty 对新增属性需要⼿动进⾏ Observe , 由于 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使⽤ Object.defineProperty 进⾏劫持。 也正是因为这个原因,使⽤ Vue 给 data中的数组或对象新增属性时,需要使⽤ vm.$set 才能保证新增的属性也是响应式的。
- Proxy ⽀持13种拦截操作,这是 defineProperty 所不具有的。
- 新标准性能红利Proxy 作为新标准,⻓远来看,JS引擎会继续优化 Proxy ,但 getter 和 setter 基本不会再有针对性优化。
- Proxy 兼容性差,Proxy是ES6新增的,IE浏览器不支持。 ⽬前并没有⼀个完整⽀持 Proxy 所有拦截⽅法的Polyfill ⽅案。Object.defineProperty兼容性好,支持 IE9。
3. v-model是如何实现双向绑定的?
- Vue2.0
v-model 是⽤来在表单控件或者组件上创建双向绑定的,他的本质是 v-bind 和 v-on 的语法糖,在⼀个组件上使⽤ v-model ,默认会为组件绑定名为 value 的 prop 和名为 input 的事件。
- v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
- Vue3.0
在 3.x 中,⾃定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的
update:modelValue 事件
4. 关于Vue组件化
- 定义: 组件是可复用的 Vue 实例,准确讲它们是 VueComponent 的实例,继承自Vue。
- 优点:组件化可以增加代码的复用性、可维护性和可测试性
-
什么时候使用组件?以下分类可作为参考:
-
通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等。
-
业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件。
-
页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情页组件
- 组件的本质:组件配置 => VueComponent实例 => render() => Virtual DOM=> DOM,组件的本质是产生虚拟DOM
- 组件内的data为什么一定要是个函数(1.使每个组件闭包 2. 不容易互相影响) or : 为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
当data定义为对象后,这就表示所有的组件实例共⽤了⼀份data数据,因此,⽆论在哪个组件实例中修改了data,都会影响到所有的组件实例。 data是一个函数的时候,每一个实例的data属性都是独立的,不会相互影响了。
实例化出来的根组件只有一个,你可以将它写成对象,或者是返回一个对象的函数。但是子组件不够健壮,容易在内存的地址中互相影响,就像我们常用的深浅拷贝所能解决的那样。所以为了防止意外的发生,是不被允许在子组件中使用对象来写data。
-
Vue 组件间通信有哪几种方式?
-
props / $emit
适用 父子组件通信 -
ref
与$parent / $children
适用 父子组件通信 -
EventBus ($emit / $on)
适用于 父子、隔代、兄弟组件通信 -
$attrs/$listeners
、provide / inject
适用于 隔代组件通信 -
Vuex 适用于 父子、隔代、兄弟组件通信
5. vue3.0 相对于 vue2.x 有哪些变化
- 监测机制的改变(Object.defineProperty —> Proxy)
- composition-api ( 更好的逻辑复用与代码组织,更好的类型推导 )
- 对象式的组件声明⽅式 (class)
- 使⽤ts
- 其它⽅⾯的更改:⽀持⾃定义渲染器、 ⽀持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件、基于 treeshaking 优化,提供了更多的内置功能
-
webpack打包的配置:vue-loader 依赖 "vue-template-compiler" 3.0 改为 @vue/compiler-sfc 需要在plugins里配置 VueLoaderPlugin
-
vue-cli的支持:目前通过 vue-cli-plugin-vue-next 升级到vue3.0beta版 vue add vue-cli-plugin-vue-next
-
v-if 与 v-for 的优先级对比:2.x 版本中在一个元素上同时使用
v-if
和v-for
时,v-for
会优先作用。3.x 版本中v-if
总是优先于v-for
生效
6. 关于 Vue的性能优化
- 编码阶段
- 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
- v-if 和 v-for不能连⽤,v-for 遍历必须为 item 添加 key
- 如果需要使⽤v-for给每项元素绑定事件时使⽤事件代理
- SPA ⻚⾯采⽤keep-alive缓存组件
- 在更多的情况下,使⽤v-if替代v-show
- key保证唯⼀
- 图⽚资源懒加载
- 使⽤路由懒加载、异步组件
- 防抖、节流
- 第三⽅模块按需导⼊
- 长列表性能优化,⻓列表滚动到可视区域动态加载
- SEO,Web 技术的优化
- 预渲染
- 服务端渲染SSR
- 缓存(客户端缓存、服务端缓存)优化
- 使用Chrome Performance 查找性能瓶颈
- 打包优化
- 压缩代码,服务端开启gzip压缩
- Tree Shaking/Scope Hoisting
- 使⽤cdn加载第三⽅模块
- 多线程打包happypack
- splitChunks抽离公共⽂件
- sourceMap优化
- ⽤户体验
- ⻣架屏
- PWA