初中级面试题(vue)

172 阅读15分钟

mvc和mvvm的理解

mvc

mvc为model view controller,就是说通过controller的控制区操作model层的数据,并返回给view层展示。

整个过程为:view接受用户交互请求,然后将请求转交给controller处理,controller操作model进行数据更新保存,保存之后model会通知view层更新,view更新数据使得用户得到反馈。

mvvm

mvvm即为model-view-viewmodel,就是将其中的view的状态和行为抽象化,让我们可以将UI和业务逻辑分开。mvvm的优点是低耦合、可重用性、以及独立开发。

数据变化的过程为:view接收用户交互请求,然后将请求转交给viewmodel,viewmodel操作model数据更新,model更新完数据,就通知viewmodel数据发生变化,viewmodel更新view数据。

mvvm模式和mvc有些类似,但有3点不同:

  1. viewmodel替换了controller,在UI层之下;
  2. viewmodel向view暴露它所需要的数据和指令对象;
  3. viewmodel接收来自model的数据

概况起来,mvvm是由mvc发展而来,通过在model指向而在view之下增加一个非视觉的组件将来自model的数据映射到view中。

vue的响应原理

vue3.0x以下采用数据劫持结合发布者-订阅者模式的方式,通过object.defineProperty劫持data属性的setter、getter,在数据变动时发布消息给订阅者,触发响应的监听回调,从而更新视图。

具体就是:mvvm作为绑定的入口,整合Observe,Compile和Watcher三者,通过Observe来监听model的变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observe和Compile之间的通信桥梁,从而达到数据变化,更新视图,视图交互变化,数据model变更的双向绑定效果。

Vue的响应式系统

任何一个vue component都有一个与之对应的watcher实例,vue的data上的属性会被添加getter和setter属性。当vue component render函数被执行的时候,data上会被触碰(touch被读),getter方法会被调用,此时vue会去记录此vue component所依赖的所有data,这一过程被称为依赖收集。Data被改动时,setter方法会被调用,此时vue会去通知所有依赖于此data的组件去调用他们的render函数进行更新。

生命周期

  • beforeCreate创建前,vue实例的挂载元素$el和数据对象data都是undefined,还未初始化
  • created创建后,完成了data数据初始化,但$el还未初始化,已经可以申请异步请求的数据
  • beforeMount载入前,vue实例的$el和data都初始化了,相关的render函数首次被调用
  • mounted载入后,此过程中进行ajax交互
  • beforeUpdate更新前
  • updated更新后
  • beforeDestroy销毁前
  • destroyed销毁后

v-cloak主要是用来避免页面加载时出现闪烁的问题,可以结合css的[v-cloak]{display:none}方式解决这一问题。

chunkwebpack打包过程中的Modules的集合,是打包过程中的概念。

vue-router

vue-router是vue.js官方的路由管理器,它和vue.js的核心深度集成,让构建页面变得简单易操作。

  • 组件支持用户通过to属性指定目标地址,使用点击事件就能进行页面切换
  • 是一个functional组件,渲染路径匹配到的视图组件
  • 是一个内置缓存组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
  • router.beforeEach、router.afterEach,参数有to、from、next,to对应的是即将要进入的路由对象,from是即将要离开的路由对象,next则是执行回调方法。

keep-alive是什么

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

  • 一般结合路由和动态组件一起使用,用于缓存组件;
  • 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
  • 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

vuex的理解

vuex是一个转为vue.js应用程序开发的一个状态管理器,它集中于MVC模式中的Model层,规定所有的数据操作必须通过action-mutation-state,change的流程来进行,再结合Vue的数据视图双向绑定特性来实现页面的展示更新。vuex有5个属性:state、mutation、action、getter、modules。

统一的页面状态管理以及操作处理,可以让复杂的组件交互变得简单清晰,同时可在调试模式下进行时光机般的倒退前进操作,查看数据改变过程,使code debug更加方便。

  • state是vuex中的单一状态树,用一个对象包含全部的应用层的状态;
  • mutation是vuex中更改state状态的唯一方法,搭配commit调用;
  • action是vuex中唯一可以包含异步操作的方法,action提交的是mutation,而不是直接变更状态;
  • getter相当于vue组件中的computed计算属性
  • modules:将state分成多个modules,便于管理

组件data为什么返回函数

组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data。如果单纯的写成对象形式,就使得所有组件实例共用一份data,造成数据污染。

vue给对象新增属性页面没有响应

由于Vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data对象上存在才能让Vue将它转换为响应式的。Vue提供了$set方法用来触发视图更新。

v-if和v-show的区别

v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。

所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

v-model双向绑定原理(语法糖)

vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件;

这个语法糖必须是固定的,也就是说属性必须为value,方法名必须为input或change。

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

可以。如<input type=”text” v-on=”{input: onInput, focus: onFocus, blur: onBlur}”>

key的作用

Key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。

另外,若不设置key还可能 在列表更新时引发一些隐蔽的bug;vue中在使用相同标签元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

  1. 让vue精准的追踪到每一个元素,并高效的更新虚拟DOM
  2. 触发过渡

当text改变时,这个元素的key属性就发生了改变,在渲染更新时,Vue会认为这里新产生了一个元素,而老的元素由于key不存在了,所以会被删除,从而触发了过渡。

scoped属性作用

在Vue文件中的style标签上有一个特殊的属性,scoped。当一个style标签拥有scoped属性时候,它的css样式只能用于当前的Vue组件,可以使组件的样式不相互污染。如果一个项目的所有style标签都加上了scoped属性,相当于实现了样式的模块化。

scoped属性的实现原理是给每一个dom元素添加了一个独一无二的动态属性,给css选择器额外添加一个对应的属性选择器,来选择组件中的dom。

scoped样式穿透

scoped虽然避免了组件间样式污染,但是很多时候我们需要修改组件中的某个样式,但是又不想去除scoped属性。

  1. 使用/deep/
  2. 使用两个style标签
  3. 使用>>>符号
  4. 使用::v-deep

ref的作用

  1. 获取dom元素this.$refs.box
  2. 获取子组件中的datathis.$refs.box.msg
  3. 调用子组件中的方法this.$refs.box.open()

computed和watch的区别

  1. 当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性computed。
  2. watch用于观察和监听页面上的vue实例,如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择。

Computed和watch的区别和运用的场景?

Computed是计算属性,依赖其他属性值。比如当一个属性受多个属性影响的时候就需要用到computed,最典型的例子就是购物车商品结算的时候。

Watch更多的是【观察】的作用。举例的话,就是当一条数据影响多条数据的时候就需要用watch,如搜索数据。使用watch选项允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

Created和mounted区别

Create在模板渲染成html前调用,通常初始化某些属性值,然后在渲染成视图。

Mounted在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

Vue获取数据在哪个周期函数

一般created/beforeMount/mounted皆可。但是要操作dom的话,那肯定是mounted函数中。

但是我推荐在created钩子函数中调用异步请求,优点是:

能更快的获取到服务端数据,减少页面loading时间;SSR(服务端渲染)不支持beforeMount和mounted钩子函数,所以放在created中有助于一致性。

nextTick的使用

在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态。当你修改了data的值后想马上获取这个dom元素的值,是不能获取到更新后的值。而是需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取才能成功。

  • 新版本中默认是microtasks, v-on中会使用macrotasks

  • macrotasks任务的实现:

    • setImmediate / MessageChannel / setTimeout

vue路由有几种模式

  1. hash模式:即地址栏URL中的#符号,它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,不需要后台进行配置,因此改变hash不会重新加载页面。
  2. history模式:利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法(需要特定浏览器支持)。history模式改变了路由地址,因为需要后台配置地址。

组件之间的传值通信

  1. 父组件给子组件传值通过props
  2. 子组件给父组件传值通过$emit触发回调
  3. 兄弟组件通信,通过实例一个vue实例eventBus作为媒介,要相互通信的兄弟组件之中,都引入eventBus
  4. 官方推荐的状态管理器vuex
  5. parentparent和children
  6. $refs获取子数据,子方法等。

axios拦截器怎么配

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

Vue有哪些性能优化方法?

  • 合理使用v-show和v-if
  • 合理使用computed
  • v-for时加key,diff算法能高效更新dom,以便vue对组件状态进行跟踪。
  • v-for应该避免和v-if同时使用(v-for优先级更高,每次v-for都需要使用v-if,对性能是一种消耗)
  • 自定义事件,dom事件及时销毁
  • 合理使用异步组件,合理使用keep-alive,data层级不要太深
  • 使用vue-loader,在开发环境做编译(预编译),前端通用的性能优化,如图片懒加载,使用ssr。
  • 指令可以简写。
  • 不要在created、watch中直接调用方法,在created阶段调用方法的需求无非是希望组件是实例完成后立即调用的,其实这种情况可以在watch中实现。

代码层面优化

路由懒加载。keep-alive缓存页面、使用v-show复用dom。使用v-for遍历要避免同时使用v-if。长列表性能优化【如果列表是纯数据展示,不做响应化改变;大数据长列表,采用虚拟滚动,渲染少部分区域内容——:item-size:20】。图片懒加载。第三方插件按需引入。

事件的销毁,如clearInterval()。无状态的组件标记为函数式组件。子组件分割。变量本地化。SSR。

Webpack层面的优化

Webpack对图片进行压缩,减少es6转化为es5的冗余代码,提取公共代码,并进行模板预编译。提取组件的css,优化sourceMap,构建结果输出分析,vue项目的编译优化。

基础的web技术的优化

开启gzip压缩,浏览器缓存,cdn的使用,使用chrome performance查找性能瓶颈。

virtual dom 原理实现

创建 dom 树

树的diff,同层对比,输出patchs(listDiff/diffChildren/diffProps)

  • 没有新的节点,返回

  • 新的节点tagNamekey不变, 对比props,继续递归遍历子树

    • 对比属性(对比新旧属性列表):

      • 旧属性是否存在与新属性列表中
      • 都存在的是否有变化
      • 是否出现旧列表中没有的新属性
  • tagNamekey值变化了,则直接替换成新节点

渲染差异

  • 遍历patchs, 把需要更改的节点取出来
  • 局部更新dom

Proxy 相比于 defineProperty 的优势

  • 数组变化也能监听到
  • 不需要深度遍历监听
let data = { a: 1 }
let reactiveData = new Proxy(data, {
    get: function(target, name){
        // ...
    },
    // ...
})

vue插槽

单个插槽

当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM 位置, 并替换掉插槽标签本身

命名插槽

solt元素可以用一个特殊的特性name来进一步配置如何分发内容。 多个插槽可以有不同的名字。 这样可以将父组件模板中 slot 位置, 和子组件 slot 元素产生关联,便于插槽内容对应传递

作用域插槽

可以访问组件内部数据的可复用插槽(reusable slot) 在父级中,具有特殊特性 slot-scope 的