vue 相关
v-if 与 v-for可以一起使用吗?
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,所以不推荐 v-if 和 v-for 同时使用
1 2 3 4 5 6 7 8 9 10 11
| // 推荐写法 <ul v-if="shouldShowUsers"> <li v-for="user in users" :key="user.id"> {{ user.name }} </li> </ul>
// 并不是任何时候都不推荐,想渲染特定节点时,如下代码渲染未完成的todolist <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
|
watch 和 computed 区别
computed 会创建新的响应式数据。并且具有可缓存,可依赖多个属性等特点。
watch 是响应式数据的自定义侦听器,用于监听响应数据的变化。
ps:
- 如果一个数据依赖于其他数据,那么把这个数据设计为computed的
- 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
watch deep属性实现原理
vue会一层层遍历,给这个对象的所有属性都加上这个监听器。但是这样性能开销会比较大,修改任何一个属性,都会出发这个监听器里的handler.
keep-alive刷新前一个页面
使用keep-alive时,再次进入了缓存页面会走以下生命周期:beforeRouteEnter –>activated –> deactivated,
所以如果需要在进入页面时刷新数据就在activated 里面做操作
vue按钮级别鉴权
- 储存权限数据
- 自定义指令传入当前权限的参数
1 2
| // 示例代码 <el-button type="primary" v-has="'line_add'">新增</el-button>
|
- 遍历对比权限数据中字段是否与标签中的自定义参数line_add相等,匹配成功则表明有权限,失败没有权限,返回false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function permissionJudge(value) { let list = store.getters.getMenuBtnList; for (let item of list) { if (item.permission === value) { return true; } } return false; }
Vue.directive('has', { bind: function (el, binding) { if (permissionJudge(binding.value)) { el.parentNode.removeChild(el); } } });
|
vuex多个模块之间action如何调用
1 2 3
|
dispatch(“tagsView/delAllViews”, {}, { root: true });
|
vue中data为什么是函数
因为组件是用来复用的,且js里对象是引用关系,如果组件中data是一个对象,那么作用域没有隔离,组件中的data属性值会相互影响。
组件中data是函数的话,每个实例可以维护一份独立的拷贝,组件实例之间的data属性值不会互相影响;
new Vue的实例,是不会被复用的,因此不存在引用对象的问题。
vue二次封装组件,参数如何传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <el-select v-bind="$props" v-on="$listeners"></el-select> <div>{{myProps}}</div> </div> </template> <script> import {Select} from 'element-ui'
export default { name: 'my-select', props: { ...Select.props, myProps: String } } </script>
|
vue2和vue3的区别
- 双向数据绑定原理不同,vue2是使用Object.definePropert(),vue3是使用ES6的Proxy API对数据代理。
- 语法不同,vue2使用选项类型api,选项型api在代码里分割了不同的属性:data,computed,methods等。vue3使用组合式api,相比于旧的api使用属性来分组,这样代码会更加简便和整洁。
- 定义数据变量和方法不同,vue2是把数据放入data中。vue3使用ref或者reactive,在setup()方法来返回我们的反应性数据
- 生命周期钩子函数不同,vue3删除了beforeCreate、created取代的是setup
什么是数据双向绑定
vue中双向绑定是一个指令v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v-model是语法糖,默认情况下相当于:value和@input。
Vue3为什么要用 Proxy API 替代 defineProperty API ?
object.defineProperty有下面几个缺陷
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
v-for中key的作用以及实现原理
vue组件高度复用,增加Key可以标识组件的唯一性,可以更高效的更新虚拟DOM
原理:
key是vue在对比过程中判断两个节点是否是同一结点的必要条件。如果不设置key则a.key === b.key
一直为true,vue内部会认为比较的两个节点是相同的节点,只能去做更新操作,这造成了大量的dom更新操作。
1 2 3 4 5 6 7 8 9 10 11
| function sameVnode(a, b) { return ( a.key === b.key && a.asyncFactory === b.asyncFactory && ((a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b)) || (isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error))) ) }
|
vue路由获取参数
1 2 3 4 5 6 7 8 9 10
| <router-link to="/about/123">About</router-link>
// 获取参数 this.$route.params
<router-link :to="{path:'/about',query:{num:123}}">About</router-link>
// 获取参数 this.$route.query.num
|
diff原理
diff就是调用patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。
- 首先看有没有新旧节点,没有新节点,直接触发旧节点的destory钩子;没有旧节点,直接用新节点生成dom元素
- 如果有新旧节点,判断两节点是否一样,一样执行patchVnode方法;否则直接销毁旧节点,根据新节点生成dom元素
patchVnode部分
- 新节点是否是文本节点,如果是,则直接更新dom的文本内容为新节点的文本内容
- 新节点和旧节点如果都有子节点,则处理比较更新子节点
- 只有新节点有子节点,旧节点没有,那么不用比较了,所有节点都是全新的,所以直接全部新建就好了,新建是指创建出所有新DOM,并且添加进父节点
- 只有旧节点有子节点而新节点没有,说明更新后的页面,旧节点全部都不见了,那么要做的,就是把所有的旧节点删除,也就是直接把DOM 删除
nextTick原理和作用
如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()
vue中scope和slot-scope区别
这两个其实都是被废除的标签了,vue官方推荐使用 v-slot
这是用来在指定的位置输出我们的子元素的标签
vue模版data属性里面一个对象,给该对象新增属性,视图没有更新,如何解决
1 2
| this.$set(this.obj, 'a', '呆呆');
|
实现一个双向绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <body> <span></span> <input type="text"> </body> <script> const span = document.querySelector('span'); const input = document.querySelector('input');
let obj = {}; Object.defineProperty(obj, 'inputText', { enumerable: true, configurable: true, get() { console.log('被读取了') }, set(newValue) { input.value = newValue; span.innerHTML = newValue; console.log('数据更新了') } })
input.addEventListener('keyup', function (e) { obj.inputText = e.target.value; }) </script>
|
vue的生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| beforeCreate() {
}, created() {
}, beforeMount() {
}, mounted() {
}, beforeupdate() {
}, updated() {
}, beforeDestroy() {
}, destroyed() {
},
|
vue第一次加载页面,会触发哪几个生命周期
beforeCreate、created、beforeMount、mounted
写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
key 的特殊属性主要用在 Vue/React 的虚拟DOM算法,在新旧nodes对比时辨识VNodes
但是注意,不要用遍历时的index做key,容易引发奇怪的bug
说说你对 SPA 单页面的理解,它的优缺点分别是什么?
spa但页面应用是指:在页面初始化时加载相应的html、js、css,页面加载完成后,不会因为用户的操作而对页面进行跳转或重新加载
优点:用户体验好、前后端分离职责清晰
缺点:首次加载耗时长、seo难度大
如何seo:ssr服务器渲染
操作dom与操作数据优缺点(vue和jquery优缺点)
1.dom操作过多会影响页面性能
2.数据双向绑定在处理表单时更方便
3.组件化的开发模式更有利于项目维护
vue2.0 $router和$route的区别
1.$router是Vue的一个实例,里面有路由的很多关键的对象和属性(例如:this.$router.push、this.$router.go等)
2.$route对象表示当前的路由信息,包含当前的路径,参数,query对象等(例如:this.$route.query.id等)
$route.params和$route.query区别
1.params传参不会拼接在url上,query会
2.params刷新会丢失传参,query不会
vue路由传参的几种方式
第一种 直接传
1 2 3 4 5 6 7 8 9 10 11 12
| // 如果是多个参数 this.$router.push(`/uploadFileDetail/${row.id}${row.age}`) this.$router.push(`/uploadFileDetail/${row.id}`);
// 这种形式路由文件需要配置一下,如果是多个参数 path: '/uploadFileDetail/:id:age' { path: '/uploadFileDetail/:id', component: resolve => require(['@/pages/details/uploadFileDetail'], resolve), name: '文件详情' }
// 取参数,this.$route.params是一个对象,参数以键值对形式储存 this.$route.params.id
|
第二种 query传参(传递的参数会拼接在url上)
1 2 3 4 5 6 7 8 9 10 11
| this.$router.push({ path: "/uploadFileDetail", query: { id: 1, name: "呆呆的", age: 18 } });
this.$route.query.id
|
第三种 params传参(params传参不会拼接在url上,刷新会丢失传参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| this.$router.push({ name: "文件详情", params: { id: 1, name: "呆呆的", age: 18 } });
{ path: '/uploadFileDetail', component: resolve => require(['@/pages/details/uploadFileDetail'], resolve), name: '文件详情' }
this.$route.params
|
history路由和hash路由区别
vuex的使用场景
1.解决页面之间复杂数据传输的问题
vue是双向数据流吗?为什么页面会实时更新?
Vue是单向数据流,不是双向绑定,Vue的双向绑定不过是语法糖,Object.definePropert是用来做响应式更新的
1 2 3
| <input v-model=“phoneInfo.phone”/> <input :value="PhoneInfo.phone" @input="val => { PhoneInfo.phone = val }"
|
描述下vuex的几个属性
state:vuex的基本数据,用来存储变量
getters:从基本数据(state)派生的数据,相当于state的计算属性
mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数)
action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作
modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
说说你对slot的理解有多少?slot使用场景有哪些?
通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理。
slot有三类,默认插槽、具名插槽、作用域插槽
默认插槽
1 2 3 4 5 6 7 8
| <FancyButton> </FancyButton>
// FancyButton组件内部 <button class="fancy-btn"> <slot></slot> </button>
|
具名插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <BaseLayout> <template #header> <h1>Here might be a page title</h1> </template>
<template #default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template>
<template #footer> <p>Here's some contact info</p> </template> </BaseLayout>
// BaseLayout组件内部 <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
|
作用域插槽,可以通过子组件绑定数据传递给父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <MyComponent v-slot="slotProps"> {{ slotProps.text }} {{ slotProps.count }} </MyComponent>
// 组件内部 <script setup> const greetingMessage = 'hello' </script>
<template> <div> <slot :text="greetingMessage" :count="1"></slot> </div> </template>
|
Vue中的自定义指令是如何工作的?描述一下Vue中自定义指令的注册和使用过程
在Vue中,可以通过Vue.directive方法来创建自定义指令。自定义指令可以用于扩展Vue的功能,实现特定的DOM操作或交互行为。
1 2 3 4 5 6 7
| // 自定义指令的生命周期钩子
bind:在指令绑定到元素时调用,只调用一次。 inserted:在被绑定元素插入到父节点时调用。 update:在组件更新时调用,可能会触发多次。 componentUpdated:在组件及其子组件更新完成后调用。 unbind:在指令从元素上解绑时调用。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| // 在Vue实例中定义自定义指令 Vue.directive('my-directive', { // 指令生命周期钩子 bind(el, binding, vnode) { // 在指令绑定到元素时调用,只调用一次 // el:指令所绑定的元素 // binding:指令的绑定信息,包括值、修饰符等 // vnode:Vue编译生成的虚拟节点
// 指令绑定时的初始化操作 el.textContent = 'Initialized!'; },
inserted(el, binding, vnode) { // 在被绑定元素插入到父节点时调用 // 只保证父节点存在,但不一定已经插入到文档中
// 执行插入后的操作 el.textContent = 'Inserted!'; },
update(el, binding, vnode, oldVnode) { // 在组件更新时调用,可能会触发多次
// 执行更新操作 el.textContent = 'Updated!'; },
componentUpdated(el, binding, vnode, oldVnode) { // 在组件及其子组件更新完成后调用
// 执行更新完成后的操作 el.textContent = 'Component Updated!'; },
unbind(el, binding, vnode) { // 在指令从元素上解绑时调用
// 执行解绑操作 el.textContent = 'Unbind!'; } });
|