前端常见面试题(五)@郝晨光


对MVC、MVP、MVVM的理解

  1. MVC


    阮一峰老师MVC图示

M 表示 Model , 专门用来处理数据模型。
V 表示View, 专注页面布局和数据显示。
C 表示Controller 专注于控制,执行业务逻辑,操作模型和视图。

MVC的数据之间通信都是单向的

View(视图层) 传送指令到 Controller(控制层)
Controller(控制层) 完成业务逻辑后,要求 Model(模型层) 改变状态
Model(模型层) 将新的数据发送到 View(视图层),用户得到反馈

在MVC中,虽然View与Model之间的耦合度非常小,只需要Model修改的时候通知View发生改变即可,但是它们之间还有有很重要的联系,于是,就有了MVP

  1. MVP


    阮一峰老师MVP图示

M 表示 Model,专门用来处理数据模型
V 表示View,专门用来处理用户视图
P 表示Presenter,用来处理业务逻辑,在MVC的基础上,修改了通信方向

MVP中,View和Model之间没有任何通信关系,所有的通信和业务逻辑都放在Presenter层中

View(视图层) 发送指令到 Presenter层,
Presenter层 处理业务逻辑,要求 Model(模型层) 改变状态
Model(模型层) 修改状态之后,发送指令到 Presenter层
Presenter层通知View(视图层)做出改变

在MVP中,所有的通信都是双向的,View和Model不会直接发生通信,都通过Presenter层进行传递
在MVP中,所有的业务逻辑都写在Presenter层中,会导致Presenter层过于臃肿
在MVP中,View只负责显示视图,不包含任何业务逻辑,导致View层过薄,不具备任何主动性

由于在MVP模式中,所有的业务逻辑都放在Presenter层中,必须通过Presenter层来修改View层的界面,所以出现了MVVM

  1. MVVM


    阮一峰老师MVVM图示

M 表示 Model,专门用来处理数据模型
V 表示View,专门用来处理用户视图
VM 表示ViewModel,用来使的View视图层与Model层双向绑定,View的任何变动都会通知ViewModel,而Model的任何变动,也都会通知ViewModel,而不论哪一项发生改变,都会使对应的视图/数据模型同步发生改变

在MVVM中,与MVP一样,所有的通信都是双向的,数据与视图不直接发生依赖,全部通过VM层来进行双向绑定
所有的业务逻辑都由VM来进行处理,但是在View层和Model层修改都会通过VM来双向的绑定修改



谈谈对$nextTick的理解及使用场景

  1. 对nextTick的理解
    • Vue的视图更新是通过数据驱动的,当数据发生改变的时候,将当前的数据更改保存在队列中,然后异步的更新视图
    • 由于Vue的视图更新是异步的,所以我们在修改完成数据之后,不一定当前的View已经发生改变,于是就有了nextTick
    • nextTick是在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
  2. nextTick的使用场景
    • 如果要在created()钩子函数中进行的DOM操作,由于created()钩子函数中还未对DOM进行任何渲染,所以无法直接操作,需要通过$nextTick()来完成。在created()钩子函数中进行的DOM操作,不使用$nextTick()会报错

    • 更新数据后,想要使用js对新的视图进行操作时

    • 在使用某些第三方插件时 ,这些插件需要dom动态变化后重新应用该插件,这时候就需要使用$nextTick()来重新应用插件的方法

  3. nextTick的使用方法
    1. 使用Vue.nextTick全局方法
      // 修改数据
      vm.msg = 'Hello'
      // DOM 还没有更新
      Vue.nextTick(function () {
         // DOM 更新了执行的回调
      })
      
      // 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
      Vue.nextTick() .then(function () {
            // DOM 更新了
      })
      
    2. 在Vue实例对象中,使用this.$nextTick方法
      new Vue({
           // ...
                methods: {
               // ...
                  example () {
                  // 修改数据
                 this.message = 'changed'
                 // DOM 还没有更新
                this.$nextTick(function () {
                // DOM 现在更新了
                // `this` 绑定到当前实例
                this.doSomethingElse()
            })
          }
        }
      })
      



渐进式框架的理解

对于渐进式框架来说,主要就代表着它的主张性最小,没有多做职责之外的事。

  • 什么是主张性?
    在你使用一个框架的时候,如果你使用了它的一部分,你就必须使用它的全部,或是很大一部分东西,这个就叫强主张
    而说Vue的主张性最小,是因为你可以只使用它的一部分东西,它可以很容易的集成到你原有的项目中去,甚至说你只是用它来改造一下一些公共的组件,你可以在传统的html页面中使用它,也可以使用脚手架工具来搭建一个大型的项目
  • 在学习方面,Vue的学习代价也不是很大,你可以非常容易的上手,我们没有必要一上来就搞明白Vue的所有功能,可以先从核心功能扩展,然后在逐步学习,当你需要的时候,你再去使用它的其他功能,并且你可以非常容易的使用Vue结合其他的框架来进行搭配



组件样式属性 scoped 问题及解决方法

在Vue组件中,为了使样式私有化(模块化),不对全局造成污染,可以在style标签上添加scoped属性以表示它的只属于当下的模块,局部有效。
Vue中的scoped属性的效果主要通过PostCSS转译实现,给所有的样式动态的生成一个属性,然后通过在编译后使用属性选择器加原有选择器的方式来设置样式,这样就不会对其它组件造成影响,当前组件的样式只对当前组件生效

然而,我们在使用scoped的时候,经常会遇到样式问题,比如说

  1. 要在父组件中修改子组件的布局样式等
  2. 要修改v-html渲染出的内容的样式时
  3. 使用一些UI框架时,修改样式的问题

对于这些问题的解决方案:

1.同时使用scoped局部样式和全局样式

<style>
/*全局样式*/
</style>
<style scoped>
/*局部样式*/
</style>
  1. 子组件的根节点元素会同时被设置了scoped的父css样式和设置了scoped的子css样式影响,这么设计的目的是父组件可以对子组件根元素进行布局。

  2. 使用深选择器,>>>或者/deep/或者::v-deep进行样式穿透
    在Vue中,可以使用>>>或者/deep/或者::v-deep来进行样式穿透,达到修改样式的功能
    在sass或者less这种预编译器中,不支持>>>,可以使用/deep/或者::v-deep
    在vue-cli3.0以上的版本,建议始终使用::v-deep

<style scoped>
.el-form  >>>  .el-input {
    // 修改样式
}
.el-form  /deep/  .el-input {
    // 修改样式
}
.el-form  ::v-deep  .el-input {
    // 修改样式
}
</style>

建议使用第三种方法,这样既不会对全局样式造成污染,也可以达到修改样式的功能



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

Vue的双向数据绑定是通过数据劫持结合发布者订阅者模式来实现的
要实现这种双向数据绑定,必要的条件有:

1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
4、MVVM入口函数,整合以上三者

Vue数据双向绑定实现的原理

个人理解:在new Vue的时候,在Observer中通过Object.defineProperty()达到数据劫持,代理所有数据的getter和setter属性,在每次触发setter的时候,都会通过Dep来通知Watcher,Watcher作为Observer数据监听器与Compile模板解析器之间的桥梁,当Observer监听到数据发生改变的时候,通过Updater来通知Compile更新视图
而Compile通过Watcher订阅对应数据,绑定更新函数,通过Dep来添加订阅者,达到双向绑定



详述虚拟DOM中的diff算法

  1. 首先要先讲一下虚拟DOM是如何实现的
    虚拟DOM是通过js语法来在内存中维护一个通过数据解构描述出来的一个模拟DOM树,当数据发生改变的时候,会先对虚拟DOM进行模拟修改,然后在通过新的虚拟DOM树与旧的虚拟DOM树来对比,而这个对比就是通过diff算法来进行的
    虚拟DOM最大的意义不在于性能的提升(JavaScript对象比DOM对象性能高),而在于抽象了DOM的具体实现(对DOM进行了一层抽象)
  2. 接着,来看一下diff算法是如何进行比对的
    正常的diff算法,是通过层层对比,单单对比来进行的,对于我们的前端性能来说,很明显是不可以这样实现的。
    前端的虚拟DOM对比

所以,前端的diff算法是通过以下步骤来实现的

步骤一:用JS对象模拟DOM树
步骤二:比较两棵虚拟DOM树的差异
步骤三:把差异应用到真正的DOM树上

同时维护新旧两棵虚拟DOM树,当数据发生改变的时候,开始执行对比
首先对根元素进行对比,如果根元素发生改变就直接对根元素替换
如果根元素没有发生改变的话,再对下一层元素进行对比,如果对比发现元素发生删除,就执行删除,发现元素被替换就执行替换,发现添加了新的元素就执行添加
对比的同时,会通过key值来判断元素是否发生改变,判断元素是仅仅位置发生改变还是需要整个替换或删除
如果不是元素发生改变的话,再对内容进行对比,如果是内容发生改变的话,就直接修改内容
其实就是进行逐层对比,再通过不同的对比来判断执行不同的操作



Vue提供了几种脚手架模板

  • webpack - 一个全面的webpack+vue-loader的模板,功能包括热加载,linting,检测和CSS扩展。

  • webpack-simple - 一个简单webpack+vue-loader的模板,不包含其他功能,让你快速的搭建vue的开发环境。

  • browserify - 一个全面的Browserify+vueify 的模板,功能包括热加载,linting,单元检测。

  • browserify-simple - 一个简单Browserify+vueify的模板,不包含其他功能,让你快速的搭建vue的开发环境。

  • simple - 一个最简单的单页应用模板。

  • pwa - 一个集成pwa环境的webapp的模板



常见的几种MVVM的实现方式

实现数据绑定的做法有大致如下几种:

  • 发布者-订阅者模式(backbone.js)
  • 脏值检查(angular.js)
  • 数据劫持(vue.js)
  1. 发布者-订阅者模式
    一般通过sub,pub的方式实现数据和视图的绑定监听,
    更新数据方式通常做法是 vm.set('property', value)

    这种方式现在毕竟太low了,我们更希望通过vm.property = value这种方式更新数据,同时自动更新视图,于是有了下面两种方式。

  2. 脏值检查

    angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,
    最简单的方式就是通过 setInterval() 定时轮询检测数据变动,
    angular只有在指定的事件触发时进入脏值检测,大致如下:

    1、DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
    2、XHR响应事件 ( $http )
    3、浏览器Location变更事件 ( $location )
    4、Timer事件( $timeout , $interval )
    5、执行 $digest()$apply()

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



本文 CSDN 地址: 前端常见面试题(五)@郝晨光

结言
感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 一:什么是闭包?闭包的用处? (1)闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就 是将函数内部和函数外...
    xuguibin阅读 9,194评论 1 52
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 借鉴别人 1,对MVC 、MVVM、MVP的理解 (1)MVC Models: 数据层,负责数据的请求和存储以及处...
    小程要谦虚阅读 745评论 0 8
  • 【转载】CSDN - 张林blog http://blog.csdn.net/XIAOZHUXMEN/articl...
    竿牍阅读 3,459评论 1 14
  • Vue八个生命周期 beforeCreate【创建前】created【创建后】 beforeMount【载入前】 ...
    艾萨克菊花阅读 1,269评论 0 12