vue面试题(一)

 1.谈谈你对MVVM开发模式的理解?

1.MVVM由Model、View、ViewModel组成。

2.Model表示提供数据,View用来展示数据,

3.ViewModel用来监听model中的数据变化,控制视图更新,处理用户操作

Model 和 ViewModel 进行双向数据绑定。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中用户交互操作,改变的数据也会在 Model 中同步。

为什么使用MVVM:低耦合,可复用,独立开发,可测试

2.v-if 和 v-show 有什么区别?

理解

  • v-if是动态的向DOM树内添加或者删除DOM元素;
  • v-show是通过设置DOM元素的display样式属性控制显示隐藏;

在编译阶段

  • v-if,只有条件为真,才会开始编译;
  • v-show不管条件真假,都会编译,然后被缓存,而且DOM元素保留;

性能消耗

  • v-if有更高的切换消耗;
  • v-show有更高的初始渲染消耗
  • v-if指令可以应用于template包装元素上,v-show不支持

一般来说,节点要是经此显示隐藏 就用v-show

3.route和router区别

$route 是“路由信息对象”,拥有path,params,hash,query,fullPath,matched,name 等路由信息参数

$router 是“路由实例”,可以在页面之间来回切换,通过push、replace、go、back等方法,来实现页面间的跳转

4.vue自定义指令

vue2

在vue2中,如果是局部注册,在directives中创建一个自定义指令名,他是一个对象,对象中拥有多个生命周期

  • bind:只调用一次,指令第一次绑到元素调用,用于初始化
  • inserted:被绑定元素插入父节点时调用
  • update:所在组件vnode更新调用
  • componentUpdate:指令在组件的vnode及子组件的vnode全部更新完调用
  • ubind:只调用一侧,指令解绑

每个生命周期都可以获取节点跟节点上的信息,el,binding

directives: {
    'focus': {
      bind(el, binding, vnode) {
        el.focus()
      }
    }
  }

全局注册的话,在main.js中使用Vue实力创建Vue.directives(),第一个参数为'指令名',第二个是个对象,对象可以写生命周期

Vue.directives('focus',{
  bind(el, binding, vnode) {
        el.focus()
      }
})

vue3

局部注册:添加一个 directives 属性,内部可以写多个对象,内部的对象名字为自定义指令名字,可以给指令添加生命周期,例如

  • created 元素初始化的时候
  • beforeMount 指令绑定到元素后调用 只调用一次
  • mounted 元素插入父级dom调用
  • beforeUpdate 元素被更新之前调用
  • update 这个周期方法被移除 改用updated
  • beforeUnmount 在元素被移除前调用
  • unmounted 指令被移除后调用 只调用一次

每个生命周期都可以获取节点跟节点上的信息,el,binding

// MyComponent.vue  
<template>  
  <input v-focus />  
</template>  
<script>  
export default {  
  directives: {  
    focus: {  
      mounted(el) {  
        el.focus()  
      }  
    }  
  }  
}  
</script>

全局注册

在main.js文件中,在 Vue 应用实例上调用 directive 方法,传入指令名称和指令对象作为参数

// main.js 或其他全局文件  
import { createApp } from 'vue'  
import App from './App.vue'  
// 自定义指令 v-focus  
const focusDirective = {  
  mounted(el) {  
    el.focus()  
  }  
}  
// 创建一个 Vue 应用实例  
const app = createApp(App)  
// 全局注册 v-focus 指令  
app.directive('focus', focusDirective)  
// 挂载应用实例  
app.mount('#app')

5.vue项目优化

vue项目优化分3个层面:代码层,基础的web技术优化,webpack优化

代码层面优化:

  • Object.freeze 方法来冻结一个对象,
  • 图片懒加载
  • 路由懒加载
  • keep-alive组件缓存
  • v-if,v-for不要同时使用,for循环中要添加key值
  • 频繁显示隐藏节点,使用v-show
  • input防抖节流

基础的web技术优化

  • 使用浏览器缓存,减少http请求
  • 使用CDN加速
  • 延迟加载或者异步加载
  • 压缩文件,减少不必要的打印注释

webpack优化

6.vue模板如何编译

Vue的模板编译就是将HTML翻译为render函数的过程,可以分为三个阶段

1.解析阶段:将“HTML”模板解析成AST语法树

核心 parseHTML( template ,{}) 会根据正则匹配生成AST语法树

2.优化阶段:在AST语法树中找出静态子树并进行标记(被标记的静态子树在虚拟dom比对时会被忽略,提高性能)

生成虚拟dom时候,如果不是首次渲染,并且当前的节点被标记,就会去复制存在的静态子树

在对比虚拟dom时候,发现当前节点是静态子树则不会进行对比

(只有当前节点及其子节点是静态的才会被标记)

3.代码生成阶段:通过AST生成代码字符串,并最终生成render函数。

7.vue2响应式原理

vue 采用了几个核心部件 : Observer ,Dep, Watcher ,Scheduler

首先vue会通过object.defineProperty 将对象的每个属性转化为带有 getter 和 setter 的属性,

将普通的 JavaScript 对象转换为响应式对象

dep会记录谁用了它,并在更新的时候通知别人更新

创建Watcher对象:当依赖的数据发生变化时,执行相应的回调函数以更新视图。

因为Vue 的更新操作是异步的,所以Scheduler用来协调 Watcher 的更新,确保它们以合适的顺序和时机执行

Scheduler 不会立即执行更新,通过nexttick异步更新

8.vue3响应式原理

通过Proxy(代理): 拦截对象中任意属性的变化,包括:属性值的读写,属性的增加,属性的删除等。

通过Reffect(反射): 对源对象的属性进行操作, Reflect不是一个函数对象,因此它是不可构造的。

  • Vue 3 在创建组件实例时,会使用 Proxy 来代理组件的 data 对象。
  • Proxy 允许 Vue 3 拦截对目标对象的各种操作,如属性读取、属性设置、属性删除等
  • 当响应式属性发生变化时,Vue 3 会触发一个更新过程,通知所有依赖该属性的人更新状态
  • Vue 3 在使用 Proxy 拦截属性操作时,会使用 Reflect API 来执行相应的默认操作。
  • 例如,当拦截到属性读取时,Vue 3 会使用 Reflect.get() 来读取属性值;当拦截到属性设置时,Vue 3 会使用 Reflect.set() 来设置属性值

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

不存在

原因: 因为 store 里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,store里面的数据就会被重新赋值初始化。

我们有两种方法解决该问题:

  • 使用 vuex-along
  • 使用 localStorage 或者 sessionStroage

10.vue和react共同点?区别

共同点:

  • 数据驱动视图
  • 组件化开发
  • 都使用 Virtual DOM来提高性能,减少dom,加快页面渲染速度
  • 都支持服务器端渲染

不同点:

核心思想

  • vue定位就是尽可能的降低前端开发的门槛,让更多的人能够更快地上手开发。这就有了vue的主要特点:灵活易用的渐进式框架,进行数据拦截/代理,它关注数据的变化,通过数据驱动视图,并提供了许多易于理解和使用的API。
  • React的核心思想是提出UI开发的新思路,通过组件化来构建用户界面。,数据不可变以及单向数据流,当然需要双向的地方也可以手动实现, 比如借助onChange和setState来实现。

组件写法

  • Vue:Vue的组件写法更接近于传统的HTML和JavaScript的组合。Vue的模板语法直接写在HTML文件中

例如:你可以在一个 .vue 文件中直接编写模板(template)、脚本(script)和样式(style)

  • React:React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中
function MyComponent(props) {  
  return (  
    <div>  
      <h1>{props.message}</h1>  
      <button onClick={() => props.increment()}>Increment</button>  
    </div>  
  );  
}
///
function MyComponent(props) {  
  const style = {  
    color: 'blue',  
    fontSize: '20px'  
  };  
    
  return (  
    <div style={style}>  
      <h1>{props.message}</h1>  
    </div>  
  );  
}

diff算法

  • React的Diff算法通过比较新旧Virtual DOM的差异,仅更新有变化的部分,以提高渲染效率。
  • Vue的Diff算法旨在找出新旧VNode之间的差异,并将这些差异应用到实际的DOM上,以最小化DOM操作并提高性能。 就类似2个dom树进行比较,找出差异,重新渲染新的dom

响应式原理

  • vue2采用object.defineProperty ,vue3采用proxy,reflect
  • React基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。

11.vue双向数据绑定原理

  1. 数据劫持
    • Vue在实例初始化时,会遍历data选项中的数据,并使用Object.defineProperty()方法将其转换为getter/setter。
    • 每个组件实例都有一个对应的watcher实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
  2. 发布订阅模式
    • 当数据变化时,setter方法被调用,此时会通知订阅了该数据的所有watcher,watcher接收到通知后,会调用对应的回调函数(通常是组件的更新函数),进行视图更新。
    • 视图层也会监听DOM事件,如input输入框的输入事件,当事件触发时,会通过Vue的指令(如v-model)来更新数据,从而触发数据的setter,进而更新视图。
<div id="app">  
  <input type="text" v-model="message">  
  <p>{{ message }}</p>  
</div>  
  
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>  
<script>  
  var app = new Vue({  
    el: '#app',  
    data: {  
      message: 'Hello Vue!'  
    }  
  })  
</script>

12.computed和watch作用,区别

computed(计算属性)

定义与用途

  • computed是Vue中的一个内置函数,用于声明计算属性。这些属性基于其响应式依赖进行缓存,只有当依赖发生变化时才会重新计算。
  • 它常用于根据已有的响应式数据派生出新的数据,使得模板中可以直接使用这些计算后的数据,而无需在模板中编写复杂的逻辑。

特点

  • 自动缓存computed属性会缓存其计算结果,只有在其依赖的响应式数据发生变化时才会重新计算。这有助于提升性能,避免不必要的重复计算。
  • 同步计算computed属性的计算是同步的,即它会立即返回计算结果。
  • 依赖关系管理computed可以更清晰地管理数据之间的依赖关系,而不需要手动跟踪它们的变化。

使用场景

  • 过滤和排序列表。
  • 格式化数据,如日期、货币等。
  • 根据条件动态设置元素的样式或类。

watch(侦听器)

定义与用途

  • watch是Vue中用于侦听数据变化并执行异步操作或复杂逻辑的功能。
  • 它允许你执行数据变化时的副作用(side effects),如发送网络请求、执行动画等。

特点

  • 无缓存watch不会缓存旧值或计算结果,每次侦听到数据变化时都会执行回调函数。
  • 异步支持watch的回调函数可以执行异步操作,如发送网络请求或执行定时器等。
  • 复杂逻辑处理:适用于在数据变化时执行复杂逻辑或副作用操作。

使用场景

  • 需要在数据变化时执行异步操作,如发送网络请求。
  • 需要在数据变化时执行复杂的逻辑判断或计算。
  • 需要在数据变化时触发一些副作用,如更新DOM、改变其他数据等。

区别

13.Vuex

Vuex是一种状态管理模式,可用来共享不同组件之间的联系。

主要包括以下几个模块:

  • State => 基本数据,定义了应用状态的数据结构,可以在这里设置默认的初始状态。
  • this.$store.state.yourProperty
  • Getter => Getter允许我们从Store中的state计算派生出一些状态,可以认为是store的计算属性。
this.$store.getters.yourGetter 来调用
const store = new Vuex.Store({  
  state: {  
    todos: [  
      { id: 1, text: '...', done: true },  
      { id: 2, text: '...', done: false }  
    ]  
  },  
  getters: {  
    doneTodos: state => {  
      return state.todos.filter(todo => todo.done)  
    }  
  }  
});
  • Mutation => 是唯一更改 store 中状态的方法,且必须是同步函数。
this.$store.commit('mutationName', payload)  来调用
const store = new Vuex.Store({  
  state: {  
    count: 0  
  },  
  mutations: {  
    increment (state) {  
      // 变更状态  
      state.count++  
    }  
  }  
});
  • Action => Action类似于Mutation,但是它是异步的。Action可以提交mutation,而不是直接变更状态。
const store = new Vuex.Store({  
  state: {  
    count: 0  
  },  
  mutations: {  
    increment (state) {  
      state.count++  
    }  
  },  
  actions: {  
    incrementAsync ({ commit }) {  
      setTimeout(() => {  
        commit('increment')  
      }, 1000)  
    }  
  }  
});
  • Module => Module允许我们将单一的Store拆分为多个模块(module),每个模块拥有自己的state、mutation、action、getter。
const moduleA = {  
  state: { ... },  
  mutations: { ... },  
  actions: { ... },  
  getters: { ... }  
}  
  
const moduleB = {  
  state: { ... },  
  mutations: { ... },  
  actions: { ... },  
  getters: { ... }  
}  
  
const store = new Vuex.Store({  
  modules: {  
    a: moduleA,  
    b: moduleB  
  }  
})

14.vuex辅助函数

mapState, mapMutations, mapActions, mapGetters

通过辅助函数mapGetters、mapState、mapActions、mapMutations,把vuex.store中的属性映射到vue实例身上,这样在vue实例中就能访问vuex.store中的属性了,对于操作vuex.store就变得非常方便。

import { mapState } from 'vuex';  
  
export default {  
  computed: {  
    ...mapState({  
      // 将 state 中的 userInfo 映射为 this.userInfo  
      userInfo: state => state.userInfo,  
      // 将 state 中的 orderList 映射为 this.orderList  
      orderList: state => state.orderList,  
      // ... 其他状态映射  
    }),  
  },  
};

使用 mapState 可以将 Vuex 中的状态映射到组件的计算属性中,
  通过直接访问这些计算属性(如 this.userInfo),我们可以获取到 Vuex 中的状态数据。

mapMutations

  • mapMutations 是 Vuex 的辅助函数,用于将 Vuex 中的 mutations 映射到组件的方法中,使得组件可以直接调用 mutations 来修改 state 的值。
import { mapMutations } from 'vuex';  
  
export default {  
  methods: {  
    ...mapMutations([  
      // 映射 mutations 中的 increment 到组件的方法 increment  
      'increment',  
      // ... 其他 mutations 映射  
    ]),  
  },  
};
使用 mapMutations 可以将 Vuex 中的 mutations 映射到组件的方法中,
  这样我们就可以在组件中直接调用这些
  方法(如 this.increment())来触发对应的 mutations,从而修改 Vuex 中的状态。

mapActions

  • mapActions 是 Vuex 提供的辅助函数,用于将 Vuex 中的 actions 映射到组件的方法中,方便在组件中调用和触发 Vuex 中定义的 actions。
import { mapActions } from 'vuex';  
  
export default {  
  methods: {  
    ...mapActions([  
      // 映射 actions 中的 fetchData 到组件的方法 fetchData  
      'fetchData',  
      // ... 其他 actions 映射  
    ]),  
  },  
};
使用 mapActions 可以将 Vuex 中的 actions 映射到组件的方法中,
  这样我们就可以在组件中直接调用这些方法(如 this.fetchData())来触发对应的 actions,
  这些 actions 通常包含异步操作,如 API 请求等。
  通过 actions,我们可以执行异步操作并在完成后提交 mutations 来修改状态。

mapGetters

  • mapGetters 是 Vuex 提供的辅助函数,用于将 Vuex 中的 getters 映射到组件的计算属性中。
import { mapGetters } from 'vuex';  
  
export default {  
  computed: {  
    ...mapGetters([  
      // 映射 getters 中的 doneTodosCount 到组件的计算属性 doneTodosCount  
      'doneTodosCount',  
      // ... 其他 getters 映射  
    ]),  
  },  
};
使用 mapGetters 可以将 Vuex 中的 getters 映射到组件的计算属性中,
  使得我们可以在组件中直接访问这些 getters 的计算结果。

15.vuex模块化使用(module)

在Vuex中,随着应用复杂性的增长,将所有状态、getters、mutations和actions都放在一个单独的store对象中可能会变得难以管理。为了解决这个问题,Vuex允许我们将store分割成模块(modules)。每个模块都拥有自己的state、mutations、actions、getters,甚至是嵌套子模块。

前提:创建两份js文件,含有属性与vuex写法相同,需要通过 namespaced:true开启命名空间store/index.js:在modules中引入文件

我们可以在另一个js文件中写好这些数据模板

// 假设我们有一个名为user的模块  
const userModule = {  
  state: {  
    name: 'John Doe',  
    age: 30  
  },  
  mutations: {  
    setName(state, newName) {  
      state.name = newName;  
    },  
    setAge(state, newAge) {  
      state.age = newAge;  
    }  
  },  
  actions: {  
    updateName({ commit }, newName) {  
      commit('setName', newName);  
    },  
    updateAge({ commit }, newAge) {  
      commit('setAge', newAge);  
    }  
  },  
  getters: {  
    fullName(state) {  
      return `${state.name} (${state.age} years old)`;  
    }  
  }  
};

然后直接注册到store里面的模块对象

import Vue from 'vue';  
import Vuex from 'vuex';  
import userModule from './modules/user'; // 假设userModule定义在./modules/user.js文件中  
Vue.use(Vuex);  
export default new Vuex.Store({  
  modules: {  
    user: userModule // 注册名为'user'的模块  
  }  
});

后续访问就直接从store中引入模块名

印射到vue中就需要在,注册那个变量或者函数之前添加个/,/前面写模块名

因为他原本接收参数是一个数组,也可以让他接收2个参数,第一个是模块名,第二个就正常变量,这样也可以印射

this.$store.getters['user/fullName']; // 'John Doe (30 years old)'  
// 或者使用mapGetters辅助函数  
import { mapGetters } from 'vuex';  
  
export default {  
  computed: {  
    ...mapGetters(['user/fullName'])  
    ...mapGetters('user',['fullName'])  
  }  
};

16.vue中mixin

Mixin 主要有以下几个用途:

复用代码:如果你有一些组件有相同的逻辑或功能,你可以将这些逻辑或功能提取到一个 mixin 中,然后在多个组件中引入这个 mixin。

需要注意的是,如果 mixin 和组件有同名的选项(如 methods、components 等),那么这些选项将会以某种方式合并。对于大多数选项,如 methods、components 和 directives,Vue 将以组件自身的选项优先,也就是说组件自身的选项会覆盖 mixin 的同名选项。但是,对于某些选项(如生命周期钩子、created、mounted 等),它们将会合并为一个数组,因此所有的钩子函数都会被调用。

let mixin = {
    created() {
        console.log('我是mixin中的');
    },
    methods: {
        hellow() {
            console.log('你好');
        },
    },
}
export default mixin
import mixin from "./mixins";
export default {
  mixins: [mixin],
  mounted() {
    this.hellow();//你好
  },
};
import { createApp } from 'vue'
import App from './App.vue'
import mixins from "./mixins";
const app = createApp(App)
app.mixin(mixins)
app.mount('#app')

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

原因:vue2响应式采用object.defineProperty进行劫持,那个添加新属性时,新的属性不会具有get和set方法,不是一个响应式所以界面不刷新

解决:this.$set(要响应的内容)向响应式对象中添加一个property,并确保这个新 property 同样是响应式的

vue3通过proxy劫持和reflect映射实现响应式,不会有这个问题

18.vue组件通讯方式

通过 props 传递

    • props校验:name:{type:String,required:true,default:默认值} required是否必要
    • 可以在子组件内部添加属性传递数据,让子组件通过props接收

通过 $emit 触发自定义事件

  • 子组件中可以使用this.$emit传递内容,第一个参数为('事件名'),第二个为传递的内容,然后父组件在子组件上监听这个事件

使用 ref

  • 在子组件中添加ref属性添加实例名,然后在父组件通过this.$refs.实例名来获取子组件内部的实例

EventBus

import Vue from 'vue';  
export const EventBus = new Vue();
EventBus.$emit('some-event', 'data to send');
EventBus.$on('some-event', (data) => {  
  console.log(data); // 输出: data to send  
});

Provide 与 Inject

祖先组件---------------------------------
<script>  
export default {  
  provide() {  
    return {  
      themeColor: 'blue'  
    };  
  }  
}  
</script>

  后代组件-----------------------------
  <script>  
export default {  
  inject: ['themeColor'],  
  mounted() {  
    console.log(this.themeColor); // 输出: blue  
  }  
}  
</script>

Vuex        

19.vue3setup的父传子怎么去写?

第一种:使用vue2写法通过props和$emit

第二种:setup函数写法

  • setup(props,context),通过props接收数据直接结构出来,通过context.emit(‘调用父组件方法’,传递参数)

第三种:script中setup

  • vue3自带defineProps,defineEmits
const emits = defineEmits(["changeNumber"]);
// 也可以不赋值,取值通过{{num}}获取
const props = defineProps({
  num: {
    type: Number,
    default: () => [],
  },
  list: {
    type: Array,
  },
});
const changeNum = function () {
  emits("changeNumber", 888);
  //   console.log(11111111111);
};

20.setup可不可以直接写async和await?

如果是script中setup写法

setup 语法糖中可直接使用 await,不需要写 async , setup 会自动变成 async setup

<script setup>
  import Api from '../api/Api'
  const data = await Api.getData()
  console.log(data)
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值