1.为什么使⽤虚拟DOM(常问)
1.创建真实DOM的代价⾼:真实DOM节点实现的属性很多,⽽虚拟节点仅仅实现⼀些必要的属性,相⽐起来,创建⼀个虚拟节点的成本⽐较低。
2.使⽤虚拟节点,相当于加了⼀个缓冲,让⼀次数据变动所带来的所有节点变化,先在虚拟节点中进⾏修改,然后通过diff算法之后对所有产⽣差异的节点集中⼀次对DOM树进⾏修改,以减少浏览器的重绘及回流
3.虚拟dom由于本质是⼀个js对象,因此天⽣具备跨平台的能⼒,可以实现在不同平台的准确显示。
4.现代前端框架的一个基本要求就是无须手动操作DOM,一方面是因为手动操作DOM无法保证程序性能,另一方面更重要的是省略手动DOM操作可以大大提高开发效率。
2.Vue的双向数据绑定
实现mvvm的双向绑定,是采⽤数据劫持结合发布者-订阅者模式的⽅式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
3.Vue导航守卫的钩子函数有哪些?
路由守卫就是路由跳转过程中的⼀些钩⼦函数 ,在路由跳转的时候,做⼀些判断或其它的操作。 类似于组件⽣命周期钩⼦函
数 。
分为:全局守卫、独享守卫、组件内守卫
全局守卫:全局前置路由守卫和全局后置路由守卫
router.beforeEach:全局前置守卫,进⼊路由之前,初始化时执行、每次路由切换前执行
router.beforeResolve:全局解析守卫,在beforeRouteEnter调⽤之后调⽤
router.afterEach:全局后置钩⼦,进⼊路由之后,初始化时执行、每次路由切换后执行
独享路由守卫:
beforeEnter:在独享的路由配置beforeEnter方法,进入该路由前执行。
组件内守卫:
beforeRouteEnter():进⼊路由前
beforeRouteUpdate():路由复⽤同⼀个组件时
beforeRouteLeave():离开当前路由时
4.Vuex是什么,怎么使用,有什么属性?
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),vuex形象的说是一个数据共享仓库,也是一种组件间通信的方式,且适用于任意组件间通信。
当给Vue构造函数使用use方法注入vuex插件后,在组件中可以使用 this.$store 访问到状态库;接下来通过访问、调用store中的方法实现数据通信。
属性:state/getter/mutation/actions/module
1.state:
是一个数据存放的地方,类似于页面中的data。所有的组件之间都是可以共享的,但是不可以直接的去修改state下面的数据,通过 this.$store.state 可拿到公共状态数据。
2.getter:
类似组件的computed,getters对象中定义一条属性方法,该方法第一个参数默认为state,这样就可以拿到state里的数据并通过写逻辑return一个你想要的数据。调用getters中的方法为:this.$store.fn或this.$store.fn(params)
3.Mutations:
类似组件的methods,官方规定不允许直接给 this.$store.state.xxx 属性直接赋值来修改状态,应该用调用mutations来修改状态数据。Mutation执行模式为同步执行,即不能写异步函数,比如定时器和延时器会出错。调用mutations中的方法时,使用commit指令。即 this.$store.commit('addCount'),注意这里要传字符串属性名。第一个参数为state,从第二个参数开始为调用函数的参数。
4.Actions
和Mutations类似,区别在于其执行模式为异步执行,调用actions中的方法使用dispatch指令,指令函数接受参数略有不同。即 this.$store.dispatch('addCountAsync')。那么如何知道 action 什么时候结束呢?return一个Promise实例即可 this.$store.dispatch('addCountAsync').then(()=>{//...})。Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
5.Modules:
项⽬特别复杂的时候,可以让每⼀个模块拥有⾃⼰的 state 、mutation 、 action 、 getters ,使得结构⾮常清晰,⽅便管理。
5.mvc和mvvc的区别
MVC包括view视图层、controller控制层、model数据层。各部分之间的通信都是单向的。
1.View传送指令到 Controller
2.Controller完成业务逻辑后,要求Model改变状态
3.Model将新的数据发送到View,⽤户得到反馈
MVVM包括view视图层、model数据层、viewmodel层。各部分通信都是双向的。
采⽤双向数据绑定,View的变动,⾃动反映在 ViewModel,反之亦然。 mvvm代表框
架:Angularjs、React、Vue mvvm主要解决了mvc中⼤量 dom操作使得⻚⾯渲染性能
降低,加载速度变慢,影响⽤户体验
6..说出⾄少vue 3个常⽤事件修饰符?
.stop 阻⽌点击事件冒泡
.prevent 阻⽌默认事件
.once 只执⾏⼀次
.self 只在元素本身触发
7.vue中的路由工作模式
hash模式
1、vue-router默认是hash模式
2、url中有“#”号
3、hash值(“#”后的值)不会被包含在http请求中,改变hash值不会引起页面的重新加载。
4、hash改变会触发hashChange事件,会被浏览器记录下来,可以使用浏览器的前进和后退。
5、hash兼容到IE8以上
6、 会创建hashHistory对象,在访问不同的路由的时候,会发⽣两件事:
HashHistory.push()将新的路由添加到浏览器访问的历史的栈顶
HasHistory.replace()替换到当前栈
history模式
1、url不带参数
2、history 兼容 IE10 以上
3、history 模式需要后端配合将所有访问都指向 index.html,否则用户刷新页面,会导致 404 错误
4、在HTML4中常用的方法:
history.forward():在历史记录中前进一步
history.back():在历史记录中后退一步
history.go(n):在历史记录中跳转n步骤,n=0为刷新本页,n=-1为后退一页。
5、HTML5中新增了
history.pushState(data[,title][,url]):向历史记录中追加一条记录
history.replaceState(data[,title][,url]):替换当前页在历史记录中的信息
history.state:是一个属性,可以得到当前页的state信息。
window.onpopstate:是一个事件,在点击浏览器后退按钮或js调用forward()、back()、go()时触发。监听函数中可传入一个event对象,event.state即为通过pushState()或replaceState()方法传入的data参数
8..watch/computed的区别
computed是计算属性
依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发⽣改变,下⼀次获取computed的值时才会重新计算computed的值。与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
watch是监听属性
类似于某些数据的监听回调,每当监听的数据变化时都会执⾏回调进⾏后续操作。比如做一个逻辑,用新的温度减去旧的温度,如果这个温度差达到了多少,我们可以做后续的操作,提示用户多穿衣服,或者往后台发一个请求什么的。
当我们需要进⾏数值计算,并且依赖于其它数据时,应该使⽤computed。
当我们需要在数据变化时执⾏异步或开销较⼤的操作时,应该使⽤watch。
9.vue2与vue3的区别
1. vue2和vue3双向数据绑定原理发⽣了改变
vue2的双向数据绑定是利⽤ES5的⼀个API,Object.defineProperty()对数据进⾏劫持,结合发布订阅模式的⽅式来实现的。
vue3中使⽤了es6的ProxyAPI对数据代理。相⽐于vue2.x,使⽤proxy的优势如下:
• defineProperty只能监听某个属性,不能对全对象监听
• 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
• 可以监听数组,不⽤再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化
2. 默认进⾏懒观察(lazy observation)。
在 2.x 版本⾥,不管数据多⼤,都会在⼀开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压
力。3.x 版本,只会对「被⽤于渲染初始可⻅部分的数据」创建观察者,⽽且 3.x 的观察者更⾼效。
3. 更精准的变更通知。
比例来说:2.x 版本中,使⽤ Vue.set 来给对象新增⼀个属性时,这个对象的所有 watcher 都会重新运⾏;3.x 版本中,只有依赖那个属性的 watcher 才会重新运⾏。
4.项⽬⽬录结构
vue-cli2.0与3.0在⽬录结构⽅⾯,有明显的不同
vue-cli3.0移除了配置⽂件⽬录,config 和 build ⽂件夹,同时移除了 static 静态⽂件夹,新增了 public ⽂件夹,打开层级⽬录还会发现, index.html 移动到 public 中
9.vue中nextTick
vue中的nextTick:它主要用于处理数据动态变化后,dom还未及时更新的问题,用nextTick可以获取数据更新后最新dom的变化
vue中nextTick的用法:在下次dom更新循环结束之后执行延迟回调。就是由于vue中dom更新是异步执行的,也就是说修改数据的时候,视图它不会立即更新,而是会监听数据变化,并缓存在同一事件循环中等同一数据循环中的所有数据变化完成之后,再统一进行视图更新。经常我们会在(dom)还未更新的时候就使用了某个元素,这样是拿不到变更后的dom的,所以为了确保能够得到更新后的dom,就设置了nextTick方法,在修改数据之后,立即使用这个方法获取更新后的dom。
vue实际项目中,一般什么时候使用nextTick?
比如当我们需要在生命周期的created函数进行一些dom操作的时候,就一定要把相关的代码逻辑写在nextTick的回调函数中,这是因为在created钩子函数中,dom还未进行任何渲染,此时如果进行dom操作是没有用的,所以我们这个时候一定要把相关的js代码放进nextTick的回调函数中,又或者说在数据变化的时候,我们要想执行某个动作,而这个动作需要使用随数据变化而改变的dom结构的时候,也需要把相关逻辑写入nextTick的回调函数中
10.vue组件间通信方式
1.props / $emit
父组件通过 props 向子组件传递数据,子组件通过$emit方法和父组件通信
2.ref属性 自定义事件(适用于:子组件 ===> 父组件</strong>)
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(<span style="color:red">事件的回调在A中
3.全局事件总线
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
4.消息订阅与发布(pubsub-js)
5.Vuex