【前端面试题——vue3篇】

1.v-show和v-if指令的共同点和不同点

当条件改变的时候v-if是通过DOM节点的添加和删除来控制节点的显示隐藏的,
v-show是通过css样式控制节点的显示隐藏的。
v-if的切换成本比较高,v-show渲染成本比较高,
所以当切换频繁的时候使用v-show,如果初次渲染数据较多的时候,使用v-if

2.vue组件中data为什么必须是一个函数

vue组件可能存在多个实例,如果使用对象形式定义data,可能会导致它们共用一个data对象,那么状态变更会影响所有组件实例,这是不合理的采用函数的形式定义,在initData时会将作为工厂函数返回全新的data对象,有效避免了多个实例之间状态污染的问题。而在vue根实例创建过程中不存在这个限制,也是因为根实例只能有一个,不需要担心这种情况。

如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据)
Object是引用数据类型,里面保存的是内存地址,单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果

3.$nextTick的使用

vue的$nextTick其本质是对js执行原理EventLoop的一种应用。
$nextTick的核心是利用promise,mutationObserver、setImmediate、setTimeout的原生
javascript方法来模拟对应的微/宏任务的实现,本质是为了利用JavaScript的这些异步回调任务队列来实现vue框架中自己的异步回调队列。
nextTick是典型的将底层的javascript执行原理应用到具体案例中的示例,引入异步更新队列机制的原因:

  • 如果是同步更新,则多次对一个或者多个属性赋值,会频繁触发dom的渲染,可以减少一些无用的渲染。
  • 同时由于virtualDom的引入,每一次状态发生变化之后,状态变化的信号会发送给组件,组件内部使用virtualDom进行计算得出需要更新的具体的dom节点,然后对dom进行更新操作,每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要。
  • vue采用了数据驱动的思想,但是在一些情况下,仍然需要操作dom。有时候,可能遇到这样的情况,dom1的数据发生了变化,而dom2需要从dom1中获取数据,那这时候就会发现dom2的试图并没有更新,这时就需要用到nextTick了。

4.Vue中双向数据绑定是如何实现的

vue2.0
vue2.0的双向数据绑定是通过数据劫持,结合发布订阅的方式来实现的,也就是说数据和视图同步,数据发生变化,视图也会发生变化,视图变化,数据也会随之发生改变。vue2的双向数据绑定核心是object.definedproperty()方法。给vue中的数据绑定get和set方法,当获取数据的时候,调用get方法,当修改数据的时候调用set方法。通过watcher监听器去更新视图,完成数据的双向绑定。

vue3.0
Object.defineProperty 的问题:在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。目前只针对以上方法做了hack处理,所以数组属性是检测不到的,有局限性Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。
Proxy的两个优点:可以劫持整个对象,并返回一个新对象,有13种劫持。

5.v-if和v-for的优先级

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染。
v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组或者对象,而 item 则是被迭代的数组元素的别名。
在 v-for 的时候,建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化。
在vue中,永远不要把v-if和v-for同时用在同一个元素上,会带来性能方面的浪费。
vue 2.x:当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级
vue 3.x:当 v-if 与 v-for 一起使用时,v-if 具有比 v-for 更高的优先级。

6.vue常用的修饰符

事件修饰符:
stop(阻止事件冒泡)
prevent(阻止默认行为)
self(只触发本身)
once(只触发一次)
captrue(事件捕获,与事件冒泡的方向相反,事件捕获由外向内。)
表单修饰符:
lazy(当光标离开标签之后,才会赋值给value)
trim(过滤掉两边的空格)
number(自动将用户输入的值转化为数值类型。)
键盘修饰符:
普通键(enter、tab、delete、space、esc、up…)
系统修饰键(ctrl、alt、meta、shift…)
鼠标修饰符:lefft,right,middle

7.自定义指令详解

除了我们常用的核心的默认内置的指令像是v-model,v-show,v-if等,vue也允许我们自定义指令,有的情况下,对普通dom元素进行底层操作,这个时候就需要自定义指令。

自定义指令时用来操作dom的,尽管vue推崇的是数据驱动视图的理念,但并不是所有的情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于任何dom操作,而且是可复用的。

自定义指令分为两种:一种是自定义的局部指令,一种是全局指令

8.vue slot

slot又名插槽,是vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而在一个标签元素是否显示,以及怎么显示是父组件决定的。
slot又分为三类,默认插槽,具名插槽,作用域插槽。

  • 默认插槽:又称匿名插槽,当slot没有指定name属性值的时候,一个组件内只能有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽,剧名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件传递过来的数据决定如何渲染该插槽。

9.vue初始化页面闪动问题

当网速较慢,vue.js文件还没有加载完时,在页面上会显示{{message}}的字样,知道vue创建实例,编译模板时,dom才会被替换,所以这个过程屏幕是闪动的。
所以解决这个问题,需要在style样式中设置【v-cloak】{display:none}。在一般情况下,v-clock是一个解决初始化慢导致页面闪动的最佳实践,对于简单的项目很实用。
但是在具有工程化的项目里,比如使用了webpack和vue-router的项目中,html结构只是一个空的div元素,剩余的内容都是由路由去挂载不同的组件完成的,所以不需要v-cloak。

10.vue更新数组时触发视图更新的方法

  • vue.set相应式新增与修改数据可以设置对象或数组的值,通过key或数组索引,可以触发视图更新。

  • vue.delete,删除对象或数组中元素,通过key或者数组索引,可以触发视图更新。

  • 数组对象直接修改属性,可以触发视图更新

  • 数组整体修改,可以触发视图更新

Vue包含一组观察数组变异的方法,使用它们改变数组也会触发视图更新不变异的方法:

    • push() 向数组的末尾添加一个或多个元素,并返回新的长度。
    • pop() 删除最后一个元素,把数组长度减 1,并且返回它删除的元素的值。
    • shift() 把数组的第一个元素从其中删除,并返回第一个元素的值。
    • unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
    • splice() 向/从数组中添加/删除项目,然后返回被删除的项目。 该方法会改变原始数组。
    • sort() 对数组的元素进行排序。
    • reverse() 颠倒数组中元素的顺序。

不变异的方法:

  • filter()
  • concat()
  • slice()
    他们返回的是一个新数组,使用这些方法时,可以用新数组来替换原始数组

11.Vue2.0 和 Vue3.0 有什么区别

  • 性能:Vue 3 比 Vue 2 更快,因为它采用了新的渲染引擎,这使得它在大型应用程序中更快。
  • 语法:Vue 3 使用了更简单的语法,并移除了一些 Vue 2 中的不常用功能,这使得代码更容易维护和阅读。
  • 设计:Vue 3 采用了更加模块化的设计,把各个组件的功能分离开,使得应用程序更加灵活和可扩展。
  • TypeScript 支持:Vue 3 原生支持 TypeScript,可以更轻松地与其他 TypeScript 项目集成。
  • ***position API:Vue 3 引入了 ***position API
  • Vue2 在模板中如果使用多个根节点时会报错,
    Vue3 支持多个根节点,也就是 fragment

12.Vue3带来了什么改变

vue3.0对vue的主要3个特点:响应式、模板、对象式的组件声明方式,进行了全面的更改,底层的实现和上层的api都有了明显的变化,基于Proxy重新实现了响应式,基于treeshaking内置了更多功能,提供了类式的组件声明方式。而且源码全部用typescript重写

13.reactive与ref的区别

  • 从定义数据角度对比:
    ref用来定义:基本类型数据
    reactive用来定义对象(或数组)类型数据
    备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。

  • 从原理角度对比:
    ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
    reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。

  • 从使用角度对比:
    ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
    reactive定义的数据:操作数据与读取数据:均不需要.value。

14.计算属性和watch以及methods的区别

  • 作用机制上:
    watch 和 ***puted 都是以 Vue 的依赖追踪机制为基础的,当某一个数据发生变化的时候,
    所有依赖这个数据的相关数据,自动发生变化,也就是自动调用相关的函数去实现数据的变动
    methods 里面是用来定义函数的,它需要手动调用才能执行。而不像 watch 和 ***puted 那样,
    “自动执行”预先定义的函数,相比于 watch / ***pute;methods 不处理数据逻辑关系,只提供可调用的函数,类似储存函数的一个库。
  • 从性质上
    methods里面定义的是函数,你显然需要像"fuc()"这样去调用它(假设函数为fuc)。
    ***puted是计算属性,事实上和和data对象里的数据属性是同一类的(使用上)。
    watch:类似于监听机制+事件机制,回调函数自动调用。

15.$route 和 $router 的区别

router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,包含了所有的路由包含了许多关键的对象和属性。例如history对象

route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等

16.v-model的使用

表单控件在实际开发中是非常常见的,特别是对于用户信息的提交,需要大量的表单

Vue中使用v-model指令来实现表单元素和数据的双向绑定

  • 案例的解析:

    • 当我们在输入框输入内容时
    • 因为input中v-model绑定了message,所以会实时将输出的呢容传递给message,message发生改变。
    • 当message发生改变时,因为上面我们使用Mustache语法,将message的值参入到DOMZ中,所以DOM会发生相应的变化
    • 所以通过v-model实现了双向绑定

17.v-on可以监听多个方法吗

可以

 <div class="pa">
 <button v-on='
 {
     click:dianji,
     focus:focu,
     blur:blu
 }'>
监听多个方法的写法
</button>
 </div>

18.vue中过滤器有什么作用及详解

过滤器分为全局过滤器和局部过滤器,
全局过滤器可以在任何组件调用,局部过滤器只能在当前组件调用,
过滤器使用|管道符调用。可以串联使用。

19.EventBus注册在全局上时路由切换时会重复触发事件,如何解决

建议在created里注册,在beforeDestory移出
在组件内的beforeRouteLeave中移除事件监听

20.vuex有哪几种属性

  • state
    state:state类似容器,包含应用的大部分状态
    一个页面只能有一个state
    状态存储是响应式的
    不能直接改变 state 中的状态,唯一途径显式地提交mutations
  • mutations
    mutations:更改store中的状态唯一方法就是提交mutation,它是同步的,每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。更改 Vuex 的 state 中的状态的唯一方法是提交 mutation。回调函数可以接受两个参数,第一个参数是state状态,第二个参数是 mutation 的载荷就是外面store.***mit 传入额外的参数。
    mutations其实就相当于我们vue里面的methods,也是定义方法的,只不过这个方法可以在多个组件调用就是了。
    使用mutations中的方法格式: this.$store.***mit(‘名称’)
  • getters
    getters:相当于state的计算属性,从state派生出一些状态
  • actions
    actions:Action 类似于 mutation,不同在于:
    action 提交的是 mutation,而不是直接变更状态;
    action可以进行任何异步操作,mutation不可以;
  • modules
    modules:模块化管理,有多个state可以通过模块化import到index.js的module属性中,可直接进行调用。参考vue的组件化思想。

21.请详细说下你对vue生命周期的理解

vue2.0生命周期:
创建阶段
beforeCreate()创建前阶段,这个时候还不能使用data中的数据。
created()创建完成 最早可以使用data中的数据
挂载阶段
beforeMount:在挂载开始之前被调用: 相关的 render 函数首次被调用
mounted: 挂载完成,DOM节点挂载到实例上去之后调用该钩子
更新阶段
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
updated: 数据更新完成并且DOM更新完成后调用销
销毁阶段
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用
destroved:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定

vue3.0

  • 去掉了vue2.0中的 beforeCreate 和 created 两个阶段,同样的新增了一个 setup
  • beforeMount 挂载之前 改名 onBeforeMount
  • mounted 挂载之后 改名 onMounted
  • beforeUpdate 数据更新之前 改名 onBeforeUpdate
  • updated 数据更新之后 改名 onUpdated
  • beforeDestroy 销毁前 改名 onBeforeUnmount,可在此进行清除定时器,进行事件监听,发布订阅。
  • destoryed 销毁后 改名 onUnmounted
  • errorCaptured 报错 改名 onErrorCaptured
    总结
    不难发现3.0中钩子函数最大的变化就是setup代替了2.0中的beforeCreate和created,而对应其它几个函数来说都是名称上发生了一些变化,其功能基本上还是一样的。

22.vue优点

  • vue作为一款轻量级框架,门槛低,上手快,简单易学。
  • vue可以进行组件化开发,数据与结构相分离,使代码量减少,从而提升开发效率,易于理解
  • vue最突出的优势在于对数据进行双向绑定,使用虚拟DOM
  • 相较于传统页面通过超链接实现页面跳转,vue会使用路由跳转不会刷新页面
  • vue是单页面应用,页面局部刷新,不用每次跳转都请求数据,加快了访问速度,提升了用户体验

23.单页面应用和多页面应用区别及优缺点

单页应用 页面跳转---->js渲染
优点:页面切换快 缺点:首屏加载稍慢,seo差

多页应用 页面跳转---->返回html
优点:首屏时间快,seo效果好 缺点:页面切换慢

24.vue路由跳转传参的方式有哪些

在template中可以使用标签实现跳转。

  • router-link 标签直接拼接参数实现跳转
<router-link to="/t?index=1">
     <button class="btn btn-default">点击跳转</button>
</router-link>

  • router-link 标签使用to属性结合query实现跳转,里面是参数字典
<router-link :to="{path:'/t',query: {index: 1}}">
     <button class="btn btn-default">点击跳转</button>
</router-link>

  • this.$router.push() (@click函数里面调用)
this.$router.push({path: '/路径?参数=1'})
接收参数
this.$route.query.index

  • query和 params区别
    • query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传,密码之类还是用params刷新页面id还在
    • params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
  • VUE几种路由跳转几种方式的区别
    • this.$router.push:跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面

    • this.$router.replace:跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)

    • this.$router.go(n):向前或者向后跳转n个页面,n可为正整数或负整数

25.vue遇到的坑,如何解决的

axios封装和api接口的统一管理

  • axios的封装,主要是用来帮我们进行请求的拦截和响应的拦截。
  • 在请求的拦截中我们可以携带userToken,post请求头、qs对post提交数据的序列化等。
  • 在响应的拦截中,我们可以进行根据状态码来进行错误的统一处理等等。
  • axios接口的统一管理,是做项目时必须的流程。这样可以方便我们管理我们的接口,在接口更新时我们不必再返回到我们的业务代码中去修改接口。

26.vue封装通用组件方法思路

点击查看

27.Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中

如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用。

28.Vuex中如何异步修改状态

actions和之前讲的Mutations功能基本一样,不同点是,actions是异步的改变state状态,而Mutations是同步改变状态(会互相干扰).
actions 里面放我们函数,函数里面的参数context ,可以帮我们提交的mutations。主要逻辑其实就是在组件内部,调用actions之后,actions里面又去调用mutations,而mutations又去修改了state。

    mutations:{
      increate(state){ //payload:携带参数
         state.counter++
      }
    },
    actions:{
       //放函数,
        increateAction(context){
            context.***mit("increate")
        }
    }

import {useStore} from 'vuex'
import {onMounted} from "vue"
export default {
setup(){
    const store=useStore()
    onMounted(() => {
        store.dispatch("getHomeMutidata")
    }),
    return{
 
    }
}

29.刷新浏览器后,Vuex的数据是否存在?如何解决

vuex的数据在刷新浏览器后不会保存,
这是因为vuex实例store里的数据保存在运行内存中,页面刷新会重载vue实例,导致store里的数据丢失
解决方法:

  • 下载持久化存储插件,例如:vuex-along,vuex-persist、vuex-persistedstate
  • 使用localStorage 或者 sessionStroage

30.vuex中的辅助函数怎么使用

  • mapState
    mapState 函数返回的是一个对象。通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 ***puted 属性。自从有了对象展开运算符,我们可以极大地简化写法:(使用扩展运算符)
  • mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性
  • 使用 mapMutations 辅助函数将组件中的 methods 映射为 store.***mit,其原理就是将this.montify 映射为this.$store.***mit(‘montify’)
  • 在组件中使用 this.$store.dispatch(‘prodect’) 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用

31.vue插槽?

slot又名插槽,是vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而在一个标签元素是否显示,以及怎么显示是父组件决定的。slot又分为三类,默认插槽,具名插槽,作用域插槽。
默认插槽:又称匿名插槽,当slot没有指定name属性值的时候,一个组件内只能有一个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
作用域插槽:默认插槽,剧名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,作用域插槽数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定,也就是说,作用域插槽的不同之处就在于,数据不在父组件身上,而是在子组件身上,且组件的结构和内容由父组件决定。作用域组件限定了组件内结构和数据的展示范围,以便在开发中我们可以根据一个组件而不断变换其中的内容和结构。

32.vue3中如何获取refs,dom对象的方式?

方式一:setup函数方法内,获取单个ref属性绑定的dom元素:

  • 先定义一个空的响应式数据ref定义的
  • setup中返回该数据,你想获取哪个dom元素,在该元素上使用ref属性绑定该数据即可
<template>
  <h1 ref="box">Ref属性获取dom</h1>
</template>
 
<script>
import { ref, onMounted } from 'vue'
export default {
    setup () {
        let box = ref(null)
        onMounted(function () {
            console.log(box.value);
        })
        return {
            box
        }
    }
}
</script>

方式二:获取多个ref属性绑定的dom元素。

  • 使用ref绑定一个函数,在函数里把dom添加到数组里面
<template>
  <h3 :ref="getlist" v-for="index in 3">我是一组元素{{index}}</h3>
</template>
 
<script>
import { ref, onMounted } from 'vue'
export default {
    name: 'Ref',
    setup () {
        // 获取v-for遍历的元素
        // 定义一个空数组,接收所有的LI
        // 定义一个函数,往空数组push Dom
        let listDom = []
        const getlist = (el) => {
            listDom.push(el)
        }
        console.log(listDom);
        return {
            getlist
        }
    }
}
</script>

总结

  • 单个元素: 先声明ref响应式数据,返回给模板使用,通过ref绑定数据

  • 遍历的元素:先定义一个空数组,定一个函数获取元素,返回给模板使用,通过ref绑定这个函数

33.vue3中生命周期的和vue2中的区别?

vue2.0生命周期:
创建阶段
beforeCreate()创建前阶段,这个时候还不能使用data中的数据。
created()创建完成 最早可以使用data中的数据
挂载阶段
beforeMount:在挂载开始之前被调用: 相关的 render 函数首次被调用
mounted: 挂载完成,DOM节点挂载到实例上去之后调用该钩子
更新阶段
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
updated: 数据更新完成并且DOM更新完成后调用销
销毁阶段
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用
destroved:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定

vue3.0

  • 去掉了vue2.0中的 beforeCreate 和 created 两个阶段,同样的新增了一个 setup
  • beforeMount 挂载之前 改名 onBeforeMount
  • mounted 挂载之后 改名 onMounted
  • beforeUpdate 数据更新之前 改名 onBeforeUpdate
  • updated 数据更新之后 改名 onUpdated
  • beforeDestroy 销毁前 改名 onBeforeUnmount,可在此进行清除定时器,进行事件监听,发布订阅。
  • destoryed 销毁后 改名 onUnmounted
  • errorCaptured 报错 改名 onErrorCaptured
    总结
    不难发现3.0中钩子函数最大的变化就是setup代替了2.0中的beforeCreate和created,而对应其它几个函数来说都是名称上发生了一些变化,其功能基本上还是一样的。

34.说说vue中的diff算法?

点击查看

35.说说 Vue 中 CSS scoped 的原理?

在vue文件中的style标签上,有一个特殊的属性:scoped。当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,也就是说,该样式只能适用于当前组件元素。通过该属性,可以使得组件之间的样式不互相污染。如果一个项目中的所有style标签全部加上了scoped,相当于实现了样式的模块化。

vue中的scoped属性的效果主要通过PostCSS转译实现

PostCSS给一个组件中的所有dom添加了一个独一无二的动态属性,然后,给CSS选择器额外添加一个对应的属性选择器来选择该组件中dom,这种做法使得样式只作用于含有该属性的dom——组件内部dom。

36.vue3中怎么设置全局变量?

在vue2中,我们知道vue2.x是使用Vue.prototype.xxxx = xxx 来定义全局变量,然后通过 this . xxxx=xxx来定义全局变量,然后通过this.xxxx=xxx来定义全局变量,然后通过this.xxx来获取全局变量。

但是在vue3中,这种方法显然不行了。因为vue3中在setup里面我们是无法获取到this的。
vue3.0依赖注入的方式注册使用全局变量
在 main.js 中

const app = createApp(App)
 
// 配置全局变量 页面中使用 inject 接收
app.provide('global',{
  store,
  axios
})
 
app.use()进行使用

采用这种方法在全局变量的创建上会更加的方便 , 而且不用担心会出现像axios在使用globalProperties设置为全局对象后丢失axios对象只剩 axios对象只剩axios对象只剩http之类的问题
vue3.0store仓库进行存放全局变量
创建store文件夹
store文件夹下创建index文件
state中定义状态或数据

const store = createStore({
    state () {
        return {
            base:"",//基本数据
        }
    },
    mutations: {
        /*修改基础数*/
        changeBase(state,base) {
            state.base=base;
        },
    
    }
})
export default store;

37.Vue中给对象添加新属性时,界面不刷新怎么办?

vue2的响应式原理使用的是对象代理去实现的,对象代理中有一个get和set方法,当我们访问对象的时候就会触发get方法,当我们对对象中的值进行修改时会触发set方法。但是当我们给对象添加一个新的属性时对象代理是检测不到的,所以就会出现直接给对象添加属性响应式不生效的问题。
所以在vue中可以使用this.$set(对象名,‘属性名’,属性值)的方法去给对象添加属性,或者使用Vue.set(对象名,‘属性名’,属性值)的方法进行添加,添加之后的属性就带有响应式了

38.谈谈对Vue中双向绑定的理解?

vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
VUE双向数据绑定,其核心是 Object.defineProperty()方法,给Vue中的数据绑定get和set方法,当获取数据的时候,调用get方法,修改data中的数据的时候调用set方法,通过watcher监听器去更新视图,完成数据的双向绑定。

  1. 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,
    给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。
  1. ***pile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定
    更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。

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

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

39.为什么vue2和vue3语法不可以混用?

优先级:
出现重名自变量,会根据书写顺序进行页面的展示。
vue2中的data和vue3中的setup

40.vue3中setup函数如何进行组件通讯?

setup语法糖方式通信:
父传子——defineProps

		1.父组件使用子组件,在子组件标签里面,通过标签属性的方式进行数据传递
		<template>
			<children :list="list"></children>
		</template>
		2.在子组件引入import { defineProps } from 'vue'
		
			<script setup>
		 import { defineProps } from 'vue'
		const props = defineProps({
			  	  list: {
			      type: Array,
			      default: () => []
			   	 }
			  	})
			</script>
			 defineProps方法定义变量名进行接收,template模板中进行使用。

子传父——defineEmits
父组件在子组件标签上通过v-on绑定自定义事件,子组件通过 const emits = defineEmits([‘事件名’])进行接收。
在子组件B中通过defineEmits注册一个事件名,然后在子组件B的方法中通过emit传递给父组件A。在组件A中,就可以为一些元素添加该事件名绑定事件方法。然后组件A在script setup中写这个事件方法,传递的参数就是子组件B在emit传递过来的值。这个值我们赋值给const的ref定义的数据,从而动态修改数据。

	<template>
			 <div>
			    <input v-model="value" type="text" placeholder="请输入" />
			    <button @click="handleAdd"> 添加 </button>
			  </div>
			</template>
			<script setup>
			  import { ref, defineEmits } from 'vue'
			
			  const value = ref('')
			  const emits = defineEmits(['add'])
			
			  const handleAdd = () => {
			    emits('触发的方法', 传递的参数)
			  }
			</script>

setup函数嵌套方式通信
父传子props

 父组件中使用子组件的标签
 在 子组件 setup 方法内使用props 来接收父组件传过来的数据。
 	<template>
 	  <div>
 	    <Article :msg="name"></Article>
 	  </div>
 	</template>
 	
 	<script>
 	import Article from '@/***ponents/Article.vue' 
 	export default {
 	  ***ponents: {
 	    Article
 	  },
 	  setup() {
 	    return {
 	      name: '渐行渐远渐无书,水阔鱼沉何处问'
 	    }
 	  }
 	}

子传父 content

<template>
  <div>
    {{msg}}
    <button @click="sendToParent">子组件向父组件传递数据</button>
  </div>
</template>

<script>
export default {
  props:['msg'],
  setup(props, content) {
    console.log(props.msg)
    function sendToParent() {
      content.emit('change')
    }
    return {
      sendToParent
    }
  }
}
</script>


41.vue3,***position API 的优势?

***position
在 Vue3 ***position API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合),即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API,***positon API,将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去。
***position APi

  • setup

Vue3.0中一个新的配置向,值为一个函数
setup是所有***position API(组合API) ”表演的舞台“
组件中所有用到的:数据、方法等等,均要配置在setup中

  • setup语法糖

语法糖里面的代码会被编译成组件setup()函数的内容,不需要通过return暴露声明的变量、函数以及import引入的内容,即可在template使用,并且不需要export default{}

  • ref函数

作用:定义一个响应式的数据。
基本类型的数据:响应式依然是靠Object.defineProperty()的get和set完成的
对象类型的数据:内部“求助”了Vue3.0的一个新的函数------reactive函数

  • reactive函数

作用:定义一个对象类型的响应式数据

  • defineEmits

setup语法糖中引入defineEmits方法,传入数组,数组里面则是父组件传递的自定义事件名。

  • 自定义hooks

ve3中还可以自定义 hooks, 将单个功能封装成单个的函数,在集成到组件中去复用! 将功能进行解耦,方便维护、复用、可读性强

42.shallowReactive和shallowRef的区别

shallowReactive:只处理对象最外层的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。

43.provide与inject如何使用

概述:父子组件传参可以通过props和emit来实现,但是当组件的层次结构比较深时,props和emit就没什么作用了。vue为了解决这个提出了Provide / Inject;provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量.
作用:实现祖和后代组件间通信。
provide()函数
定义:提供一个值,可以被后代组件注入。

具体实现:
父组件有一个provide选项提供数据,后代组件有一个inject选项来开始使用这些数据。
provide(第一个参数是要注入的key,它可以是一个字符串或一个symbol;第二个参数是要注入的值(具体要传递给子孙组件的数据)。

import {reactive,provide} from "vue"; 
let person = reactive({name: '张三',age: 14});
provide('person',person);

inject()函数
定义: 注入一个由祖先(父)组件或整个应用提供的值
实现: 接收父(祖)组件传递过来的值.
inject(第一个参数,第二个参数(可选)):第一个参数是注入的key,来自父(祖)组件,它们两者是需要保持一致的

Vue会遍历父组件链,通过匹配key来确定所提供的值,如果父组件链上多个组件对同一个key提供了之,那么离得更近的将会覆盖链上更远的组件所提供的值

如果没有能通过key匹配到的值,inject()函数将返回undefined,除非提供一个默认值

第二个参数是可选的,即没有匹配到key时,使用默认值,它也可以是一个函数,用来返回某些创建起来比较复杂的值,如果默认值本身就是一个函数

那么必须将false作为第三个参数传入,表明这个函数就是默认值,而不是一个工厂函数。

与注册生命周期钩子的API类似,inject()必须在组件的setup()阶段同步调用

import {inject,toRefs} from  "vue";
const person = inject('person');
// 若是使用解构,则会丢失响应式,修改数据时,页面不会更新,具体解决,可以引入toRef或toRefs函数
const {name,website} = toRefs(person);

模板使用
{{person.name}}---{{person.website}}

总结
provide/inject 做全局状态管理的原则:
多人协作时,做好作用域隔离;
尽量使用一次性数据作为全局状态

44.toRaw 与 markRaw是什么作用?

  • toRaw
    作用:将一个由reactive生成的响应式对象转化为普通对象。
    使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新.
  • markRaw
    作用:标记一个对象,使其永远不会再成为响应式对象。
    应用场景:
    1.有些值不应被设置为响应式的,例如复杂的第三方类的库。
    2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

45.readonly 与 shallowReadonly谈谈你对他的理解?

  • readonly: 让一个响应式数据变为只读的(深只读)。
    readonly是一个函数,他会接收一个响应式的数据,如上定义的name变量,将该变量加工一番readonly函数
    包裹后后重新返回一个name,这个重新返回的name变量中的所有的东西都不允许修改,所以我们现在再
    点击上面的修改按钮 name 变量也不会有任何改变,并且控制台会有警告。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
    shallowReadonly只限制对象中的第一层数据(不能改动,如:salary),但是嵌套的深层次的value属性值
    是可以更改的,我们点击更改按钮测试就能发现,被shallowReadonly包裹的对象的深层次值改变了。

46.自定义hook是什么?

在 Vue 3 中,Hook 是通过 Vue 3 新特性的最重要的部分——组合式 API 来实现的.
本质是一个函数。自定义hook,就是将setup中使用过的组合式函数进行封装,实现代码复用。
把数据、方法、和生命周期钩子封装在一个js文件。
在src目录下新建子目录hooks,并在hooks目录下新建文件:usePoint.js

47.虚拟DOM一定更快吗?

虚拟DOM/domDiff

我们常说的虚拟DOM是通过JS对象模拟出来的DOM节点,domDiff是通过特定算法计算出来一次操作所带来的DOM变化。react和vue中都使用了虚拟DOM,我们借着react聊聊虚拟DOM。

react中涉及到虚拟DOM的代码主要分为以下三部分,其中核心是第二步的domDiff算法:

把render中的JSX(或者createElement这个API)转化成虚拟DOM
状态或属性改变后重新计算虚拟DOM并生成一个补丁对象(domDiff)
通过这个补丁对象更新视图中的DOM节点

虚拟DOM不一定更快
前端的都知道DOM操作是性能杀手,因为操作DOM会引起页面的回流或者重绘。相比起来,通过多一些预先计算来减少DOM的操作要划算的多。

但是,“使用虚拟DOM会更快”这句话并不一定适用于所有场景。例如:一个页面就有一个按钮,点击一下,数字加一,那肯定是直接操作DOM更快。使用虚拟DOM无非白白增加了计算量和代码量。即使是复杂情况,浏览器也会对我们的DOM操作进行优化,大部分浏览器会根据我们操作的时间和次数进行批量处理,所以直接操作DOM也未必很慢。

那么为什么现在的框架都使用虚拟DOM呢?因为使用虚拟DOM可以提高代码的性能下限,并极大的优化大量操作DOM时产生的性能损耗。同时这些框架也保证了,即使在少数虚拟DOM不太给力的场景下,性能也在我们接受的范围内。

而且,我们之所以喜欢react、vue等使用了虚拟DOM框架,不光是因为他们快,还有很多其他更重要的原因。例如react对函数式编程的友好,vue优秀的开发体验等,目前社区也有好多比较这两个框架并打口水战的,我觉着还是在两个都懂的情况下多探究一下原理更有意义一些

48.vue路由中,history和hash两种模式有什么区别?

  • history模式
    history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求。
<a href="javascript:toA();">A页面</a>
<a href="javascript:toB();">B页面</a>
<div id="app"></div>
<script>
  function render() {
    app.innerHTML = window.location.pathname
  }
  function toA() {
    history.pushState({}, null, '/a')
    render()
  }
  function toB() {
    history.pushState({}, null, '/b')
    render()
  }
  window.addEventListener('popstate', render)
</script>

history API 提供了丰富的函数供开发者调用:

history.replaceState({}, null, '/b') // 替换路由
history.pushState({}, null, '/a') // 路由压栈
history.back() // 返回
history.forward() // 前进
history.go(-2) // 后退2次

history 模式的优缺点:
优点:路径比较正规,没有井号 #
缺点:兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了。

  • hash模式
    hash 模式是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 hashchange 事件。
<script>
<a href="#/a">A页面</a>
<a href="#/b">B页面</a>
<div id="app"></div>
<script>
  function render() {
    app.innerHTML = window.location.hash
  }
  window.addEventListener('hashchange', render)
  render()
</script>

在上面的例子中,我们利用 a 标签设置了两个路由导航,把 app 当做视图渲染容器,当切换路由的时候触发视图容器的更新,这其实就是大多数前端框架哈希路由的实现原理。

hash 模式的优缺点:
优点:浏览器兼容性较好,连 IE8 都支持
缺点:路径在井号 # 的后面,比较丑。

转载请说明出处内容投诉
CSS教程_站长资源网 » 【前端面试题——vue3篇】

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买