前端面试vue
目录
Vue生命周期钩子函数
Vue2响应式原理(检测变化的注意事项)
Vue动态组件
Vue Router中,动态路由传参query和params的区别
Watch、Computed、方法的区别,以及Computed如何传参数
Vue异步组件
Vue组件通信的几种方式?
vue 组件定义,data为什么必须是函数
v-if 和 v-show 的区别
v-if 和 v-for为啥不能一起使用
自定义组件的v-model
导航守卫
Object.defineProperty() 和 proxy的区别
Vue 中diff算法
Vue2和Vue3的区别
虚拟DOM是什么
vue插槽有哪些
vue性能优化
watch 和 watchEffect 的区别
如果用户反馈页面加载慢,你会如何排查和优化
ref 和 reactive 的区别和使用场景
如何实现一个权限管理系统
vue中有哪些内置指令
12
1
1
1
1
1
1
1
1
Vue生命周期钩子函数
vue2 生命周期构造函数 beforeCreate: 创建之前调用,此时data和el都还未初始化,无法通过vm访问到data中的数据、 methods中的方法 created: 实例创建完成之后调用,此时data已经完成了初始化,el还未初始化,可以通过vm访问到data 中的数据、methods中的方法 beforeMount: 实例挂载开始之前调用,data和el都已经初始化,页面呈现的是虚拟DOM,还没渲染到 HTML页面中 mounted: 实例挂载之后调用,模版中的HTML渲染到HTML页面中 beforeUpdate: 数据更新前调用 updated: 数据更新后调用,避免在此期间更新状态,可能会造成无限循环 beforeDestroy: 组件销毁前调用,还能访问组件实例。在此可以清除定时器以及dom绑定的事件 destroyed: 组件销毁后调用,销毁后组件实例不可访问 activated:被 keep-alive 缓存的组件激活时调用 deactivated:被 keep-alive 缓存的组件失活时调用 errorCaptured:在捕获一个来自后代组件的错误时被调用 vue3 选项式 beforeCreate:在实例初始化完成并且 props 被解析后立即调用 created:在组件实例处理完所有与状态相关的选项后调用,当这个钩子被调用时,响应式数据、计算属性、方法和侦听器已经设置完成。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用 beforeMount:在组件被挂载之前调用,当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。 mounted:在组件被挂载之后调用,这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用。 beforeUpdate:在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用,这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态 updated:在组件因为一个响应式状态变更而更新其 DOM 树之后调用,父组件的更新钩子将在其子组件的更新钩子之后调用 beforeUnmount:在一个组件实例被卸载之前调用,当这个钩子被调用时,组件实例依然还保有全部的功能 unmounted:在一个组件实例被卸载之后调用,可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接 activated:组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用 deactivated:若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用 errorCaptured:在捕获了后代组件传递的错误时调用 renderTracked:在一个响应式依赖被组件的渲染作用追踪后调用 renderTriggered:在一个响应式依赖被组件触发了重新渲染之后调用 ***与vue2的区别就是销毁钩子的命名改变*** vue3 组合式 onBeforeMount():在组件被挂载之前被调用,当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。 onMounted():在组件挂载完成后执行,个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用。 onBeforeUpdate():在组件即将因为响应式状态变更而更新其 DOM 树之前调用,这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。 onUpdated():在组件因为响应式状态变更而更新其 DOM 树之后调用,父组件的更新钩子将在其子组件的更新钩子之后调用。 onBeforeUnmount():在组件实例被卸载之前调用。 onUnmounted():在组件实例被卸载之后调用,可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。 onActivated():若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。 onDeactivated():若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。 onErrorCaptured():在捕获了后代组件传递的错误时调用。 onRenderTracked() :当组件渲染过程中追踪到响应式依赖时调用。 onRenderTriggered() :当响应式依赖的变更触发了组件渲染时调用。Vue2响应式原理(检测变化的注意事项)
Vue 2 的响应式原理核心是 Object.defineProperty 1、通过 Object.defineProperty 劫持对象的getter/setter 2、在getter 中收集依赖 3、在setter 中通知依赖更新 4、每个响应式属性都有一个对应的 Dep 实例 5、通过 Watcher 连接视图和数据 Vue 2 响应式有什么缺陷 1、无法检测对象属性的添加/删除 → 需要 Vue.set/Vue.delete 由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如: var vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应式的 vm.b = 2 // `vm.b` 是非响应式的 对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。 也可以使用 this.$set(this.someObject,'b',2) 有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。 // 代替 `Object.assign(this.someObject, { a: 1, b: 2 })` this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }) 2、对于数组 Vue 不能检测以下数组的变动: 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength 解决第一类问题可以使用: vm.$set(vm.items, indexOfItem, newValue) vm.items.splice(indexOfItem, 1, newValue) 解决第二类问题可以使用: vm.items.splice(newLength) 3. 初始化时递归遍历所有属性,性能较差 4. 对 ES6+ 的数据结构(Map、Set)支持不好Vue动态组件
通过 Vue 的 <component> 元素和特殊的 is attribute 实现 <component :is="tabs[currentTab]"></component>Vue Router中,动态路由传参query和params的区别
1、写法上的区别:params传值必须使用路由的name属性,query传值用path和name属性都可以 router.push({ name: 'user', params: { username: 'eduardo' } }) router.push({ path: '/register', query: { plan: 'private' } }) router.push({ name: 'register', query: { plan: 'private' } }) 2、params 传值需要在路由定义时指定相应的路径参数,如果不指定路由就会跳转不过去(Vue Router4) (Vue Router3 不在路由定义指定相应参数也可以跳转传参) 3、如果不在路由定义时指定相应的路径参数,params传值在url看不到,并且如果页面刷新的话参数会丢失 4、query传值能在url上看到传递的参数且页面刷新参数不会丢失Watch、Computed、方法的区别,以及Computed如何传参数
1、computed是计算属性,watch是监听一个值的变化而执行对应的回调 2、computed函数所依赖的属性不变的时候会调用缓存;watch每次监听的值发生变化时候都会调用回调 3、computed必须有return;watch可以没有 4、computed函数不能有异步;watch可以 5、computed当一个属性受多个属性影响的时候使用;watch一条数据影响多条数据的时候使用 6、computed 传参数使用闭包 computed: { asyncRules () { return function (record, key) { return } }, }, 计算属性缓存 vs 方法 计算属性是基于响应式依赖缓存的,只在相关响应式依赖发生改变时它们才会重新求值。 方法:每当触发重新渲染时,调用方法总会再次执行。 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }Vue异步组件
1、vue2 实现 // 全局注册 Vue.component('async-example', () => import('./MyComponent.vue')) // 局部注册 export default { components: { 'async-example': () => import('./MyComponent.vue') } } 2、vue3使用defineAsyncComponent函数 defineAsyncComponent 方法接收一个返回 Promise 的加载函数,ES 模块动态导入也会返回一个 Promise import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => import('./components/MyComponent.vue') )Vue组件通信的几种方式?
1、Props / Emits(父子通信) // 父组件 <Child :title="parentTitle" @update="handleUpdate" /> // 子组件 defineProps(['title']) defineEmits(['update']) emit('update', newValue) 2、Provide / Inject(跨层级) //祖先组件 provide('theme', ref('dark')) //后代组件 const theme = inject('theme', 'light') // 默认值 3、Event Bus(事件总线) //使用 mitt 或自定义事件中心 emitter.on(event, callback): 订阅事件 emitter.emit(event, data): 触发事件 emitter.off(event, callback): 取消订阅事件 4、Vuex / Pinia(状态管理)全局状态管理 5、v-model 双向绑定 3.4 版本之前使用 defineProps({firstName: String, lastName: String}) defineEmits(['update:firstName', 'update:lastName']) <template> <input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)"/> <input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)"/> </template> 3.4 版本之后使用 defineModel <script setup> const firstName = defineModel('firstName') const lastName = defineModel('lastName') </script> <template> <input type="text" v-model="firstName" /> <input type="text" v-model="lastName" /> </template> 父子组件通信:props/emit vue的基础设计,保持单向数据流 兄弟组件通信:逻辑简单通过父组件做状态提升,逻辑复杂的话可以采用数据共享处理pina vuex 跨多层嵌套的组件:可以采用依赖注入 完全不相关的全局模块:采用pina统一管理,事件总线,vue3已经不建议使用 总之:优先使用props/emit 其次用provide/inject 复杂状态使用pina,避免使用事件总线vue 组件定义,data为什么必须是函数
当一个组件被定义,data必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。 如果data是一个纯粹的对象,则所有实例都将共享引用同一个数据对象。当你修改其中一个实例的数据时,这个改变会影响所有的实例。通过提供data函数,每次创建一个新实例后,我们都会调用data函数,就会返回初始数据的一个全新副本数据对象。v-if 和 v-show 的区别
相同点:都是用于条件性的渲染一块内容,这块内容只有在表达式返回值为真值时渲染 不同点: 1、v-if 在切换时,条件区块内的事件监听器和子组件都会被销毁与重建 2、v-if 也是惰性的,如果在初次渲染时条件值为 false,则不会做任何事。只有当条件首次变为 true 时才被渲染 3、v-show 无论初始条件如何,始终会被渲染,只是通过 CSS display 属性会切换隐藏与显示 4、v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适 5、v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用 6、v-if 可以在<template> 元素上使用,而且最后渲染的结果不包含<template> 元素v-if 和 v-for为啥不能一起使用
同时使用 v-if 和 v-for 是不推荐的,因为这样二者的优先级不明显 vue2: 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级 vue3: 当 v-if 和 v-for 一起使用时,v-if 具有比 v-for 更高的优先级 vue2中的执行逻辑: 先遍历整个list数据 对每一项数据都做v-if判断 即使最终只渲染部分满足条件的数据,也会先遍历全部 vue3中的执行逻辑 v-if的优先级高,但是依然会在v-for的作用域内使用 同样存在性能问题,且容易产生逻辑混乱 正确的解决方案: 1、使用计算属性过滤出满足条件的数据,同时计算属性有缓存,数据不变化时不会重新计算 2、可以使用 v-for 外层包裹template (Vue 2/3 通用)自定义组件的v-model
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。 // 父组件 <template> <div> <CustomInput v-model="message" /> <p>Message: {{ message }}</p> </div> </template> <script> import CustomInput from '../components/CustomInput.vue'; export default { components: { CustomInput }, data() { return { message: '' }; } } </script> //子组件 <template> <div> <input :value="value" @input="updateValue($event.target.value)" /> </div> </template> <script> export default { props: ['value'], methods: { updateValue(value) { this.$emit('input', value); } } } </script>导航守卫
导航守卫分为三类:全局的, 单个路由独享的, 组件级的 一、全局守卫 1、全局前置守卫 beforeEach const router = createRouter({ ... }) // to: 即将要进入的目标,from: 当前导航正要离开的路由, // next: 用该方法来 resolve 这个钩子,进行管道中的下一个钩子 router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() }) 2、全局解析守卫 beforeResolve router.beforeResolve 和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件 内守卫和异步路由组件被解析之后,解析守卫就被调用 3、全局后置钩子 afterEach 和守卫不同的是,这些些钩子不会接受 next 函数也不会改变导航本身 router.afterEach((to, from) => { // ... }) 它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用 二、路由独享的守卫 beforeEnter 可以在路由配置上直接定义 beforeEnter 守卫,只在进入路由时触发 const router = new VueRouter({ routes: [{ path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ...} }]}) 三、组件内的守卫 可以在路由组件内直接定义以下路由导航守卫 beforeRouteEnter beforeRouteUpdate (2.2 新增) beforeRouteLeave const Foo = { template: `...`, beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 // 可以通过传一个回调给 next来访问组件实例 // 导航被确认的时候执行回调,并且把组件实例作为回调方法的参数 next(vm => { // 通过 `vm` 访问组件实例 }) }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } }vue2 和 vue3 响应式原理的区别(Object.defineProperty() 和 proxy)
1、Object.defineProperty 是属性拦截,Proxy 是对象代理 2、vue2需要递归遍历对象上的所有属性,单独劫持进行依赖收集和触发更新。vue3直接代理 整个对象,实现全功能响应式 3、vue2 不能监听对象属性的新增、删除,数组通过下标操作和修改length长度的操作,vue3 不存在这些问题 4、vue2初始化需要遍历所有属性,深度越大对象开销越大,vue3 初始化时只代理顶层对象,懒代理子对象,只有在属性被访问时,才代理子对象Vue 中diff算法
diff算法:是一种对比新旧虚拟dom树,找出最小差异并只更新变化部分的算法。它的目标是 用最少的dom操作来更新视图 为什么需要diff 如果每次数据变化,都要重新渲染整个页面,性能比较差 diff算法让vue可以复用已有的dom节点,只更新变化的部分 diff算法的核心逻辑 当数据变化重新触发组件渲染时,vue会 1、生成新的虚拟dom树 2、与旧的虚拟dom树进行diff比较 3、根据差异打补丁,更新真是dom diff算法的核心策略 1、同层比较:只比较同一层级节点,跨层不比较 2、类型判断:类型不同,直接替换整个节点 3、key复用:通过key标识节点,尽可能复用已有节点 vue2的实现:采用双端比较(头头、头尾、尾尾、尾头)高效处理列表更新 vue3的优化: 1、静态提升:静态节点只创建一次,复用即可 2、补丁标志:编译标记动态属性,更新时只对比标记的部分 3、最长递增子序列:优化列表移动操作 这些优化让 Vue 3 的 diff 性能比 Vue 2 提升了约 50% React 的 diff 差异:React 也是同层比较,但采用仅向前比较,没有 Vue 的双端比较 虚拟dom:即一个纯 JavaScript 的对象,它代表着一个 <div> 元素,包含我们创建实际 元素需要的所有信息 const vnode = { type: 'div', props: { id: 'hello' }, children: [ /* 更多 vnode */ ] }Vue2和Vue3的区别
1、数据响应式原理发生改变 vue2采用Object.defineProperty(), vue3 采用proxy对属性操作进行拦截。vue2不能 检测对象属性新增、删除,数组通过下标修改数组项、数组长度改变的操作,vue3 不存在这些问题。 2、vue3支持碎片(Fragments), 即可以拥有多个根节点,vue2只能拥有一个。 3、vue3支持组合式api Composition API 4、生命周期钩子函数不同,具体参考(20 Vue生命周期钩子函数) 5、vue2 把数据存放在data中,vue3放在setup 函数中 6、setup函数只能是同步的,不能是异步的虚拟DOM是什么
虚拟 DOM (Virtual DOM,简称 VDOM) 是一种编程概念,意为将目标所需要的ui通过数据结构"虚拟"的表示出来,保存在内存中,然后将真实的DOM与之保持同步。 与其说虚拟 DOM 是一种具体的技术,不如说是一种模式,所以并没有一个标准的实现。我们可以用一个简单的例子来说明: const vnode = { type: 'div', props: { id: 'hello' }, children: [ /* 更多 vnode */ ] } 这里所说的 vnode 即一个纯 JavaScript 的对象 (一个“虚拟节点”),它代表着一个 <div> 元素。它包含我们创建实际元素所需的所有信息如标签名、属性、子节点信息,这使它成为虚拟 DOM 树的根节点。vue插槽有哪些
1、具名插槽 #default 插槽定义name 属性 2、条件插槽 $slots.header 判断 3、作用域插槽 v-slot="slotProps" 或者解构 #default="{ message }vue性能优化
1. 代码优化:使用组件懒加载减少初始包体积,合理代码分割,利用 Tree Shaking 移除未使用代码 1.组件懒加载 const LazyComponent = defineAsyncComponent(() => import('./components/HeavyComponent.vue')) 2.路由懒加载 const routes = [{ path: '/dashboard', component: () => import('./views/Dashboard.vue') // 按需加载 }] 2. 组件优化:合理选择 v-if 和 v-show,优化列表渲染,使用 computed 缓存计算结果 1.始终为 v-for 提供唯一 key <ListItem v-for="item in items" :key="item.id" /> 2.长列表使用虚拟滚动处理 3.使用computed缓存结果 4.使用v-show代替频繁切换的v-if 5.大型静态数据可以使用 响应式容器+数据冻结 const list = ref([]) list.value = Object.freeze(data) // 跳过不必要的响应式转换 6.使用 watchEffect 替代深度 watch 3. 构建优化:配置压缩、分包、Gzip 压缩,优化图片等静态资源 1.按需引入UI组件库 import { ElButton, ElInput } from 'element-plus' 2.压缩配置、制定分包策略 build:{ minify: "terser", // 默认esbuild 可以删除console、debugger terserOptions: { compress: { drop_console: process.env.NODE_ENV === 'production', drop_debugger: true } }, // Rollup 打包选项、分包策略 rollupOptions: { output: { manualChunks: { // 将 Vue 的核心库打包成一个单独的 chunk 'vue-vendor': ['vue', 'vue-router', 'pinia'], // 将 Element Plus 单独拆分 'element-plus': ['element-plus'], // 将工具库单独拆分 'utils': ['lodash-es', 'axios'], }, }, }, } 3.图片优化,assetsInlineLimit 4kb以下转为base64 4. 网络优化:利用浏览器缓存、CDN 加速、预加载关键资源 5. 运行时优化:及时清理事件监听器,使用防抖节流,复杂计算使用 Web Worker 1.组件销毁,及时清理定时器和事件监听器 2.使用防抖节流 6. 监控优化:建立性能监控体系,及时发现和修复性能问题watch 和 watchEffect 的区别
1、依赖追踪方式不同 watch: 需要明确指定监听源 watchEffect: 自动追踪函数内的响应式依赖 2、立即执行行为 watch: 默认不会立即执行,需要配置属性 watchEffect: 默认会立即执行一次 3、获取旧值的能力 watch: 可以获取旧值和新值 watchEffect: 只能获取当前值,每次执行前清理上一次的副作用如果用户反馈页面加载慢,你会如何排查和优化
1、首先第一步复现问题,观察象限 2、使用 Lighthouse 和 Performance 进行页面性能分析,根据分析结果进行优化 体积过大: 代码分割、按需加载、移除未使用的代码 请求过多: 请求合并、配置缓存 渲染卡顿: 优化列表渲染,减少不必要的列表渲染ref 和 reactive 的区别和使用场景
1、数据类型支持 ref 可包装任意类型(基本类型:数字、字符串、布尔值;对象类型:对象、数组) reactive 仅支持对象或数组,无法处理基本类型 2、访问与修改方式 ref 必须通过 .value 读写(如 count.value++),但在模板中自动解包(无需 .value) reactive 直接操作属性(如 state.count++),无需额外语法 3、重新赋值行为 ref 允许整体替换(如 refObj.value = { new: 1 }),保持响应性 reactive 禁止整体替换(如 reactiveObj = { new: 1 } 会丢失响应性),只能修改内部属性 如果需要替换整个对象需要使用 Object.assign(refObj, {new: 1}) 4、实现原理。 ref 对基本类型用轻量劫持,对对象内部调用 reactive 的 Proxy reactive 基于 Proxy 深度代理整个对象,自动追踪嵌套属性变化如何实现一个权限管理系统
前端权限控制可以分为4个方面: 接口权限、按钮权限、菜单权限、路由权限 1、接口权限: 创建菜单,API接口与菜单ID关联,接口请求先通过auth鉴权,鉴权失败返回错误码,前端根据 错误码进行业务处理 2、按钮权限: 可以使用 v-if + hooks 和 自定义指令实现 自定义指令核心代码 <button v-permission="'user:add'">添加用户</button> if (!userStore.hasPermission(value)) { // 1. 直接移除元素 el.parentNode?.removeChild(el) // 或 2. 禁用元素 // el.disabled = true // el.classList.add('disabled') // 或 3. 隐藏元素 // el.style.display = 'none' } hooks核心代码 // usePermission 文件 const hasPermission = (permission: string) => { return userStore.hasPermission(permission)} <button v-if="canAddUser.value" @click="addUser"> // 方式1:基础权限检查 const { hasPermission, checkPermissions, filterByPermission } = usePermission() // 检查单个权限 const canAddUser = hasPermission('user:add') 3、菜单权限: 菜单数据由后端返回,后端根据用户权限和角色权限返回对应的菜单数据,前端把单数据处理成 对应的路由数据 如: router.addRoute(routes) //routes 处理后的路由数据 4、路由权限: 初始化加载全部路由,并且在路由上标记相应的权限信息,每次路由跳转前做校验vue中有哪些内置指令
vue内置的指令,大概有10几个,我按照共说一下 1、条件渲染:v-if v-else-if v-else v-show, 其中v-if是真正的条件渲染,v-show只是css切换 适合于频繁切换的场景 2、列表循环:v-for 通常配合 :key使用, 用于性能优化 3、属性绑定:v-bind v-model, v-bind 用于动态属性绑定 v-model 用于表单双向绑定 4、事件绑定:v-on 支持 .stop .noce .prevent 等修饰符 5、内容渲染:v-text v-html v-html 可以渲染html,但是要注意 xss 风险 6、内容分发:v-slot 7、优化类 v-once 只渲染一次 v-pre 跳过编译 v-cloak 配合css解决模板闪烁的问题 v-model的本质是 :value 和 @input 的语法糖(vue2)或者 :modelValue 和 @update:modelValue(Vue 3)v-show 算不算重排
v-show的本质是切换 display: none 的值,是否发生重排取决于元素是否占据文档流中的空间 如果元素是块级元素且处于正常的文档流空间,隐藏时其他元素会填补他的位置,这会发生重排。 如果元素是绝对定位或者脱离文档流,则不会影响其他元素的布局,不会触发重排,只会触发重绘。 但无论是否重排,v-if会直接删除/新增dom元素,一定会触发重排。所以对于频繁切换的场景 即使v-show可能会触发重排,也比v-if性能更好,因为它避免了dom的重建和销毁 触发重排的操作:元素的位置、尺寸、布局发生改变1
1
1
1
1
1
1
1
