Vue 面试题

面试题1

1. v-if与v-show的区别

  • 共同点:都能控制元素的显示和隐藏。
  • 不同点:实现本质方法不同。v-show本质是通过控制css中的display设置为none来控制隐藏,只会编译一次;v-if是动态地向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译。并且v-if不停的销毁和创建比较消耗性能。总结来说,如果要频繁切换某节点,使用v-show(切换开销较小,初始开销较大);如果不需要频繁切换某节点,使用v-if(初始渲染开销较小,切换开销比较大)。

2. v-if与v-for的优先级?

  • v-for优先于v-if被解析。
  • 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,这会浪费性能。
  • 要避免这种情况,可在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环。
  • 如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项。

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

如果data是一个函数,每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而若单纯写成对象形式,所有组件实例会共用一份data,就会造成一个变了全都会变的结果。这是由js的特性决定的,与vue本身设计无关,js的面向对象编程基于原型链和构造函数,通常在原型链上添加的是函数方法而非对象。

4. Vuex之actions与mutations的区别?

  • actions:用于通过提交mutation改变数据;会默认将自身封装为一个promise;可以包含任意的异步操作 。
  • mutations:通过提交commit改变数据;只是一个单纯的函数;不要使用异步操作,因为异步操作会导致变量不能追踪。

5. 如何在vuex中使用异步修改?

在调用vuex中的方法action的时候,用promise实现异步修改。示例代码如下:

const actions = {
    asyncLogin({ commit }, n) {
        return new Promise(resolve => {
            setTimeout(() => {
                commit('types.UserLogin', n);
                resolve();
            }, 3000)
        })
    }
}

6. Vue有哪些组件间的通信方式?

Vue组件间通信主要包括父子组件通信、隔代组件通信、兄弟组件通信,具体方式如下:

  • props/$emit:父组件通过props向子组件传递数据,子组件通过$emit触发事件并携带数据传递给父组件。例如父组件App.vue向子组件Users.vue传递userList数据,子组件通过遍历userList展示数据;子组件AppHeader.vue通过$emit触发自定义事件,将数据传递给父组件App.vue。
  • emit/on:通过一个空的vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,可实现父子、兄弟、跨级组件间的通信。不过在项目较大时,更适合选择vuex作为状态管理解决方案。
  • Vuex与localStorage:vuex是vue的状态管理器,存储的数据是响应式的,但刷新后会回到初始状态。做法是在vuex里数据改变时,把数据拷贝一份保存到localStorage,刷新后若localStorage有保存的数据,就取出来替换store里的state。
  • attrs/listeners:$attrs包含父作用域中不作为prop被识别(且获取)的特性绑定(class和style除外),可通过v-bind="$attrs"传入内部组件,用于父传孙;$listeners包含父作用域中的(不含.native修饰器的)v-on事件监听器,可通过v-on="$listeners"传入内部组件,用于孙传父。

7. vue中双向数据绑定是如何实现的?

vue.js采用数据劫持结合发布者 - 订阅者模式实现双向数据绑定。具体是通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。示例代码如下:

var obj = {};
Object.defineProperty(obj, "name", {
    get: function () {
        console.log("我被获取了");
        return val;
    },
    set: function (newVal) {
        console.log("我被设置了");
        val = newVal;
    }
});
obj.name = 'fei'; 
var val = obj.name;

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

  • 单页面应用(SPA):只有一个主页面,浏览器一开始要加载所有必须的html、js、css,页面内容都包含在主页面中,写的时候分开写(页面片段),交互时由路由程序动态载入,页面跳转仅刷新局部资源,多应用于pc端。优点是用户体验好、快,内容改变不需要重新加载整个页面,对服务器压力较小,前后端分离,页面效果炫酷;缺点是不利于seo,导航不可用(需自行实现前进、后退),初次加载耗时多,页面复杂度提高很多。
  • 多页面(MPA):一个应用中有多个页面,页面跳转时是整页刷新。

9. vue中v-if和v-for优先级?

v-for优先于v-if被解析,二者同时出现时每次渲染都会先执行循环再判断条件,这会影响速度,尤其是当只需要渲染很小一部分数据时。通常应避免同时使用,必要时可将v-if替换成computed属性,通过计算属性提前过滤掉不需要显示的项。例如:

<!-- 不推荐的写法 -->
<li v-for="user in users" v-if="user.isActive" :key="user.id">{{ user.name }}</li>
<!-- 优化后的写法 -->
<computed>
    activeUsers: function () {
        return this.users.filter(function (user) {
            return user.isActive
        })
    }
</computed>
<ul>
    <li v-for="user in activeUsers" :key="user.id">{{ user.name }}</li>
</ul>

10. vue事件的修饰符()?

  • .stop:等同于JavaScript中的event.stopPropagation(),用于防止事件冒泡。
  • .prevent:等同于JavaScript中的event.preventDefault(),可防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播)。
  • .capture:与事件冒泡的方向相反,事件捕获由外到内。
  • .self:只会触发自己范围内的事件,不包含子元素。
  • .once:只会触发一次。
  • .passive:表示listener函数不会调用preventDefault(),主要用在移动端的scroll事件,可提高浏览器响应速度,提升用户体验。因为设置passive=true等于提前告诉浏览器,touchstart和touchmove不会阻止默认事件,手刚开始触摸,浏览器就可以立刻给予响应;否则,手触摸屏幕了,但要等待touchstart和touchmove的结果,这会增加响应时间,降低用户体验。

11. vue的两个核心是什么?

  • 数据驱动:在vue中,数据的改变会驱动视图的自动更新。与传统需要手动改变DOM来更新视图不同,vue只需要改变数据就能实现视图更新。
  • 组件:组件化开发具有诸多优点,能很好地降低数据之间的耦合度。将常用代码封装成组件后可高度复用,提高代码的可重用性,一个页面或模块可由多个组件组成。

12. react和vue的区别

  • 相同点:数据驱动页面,提供响应式的视图组件;都有虚拟DOM,都采用组件化开发,通过props参数进行父子组件间数据传递,都实现了webComponents规范;数据流动单向,都支持服务器的渲染SSR;都有支持native的方法,react有React native,vue有weex。
  • 不同点:数据绑定方面,vue实现了双向数据绑定,react数据流动是单向的;数据渲染方面,大规模的数据渲染react更快;使用场景方面,React配合Redux架构适合大规模多人协作复杂项目,Vue适合小快的项目;开发风格方面,react推荐做法jsx + inline style把html和css都写在js里,vue是采用webpack + vue-loader单文件组件格式,html、js、css在同一个文件。

13. vue3.0有哪些新特性

  • 设计目标:更小、更快、加强TypeScript支持、加强API设计一致性、提高自身可维护性、开放更多底层功能。
  • 具体特性:压缩包体积更小;将Object.defineProperty替换为Proxy,在目标对象之上架了一层拦截,代理对象而不是对象的属性,使操作颗粒度变大,优化了javascript引擎解析;重构Virtual DOM,将vdom的操作颗粒度变小,更新性能由与模版整体大小相关提升为与动态内容的数量相关;进行更多编译时优化,如Slot默认编译为函数、Monomorphic vnode factory、Compiler-generated flags for vnode/children types ;选用Function - based API,更好地支持TypeScript,且vue中的UI组件很少用到继承,一般都是组合,这种API更适合。

14. vue性能优化方法

  • 编码阶段:尽量减少data中的数据,因为data中的数据都会增加getter和setter,会收集对应的watcher;如果需要使用v-for给每项元素绑定事件时使用事件代理;SPA页面采用keep - alive缓存组件;在更多的情况下,使用v-if替代v-show;保证key唯一;使用路由懒加载、异步组件;防抖、节流;第三方模块按需导入;长列表滚动到可视区域动态加载;图片懒加载。
  • 用户体验方面:采用骨架屏、PWA;还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
  • SEO优化:预渲染、服务端渲染SSR。
  • 打包优化:压缩代码;Tree Shaking/Scope Hoisting;使用cdn加载第三方模块;多线程打包happypack;splitChunks抽离公共文件;sourceMap优化。

15. v-model的原理

v-model本质是一个语法糖,可以看成是value + input方法的语法糖。可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性:text和textarea元素使用value属性和input事件;checkbox和radio使用checked属性和change事件;select字段将value作为prop并将change作为事件。例如,自定义v-model的示例代码如下:

<template>
    <input v-model="message" />
</template>
<script>
export default {
    data() {
        return {
            message: ''
        };
    },
    model: {
        prop: 'value',
        event: 'input'
    }
};
</script>

16. nextTick的实现原理是什么?

nextTick用于在下次DOM更新循环结束之后执行延迟回调。它主要使用了宏任务和微任务,根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,如果以上都不行则采用setTimeout。定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。