当前位置: 首页 > news >正文

Vue2/3面试题

Vue2

1. Vue 的基本原理

当 一 个 Vue 实 例 创 建 时 , Vue 会 遍 历 data 中 的 属 性 , 用 Object.defineProperty ( vue3.0 使 用 proxy) 将 它 们 转 为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时 通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组 件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用 时,会通知 watcher 重新计算,从而使它关联的组件得以更新。

2. 双向数据绑定的原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数 据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几 个步骤:

  1. 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会 触发 setter,那么就能监听到了数据变化

  2. compile 解析模板指令,将模板中的变量替换成数据,然后初始化 渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数 据的订阅者,一旦数据有变动,收到通知,更新视图

  3. Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做 的事情是: ①在自身实例化时往属性订阅器(dep)里面添加自己 ② 自身必须有一个 update()方法 ③待属性变动 dep.notice()通知时,能调用自身的 update()方法,并触发 Compile 中绑定的回调,则成功 成身退。

  4. MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input)-> 数据 model 变更的双向绑定效果。

3. MVVM、MVC 的区别

  • MVC
    MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。其中 View 负责页面的显示逻辑,Model 负责存储页面的业务数据,以及对相应数据的操作。并且 View 和 Model 应用了观察者模式,当 Model 层发生改变的时候它会通知有关 View 层更新页面。Controller 层是 View 层和 Model 层的纽带,它主要负责用户与应 用的响应操作,当用户与页面产生交互的时候,Controller 中的事 触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修 改,然后 Model 层再去通知 View 层更新。

  • MVVM
    MVVM 分为 Model、View、ViewModel:
    Model 代表数据模型,数据和业务逻辑都在 Model 层中定义;
    View 代表 UI 视图,负责数据的展示;
    ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理 用户交互操作;
    Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中 的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改 变的数据也会在 Model 中同步。
    这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要 专注于数据的维护操作即可,而不需要自己操作 DOM。

4. 单页应用与多页应用的区别

  • SPA 单页面应用(SinglePage Web Application),指只有一个主页面的应用,一开始只需要加载一次 js、css 等相关资源。所有内容都 包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换 相关组件,仅仅刷新局部资源。

  • MPA 多页面应用 (MultiPage Application),指有多个独立页面的 应用,每个页面必须重复加载 js、css 等相关资源。多页应用跳转,需要整页资源刷新。

5. 为何组件 data 必须是一个函数

在 Vue 中,组件的 data 必须是一个函数,这是为了确保每个组件实例都有独立的数据作用域。函数形式的 data 会返回一个新的数据对象,当多个组件实例化时,每个实例都可以拥有自己的独立数据副本,避免数据相互污染。如果 data 是一个对象,则所有实例会共享同一个数据对象,导致状态被意外修改。这个设计主要是为了保证组件的复用性和稳定性。

首先组件data必须是一个函数,最根本的原因在于我们定义的vue组件是一个class(类),每个地方去使用这个组件的时候,相当于这个类的一个实例化,确保每个组件实例都有独立的数据作用域,避免相互污染。

6. Vue data 中某一个属性的值发生改变后,视图会立即同步执 行重新渲染吗

不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之 后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在 缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要 的。然后,在下一个的事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。

7. 异步更新DOM

  1. 为什么 Vue 要异步更新 DOM
  • 批量合并更新:同一事件循环内多次修改响应式数据,Vue 会合并为一次 DOM 更新,避免重复渲染,提升性能。
  • 保持一致性:数据变更是同步的,但 DOM 更新被延后到本轮任务末尾(微任务优先),保证在你本次逻辑结束后再进行统一渲染。
    例:在同一次点击回调里连改多次状态,DOM 只重渲染一次。
  1. 更新何时发生(事件循环视角)
  • Vue 会在当前宏任务内收集依赖变更,调度一个异步刷新队列的任务。
  • 刷新顺序:微任务优先(Promise.then/MutationObserver)> 宏任务(setTimeout 等)。
  • 刷新时机:当前同步代码执行完后,进入微任务队列,执行“渲染队列”,触发虚拟 DOM diff,再更新真实 DOM。

8. 计算属性和监听的区别

  • 计算属性:
    • 基于现有响应式数据“派生出新数据”;用于模板展示或进一步计算。
    • 会缓存,依赖不变时多次访问不会重新计算。
    • 以属性的形式使用,如在模板或代码中直接访问 this.fullName
    • 计算属性有返回值return。
    • 当其依赖的响应式数据发生变化且该属性被访问/渲染时才重新求值。
  • 监听器:
    • 在某个响应式数据变化时“执行副作用逻辑”;用于异步请求、手动控制复杂逻辑等。
    • 不缓存,每次被监听的值变化时都会触发回调。
    • 以回调的形式配置 watch,监听某个数据或计算结果的变化。
    • 执行副作用(可发请求、更新其他状态、节流/防抖、路由跳转等),无返回值约束。
    • 当被监听的源值变化时立即调用回调(可选 immediate 首次触发)。

9. Vue生命周期

  • beforeCreate:创建之前,此时还没有data和Method
  • Created:创建完成,此时data和Method可以使用了
  • beforeMount:在渲染之前
  • mounted:页面已经渲染完成,并且vm实例中已经添加完$el了,已经替换掉那些DOM元素了(双括号中的变量),这个时候可以操作DOM了(但是是获取不了元素的高度等属性的,如果想要获取,需要使用nextTick())
  • beforeUpdate:data改变后,对应的组件重新渲染之前
  • updated:data改变后,对应的组件重新渲染完成
  • beforeDestory:在实例销毁之前,此时实例仍然可以使用
  • destoryed:实例销毁后

父子组件的生命周期:

  • 渲染的过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
  • 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
  • 父组件更新过程:父beforeUpdate->父updated
  • 销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

10. $nextTick 原理及作用

  • 解释
    • nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
  • 应用
    • 想要在Vue生命周期函数中的created()操作DOM可以使用Vue.nextTick()回调函数
    • 在数据改变后要执行的操作,而这个操作需要等数据改变后而改变DOM结构的时候才进行操作,需要用到nextTick。

11. Vue相关指令问题

  1. 为什么避免v-for和v-if在一起使用
    Vue 处理指令时,v-for 比 v-if 具有更高的优先级, 虽然用起来也没报错好使, 但是性能不高, 如果你有5个元素被v-for循环, v-if也会分别执行5次
  2. Vue中Key值作用
    key值的作用是给元素添加一个唯一的标识符,提高vue渲染性能。当数据变化的时候,vue就会使用diff算法对比新旧虚拟Dom。 如果遇到相同的key值,则复用元素。如果遇到不同的key值则强制更新。
  3. Vue中有时候数组会更新页面,有时候不更新为什么
    因为vue内部只能监测到数组顺序/位置的改变/数量的改变, 但是值被重新赋予监测不到变更, 可以用 Vue.set() / vm.$set()
  4. v-if 和 v-show 的区别
    v-show 通过CSS display 控制显示与隐藏
    v-if 是组件真正的渲染和销毁,而不是显示与隐藏
  5. 自定义指令
    局部注册和使用
<template><div><!-- <input type="text" v-gfocus> --><input type="text" v-focus></div>
</template><script>
// 目标: 创建 "自定义指令", 让输入框自动聚焦
// 1. 创建自定义指令
// 全局 / 局部
// 2. 在标签上使用自定义指令  v-指令名
// 注意:
// inserted方法 - 指令所在标签, 被插入到网页上触发(一次)
// update方法 - 指令对应数据/标签更新时, 此方法执行
export default {data(){return {colorStr: 'red'}},directives: {focus: {inserted(el){el.focus()}}}
}
</script>

全局注册

// 在main.js用 Vue.directive()方法来进行注册, 以后随便哪个.vue文件里都可以直接用v-gfocus指令
Vue.directive("gfocus", {inserted(el) {el.focus() // 触发标签的事件方法}
})

自定义指令-传值

// el为绑定指令dom对象,binding为所传值
Vue.directive('color', {inserted(el, binding) {el.style.color = binding.value},update(el, binding) {el.style.color = binding.value}
})

12. Vue组件通信

  • 父传子:子组件设置props + 父组件设置v-bind:/:
  • 子传父:子组件的$emit + 父组件设置v-on/@
  • 任意组件通信:新建一个空的全局Vue对象,利用 e m i t 发 送 , emit发送, emit发送,on接收
Vue.prototype.Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {})
  • 祖先组件使用provide提供数据,子孙组件通过inject注入数据
// Provider.vue
export default {data() {return {theme: { color: "#409EFF" } // 对象是响应式的};},provide() {return {theme: this.theme};},template: `<div><slot></slot><button @click="theme.color = '#67C23A'">切换主题</button></div>`
};// Child.vue
export default {inject: ["theme"],template: `<div :style="{ color: theme.color }">当前主题色:{{ theme.color }}</div>`
};
  • ref—$refs:通过this.$refs.XXX获取子组件的属性或方法。
  • 直接获取父组件:this.$parent。
  • 直接获取根组件:this.$root。
  • keep-alive:缓存当前组件,生命周期:deactivated、activated。
  • <component :is="comName"></component>通过控制is属性绑定相应组件名称可实现动态组件展示

13. Vuex相关属性

  • state∶ 进行数据存储。this.$store.state.xxx
  • getters∶ 针对于state数据进行二次计算。this.$store.getters.xxx
  • mutatioins:状态改变操作方法,是 Vuex 修改 state 的唯一推荐方法,该方法只能进行同步操作,且 方法名只能全局唯一。this.$store.commit(“方法名”,数据)
  • actions:存放异步方法的,并且是来提交mutations。this.$store.dispatch(“方法名”,数据)
  • modules:把vuex再次进行模块之间的划分

Vuex 和 localStorage 的区别:

(1)存储方式

  • vuex 存储在内存中
  • localstorage 则以文件的方式存储在本地,只能存储字符串类型的 数据,存储对象需要 JSON 的 stringify 和 parse 方法进行处理。 读 取内存比读取硬盘速度要快

(2)应用场景

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集 中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一 种可预测的方式发生变化。vuex 用于组件之间的传值。
  • ocalstorage 是本地存储,是将数据存储到浏览器的方法,一般是 在跨页面传递数据时使用 。
  • Vuex 能做到数据的响应式,localstorage 不能

(3)永久性

  • 刷新页面时 vuex 存储的值会丢失,localstorage 不会。
    注意:对于不变的数据确实可以用 localstorage 可以代替 vuex,但 是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件 改变了该数据源,希望另一个组件响应该变化时,localstorage 无 法做到,原因就是区别 1。

14. slot/插槽

  1. 匿名插槽
<template><div class="child"><h2>我是子组件的标题</h2>// 默认插槽<slot></slot></div>
</template><template><div><DefaultSlotChild>// 这里的内容会被渲染到子组件的默认插槽中<p>这是来自父组件的默认插槽内容。111</p><p>这是来自父组件的默认插槽内容。222</p></DefaultSlotChild></div>
</template><script>
import DefaultSlotChild from './DefaultSlotChild.vue';export default {components: {DefaultSlotChild}
}
</script>
  1. 具名插槽
// 父组件
<child-component>
<template slot="header"><h1>这是头部内容</h1></template>
<template slot="footer"><p>这是底部内容</p></template>
</child-component>// 子组件
<template>
<slot name="header"></slot>
<slot name="footer"></slot>
</template>
  1. 作用域插槽
// 父组件
<child-component>
<template slot="item" slot-scope="{text}">
// 可简写为 <template #item="{text}">
<p>{{ text}}</p>
</template>
</child-component>// 子组件
<template>
<slot name="item" text="itemText"></slot>
</template>

15. 路由

在单页应用(SPA)中。它描述的是URL与UI之间的映射关系,即当用户访问不同的URL时,前端应用会相应地展示不同的界面或组件,而无需重新从服务器加载整个页面。
路由分为:后端路由和前端路由

后端路由:由服务器端进行实现并实现资源映射分发(nodejs、jsp、php等)

  • 概念:根据不同的用户URL请求,返回不同的内容(地址与资源产生对应关系)
  • 本质:URL请求地址与服务器资源之间的对应关系(映射)

前端路由:根据不同的事件来显示不同的页面内容,是事件与事件处理函数之间的对应关系

  • 概念:根据不同的用户事件,显示不同的页面内容(地址与事件产生对应关系)
  • 本质:用户事件与事件处理函数之间的对应关系
前端路由的实现方式

hash模式:

  • 用 # 后的片段作为前端路由标识,变化不会触发服务器请求。
  • 依赖浏览器的 hashchange 事件,历史记录由浏览器维护。
  • 服务器只看到 # 之前的路径,通常是根路径,不需要后端配合。

history模式:

  • 使用 HTML5 History API(pushState/replaceState),改变路径但不刷新页面。
  • 刷新或直接访问非根路径时,浏览器会向服务器请求该路径,需服务器正确返回前端入口文件。
  • 更贴近真实路径,URL 更干净。
$route 与$router的区别

$router是用于做编程式导航的(改变路由的);
$route是用户获取路由信息的。

路由跳转与传参方式

query传参:

  • 以?形式拼接在路由中,示例:/user?id=123
  • 用path指定目标路由,query传递参数
  • this.$route.query接收参数
this.$router.push({
path:'/naws',
query:{id:'123'}
})

params传参

  • 不在路由中展示,URL地址栏传参隐藏
  • 刷新页面会导致数据丢失
  • 用name指定目标路由,params传递参数
  • this.$route.params接收参数
this.$router.push({
name:'News',
params:{name:'xzl'}
})

以上方式都可以实现路由跳转,统称为编程式导航,使用<router- link to="xxx"></ router- link>为声明式导航。

动态路由

所谓动态路由就是路由规则中有部分规则是动态变化的,不是固定的值,需要去匹配取出数据(即路由参数)。

  • 如何传递
    在声明路由的时候,将可变部分通过“:变量名”的形式进行替代
  • 如何获取
    获取this.$route来获取
// router.js
{path:'/about/:id?', // ?表示非必传name: 'About',component: () => import('@/views/About.vue')
}<router-link to='/about/123'>about</router-link>console.log(this.$route.params.id) // 123
嵌套路由

嵌套路由用于表达页面的层级结构:父路由渲染公共布局(如头部、侧栏、面包屑),子路由在父布局的占位处切换显示相应页面。

// 懒加载页面(推荐)
const Layout = () => import('@/layouts/AppLayout.vue')
const Home = () => import('@/views/Home.vue')
const Users = () => import('@/views/users/Users.vue')
const UserDetail = () => import('@/views/users/UserDetail.vue')const routes = [{path: '/',component: Layout,      // 父路由:提供公共布局children: [{ path: '', name: 'home', component: Home },               // 默认子路由(相对路径为空){ path: 'users', name: 'users', component: Users },        // /users{ path: 'users/:id', name: 'user-detail', component: UserDetail } // /users/123]}
]<template><div class="layout"><header>Header</header><aside>Sidebar</aside><main><!-- 子路由会渲染到这里 --><router-view /></main></div>
</template>
路由守卫

路由守卫有三种:

  • 全局钩子: beforeEach(跳转路由后进入页面前触发)、 afterEach(跳转路由后进入页面后触发)
  • 独享守卫(单个路由里面的钩子): beforeEnter、 beforeLeave
  • 组件内守卫:beforeRouteEnter、 beforeRouteUpdate、 beforeRouteLeave

每个守卫方法接收三个参数:

  1. to: Route: 即将要进入的目标路由对象(to是一个对象,是将要进入的路由对象,可以用to.path调用路由对象中的属性)
  2. from: Route: 当前导航正要离开的路由
  3. next: Function: 这是一个必须需要调用的方法,执行效果依赖 next 方法的调用参数。

前置路由守卫(每次切换前被调用)

{path: '/',name: 'Home',component: () => import('../views/Home.vue'),meta: { isAuth: true, title:'主页' },
},//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to, from, next) => {//如果路由需要跳转if (to.meta.isAuth) {//判断 如果school本地存储是qinghuadaxue的时候,可以进去if (localStorage.getItem('school') === 'qinghuadaxue') {next()  //放行} else {alert('抱歉,您无权限查看!')}} else {// 否则,放行next()}

后置路由守卫(每次切换后被调用)

//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to, from) => {document.title = to.meta.title || '默认名'    //修改网页的title
})

独享路由守卫(某一个路由所单独享用的路由守卫)

{path: '/',name: 'Home',component: () => import('../views/Home.vue'),meta: { isAuth: true },beforeEnter: (to, from, next) => {if (to.meta.isAuth) { //判断是否需要授权if (localStorage.getItem('school') === 'qinghuadaxue') {next()  //放行} else {alert('抱歉,您无权限查看!')}} else {next()  //放行}}
},

组件内守卫(写在路由组件内部的路由守卫)

//通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next) {if(toString.meta.isAuth){if(localStorage.getTime('school')==='qinghuadaxue'){next()}else{alert('学校名不对,无权限查看!')}} else{next()}
},//通过路由规则,离开该组件时被调用 
beforeRouteLeave(to,from,next) {next()
}

16.diff算法

Diff 算法是一种对比算法。对比两者是 旧虚拟 DOM 和新虚拟 DOM,对比出是哪个 虚拟节点更改了,找出这个 虚拟节点并只更新这个虚拟节点所对应的 真实节点而不用更新其他数据没发生改变的节点,实现 精准地更新真实 DOM,进而 提高效率

在新老虚拟 DOM 对比时:

  • 首先,对比节点本身,判断是否为同一节点,如果不为相同节点,则删除该节点重新创建节点进行替换
  • 如果为相同节点,进行 patchVnode,判断如何对该节点的子节点进行处理,先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)
  • 比较如果都有子节点,则进行 updateChildren,判断如何对这些新老节点的子节点进行操作(diff 核心)。
  • 匹配时,找到相同的子节点,递归比较子节点

Vue3

1. Vue3 相比 Vue2 的区别

  • 响应式系统重写
    • vue2通过Object.defineProperty遍历每个属性,监听劫持数据进行更改
    • vue3使用Proxy监听整个对象变化
// Vue 2 响应式片段
data() { return { list: [1,2,3] } 
},
mounted() {this.list[5] = 10; // 无法被劫持 -> 不会触发视图更新
}// Vue 3 响应式片段
import { reactive } from 'vue';const state = reactive({ list: [1,2,3] });
state.list[5] = 10; // 自动触发更新
  • 组合式API vs 选项式API
    • Vue 2 选项式组织方式–逻辑分散
    • Vue 3 组合式API(Composition API)–逻辑复用能力好
// vue2选项式
export default {data() { return { count: 0 } },methods: { increment() { this.count++ } },mounted() { console.log('已加载') }
}// vue3组合式
import { ref, onMounted } from 'vue';export default {setup() {const count = ref(0);const increment = () => count.value++;onMounted(() => {console.log('组件已挂载');});return { count, increment };}
}
  • 性能优化提升
    • 编译器优化:在编译阶段标记哪些是动态内容(如 {{ count }}),更新时跳过静态内容(如纯文字)。
    • Tree-shaking支持:按需引入,只打包你用到的功能,减少代码体积
    • Fragment支持:多根节点无需额外包裹div
  • 新组件
    • <Teleport>:把组件渲染到任意位置(比如弹窗放到 body 下,避免被父组件样式影响)。
    • <Suspense>:优雅处理异步加载(比如数据加载时显示 Loading 动画)。它可以定义一个fallback内容,在异步组件加载完成前显示。
// teleport 
<teleport to="#modal-container"><div class="modal">模态框内容</div>
</teleport>// Suspense
<Suspense><template #default><AsyncComponent/></template><template #fallback>加载中...</template>
</Suspense>

2. Vue3 的响应式原理

  • Proxy 代理对象:
    -通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等
    • 通过Reflect(反射): 对源对象的属性进行操作。
    • 对比 Vue2:Vue2 使用 Object.defineProperty,无法监听新增属性和数组下标变化(必须用 this.$set)
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' 

3. ref 和 reactive 的区别

  • ref
    • 用于包装 基本类型(数字、字符串等),因为 Proxy 无法直接监听基本类型。
    • 使用方式:必须通过 .value 访问。
    • 通过Object.defineProperty()的get与set来实现响应式(数据劫持)
const count = ref(0);  
console.log(count.value); // 0  
count.value++;  
  • reactive
    • 用于包装 对象/数组,可以直接访问属性。
    • 通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
const user = reactive({ name: '张三' });  
console.log(user.name); // 张三  
user.name = '李四';  
4. toRef和toRefs
  • toRef用于将响应式对象的某个属性转换为ref
  • toRefs用于将响应式对象的所有属性转换为ref对象,并返回一个包含这些ref的普通对象
  • 这两个函数常用于在解构响应式对象时保持响应性
const user = reactive({ name: 'John', age: 30 })
const nameRef = toRef(user, 'name')
const { name, age } = toRefs(user)

5. shallowReactive和shallowRef

  • shallowReactive创建一个浅响应式对象,只监听对象的顶层属性变化
  • shallowRef创建一个浅响应式ref,只监听.value的变化,不关心.value内部的变化
  • 这两个函数常用于优化性能,避免不必要的深度响应式
const shallowObj = shallowReactive({ a: 1, b: { 
c: 2 } })
// 修改shallowObj.a会触发更新
// 修改shallowObj.b.c不会触发更新const shallowValue = shallowRef({ a: 1 })
// 修改shallowValue.value会触发更新
// 修改shallowValue.value.a不会触发更新

6. readonly和shallowReadonly

  • readonly创建一个只读的响应式对象,任何修改都会被阻止
  • shallowReadonly创建一个浅只读的响应式对象,只阻止顶层属性的修改
  • 这两个函数常用于保护不可变数据
const user = readonly({ name: 'John', age: 30 })
// 尝试修改user.name会被阻止const shallowUser = shallowReadonly({ name: 
'John', address: { city: 'Beijing' } })
// 尝试修改shallowUser.name会被阻止
// 但可以修改shallowUser.address.city

7. toRaw 与 markRaw

  • toRaw:将一个由reactive生成的响应式对象转为普通对象。
  • toRaw:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:标记一个对象,使其永远不会再成为响应式对象。
  • markRaw:有些值不应被设置为响应式的,例如复杂的第三方类库等。

8. vue3响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

9. 生命周期

      vue2                   vue3

在这里插入图片描述

10. setup函数

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。
  2. setup是所有Composition API(组合API)“ 表演的舞台 ”。
  3. 组件中所用到的:数据、方法等等,均要配置在setup中。
  4. setup函数的两种返回值:
    • 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
    • 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  5. setup的几个注意点
    • setup执行的时机
      • 在beforeCreate之前执行一次,this是undefined。
    • setup的参数
      • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
      • context:上下文对象
        • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。
        • slots: 收到的插槽内容, 相当于 this.$slots。
        • emit: 分发自定义事件的函数, 相当于 this.$emit。
    • setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

11. watch 和 watchEffect

  • watch:明确监听某个数据,适合精确控制(比如监听搜索关键词变化,触发请求)。
watch(  keyword,  (newVal) => { fetchData(newVal) },  { immediate: true } // 立即执行一次  
);  
  • watchEffect:自动收集依赖,并在依赖变化时重新执行。与watch不同,watchEffect不需要明确指定要监听的数据,而是会自动追踪函数内部使用的响应式数据。watchEffect通常用于处理副作用,如数据fetching、DOM操作等。
watchEffect(() => {  console.log('关键词和页码变化了:', keyword.value, page.value);  fetchData();  
});  

12. 使用expose和ref实现组件通信

子组件可以通过expose暴露方法或属性,父组件通过ref获取子组件实例并调用这些方法或访问属性。

// 子组件
import { ref, defineExpose } from 'vue'const count = ref(0)
const increment = () => { count.value++ }defineExpose({ count, increment })// 父组件
import { ref, onMounted } from 'vue'export default {setup() {const childRef = ref(null)onMounted(() => {// 访问子组件的count属性console.log(childRef.value.count)// 调用子组件的increment方法childRef.value.increment()})return { childRef }}
}

13. 什么是静态提升

静态提升是指将模板中的静态节点(不包含动态数据的节点)提升到渲染函数之外,避免在每次渲染时都重新创建这些节点。这可以减少内存占用和提高渲染性能。Vue3的编译器会自动进行静态提升。

14. 什么是PatchFlag

PatchFlag是Vue3中新增的一个标记,用于标记动态节点的变化类型(如文本变化、属性变化等)。在diff过程中,Vue可以根据PatchFlag只检查节点的特定部分,而不是整个节点,从而提高diff效率。常见的PatchFlag包括TEXT、CLASS、STYLE、PROPS等。

15. 如何优化Vue3的渲染性能

  • 使用v-once指令缓存静态内容
  • 使用v-memo指令缓存动态内容
    • v-memo指令用于缓存模板中的一部分内容,只有当依赖的数据变化时才会重新渲染。它接收一个依赖数组,当数组中的任何一个元素变化时,才会重新渲染缓存的内容。v-memo可以用于优化频繁渲染的列表或复杂组件。
<div v-memo="[user.id, user.name]">{{ user.name }} - {{ user.id }}
</div>
  • 避免不必要的响应式数据
  • 使用shallowReactive和shallowRef减少响应式开销
  • 合理使用组件拆分,提高组件复用性
  • 使用keep-alive缓存组件状态
  • 优化大型列表渲染
    • 使用虚拟滚动库(如vue-virtual-scroller),只渲染可视区域内的列表项
    • 使用v-memo指令缓存列表项
    • 避免在列表项中使用复杂的计算属性
    • 合理设置key,避免不必要的DOM操作
    • 懒加载列表数据,分页加载

16. Pinia和Vuex的区别

  • Pinia是Vue3推荐的状态管理库,Vuex是Vue2的官方状态管理库
  • Pinia没有mutations,直接通过actions修改状态
  • Pinia支持TypeScript,类型推断更好
  • Pinia的体积更小,性能更好
  • 定义使用
import { defineStore } from 'pinia'export const useCounterStore = defineStore
('counter', {state: () => ({ count: 0 }),getters: {doubleCount: (state) => state.count * 2},actions: {increment() {this.count++},async fetchCount() {// 异步操作const res = await fetch('/api/count')this.count = await res.json()}}
})
  • 组件中使用
import { useCounterStore } from './stores/counter'
import { computed } from 'vue'export default {setup() {const counterStore = useCounterStore()// 访问stateconst count = computed(() => counterStore.count)// 访问getterconst doubleCount = computed(() => counterStore.doubleCount)// 调用actionconst increment = () => counterStore.increment()return { count, doubleCount, increment }}
}
  • 状态持久化:将状态保存到本地存储(如localStorage、sessionStorage)中,以便在页面刷新或重新打开时恢复状态。可以使用pinia-plugin-persistedstate等插件实现,也可以手动在actions中保存状态。
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)// store.js
export const useCounterStore = defineStore
('counter', {state: () => ({ count: 0 }),// 启用持久化persist: true
})
http://www.dtcms.com/a/592452.html

相关文章:

  • C++ ODB ORM 完全指南:从入门到实战应用
  • Java-----集合
  • 金昌市网站建设vfp wordpress
  • 网站建设,从用户角度开始私人做网站
  • 哪个网站做婚礼邀请函好武进区城乡建设局网站
  • 网站开发成本报表新开传奇网站单职业
  • 网站设计与网页配色实例精讲微信登陆wordpress
  • 网站建设外贸开发软件用什么工具
  • 建设工程行业招工信息网站企业网站营销的优缺点及案例
  • 网站栏目策划如何推广自己的产品
  • 襄阳建设局网站快速排名优化推广价格
  • 手机软件制作网站平台dede自动生成网站地图
  • 漳州建设局网站首页单页关键字优化
  • 有网页源码 怎么做网站郑州外语网站建站优化
  • 宁波淘宝网站建设做电器推广的网站
  • 苏州做网站品牌公司wordpress 多主题
  • 怎么增加网站浏览量wordpress收不到
  • 建设网站费用吗怎样自己搭建网站
  • 网站双收录怎么做301跳转宁波做网站皆选蓉胜网络
  • 专门做红酒的网站海北高端网站建设哪家好
  • 定制开发响应式网站做聚类热图的网站
  • 搜索网站建设推广优化2021年网站有人分享吗
  • 网站分页效果做网站推广 seo的
  • 泰州建站价格WordPress文件归档
  • 水果网店网站建设策划书网站推广策划思路
  • 单页面网站推广网站空间管理站
  • 做网站的岗位好吗住房建设厅官方网站
  • 个人网站什么语言做注册网站会员需要填写信息
  • 静态后台管理网站模板expression wordpress主题
  • 网站开发 成都平面设计与网站开发概述