文章目录
1.vue数据监听
1. vue2
- 面试官:如何使用defineProperty监听对象的所有属性值的变化?
- 我:…
---js----
// 1. 创建一个监听类首先判断是数组还是对象类型
export class Observer {
constructor(personInfo) { // 给obj里面初始一个personInfo属性
this.personInfo = personInfo;
if (Array.isArray(personInfo)) {
// 数组的情况
} else {
// 对象的情况
this.walk(personInfo);
}
}
// 2. 循环遍历整个对象的属性,调用监听方法进行监听
walk(obj) {
const keys = Object.keys[obj]; // 将属性的键提取出来
for (let i = 0; i < keys.length; i++) { // 遍历监听
ObserverObj(obj, keys[i]); // 调用监听的方法
}
}
}
function ObserverObj(obj, key, val) {
if (arguments.length === 2) {
val = obj[key] // 只传入两个参数的时候提取出键对应的值
}
if (typeof val === 'object') { // 如果属性有套娃现象就再次进行递归监测
new Observer(val)
}
Object.defineProperty(obj, key, {
get() {
console.log(`${key}属性被读取了,属性值为${val}`);
return val;
},
set(newVal) {
console.log(`${key}属性被修改了,新的属性值为${newVal}`);
val = newVal;
}
})
}
---html---
import {Observer} from '.observer.js'
let obj = new Observer({
name: '高起墙',
age: 99
})
console.log(obj.personInfo.name); // 属性读取
obj.personInfo.age = 1; // 属性修改
2.vue3
let obj = {
name: '路飞',
age: 18
}
// 使用proxy方法对监听的对象的属性的crud操作进行拦截监听
const proxy = new Proxy(obj, {
get(target,propName){
console.log(`有人读取了proxy身上的${propName}`)
return target[propName];
//反射
return Reflect.get(target,propName)
},
// 改
// value属性必须放在最后面
set(target, propName, value){
console.log(`有人修改了p身上的${propName}属性`);
target[propName] = value;
},
// 删
deleteProperty(target, prop){
console.log(`有人删除了p身上的${prop}属性`)
return delete target[prop];
}
})
效果
2.生命周期
1.vue2的生命周期
-
什么是生命周期和生命周期函数?
- 一个组件从 创建 -> 运行 -> 销毁 的过程被称为组件的生命周期
- 为了能够在确切的某个周期执行某个操作,vue为开发者提供了生命周期函数来在确切的阶段做某件事
-
生命周期
- 创建阶段:
– beforeCrate() 组件创建之前,初始化组件实例和props, data, method等属性
– created() 在内存中创建完成,但是没有完成DOM结构的渲染,不能够获取到组件里面的任何标签元素,此时可以操作props,data,method等属性
– beforeMount() 在DOM结构渲染完成之前
– mounted() DOM结构渲染完成,同时可以进行DOM元素的获取
- 运行阶段
– beforeUpdate()
组件数据更新和页面结构重新渲染之前,这里的dom元素依旧是原始的dom元素,没有因为数据的更改进行dom结构的重新渲染
– updated()
DOM结构是最新的且数据也是最新的,这里页面至少进行了一次更新
- 销毁阶段
– beforeDestroy 组件销毁之前 这时的所有dom元素和data,method都是能够正常进行使用的
– destroyed 组件已经销毁完成
以下是原理图,可以对照着看…
- 创建阶段:
总结:
生命周期函数里面主要使用的就是mounted, updated,页面dom结构渲染完成之后可以进行dom元素的获取和任何属性的获取;updated是数据完成了更新之后的时间点,所以这个时候的dom结构以及数据都是最新的。
2.vue3的生命周期
- 创建、挂载阶段
vue3是没有beforeCreate、created这两个vue2的生命周期的,所以vue3的创建阶段的生命周期是onBeforeMount()、onMounted()这两个生命周期函数;在调用onBeforeMount钩子时DOM元素还没有进行渲染,根元素还不存在;在调用onMounted钩子时,DOM元素就存在了,可以直接访问 - 更新阶段
onBeforeUpdate(),onUpdated()在调用onBeforeUpdate()之前dom元素和所有的数据都是原始的,还不是最新的,只有调用onUpdated之后所有数据才是最新的 - 销毁阶段
onBeforeUnMount():组件卸载之前,这时候的组件实例还是完整的
UnMounted():组件实例卸载之后,所有绑定的自定义事件和事件侦听器都会被移除 - API
onBeforeMount()
onMounted()
onBeforeUpdate()
onUpdated()
onBeforeUnmount()
onUnmounted()
3. vue2中常见的事件修饰符有哪些
- stop (阻止冒泡)
- prevent(阻止默认事件)
- once(只执行一次)
- sync(用于子组件想要更新父组件传递过来的props,然后使用this.$emit方法传递自定义事件和数据,父组件接收时给事件名加上.sync就可以实现响应式更新)
4. vue2中动态组件 VS 异步组件
4.1 动态组件
- 使用component标签来进行动态组件的渲染,:is属性输入你需要渲染的组件名即可,常用于需要频繁切换的组件显示,比如两个tab切换,点击之后更改组件名就可以实现组件的动态渲染
-
动态组件常搭配keep-alive一起使用,因为我们希望切换之后能将数据进行缓存,切换回来时依旧能展示原来的数据,而不是被清空,避免再次重新渲染提升性能
-
keep-alive包含三个参数 include (只有名称匹配的会被缓存) exclude(只有名称不匹配的会被缓存) max(可以缓存的组件实例个数),它是一个抽象函数,不会渲染真实的dom标签在里面
3.1 activated deactivated 在组件切换的时候会调用这两个钩子函数
// 通过数组的形式输入需要渲染的组件名
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
4.2 异步组件
- 在组件需要被渲染的时候才进行渲染,可以使用一个函数来进行定义我们所需要异步渲染的组件,函数只要被触发之后就会进行缓存,后面就直接读取缓存中的内容进行渲染
// 动态导入我们需要异步渲染的组件
Vue.component('template-demo', () => import('./template-demo')) // 其实这有点类似于路由懒加载的写法
5. vue3中的组合式API常用的有哪些
ref( )、reactive( )、onMounted( )、onUnmounted( )、nextTick( )、provide( )、inject( )
- 响应式API:
ref:
-ref可以定义任意的数据类型为响应式,只是在脚本操作中需要.value才能读取到值,但是在template中不需要.value
reactive:
-使对象本身具有响应式,将一个普通 JavaScript 对象转换为响应式代理对象,使对象的属性能够被 Vue 的响应式系统追踪和触发更新。
区别:
-ref声明的响应式在setup中读取值需要加上.value,而在template里面使用就可以不需要.value
-ref声明的是一个特殊的包装对象,将数据转为响应式的应用对象,使之变得响应式;reactive声明使对象本身具有响应式,通过将一个普通的js对象转为响应式代理对象,使得对象的属性能够被vue的响应式系统追踪和触发更新;
-ref可以用来包装基本的数据类型,而reactive只能用于包装js对象 - 生命周期钩子
onMounted: 可以用来在组件完成初始渲染并创建 DOM 节点后运行代码
onUnmounted: 钩子可以用来在组件实例被卸载之后调用
nextTick: dom节点更新之后 - 依赖注入
-当父组件需要给自己的子组件传递值,但目标子组件又经历了多层嵌套的场景(可以使用props但很麻烦)
-如果需要在注入的组件对象中更改值,可以在注入之前的父组件声明一个方法传递给子组件对象,然后子组件解构,拿到方法进行值的修改
-如果不允许子组件修改使用readonly(传递的值)
// 提供
/* 我们也可以在整个应用层app上面提供依赖注入 应用层级别提供的数据在该应用内的所有组件中都可以注入 */
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
// 注入
const message = inject('message')
- 为什么要有组合式API
更好的逻辑复用,选项式api的逻辑服用机制是混入,而组合是api就解决了这种缺陷
更灵活的代码组织
更好的进行类型的推导,搭配ts
减小打包体积
6. vue首屏加载优化的方式有哪些
- 使用路由懒加载、图片懒加载:
只有在路由被使用到的时候才进行加载,提升页面加载的速度 - 异步组件:
使用import+组件名+回调函数的形式加载你需要进行异步加载的组件 - 打包时使用代码分割:
-该方法可以减少打包的体积,尤其是js文件的代码
-主要是针对首屏需要用户主动去触发之后才会产生回调的js代码进行代码分割,这样就能极大的减少打包的体积,提升页面渲染的速度
-将按需加载的功能单独拆分到一个文件中进行管理,在需要按需加载的位置使用function+return import的方式进行引入即可 - 服务端渲染、静态站点生成:
-服务:将浏览器需要渲染的html代码在服务器端直接预先渲染成html字符串,然后再作为服务端响应数据返回给浏览器,最后在浏览器将静态的html激活渲染即可
-静态:可能服务端渲染的页面数据是重复的,所以不需要每次都重新进行渲染,所i有可以将其预先渲染为静态的html文件交给服务器托管,然后需要的时候直接拿取即可,就减少了再次进行服务端渲染的时间
7. vue3父子组件嵌套时的生命周期顺序
-
页面加载(父子嵌套)
父: beforeCreate => created => beforeMount
子: beforeCreate => created => beforeMount=> mounted
父: mounted
-
页面改动(父子嵌套)
父:beforeUpdate
子:beforeUpdate => updated
父:updated
-
页面离开(父子嵌套)
父:beforeUnmount
子:beforeUnmount => unmounted
父:unmounted
8.vue中使用$refs通信的弊端
$refs是一个可以访问组件实例或原生DOM元素的特殊属性,可以用在组件间的通信.
-
增加组件间的耦合度:
导致组件间的依赖关系变的紧密,降低组件的复用性和维护性
-
扩展性差:
在组件数量增加或者组件结构变得复杂的时候会变得难以管理,组件层次的不断变化就需要更改$refs来适应,增加代码的复杂性
-
依赖组件的生命周期:
使用$refs必须在组件渲染完成之后才能访问,如果需要组件渲染之前进行事务处理是不可行的
-
难以进行跨级通信:
refs属性只能访问直接子组件的或原生DOM元素,如果需要更深层次的跨组件通信,refs实现起来可能不是太友好
总结:
在大多数情况下,推荐使用更灵活、松耦合的方法,如事件总线、Vuex 状态管理或父子组件间的 props 和事件等方式来进行组件间的通信。