‘壹’ Vue面试题集锦
原理:在创建Vue实例时,Vue会遍历data选项的属性,利用Object.defineProperty()为属性添加getter和setter对数据的读取进行劫持(getter用来依赖手机,setter用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。每个组件实例会有相应的watcher实例,会在组件渲染过程中记录依赖的所有数据属性,之后依赖项被改动时,setter方法会通知依赖与此data的watcher实例重新计算(派发更新),从而使它关联的组件重新渲染。
一句话总结:vue.js采用数据劫持结合发布-订阅模式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
我的理解:在new Vue的时候,在Observer中通过Object.defineProperty()达到数据劫持,代理所有数据的getter和setter属性,在每次触发setter的时候,都会通过Dep来通知Watcher,Watcher作为Observer数据监听器与Compile模板解析器之间的桥梁,当Observer监听到数据发生改变的时候,通过Updater来通知Compile更新视图。而Compile通过Watcher订阅对应数据,绑定更新函数,通过Dep来添加订阅者,达到双向绑定。
Vue实例从创建到销毁的全过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM->渲染、更新->渲染、卸载等一系列过程。
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
它可以分为8个阶段:创建前/后、载入前/后、更新前/后、销毁前/后。
第一次页面加载时会触发beforeCreate、created、beforeMounted、mounted
DOM渲染在mounted中就已经完成。
1、beforeCreate:可以在这加个loading事件,在加载实例时触发;
2、created:初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用;
3、mounted:挂载元素,获取DOM节点;
4、updated:如果对数据统一处理,在这里写上相应函数;
5、beforeDestroy:可以放一个确认停止事件的确认框;
6、nextTick:更新数据后立即操作DOM。
1、对象方法v-bind:class="{'orange': isRipe, 'green': isNotRipe}"
2、数组方法v-bind:class="[class1, class2]"
3、行内v-bind:style="{color: color, fontSize: fontSize+'px'}"
1、router-link标签会渲染为标签,咋填template中的跳转都是这种;
2、另一种是编辑式导航,也就是通过js跳转比如router.push('/home')
M- model ,model 表示数据模型,也可以在model中定义数据修改和操作的业务逻辑
V- view,view表示视图,它负责将数据模型转换为视图展示出来
VM- viewmodel,viewmodel是一个同步view和model的对象,连接view和model,用于监听数据模型的改变和控制视图行为
computed:computed是计算属性,也就是计算值,更多用于计算值的场景。它具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时重新调用对应的getter来计算。
watch:watch更多的是观察作用,类似于某些数据的监听回调,用于观察props、$emit或者本组件的值,当数据变化时用来执行回调进行后续操作。它不具有缓存性,页面重新渲染时值不会变化也不会执行。
在style标签上添加scoped属性,以表示它的样式作用于当下的模块,很好的实现了样式私有化的目的,但是也得慎用,样式不易改变。
解决方法:
① 使用混合型的css样式,混合使用全局样式和私有样式。
② 深度作用选择器:如果你希望scoped样式中的一个选择器能够作用的更深,可以使用>>>操作符。如:<style scoped>.a>>>.b{/ ... /}</style>
一个元素绑定多个事件的写法有两种:
1、修饰符的使用
2、在method方法里分别写两个事件
Vue组件中的data值不能为对象,因为对象是引用类型,组件可能会被多个实例同时引用,如果data值为对象,将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。
原理:JS执行是单线程的,它是基于事件循环的。所有同步任务都在主线程上执行,形成一个执行栈。主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件。一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看那些对应的异步任务,等结束等待状态,进入执行栈,开始执行。主线程不断重复上面的步骤。主执行的执行过程就是tick,所有的异步结果都是通过任务队列来调度的。任务分为两大类:宏任务和微任务,宏任务包括:setTimeOut等,微任务包括promise.then。
Vue用异步队列的方式来控制DOM更新和nextTick回调先后执行。在下次DOM更新循环结束之后执行延迟回调,nextTick主要使用了宏任务和微任务,nextTick把要执行的任务推入一个队列中,在下一个tick同步执行队列的所有任务,它是异步任务中的微任务。如果我们在更新了一个响应式数据后,需要同步拿到这个渲染后的DOM结果,就使用nextTick方法,异步拿这个结果。
使用方式:
① this. nextTick.then(cb); 异步
父组件调用子组件的方法
父组件: this.$refs.yeluosen.childMethod()
子组件向父组件传值并调用方法: $emit
组件之间: bus==$emit+$on
1、第一种方法是直接在子组件中通过this. emit向父组件触发一个事件,父组件监听这个事件就行了。
3、第三种都可以实现子组件调用父组件的方法。
keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
原来的组件实例会被复用。这也意味着组件的生命周期钩子不会再被调用,你可以简单的监控watch $route对象:
is用来动态切换组件,DOM模板解析
全局的:前置守卫、后置钩子(beforeEach、afterEach)beforeResolve
单个路由独享的:beforeEnter
组件级的:beforeRouteEnter(不能获取组件实例this)、beforeRouteUpdate、beforeRouteLeave
这是因为在执行路有钩子函数beforeRouteEnter的时候,组件还没有被创建出来,先执行beforeRouteEnter,再执行周期钩子函数beforeCreate,可以通过next获取组件的实例对象,如:next((vm) => {}),参数vm就是组件的实例化对象。
缺点:
优点:
计算属性是需要复杂的逻辑,可以用方法method代替。
vue-cli提供的脚手架模板有browserify和webpack。
① 是什么?
Vue框架中的状态管理,分别是State、Getter、Mutation、Action、Mole。
② 怎么使用?
新建一个目录store。
③ 功能场景?
单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车等。
④ vuex的状态:
a. State特性:vuex就是一个仓库,仓库里面放了很多对象,其中state就是数据源存放地,对应于一般Vue对象里面的data。state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新。它通过mapState把全局的state和getters映射到当前组件的computed计算属性中。
b. Getter特性:getters可以对State进行计算操作,它就是store的计算属性。虽然在组件内可以做计算属性,但是getters可以在多组件之间复用。如果一个状态只在一个组件内使用,可以不用getters。
c. Mutation特性:改变store中state状态的唯一方法就是提交mutation,每个mutation都有一个字符串类型的事件类型和一个回调函数,我们需要改变state的值就要在回调函数中改变。我们要执行这个回调函数,那我们需要执行一个相应的调用方法:store.commit。
d. Action特性:类似于mutation,不同点在于:Action提交的是mutation,而不是直接变更状态。Action可以包含任意异步操作,Action函数接受一个与store实例具有相同方法和属性的context对象,因此你可以调用context.commit提交一个mutation。或者通过context.state和context.getters来获取state和getters。Action通过store.dispatch方法触发:store.dispatch('increment')。
e. Mole特性:Mole其实只是解决了当state中很复杂臃肿的时候,mole可以将store分解为模块,每个模块中拥有自己的state、mutation、action和getter。
① 创建组件页面eg Toast.vue
② 用Vue.extend() 扩展一个组件构造器,再通过实例化组件构造器,就可以创造出可复用的组件。
③ 将toast组件挂载到新创建的div上;
④ 将toast组件的dom添加到body里;
⑤ 修改优化达到动态控制页面显示文字跟显示时间;
修饰符分为:一般修饰符、事件修饰符、按键、系统
① 一般修饰符:
② 事件修饰符
③ 按键修饰符
④ 系统修饰符(可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。)
Vue的核心的功能,是一个视图模板引擎,但这不是说Vue就不能成为一个框架。在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
在我看来,渐进式代表的含义是:主张最少。视图模板引擎每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。
比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
渐进式的含义,我的理解是:主张最少,没有多做职责之外的事。
‘贰’ 能说说vue的响应式原理吗
Vue 是一个 MVVM 框架,核心是双向数据绑定,VM(视图模型)是作为 V(视图) 和 M(模型)的桥梁。下面是对 Vue 响应式(双向数据绑定)的理解,如果错误尽请指出,一起交流,共同进步。
Vue响应式原理核心是 数据劫持,采用 ES5 的 object.defineproperty 的 getter 和 setter 方法。从一个例子出发:
‘叁’ Vue学习系列一 —— MVVM响应式系统的基本实现原理
MVVM是Model-View-ViewModel的简写。它模式是MVC—>MVP—>MVVM的进化版。
Model负责用JavaScript对象表示,View负责UI界面显示,两者做到了最大限度的分离。
而把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的界面修改同步回Model更新数据。
脏值检查 : angular.js 是通过脏值检测的方式来比对数据是否有变更而决定是否更新视图。
原理是,拷贝一份 _viewModel 在内存中,用户操作导致 viewModel 发生改变的行为时,框架都会把 _viewModel 和最新的 viewModel 进行深度比较,一旦发现有属性发生变化,则重新渲染与之绑定的DOM节点。
最简单的方式就是通过 setInterval() 定时轮询检测数据变动,angular触发时进入脏值检测。但只限 指定的事件 (如:用户点击,输入操作,ajax请求,setInterval,setTimeout等...),否则需手动调用 apply 函数去强制执行一次脏检查。
数据劫持 : vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter , getter 在数据变动时发布消息给订阅者,触发相应的监听回调,而产生更新数据和视图。
原理图告诉我们,data属性定义了getter、setter对属性进行劫持,当属性值改变是就会notify通知watch对象,而watch对象则会重新触发组件呈现功能,继而更新view上的DOM节点树。
反之,view上输入数据时,也会触发data变更,也会触发订阅者watch更新,这样子model数据就可以实时更新view上的数据变化。这样一个过程就是vue的数据双向绑定了。
vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过 Object.defineProperty() 来实现对属性的劫持,达到监听数据变动的目的。
Object.defineProperty 是ES5一个方法,可以直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象,对象里目前存在的属性描述符有两种主要形式: 数据描述符 和 存取描述符 。
数据描述符 是一个拥有可写或不可写值的属性。
存取描述符 是由一对getter-setter函数功能来描述的属性。
描述符必须是两种形式之一;不能同时是两者。即:有值和可写,或者可get和set
属性描述符包括:
我们已经知道怎么实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器 Observer ,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者 Watcher 看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器 Dep 来专门收集这些订阅者,然后在监听器 Observer 和订阅者 Watcher 之间进行统一管理的。接着,我们还需要有一个指令解析器 Compile ,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者 Watcher ,并替换模板数据或者绑定相应的函数,此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
因此接下去我们执行以下4个步骤,实现数据的双向绑定:
深入响应式原理
剖析Vue原理&实现双向绑定MVVM
《响应式系统的基本原理》.js
JavaScript实现MVVM之我就是想监测一个普通对象的变化