Vue.js常被提及的面试题

2019-08-14 10:17:25来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

对于MVVM的理解

 

ModelViewViewModel 三部分构成,由MVC衍生。

 

Model: 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑,

 

View: 代表UI 组件,它负责将数据模型转化成UI 展现出来,

 

ViewModel: 是一个同步View 和 Model的对象。

 

在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。 (注意)

 

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理

 

Vue.js双向数据原理

 

Vue.js 可以说是MVVM 架构的最佳实践,专注于 MVVM 中的 ViewModel,不仅做到了数据双向绑定,而且也是一款相对来比较轻量级的JS 库,API 简洁,很容易上手。

 

Vue.js 是采用 Object.defineProperty 的 getter 和 setter,并结合观察者模式来实现数据绑定的。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化注意:据悉vue3.0将采用Proxy替代Object.defineProperty

 

 

图的解析:

 

Observer :数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty的getter和setter来实现 。

 

Compile  :指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 。

 

Watcher  :订阅者,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数 。

 

Dep :消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法 。

 

执行过程:

 

当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行扫描和解析,初始化视图,并订阅 Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。

 

当数据发生变化时,Observer 中的 getter 方法被触发(注意这里触发什么),getter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。同理当表单输入内容发生变化时, 就会触发setter,watcher监听机制就会执行, watcher通知Vue生成新的VDOM树,再通过render函数进行渲染,生成真实DOM 。

 

通过 Object.defineProperty 实现见简单的双向数据绑定:

 

<body>
    <div id="demo"></div>
    <input type="text" id="inp">
</body>
<script>
    let obj = {};
    let demo = document.querySelector('#demo');
    let inp = document.querySelector('#inp');
    Object.defineProperty(obj,'name',{
        get : () => {
            return inp.value;
        },
        set : (newVal) => {//当该属性被赋值的时候触发
            inp.value  = newVal;
            demo.innerHTML = newVal;
        }
    });
    inp.addEventListener('input',(e) => {
        // 给obj的name属性赋值,进而触发该属性的set方法
        obj.name = e.target.value;
    })
    obj.name = 'huqinggui';//在给obj设置name属性的时候,触发了set这个方法
</script>

 

为什么vue3.0要用Proxy替代Object.defineProperty 实现双向数据绑定(待补充完善)

 

替换不是因为不好,是因为有更好的方法使用效率更高

 

那么到底为什么要用Proxy呢?既然想要替代了,说明一个有缺点,一个有优点的那么我们就来分析下他们的优缺点:

 

Object.defineProperty 的缺点

 

总体上说致命的缺点是:

 

  • 不能监听数组的变化
  • 必须遍历对象的每个属性
  • 必须深层遍历嵌套的对象

 

细分就是以下几点:

 

1.对IE11的兼容性(现在除了特殊的需求,基本上对IE都不考虑了)

 

2. 无法检测数组的变化

 

Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 而且使用这些方法(push, pop, shift, unshift,splice, sort, reverse…)是不能触发set的,Vue中能监听是因为对这些方法进行了重写

 

var a = {},
bValue = 1;
Object.defineProperty(a,"b",{
  set: function(value){
    bValue = value;
    console.log("setted");
  },
  get: function(){
    return bValue;
  }
});

a.b = []; //setted
a.b = [1,2,3]; //setted
a.b[1] = 10; //无输出
a.b.push(4); //无输出
a.b.length = 5; //无输出

 

当a.b被设置为数组后,只要不是重新赋值一个新的数组对象,任何对数组内部的修改都不会触发setter方法的执行。所以要想实现实现数组的双向绑定,则必须通过Arr = newArr;这样的语句实现。同样常见的数组方法也不会触发,在框架中对这些方法进行了重写才能实现效果。

 

3.只能监听属性,而不是监听对象本身,需要对对象的每个属性进行遍历

 

对于原本不在对象中的属性难以监听。在Vue 2.x里,是通过 “callback + 遍历 data 对象” 来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择,而Proxy就显示了这方面的优势

 

4.当对象增删的时候,是监控不到的

 

比如:data = {a:"a"},这个时候如果我们设置data.test = "test",这个时候是监控不到的。因为在observe data的时候,会遍历已有的每个属性(比如a),添加getter/setter,而后面设置的test属性并没有机会设置getter/setter,所以检测不到变化。同样的,删除对象属性的时候,getter/setter会跟着属性一起被删除掉,拦截不到变化。

 

ES6中的Proxy的优点

 

总的来说呢,Proxy是刚好解决了上述 Object.defineProperty 的缺点:

 

针对对象:针对整个对象,而不是对象的某个属性,所以也就不需要对 keys 进行遍历。

 

支持数组:Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的。

 

当然除此之外还有以下几个原因:

 

Proxy 的第二个参数可以有 13 种拦截方法,这比起 Object.defineProperty() 要更加丰富

 

Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,相比之下 Object.defineProperty() 是一个已有的老方法。

 

vue中 key 值的作用 (v-for)

 

key 的特殊属性主要用在 Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用key,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。(有效的避免就地复用)

 

有相同父元素的子元素必须有独特的key。重复的key会造成渲染错误。

 

最常见的用例是结合 v-for:

 

<ul>
  <li v-for="item in items" :key="item.id">...</li>
</ul>

 

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:

 

完整地触发组件的生命周期钩子

 

触发过渡

 

<transition>
  <span :key="text">{{ text }}</span>
</transition>

 

当 text 发生改变时,<span> 会随时被更新,因此会触发过渡。

 

vue中子组件调用父组件的方法

 

子组件调用父组件的方法可以使用this.$emit() 

 

v-showv-if指令的共同点和不同点?

 

v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏。

 

v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果(注意:v-if 可以实现组件的重新渲染),因此我们要尽量减少v-if的使用,因为它消耗性能

 

如何让CSS只在当前组件中起作用?

 

将当前组件的<style>修改为<style scoped>,关键点在scoped,代表作用域,限制css的作用在当前组件。的作用是什么?

 

<keep-alive></keep-alive>的作用是什么?

 

<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

 

大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,
这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染 。

 

 

Vue中引入组件的步骤?

 

 

引入:

 

 

1.采用ES6的import ... from ...语法

 

 

2.CommonJS的require()方法引入组件

 

 

注册

 

 

Vue.component('my-component', 
{  template: '<div>A custom component!</div>'})

 

3.使用组件<my-component></my-component>

 

指令v-el的作用是什么?

 

提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标.可以是 CSS 选择器,也可以是一个 HTMLElement 实例

 

在Vue中使用插件的步骤

 

1. 采用ES6的import ... from ...语法

 

2. 使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象 ,需要

 

Vue.use(MyPlugin, { someOption: true })

 

请列举出3个Vue中常用的生命周期钩子函数?

 

1.created: 实例已经创建完成之后调用,在这一步,实例已经完成数据观测, 属性和方法的运算, watch/event事件回调. 然而, 挂载阶段还没有开始, $el属性目前还不可见

 

2. mounted: el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内

 

3. activated::keep-alive组件激活时调用

 

请简述下Vuex的原理和使用方法

 

 

数据单向流动

 

一个应用可以看作是由上面三部分组成: View, Actions,State,数据的流动也是从View => Actions => State =>View 以此达到数据的单向流动.

 

但是项目较大的, 组件嵌套过多的时候, 多组件共享同一个State会在数据传递时出现很多问题.Vuex就是为了解决这些问题而产生的.

 

Vuex可以被看作项目中所有组件的数据中心,我们将所有组件中共享的State抽离出来,任何组件都可以访问和操作我们的数据中心

 

Vuex的组成:一个实例化的Vuex.Store由state, mutations和actions三个属性组成:

 

1.state中保存着共有数据

 

2.改变state中的数据有且只有通过mutations中的方法,且mutations中的方法必须是同步的

 

3.如果要写异步的方法,需要些在actions中, 并通过commit到mutations中进行state中数据的更改.

 

vue watch的高级用法--监听对象的属性变化

 

1.监听对象需要深度监听 ,如下代码可以监听整个msg对象的变化

 

watch: {
  msg: {
    handler(newValue, oldValue) {
      console.log(newValue)
    },
    deep: true
  }
}

 

2. 监听对象里面某个属性的变化,通过computed做中间层实现

 

computed: {
  channel() {
    return this.msg.channel
  }
  },
  watch:{
    channel(newValue, oldValue) {
    console.log('new: %s, old: %s', newval, oldVal)
    //这里面可以执行一旦监听的值发生变化你想做的操作
  }
  }

原文链接:https://www.cnblogs.com/h5hu/p/11234797.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:JavaScript知识点:分支结构(if、switch)+算法例题

下一篇:ES6新语法(一)