精华内容
下载资源
问答
  • Vue常见面试题及网友回答,收集来自阿里云社区,包括v-model, key, methods,错误处理,参数传递,父子组件,定时器,vue声明周期等问题
  • vue常见面试题

    千次阅读 2020-05-21 16:22:32
    v-if与v-show的区别: v-showv-show通过CSS dispaly控制显示和隐藏 v-if组件真正的渲染和销毁,而不是显示和隐藏 频繁切换状态使用v-show,否则使用v-if ...描述Vue组件生命周期(父子组件) 创建前后beforeCreate

    目录

     

    v-if与v-show的区别:

    对MVVM开发模式的理解:

    VUE组件data为什么必须是函数

    methods,computed,watch

    为何要在v-for中使用key

    描述Vue组件生命周期(父子组件)

    vue组件如何通讯

    描述组件和渲染的过程

    何时要使用beforeDestory

    什么是作用域插槽

    Vuex中action和mutation有何区别:

    vue-router常用的路由模式

    如何配置vue-router异步加载

    请用vnode描述一个DOM结构

    监听data变化的核心的API是什么?

    请描述响应式原理

    简述diff算法过程

    Vue为何是异步渲染,$nextTick何用

    Vue常见性能优化:


    v-if与v-show的区别:

    • v-showv-show通过CSS dispaly控制显示和隐藏
    • v-if组件真正的渲染和销毁,而不是显示和隐藏
    • 频繁切换状态使用v-show,否则使用v-if

    对MVVM开发模式的理解:

    • Model 代表数据模型,数据和业务逻辑都在Model层中定义;
    • View 代表UI视图,负责数据的展示;
    • ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作
    •  Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步

    VUE组件data为什么必须是函数

    • vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的

    methods,computed,watch

    • computed:可以进行缓存,只有在它的相关依赖发生改变时才会重新计算值,常适用于与一个数据受多个数据影响
    • method:只要发生重新渲染,methods调用总会执行该函数
    • watch:一般用于一个数据影响多个数据,或者在数据变化时来执行异步操作

    为何要在v-for中使用key

    • 必须使用key,且不能是index和random
    • diff算法中通过tag和key来判断,是否sanmeNode
    • 减少渲染次数,提升渲染性能(如果发现是sameNode,那么会将node的位置进行移动)

    描述Vue组件生命周期(父子组件)

    • beforeCreate(创建前) 在数据观测和初始化事件还未开始
    • created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
    • beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
    • mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
    • beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
    • updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
    • beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
    • destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

    vue组件如何通讯

    • 父子组件传值:父组件给子组件传值,子组件通过props进行接受;子组件向父组件传值,通过$emit方法传递参数,在父组件中穿件子组件时,需要监听$emit的事件
    • 兄弟组件传值:eventBus,创建一个事件中心(是new的一个vue实例),通过event.$on和$event.$emit发布订阅的方式实现通讯,可参考(vue - 组件之间的通信
    • vuex

    描述组件和渲染的过程

    • 初次渲染:
      • 解析模板为render函数(或在开发环境下已完成,vue-loader)
      • 触发响应式,监听data属性,setter和getter
      • 执行render函数,返回生成vnode,再执行patch(elem, vnode),页面渲染(在执行render函数时,就已经执行了data的getter,但不会引起视图变化的不会触发get) 
    • 更新过程
      • 修改data,触发setter(此前在getter中已被监听)
      • 重新执行render函数,生成newVnode
      • 执行patch(vnode, newVnode)
    • 流程图参考如下:

     

    何时要使用beforeDestory

    • 解绑自定义事件event.$off
    • 清除定时器
    • 解绑自定义的DOM事件,如window scroll等

    什么是作用域插槽

    • 作用域插槽给了子组件将数据返回组组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式
    • 一下是一个简单的demo:

    Vuex中action和mutation有何区别:

    • action处理异步,mutation不可以
    • mutation专注于修改state,而action是提交的mutation,而不直接变更状态
    • action可以整合多个mutation

    vue-router常用的路由模式

    • hash(默认):hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误
    • H5 history:且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误

    如何配置vue-router异步加载

     

    请用vnode描述一个DOM结构

    监听data变化的核心的API是什么?

    • Object.defineProperty
    • Object.defineProperty有和缺点
      • 深度监听,需要递归到底,计算量大
      • 对删除的属性和新增的属性无法实现监听,需要使用Vue.set和Vue.delete
      • 无法监听数组,需要特殊处理
    • 实现如下:该实现通过重写数组的常用方法实现数组的监听
    const data = {
        name: 'zhangsan',
        age:21,
        info: {
            address: 'BeiJing'
        },
        num: [1,2,3]
    }
    
    
    // 重新定义数组的原型,不建议直接重写数组的方法,否则就会污染全局的原型方法
    let oldArrayProperty = Array.prototype;
    var arrProto = Object.create(oldArrayProperty);
    ['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
        arrProto[methodName] = function(){
            updateView  ();
            oldArrayProperty[methodName].call(this, ...arguments);
        }
    })
    
    
    
    function updateView(){
        console.log('update view');
    }
    
    function observer(target){
        if (typeof target !== 'object' || target === null){
            return target;
        }
    
        if (Array.isArray(target)){
            target.__proto__ = arrProto;
        }
    
        for (let key in target){
            defineReactive(target, key, target[key])
        }
    }
    
    function defineReactive(target, key, value){
        // 深度监听
        observer(value);
    
        Object.defineProperty(target, key, {
            get(){
                return value
            },
            set(newValue){
                // 设置的新值可能还是一个object
                observer(newValue);
                if (newValue !== value){
                    value = newValue
                    updateView();
                }
            }
        })
    }
    
    // 执行observer必须等数组方法重写后进行监听
    observer(data);
    
    data.name = 'lisi';
    data.age = 20;
    data.info.address = 'ShangHai';
    data.num.push(4)

    请描述响应式原理

    • 当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter,在属性被访问和修改时通知变化。
    • 每个组件实例都有相应的 watcher 程序实例,当data发生变化是,之后setter 被调用,会通知 watcher 重新计算,从而执行render函数,致使它关联的组件得以更新。
    • 可参考上面题目的流程图:“描述组件和渲染的过程”

    简述diff算法过程

    • patch函数:patch(elem, vnode)   和   patch(vnode, newVnode)
      • 当第一个参数不是vnode时,创建一个空的vnode关联到dom元素上
      • 当两个vnode不一致时,创建新的vnode,将旧的vnode删除
      • 当两个vnode一致时,执行patchVnode函数
    • patchVnode函数:
      • 该函数主要用于对旧vnode和新vnode中的children进行对比,从而进行对应操作
      • 当旧vnode的children和新vnode的children同时存在,执行updateChildren
      • 当新的children有,旧的text有,那么将旧的vnode的text设置为空,添加新的children
      • 当新的children无,旧的children有,删除旧的children
      • 当新的vnode有text时,且不等于旧的vnode的text时,将旧的children移除,设置新的text
    • updateChildren
      • 该函数主要用于对children进行更新,分别从旧的children和新的children中每一项对比,如果有发现同样的vnode,那么再次执行patcVnode函数
    • sameVnode:
      • 用于判断是否是相同的vnode,通过key和sel进行比较,这也是为什么for循环中一定要有key

    Vue为何是异步渲染,$nextTick何用

    • 异步渲染(以及合并data修改),以提高渲染性能,否则每次更新数据都会对当前组件进行重新渲染
    • $nextTick用于在DOM更新完之后,触发回调

    keep-alive

    • keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
    • 属性:
      • include:  字符串或正则表达式,只有名称匹配的组件会被缓存
      • exclude: 字符串或正则表达式,名称匹配的组件不会被缓存》
      • max:     数字,最多可以缓存多少组件实例
    • 生命周期:
      • activated和deactivated会在keep-alive树内所有嵌套的组件中触发
          如:B页面是缓存页面
            当A页面跳到B页面时,B页面的生命周期:activated(可在此时更新数据)
            B页面跳出时,触发deactivated
            B页面自身刷新时,会触发created-mouted-activated

    •  router.meta 属性来实现

    export default {
      name: 'hello',
      //keep-alive钩子函数:组件被激活时调用
      activated() {
        console.log('首页被激活');
      },
      //keep-alive钩子函数:组件消失,被缓存时调用
      deactivated() {
        console.log('首页被缓存');
      },
      beforeRouteLeave(to, from, next) {
        //设置下一个路由的meta(即首页)
        to.meta.keepAlive = true;  // 让首页缓存,即不刷新
        next();
      }
    }

    Vue常见性能优化:

    • 合理使用v-if和v-show
    • 合理使用computed
    • v-for加key,以及避免和v-if同时使用(v-for比v-if的优先级高,所以每次都要v-for之后再v-if,所以避免使用)
    • 自定义事件,DOM事件,定时即使销毁
    • 合理使用异步组件
    • 合理使用keep-alive(对需要进行缓存的时候keep-alive)
    • data层级不要太深
    • webpack层面的性能优化

     

     

     

     

    展开全文
  • vue常见面试题(附带答案)超实用!!建议收藏!!一、vue常见面试题二、生命周期函数面试题三、vue路由面试题四、vuex常见面试题 一、vue常见面试题 1.vue优点? 答:轻量级框架:只关注视图层,是一个构建数据的...

    vue常见面试题(附带答案)超实用!!建议收藏!!

    一、vue常见面试题

    1.vue优点?

    答:轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
    简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
    双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
    组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
    视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
    虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
    运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。

    2.vue父组件向子组件传递数据?

    答:通过props

    3.子组件像父组件传递事件?

    答:$emit方法

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

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

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

    答:在组件中的style前面加上scoped

    6.的作用是什么?

    答:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

    7.如何获取dom?

    答:ref=“domName” 用法:this.$refs.domName

    8.说出几种vue当中的指令和它的用法?

    答:
    v-model双向数据绑定;
    v-for循环;
    v-if v-show 显示与隐藏;
    v-on事件;v-once: 只绑定一次。

    9. vue-loader是什么?使用它的用途有哪些?

    答:vue文件的一个加载器,将template/js/style转换成js模块。
    用途:js可以写es6、style样式可以scss或less、template可以加jade等

    10.为什么使用key?

    答:需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。
    作用主要是为了高效的更新虚拟DOM。

    11.axios及安装?

    答:请求后台资源的模块。npm install axios --save装好,
    js中使用import进来,然后.get或.post。返回在.then函数中如果成功,失败则是在.catch函数中。

    12.v-modal的使用。

    答:
    v-model用于表单数据的双向绑定,其实它就是一个语法糖,这个背后就做了两个操作:
    v-bind绑定一个value属性;
    v-on指令给当前元素绑定input事件。

    13.请说出vue.cli项目中src目录每个文件夹和文件的用法?

    答:assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置; app.vue是一个应用主组件;main.js是入口文件。

    14.分别简述computed和watch的使用场景

    答:
    computed:
        当一个属性受多个属性影响的时候就需要用到computed
        最典型的栗子: 购物车商品结算的时候
    watch:
        当一条数据影响多条数据的时候就需要用watch
        栗子:搜索数据
        
    15.v-on可以监听多个方法吗?

    答:可以,栗子:。

    16.$ nextTick的使用

    答:当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
    你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。

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

    答:因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象。
      组建中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。

    18.渐进式框架的理解

    答:主张最少;可以根据不同的需求选择不同的层级;

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

    答:vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
    核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法。

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

    答:
    单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
    多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
    单页面的优点:
    用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
    单页面缺点:
    不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多。

    二、生命周期函数面试题

    1.什么是 vue 生命周期?有什么作用?

    答:每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做 生命周期钩子 的函数,这给了用户在不同阶段添加自己的代码的机会。(ps:生命周期钩子就是生命周期函数)例如,如果要通过某些插件操作DOM节点,如想在页面渲染完后弹出广告窗, 那我们最早可在mounted 中进行。

    2.第一次页面加载会触发哪几个钩子?

    答:beforeCreate, created, beforeMount, mounted

    3.简述每个周期具体适合哪些场景

    答:beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
    create:data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
    beforeMount:执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
    mounted:执行到这个钩子的时候,就表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
    beforeUpdate: 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
    updated:页面显示的数据和data中的数据已经保持同步了,都是最新的
    beforeDestory:Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
    destroyed: 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。

    4.created和mounted的区别

    答:created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
    mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

    5.vue获取数据在哪个周期函数

    答:一般 created/beforeMount/mounted 皆可.
    比如如果你要操作 DOM , 那肯定 mounted 时候才能操作.

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

    答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
    创建前/后: 在beforeCreated阶段,vue实例的挂载元素 $ el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$ el还没有。
    载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
    更新前/后:当data变化时,会触发beforeUpdate和updated方法。
    销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。

    三、vue路由面试题

    1.mvvm 框架是什么?

    答:vue是实现了双向数据绑定的mvvm框架,当视图改变更新模型层,当模型层改变更新视图层。在vue中,使用了双向绑定技术,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。

    2.vue-router 是什么?它有哪些组件

    答:vue用来写路由一个插件。router-link、router-view

    3.active-class 是哪个组件的属性?

    答:vue-router模块的router-link组件。children数组来定义子路由

    4.怎么定义 vue-router 的动态路由? 怎么获取传过来的值?

    答:在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id。

    5.vue-router 有哪几种导航钩子?

    答:三种,
    第一种:是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
    第二种:组件内的钩子
    第三种:单独路由独享组件

    6.$route 和 $router 的区别

    答: r o u t e r 是 V u e R o u t e r 的 实 例 , 在 s c r i p t 标 签 中 想 要 导 航 到 不 同 的 U R L , 使 用 router是VueRouter的实例,在script标签中想要导航到不同的URL,使用 routerVueRouterscriptURL,使router.push方法。返回上一个历史history用$router.to(-1)
    $route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。

    7.vue-router的两种模式

    答:hash模式:即地址栏 URL 中的 # 符号;
    history模式:window.history对象打印出来可以看到里边提供的方法和记录长度。利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)。

    8.vue-router实现路由懒加载( 动态加载路由 )

    答:三种方式
    第一种:vue异步组件技术 ==== 异步加载,vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js文件。
    第二种:路由懒加载(使用import)。
    第三种:webpack提供的require.ensure(),vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

    四、vuex常见面试题

    1.vuex是什么?怎么使用?哪种功能场景使用它?

    答:vue框架中状态管理。在main.js引入store,注入。
    新建了一个目录store.js,…… export 。
    场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

    2.vuex有哪几种属性?

    答:有五种,分别是 State、 Getter、Mutation 、Action、 Module
    state => 基本数据(数据源存放地)
    getters => 从基本数据派生出来的数据
    mutations => 提交更改数据的方法,同步!
    actions => 像一个装饰器,包裹mutations,使之可以异步。
    modules => 模块化Vuex

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

    答:如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
    如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用。
    以上面试题仅供个人学习,如有错误请指正。谢谢。

    展开全文
  • VUE常见面试题,含答案

    千次阅读 2020-12-21 15:56:42
    本文以前端面试官的角度出发,对 Vue 框架中一些重要的特性、框架的原理以问题的形式进行整理汇总,意在帮助作者及读者自测下 Vue 掌握的程度。本文章节结构以从易到难进行组织,建议读者按章节顺序进行阅读,当然...

    前言

    本文以前端面试官的角度出发,对 Vue 框架中一些重要的特性、框架的原理以问题的形式进行整理汇总,意在帮助作者及读者自测下 Vue 掌握的程度。本文章节结构以从易到难进行组织,建议读者按章节顺序进行阅读,当然大佬级别的请随意。希望读者读完本文,有一定的启发思考,也能对自己的 Vue 掌握程度有一定的认识,对缺漏之处进行弥补。

    1、说说你对 SPA 单页面的理解,它的优缺点分别是什么?

    SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
    优点:

    1.用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
    2.基于上面一点,SPA 相对对服务器压力小;
    3.前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

    缺点:

    1.初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
    2.前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
    3.SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

    2、v-show 与 v-if 有什么区别?

    「v-if」 是「真正」的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是「惰性的」:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。「v-show」 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

    3、Class 与 Style 如何动态绑定?

    Class 可以通过对象语法和数组语法进行动态绑定:
    对象语法:

    <div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
    data: {
      isActive: true,
      hasError: false
    }
    

    数组语法:

    <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    data: {
      activeClass: 'active',
      errorClass: 'text-danger'
    }
    

    Style 也可以通过对象语法和数组语法进行动态绑定:
    对象语法:

    <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
    data: {
      activeColor: 'red',
      fontSize: 30
    }
    

    数组语法:

    <div v-bind:style="[styleColor, styleSize]"></div>
    data: {
      styleColor: {
         color: 'red'
       },
      styleSize:{
         fontSize:'23px'
      }
    }
    

    4、怎样理解 Vue 的单向数据流?

    所有的 prop 都使得其父子 prop 之间形成了一个「单向下行绑定」:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。有两种常见的试图改变一个 prop 的情形 :

    「这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。」 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

    props: ['initialCounter'],
    data: function () {
      return {
        counter: this.initialCounter
      }
    }
    

    「这个 prop 以一种原始的值传入且需要进行转换。」 在这种情况下,最好使用这个 prop 的值来定义一个计算属性

    props: ['size'],
    computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }
    

    5、computed 和 watch 的区别和运用的场景?

    「computed:」 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
    「watch:」 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;「运用场景:」

    • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
    • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

    6、直接给一个数组项赋值,Vue 能检测到变化吗?

    由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:

    • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    • 当你修改数组的长度时,例如:vm.items.length = newLength

    为了解决第一个问题,Vue 提供了以下操作方法:

    // Vue.set
    Vue.set(vm.items, indexOfItem, newValue)
    // vm.$set,Vue.set的一个别名
    vm.$set(vm.items, indexOfItem, newValue)
    // Array.prototype.splice
    vm.items.splice(indexOfItem, 1, newValue)
    

    为了解决第二个问题,Vue 提供了以下操作方法:

    // Array.prototype.splice
    vm.items.splice(newLength)
    

    7、谈谈你对 Vue 生命周期的理解?

    「(1)生命周期是什么?」Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

    「(2)各个生命周期的作用」

    生命周期描述
    beforeCreate组件实例被创建之初,组件的属性生效之前
    created组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
    beforeMount在挂载开始之前被调用:相关的 render 函数首次被调用
    mountedel 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
    beforeUpdate组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
    update组件数据更新之后
    activitedkeep-alive 专属,组件被激活时调用
    deactivatedkeep-alive 专属,组件被销毁时调用
    beforeDestory组件销毁前调用
    destoryed组件销毁后调用

    「(3)生命周期示意图」

    在这里插入图片描述

    8、Vue 的父组件和子组件生命周期钩子函数执行顺序?

    Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

    • 加载渲染过程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
    • 子组件更新过程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
    • 父组件更新过程 父 beforeUpdate -> 父 updated
    • 销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

    9、在哪个生命周期内调用异步请求?

    可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

    • 能更快获取到服务端数据,减少页面 loading 时间;
    • ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;

    10、在什么阶段才能访问操作DOM?

    在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。vue 具体的生命周期示意图可以参见如下,理解了整个生命周期各个阶段的操作,关于生命周期相关的面试题就难不倒你了。

    在这里插入图片描述

    11、父组件可以监听到子组件的生命周期吗?

    **比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
    **

    <Child @mounted="doSomething"/>
        
    // Child.vue
    mounted() {
      this.$emit("mounted");
    }
    

    **以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:
    **

    //  Parent.vue
    <Child @hook:mounted="doSomething" ></Child>
    doSomething() {
       console.log('父组件监听到 mounted 钩子函数 ...');
    },
        
    //  Child.vue
    mounted(){
       console.log('子组件触发 mounted 钩子函数 ...');
    },    
        
    // 以上输出顺序为:
    // 子组件触发 mounted 钩子函数 ...
    // 父组件监听到 mounted 钩子函数 ...  
    

    当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

    12、谈谈你对 keep-alive 的了解?

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

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

    13、组件中 data 为什么是一个函数?

    为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

    // data
    data() {
      return {
     message: "子组件",
     childName:this.name
      }
    }
    // new Vue
    new Vue({
      el: '#app',
      router,
      template: '<App/>',
      components: {App}
    })
    

    因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

    14、v-model 的原理?

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

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

    以 input 表单元素为例:

    <input v-model='something'>
        
    相当于
    <input v-bind:value="something" v-on:input="something = $event.target.value">
    

    如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

    父组件:
    <ModelChild v-model="message"></ModelChild>
    子组件:
    <div>{{value}}</div>
    props:{
        value: String
    },
    methods: {
      test1(){
         this.$emit('input', '小红')
      },
    },
    

    15、Vue 组件间通信有哪几种方式?

    Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
    (1)「props / $emit 适用 父子组件通信」这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍
    (2)「ref 与 $parent / $children 适用 父子组件通信」

    • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
    • $parent / $children:访问父 / 子实例

    「(3)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信」这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件

    「(4) a t t r s / attrs/ attrs/listeners 适用于 隔代组件通信」

    • a t t r s : 包 含 了 父 作 用 域 中 不 被 p r o p 所 识 别 ( 且 获 取 ) 的 特 性 绑 定 ( c l a s s 和 s t y l e 除 外 ) 。 当 一 个 组 件 没 有 声 明 任 何 p r o p 时 , 这 里 会 包 含 所 有 父 作 用 域 的 绑 定 ( c l a s s 和 s t y l e 除 外 ) , 并 且 可 以 通 过 v − b i n d = " attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=" attrsprop()(classstyle)prop(classstyle)vbind="attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
    • l i s t e n e r s : 包 含 了 父 作 用 域 中 的 ( 不 含 . n a t i v e 修 饰 器 的 ) v − o n 事 件 监 听 器 。 它 可 以 通 过 v − o n = " listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=" listeners(.native)vonvon="listeners" 传入内部组件

    「(5)provide / inject 适用于 隔代组件通信」祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

    「(6)Vuex 适用于 父子、隔代、兄弟组件通信」Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

    • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
    • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

    16、你使用过 Vuex 吗?

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。(1)Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。(2)改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。主要包括以下几个模块:

    • State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
    • Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
    • Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
    • Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
    • Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

    17、使用过 Vue SSR 吗?说说 SSR?

    Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。

    「(1)服务端渲染的优点:」

    • 更好的 SEO:因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
    • 更快的内容到达时间(首屏加载更快):SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;

    「(2) 服务端渲染的缺点:」

    • 更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
    • 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

    如果没有 SSR 开发经验的同学,可以参考本文作者的另一篇 SSR 的实践文章《Vue SSR 踩坑之旅》,里面 SSR 项目搭建以及附有项目源码。

    18、vue-router 路由模式有几种?

    vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:

    switch (mode) {
      case 'history':
     this.history = new HTML5History(this, options.base)
     break
      case 'hash':
     this.history = new HashHistory(this, options.base, this.fallback)
     break
      case 'abstract':
     this.history = new AbstractHistory(this, options.base)
     break
      default:
     if (process.env.NODE_ENV !== 'production') {
       assert(false, `invalid mode: ${mode}`)
     }
    }
    

    其中,3 种路由模式的说明如下:

    • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
    • history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
    • abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

    19、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?

    「(1)hash 模式的实现原理」早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 ‘#search’:

    https://www.word.com#search
    

    hash 路由模式的实现主要是基于下面几个特性:

    • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
    • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
    • 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
    • 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

    「(2)history 模式的实现原理」HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:

    window.history.pushState(null, null, path);
    window.history.replaceState(null, null, path);
    

    history 路由模式的实现主要基于存在下面几个特性:

    • pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
    • 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
    • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

    20、什么是 MVVM?

    Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表 MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示:
    在这里插入图片描述

    1. View 层 View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。
    2. Model 层 Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。
    3. ViewModel 层 ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。我们以下通过一个 Vue 实例来说明 MVVM 的具体实现,有 Vue 开发经验的同学应该一目了然:
      (1)View 层
    <div id="app">
        <p>{{message}}</p>
        <button v-on:click="showMessage()">Click me</button>
    </div>
    

    (2)ViewModel 层

    var app = new Vue({
        el: '#app',
        data: {  // 用于描述视图状态   
            message: 'Hello Vue!', 
        },
        methods: {  // 用于描述视图行为  
            showMessage(){
                let vm = this;
                alert(vm.message);
            }
        },
        created(){
            let vm = this;
            // Ajax 获取 Model 层的数据
            ajax({
                url: '/your/server/data/api',
                success(res){
                    vm.message = res;
                }
            });
        }
    })
    

    (3) Model 层

    {
        "url": "/your/server/data/api",
        "res": {
            "success": true,
            "name": "IoveC",
            "domain": "www.cnblogs.com"
        }
    }
    

    21、Vue 是如何实现数据双向绑定的?

    Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据,如下图所示:
    在这里插入图片描述
    即:

    • 输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
    • Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。
      其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。Vue 主要通过以下 4 个步骤来实现数据双向绑定的:实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。实现一个订阅者 Watcher:Watcher 订阅者是,Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。以上四个步骤的流程图表示如下,如果有同学理解不大清晰的,可以查看作者专门介绍数据双向绑定的文章《0 到 1 掌握:Vue 核心之数据双向绑定》,有进行详细的讲解、以及代码 demo 示例。
      在这里插入图片描述

    22、Vue 框架怎么实现对象和数组的监听?

    如果被问到 Vue 怎么实现数据双向绑定,大家肯定都会回答 通过,Object.defineProperty() 对数据进行劫持,但是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,同理无法对数组进行劫持,但是我们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操作)的变化,那它是怎么实现的呢?我们查看相关代码如下:

    /**
       * Observe a list of Array items.
       */
      observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
          observe(items[i])  // observe 功能为监测数据的变化
        }
      }
      /**
       * 对属性进行递归遍历
       */
      let childOb = !shallow && observe(val) // observe 功能为监测数据的变化
    

    通过以上 Vue 源码部分查看,我们就能知道 Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。

    23、Proxy 与 Object.defineProperty 优劣对比

    「Proxy 的优势如下:」

    • Proxy 可以直接监听对象而非属性;
    • Proxy 可以直接监听数组的变化;
    • Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
    • Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
    • Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
      「Object.defineProperty 的优势如下:」
    • 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue
      的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

    24、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

    受现代 JavaScript 的限制 ,Vue 「无法检测到对象属性的添加或删除」。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 来实现为对象添加响应式属性,那框架本身是如何实现的呢?我们查看对应的 Vue 源码:vue/src/core/instance/index.js

    export function set (target: Array<any> | Object, key: any, val: any): any {
      // target 为数组  
      if (Array.isArray(target) && isValidArrayIndex(key)) {
        // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
        target.length = Math.max(target.length, key)
        // 利用数组的splice变异方法触发响应式  
        target.splice(key, 1, val)
        return val
      }
      // key 已经存在,直接修改属性值  
      if (key in target && !(key in Object.prototype)) {
        target[key] = val
        return val
      }
      const ob = (target: any).__ob__
      // target 本身就不是响应式数据, 直接赋值
      if (!ob) {
        target[key] = val
        return val
      }
      // 对属性进行响应式处理
      defineReactive(ob.value, key, val)
      ob.dep.notify()
      return val
    }
    

    我们阅读以上源码可知,vm.$set 的实现原理是:

    • 如果目标是数组,直接使用数组的 splice 方法触发相应式;
    • 如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

    25、虚拟 DOM 的优缺点?

    「优点:」

    • 「保证性能下限:」 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
    • 「无需手动操作 DOM:」 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
    • 「跨平台:」 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

    「缺点:」

    • 「无法进行极致优化:」 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

    26、虚拟 DOM 实现原理?

    虚拟 DOM 的实现原理主要包括以下 3 部分:

    • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
    • diff 算法 — 比较两棵虚拟 DOM 树的差异;
    • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

    如果对以上 3 个部分还不是很了解的同学,可以查看本文作者写的另一篇详解虚拟 DOM 的文章《深入剖析:Vue核心之虚拟DOM》

    27、Vue 中的 key 有什么作用?

    key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。Vue 的 diff 过程可以概括为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。具体有无 key 的 diff 过程,可以查看作者写的另一篇详解虚拟 DOM 的文章《深入剖析:Vue核心之虚拟DOM》 所以 Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速「更准确」:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。「更快速」:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:

    function createKeyToOldIdx (children, beginIdx, endIdx) {
      let i, key
      const map = {}
      for (i = beginIdx; i <= endIdx; ++i) {
        key = children[i].key
        if (isDef(key)) map[key] = i
      }
      return map
    }
    

    28、你有对 Vue 项目进行哪些优化?

    如果没有对 Vue 项目没有进行过优化总结的同学,可以参考本文作者的另一篇文章《 Vue 项目性能优化 — 实践指南 》,文章主要介绍从 3 个大方面,22 个小方面详细讲解如何进行 Vue 项目的优化。
    「(1)代码层面的优化」

    • v-if 和 v-show 区分使用场景
    • computed 和 watch 区分使用场景
    • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
    • 长列表性能优化
    • 事件的销毁
    • 图片资源懒加载
    • 路由懒加载
    • 第三方插件的按需引入
    • 优化无限列表性能
    • 服务端渲染 SSR or 预渲染
      「(2)Webpack 层面的优化」
    • Webpack 对图片进行压缩
    • 减少 ES6 转为 ES5 的冗余代码
    • 提取公共代码
    • 模板预编译
    • 提取组件的 CSS
    • 优化 SourceMap
    • 构建结果输出分析
    • Vue 项目的编译优化

    「(3)基础的 Web 技术的优化」

    • 开启 gzip 压缩
    • 浏览器缓存
    • CDN 的使用
    • 使用 Chrome Performance 查找性能瓶颈

    29、对于即将到来的 vue3.0 特性你有什么了解的吗?

    Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:

    「(1)监测机制的改变」
    3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:

    • 只能监测属性,不能监测对象
    • 检测属性的添加和删除;
    • 检测数组索引和长度的变更;
    • 支持 Map、Set、WeakMap 和 WeakSet。

    新的 observer 还提供了以下特性:

    • 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
    • 默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
    • 更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
    • 不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
    • 更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。

    「(2)模板」
    模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。

    「(3)对象式的组件声明方式」
    vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。

    「(4)其它方面的更改」
    vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:

    • 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
    • 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
    • 基于 treeshaking 优化,提供了更多的内置功能。
    展开全文
  • (都是一些基础的vue面试题,大神不用浪费时间往下看) 一、对于MVVM的理解? MVVM是Model-View-ViewModel的缩写。 Model :代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。 View :代表UI组件,它...
  • Vue常见面试题总结

    2021-04-29 08:06:32
    文章目录1、为何组件的data必须是一个函数2.vue常用的指令3.v-if与v-show的区别4.Vue的双向数据绑定原理(极简版)5.Vue组件通信6.什么是vuex?7.vue监听和深度监听watch8.Vue 中 methods,computed, watch 的区别9....


    1、为何组件的data必须是一个函数

    vue实例中的data属性既可以是一个对象,也可以是一个函数
    
    组件中定义data属性,只能是一个函数
    

    如果为组件data直接定义为一个对象 则会得到警告信息 警告说明:返回的data应该是一个函数在每一个组件实例中

    在我们定义好一个组件的时候,vue最终都会通过Vue.extend()构成组件实例
    
    vue组件可能会有很多个实例,采用函数返回一个全新data形式,
    使每个实例对象的数据不会受到其他实例对象数据的污染
    
    
    
    根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
    
    组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。
    采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象
    

    2.vue常用的指令

    v-model 多用于表单元素实现双向数据绑定
    
    v-for 格式: v-for="(item,index) in/of 数组json" 循环数组或json
    
    v-show 显示内容 ,通过display=block/none来控制元素隐藏出现
    
    v-hide 隐藏内容 同上
    
    v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)
    
    v-else-if 必须和v-if连用
    
    v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误
    
    v-bind 动态绑定 作用: 及时对页面的数据进行更改
    
    v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
    
    v-text 解析文本
    
    v-html 解析html标签
    
    v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 
    	2、三元型 ‘isred?“red”:“blue”’
    	3、数组型 ‘[{red:“isred”},{blue:“isblue”}]’
    
    v-once 进入页面时 只渲染一次 不在进行渲染
    

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

    都可以动态控制着dom元素的显示隐藏

    v-if: **控制DOM元素的显示隐藏是将DOM元素整个添加或删除;**
    
    v-show: **控制DOM 的显示隐藏是为DOM元素添加css的样式display,设置none或者是block,
    DOM元素是还存在的**
    

    1、性能对比

    v-if有更高的切换消耗;
    v-show有更高的初始渲染消耗
    

    2、使用场景

    v-if适合运营条件不大可能改变的场景下;
    v-show适合频繁切换;
    

    4.Vue的双向数据绑定原理(极简版)

    vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。

    主要由三部分:

    observer主要是负责对Vue数据进行数据劫持,使其数据拥有get和set方法 
    指令解析器负责绑定数据和指令,绑定试图更新方法 
    watcher负责数据监听,当数据发生改变通知订阅者,调用视图更新函数更新视图
    

    5.Vue组件通信

    请参考详细内容

    6.什么是vuex?

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储和管理程序的所有组件的数据

    1.vuex的核心由五部分组成

    state: 数据
    
    actions:可以包含异步操作
    
    mutations: 唯一可以修改state数据的场所
    
    getters: 类似于vue组件中的计算属性,对state数据进行计算(会被缓存)
    
    modules:模块化管理store(仓库),每个模块拥有自己的 state、mutation、action、getter
    

    2.详述Vuex运行机制

    在组件中通过dispatch来调用actions中的方法
    在actions中通过commit来调用mutations中的方法,
    在mutations中可以直接操作state中的数据,
    state的数据只要一发生改变立马响应到组件中
    

    7.vue监听和深度监听watch

    watch可以让我们监控一个值的变化,从而做出相应的反应。

    通过watch属性可以监控data属性中name值的变化,定义监控时,name这个值对应的是一个监控处理函数name()。
    
    将name属性和对话框绑定,并在对应页面绑定name值,然后在浏览器中测试,
    页面上数据会随着对话框中输入值的变化而变化。
    

    其中监控处理函数name有两个参数:

    v1表示当前监控的值。
    v2表示上一次监控的值。
    如果监控的是一个对象,需要进行深度监控,才能监控到对象中属性的变化
    

    8.Vue 中 methods,computed, watch 的区别

    computed具有缓存性 ,依赖于属性值,只有属性发生改变的时候才会重新调用
    methods是没有缓存的,只用调用,就会执行,一般结合事件来使用
    watch没有缓存性 监听data中的属性 属性值只要发生变化就会执行 可以利用他的特性做一些异步的操作
    

    9.MVVM和MVC

    MVC: Model(模型) View(视图) Controller(控制器)

     简单的理解:视图请求数据,将请求发送至控制器,控制器再将请求发送给模型,模型去查找数据,
     找到之后传给控制器,控制器再传给视图进行渲染。
    

    MVVM: Model 代表数据模型 View 代表UI视图 ViewModel负责监听,Model中数据的改变并且控制视图的更新

    简单理解:视图请求数据,将请求发送至控制器,在控制器的两端具有监听机制,直接调用模型的数据,
     一端改变全部改变,利用数据劫持, 结合订阅者和发布者模式,实现数据的双向绑定
    简单理解就是双向数据绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,
    数据也会跟着同步变化。
    

    10.vue中的修饰符

    .stop 阻止事件冒泡; ​ 
    .capture 设置事件捕获; ​ 
    .self 只是监听触发改元素的事件; ​ 
    .once 只触发一次; ​ 
    .trim 去除文本框左右空格 ​ 
    .bumber 把文本框的内容转换成数字 ​ 
    .prevent - 阻止默认事件; ​ 
    .native 触发js原生的事件 ​ 
    .keyup.enter ;  .keyup.space
    

    11.vue中如何自定义指令directive

    我们看到的v-开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能,对普通 DOM元素进行底层操作,这时候就会用到自定义指令。除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令

    1、 如何自定义指令

    注册一个自定义指令有全局注册与局部注册
    全局注册注册主要是用过Vue.directive方法进行注册 Vue.directive第一个参数
    是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数
    
    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {
        // 聚焦元素
        el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能
      }
    })
    
    
    局部注册通过在组件options选项中设置directive属性
    
    directives: {
      focus: {
        // 指令的定义
        inserted: function (el) {
          el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
        }
      }
    }
    
    
    然后你可以在模板中任何元素上使用新的 v-focus property,如下:
    
    <input v-focus />
    
    

    2、列举常用指令以及作用

    v-html 解析输出变量,能解析html; ​ 
    v-text 解析输出变量; ​ 
    v-bind 给标签绑定属性; ​
    v-on 给元素绑定事件; ​ 
    v-pre 跨过当前的标签不解析 ​ 
    v-cloak 解决差值表达式闪烁的问题; ​ 
    v-model 实现数据的双向绑定,只能适用于表单元素; ​
    v-for 可以循环遍历数据; ​
    v-if 条件输出; ​
    v-show 条件输出
    

    3.钩子函数

    自定义指令也像组件那样存在钩子函数:

    **bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置**
    
    **inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)**
    
    **update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。
    指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
    
    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
    
    unbind:只调用一次,指令与元素解绑时调用**
    

    4.使用自定义指令背景

    1 输入框自动聚焦
    
    2 下拉菜单
    
    3 相对时间转换
    

    12.Vue之filter

    过滤器就是一个数据经过了这个过滤器之后出来另一样东西。
    

    vue中的过滤器分为两种:局部过滤器和全局过滤器

    全局过滤器通过 Vue.filter('filtername',fliterFn) 来定义,它定义好了之后,在所有的组件内都可以使用
    
    局部过滤器,定义在组件内部 filters 属性上.它只能在此组件内部使用
    

    13.vue路由,路由传参(parmas,query)

    params和query的区别?
    
    1.用法上的 刚query要用path来引入,params要用name来引入,接收参数都是类似的,
    分别是this.$ route.query.name和this.$route.params.name 
    
    2.展示上的 query更加类似于我们ajax中get传参,params则类似于post,
    说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示 3.params是路由的一部分,必须要有。
    query是拼接在url后面的参数,没有也没关系
    

    14.keep-alive

    keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。

    1.作用

    在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,
    减少加载时间及性能消耗,提高用户体验性
    

    参数(Props)

    include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    max - 数字。最多可以缓存多少组件实例
    

    对生命周期函数变化

    被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
    

    15.v-for中为何要使用key

    key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,
    更准确, 更快的找到对应的vnode节点
    
    key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的
    使用index作为key时,和不带key是一样,index作为key时每个列表的index更新前后都是一样的,
    可以直接被复用,	用ID作为key是就不会被复用了,因为ID是唯一的标识。
    

    16.vue中的slot(插槽)

    1 、slot 基本用法

    插槽指允许将自定义的组件像普通标签一样插入内容
    

    2、具名插槽

    给具体的插槽命名,并在使用的时候传入插槽的名称
    

    3 、作用域插槽

    将定义插槽的变量作用域到使用插槽中
    

    17.vue之prop

    单项数据流

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,
    但是反过来则不行。
    每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。
    这意味着你不应该在一个子组件内部改变 prop。
    
    单项数据流不允许修改
    
    在组件中修改 prop 传递过来的数据 Vue 会发出警告,所以有两种常见的用法去修改 prop 传递过来的值
    本地data中定义属性,并将 prop 作为初始值
    使用computed 将prop 的值进行处理,
    

    总结

    prop 数据单项传递,父影响子,子不影响父
    不能在组件中直接修改 prop 传递过来的值,Vue 会给出警告
    prop 验证时,会在实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 
    在 default 或 validator 函数中是不可用的
    非 prop 特性,组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。
    

    18.Vue中的$nextTick()

    Vue 在更新 DOM时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,
    视图需要等队列中所有数据变化完成之后,再统一进行更新
    
    如果没有 nextTick 更新机制,那么每次更新值都会触发视图更新,有了nextTick机制,只需要更新一次,
    所以nextTick本质是一种优化策略
    

    1、使用场景

    如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()
    第一个参数为:回调函数(可以获取最近的DOM结构)
    第二个参数为:执行函数上下文
    

    组件内使用 vm.$ nextTick () 实例方法只需要通过this.$nextTick(),并且回调函数中的 this 将自动绑定到当前的 Vue 实例上

    $nextTick()会返回一个 Promise 对象,可以是用async/await完成相同作用的事情
    

    2.小结:

    把回调函数放入callbacks等待执行
    将执行函数放到微任务或者宏任务中
    事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
    

    19.vue修改数据页面不重新渲染

    原因:

    组件初始化时,对data中的item进行递归遍历,对item的每一个属性进行劫持,添加set,get方法。
    我们后来新加的newProperty属性,并没有通过Object.defineProperty设置成响应式数据,
    修改后不会视图更新。
    

    解决方案

    Vue 不允许在已经创建的实例上动态添加新的响应式属性
    若想实现数据与视图同步更新,可采取下面三种解决方案:
    **1. Vue.set()**
    **2. Object.assign()**
    **3. $forcecUpdated()**
    

    Vue.set( target, propertyName/index, value )

    参数:

    target:要修改的对象或数组
    propertyName/index:属性或下标
    value:修改后的value值
    

    Object.assign()

    直接使用Object.assign()添加到对象的新属性不会触发更新
    应创建一个新的对象,合并原对象和混入对象的属性
    
    
    如果为对象添加少量的新属性,可以直接采用Vue.set()
    如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
    如果你需要进行强制刷新时,可采取$forceUpdate() (不建议)
    

    20.vue2与vue3的区别

    1. vue2和vue3双向数据绑定原理发生了改变

    vue2 的双向数据绑定是利用ES5 的一个 API 
    Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。
    

    vue3 中使用了 es6 的 ProxyAPI 对数据代理。

    相比于vue2.x,使用proxy的优势如下:

    1.defineProperty只能监听某个属性,不能对全对象监听
    
    2.可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
    
    3.可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化
    

    2. 更精准的变更通知。

    比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;
    3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
    

    21.生命周期共有几个?分别在什么时候使用?

    1.创建

    beforeCreate() 在执行这个钩子的时候 只有一些实例本身的事件和生命周期函数 用户自定义不能使用 
    created() 最早开始使用 data和methods中数据的钩子 
    

    2.挂载

    beforeMount() 指令已经解析完毕 内存中已经生成dom数
    mounted()dom已经渲染完毕 页面和内存的数据已经同步 
    

    3.更新

    beforeUpdate() 当data的数据发生改变会执行这个钩子 内存中数据是新的 页面是旧的
    updated() 内存和页面都是新的
    

    4.销毁

    beforeDestroy()即将销毁 data和methods中的数据此时还是可以使用的可以做一些释放内存的操作 
    (内存:定时器,事件监听,订阅发布) 
    
    destroyed()已经销毁完毕 滴死周畏 
    
    
    9.activated 被 keep-alive 缓存的组件激活时调用。该钩子在服务器端渲染期间不被调用。
    
    10.deactivated被 keep-alive 缓存的组件停用时调用。该钩子在服务器端渲染期间不被调用。
    
    11.errorCaptured (2.5.0新增)当捕获一个来自子孙组件的错误时被调用。
    此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个 包含错误来源信息的字符串。
    此钩子可以返回 false 以阻止该错误继续向上传播。
    

    22.Object.defineProperty()方法有何作用

    Object.defineProperty() 方法 直接在一个对象上定义一个新的属性

    他有三个参数

    .Object.defineProperty(obj,prop,descriptor)

    obj 是定义属性的对象
    prop 是定义的属性名
    descriptor 属性描述符 
    

    1.数据描述符

    configurable 是否重新配置(删除) 布尔
    enumerable 是否可枚举 布尔 (for in for of object.keys())
    value 默认值
    writable 是否可写 布尔
    

    2.访问(存取)描述符

    get set
    它的返回值是被操作的对象 也就是obj参数
    有两个回调函数 get set 访问(存取)描述符
    

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

    参考此篇文章

    24.Vue中的$nextTick()

    1.NextTick是什么

    在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
    
    我们可以理解成,Vue 在更新 DOM时是异步执行的。
    当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
    

    $nextTick()详解

    25.Vue-router钩子函数和执行顺序

    vue-router钩子函数
    vue-router钩子函数及组件生命周期执行

    26.axios的封装

    在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。

    安装

    npm install axios; 
    

    引入

    一般我会在项目的src目录中,在里面新建一个http.js和一个api.js文件。
    http.js文件用来封装我们的axios,api.js用来统一管理我们的接口。
    
    1. 使用elementui
    import { Message,MessageBox } from 'element-ui'
    
    1. 先要在http.js里面引入axios
    import axios from 'axios'
    
    1. 环境的切换

    在我们开发项目的时候会有多种环境,就比如开发环境、测试环境、生产环境,使用axios.defaults.baseURL可以设置axios的默认请求地址。

    //在开发环境中的测试 development
    if(process.env.NODE_ENV == 'development') {
    	axios.defaults.baseURL = 'http://120.53.31.103:84/'
    }
    //在生产环境中的测试 production
    if(process.env.NODE_ENV == 'production') {
    	axios.defaults.baseURL = 'https://wap.365msmk.com/'
    }
    //还有一种环境 debug
    
    
    1. 设置响应超时时间

    通过axios.defaults.timeout设置默认的请求超时时间。如果超出了响应时间,就会告知用户当前请求超时,请刷新等等

    //响应超时的时间
    axios.defaults.timeout = 5000;
    
    
    1. 设置接口请求拦截
    //接口请求拦截
    axios.interceptors.request.use(
    	config => {
    		config.headers = { DeviceType : 'H5' } //设置响应头部
    		return config
    	}
    )
    
    
    1. 使用promise返回axios请求的结果

      post :

    export function get(url,params){
    	return new Promise((resolve,reject) => {
    		axios.get(url,{
    			params : params
    		}).then(res => {
    			resolve(res)
    		}).catch(err => {
    			reject(err)
    		})
    	})
    }
    
    

    get :

    export function post(url,params){
    	return new Promise((resolve,reject) => {
    		axios.post(url,params)
    		.then(res => {
    			resolve(res.data)
    		})
    		.catch(err => {
    			reject(err.data)
    		})
    	})
    }
    
    

    现在是api.js中的内容

    1. 首先是要在api.js中引入刚刚封装好的axios
    import {get,post} from '../http/http.js'
    //get post 同时都要引入
    
    1. 然后就可以根据接口文档来进行数据的获取啦
    //封装接口的方法
    export function getAppIndex() {
    	return get('api/app/recommend/appIndex')
    }
    
    export function getBanner() {
    	return get('api/app/banner')
    }
    
    export function getTel() {
    	return post('api/app/smsCode',{
    			//这里用的是params传参,直接写{}就可,不用再声明params啦
    			mobile : 18567391972,
    			sms_type : 'login'
    	})
    }
    
    
    1. 最后就可以去vue页面通过生命周期来获取数据

    注意,这里不一定 非要使用async函数,这只是其中一种方法

    async mounted() {
    			// 明星讲师、精品课程等等
    			let res = await getAppIndex();
    			
    			//添加到数组
    			this.startList = res.data.data.list
    			
    			// 轮播图列表
    			var banner = await getBanner();
    			// console.log('轮播图'+ banner)
    			if (banner.data.code == 200) {
    				this.bannerList = banner.data.data
    			}
    
    			// 手机验证码接口
    			let Tel = await getTel();
    			// console.log('手机验证码'+ Tel)
    
    			//
    		},
    
    

    27.虚拟DOM和diff算法

    虚拟dom是利用js描述元素与元素的关系。; 好处:是可以快速的渲染和高效的更新元素,提高浏览器的性能
    
    diff算法:
    优点:最终表现DOM上的修改只是部分的变更,可以保证高效的渲染,提高网页的性能
    缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHtml慢点
    
    
    虚拟DOM表现为一个Object对象。并且最少包含标签名(tag)、属性(attrs)和子元素对象(children)三个属性
    创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应
    

    在vue中如何应用虚拟DOM

    定义真实DOM

    <div id="app">
        <p class="p">节点内容</p>
        <h3>{{ foo }}</h3>
    </div>
    

    实例化vue

    const app = new Vue({
        el:"#app",
        data:{
            foo:"foo"
        }
    })
    
    

    vue源码中render函数渲染虚拟DOM

    (function anonymous() {
     with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',{staticClass:"p"},
           [_v("节点内容")]),_v(" "),_c('h3',[_v(_s(foo))])])}})
    
    
    

    通过vnode,vue可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作,经过diff算法得出一些需要修改的最小单位,在更新视图,减少了dom操作,提高了性能

    diff算法

    diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,
    最后用patch记录的消息去局部更新dom
    (diff的过程就是调用patch函数,比较新旧节点,一边比较一边给真是的DOm打补丁)
    

    在vue中,作用于虚拟DOM渲染成真实dom的新旧VNode节点比较

    展开全文
  • Vue 常见面试题整理(2021版)

    千次阅读 2021-03-29 00:04:34
    6.vue几种常用的指令7.vue常用的修饰符8.v-on 可以绑定多个方法吗?9.vue中 key 值的作用?10.什么是vue的计算属性?11.vue等单页面应用及其优缺点12.怎么定义 vue-router 的动态路由? 怎么获取传过来的值13.绑定 ...
  • vue常见面试题 面试题.docx
  • 1、Vue总结: vue的使用方式有两种 方式一:像jQuery一样引入使用--vue的特性都可以使用,双向数据绑定 方式二:跟node结合起来使用,使用的时候有点像java语言(面向对象)
  • vue常见面试题汇总

    千次阅读 2020-04-10 10:48:11
    目前来看公司面试的问题还是比较基础的,但是对于某些只追求会用并不研究其原理的同学来说可能就没那么容易了。所以大家不仅要追求学习的广度,更要追求深度。 OK,希望自己能早日拿到心仪的offer.
  • Vue常见面试题

    2021-05-26 17:22:25
    Vue面试题 一、Vue框架相关知识 1.Vue的双向数据绑定原理是什么?(了解会叙述) vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时...
  • 2020年vue常见面试题汇总

    千次阅读 2020-07-11 11:52:08
    vue相关 1.vue生命周期 什么是Vue生命周期? vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译、挂载DOM——渲染、更新——渲染、卸载等一系列过程为Vue的生命周期。 下面是vue生命...
  • vue面试题_vue常见面试题和答案[文].pdf
  • vue面试题之三:vue常见面试题

    千次阅读 2019-09-20 17:26:25
    3. vue常见面试题 1. vue的优缺点 优点:性能好,简单易用,前后端分离,双向数据绑定,单页面应用用户体验好。 缺点:不利于SEO优化。 2. keep-alive的作用是什么? <keep-alive>是Vue的内置组件,能在...
  • Vue 吧,我很喜欢尤大,最近刚发布了 Vue 的首部纪录片,真的很好看。 1.那你能讲一讲 MVVM 吗? MVVM 是 Model-View-ViewModel 缩写,也就是把 MVC 中的 Controller 演变成 ViewModel。Model 层代表数据模型,...
  • Vue常见面试题汇总

    万次阅读 多人点赞 2018-03-21 17:14:39
    请谈谈Vue中的MVVM模式MVVM全称是Model-View-ViewModelVue是以数据为驱动的,Vue自身将DOM和数据进行绑定,一旦创建绑定,DOM和数据将保持同步,每当数据发生变化,DOM会跟着变化。 ViewModel是Vue的核心,它是Vue...
  • 3.Vue中是如何检测数组变化的 理解 原理 能改变数组的方法: 4.为何vue采用异步渲染 5.nextTick实现原理 6.Vue中Computed的特点 7.watch中的deep:true是如何实现的 8.vue组件的生命周期 掌握每个声明周期...
  • vue常见面试题1

    2021-03-26 15:50:49
    1.vue优点? 答:轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb; 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习; 双向数据绑定:保留了angular的特点,在数据操作方面...
  • VUE常见面试题

    2021-04-05 17:59:40
    后台管理系统的侧边栏是如何实现的,如何实现多级嵌套 首先需要组好子父级路由表 再使用elementUl的menu组件,通过v-if判断是否存在子级搭配递归组件来实现多级嵌套 详情可以下载vue-element-admin(git地址:git clone...
  • vue 常见面试题整理

    2021-04-07 15:11:50
    vue的数据双向绑定将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起...
  • VUE常见面试题整理(一)

    千次阅读 2020-04-21 19:57:59
    4月终于结束了自己的面试,拿下Offer,对此我整理了很多面试的笔记。由此其中一篇给大家分享: 1.你知道vue的模板语法用的是哪个web模板引擎的吗?说说你对这模板引擎的理解 vue模板引擎 2.你知道v-model的原理吗...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,263
精华内容 3,305
关键字:

vue常见面试题

vue 订阅