前端面试(总结了一些常见的VUE面试题)

99 阅读18分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情 >>

vue是单页面应用程序,所以页面一刷新数据部分数据也会跟着丢失,所以我们需要将store中的数据存储到本地,才能保证路由不丢失

vue 中哪些操作自动触发更新视图

vue数组对象修改触发视图更新

直接修改数组元素是无法触发视图更新的,如


this.array[0] = {
  name: 'meng',
  age: 22
}

修改array的length也无法触发视图更新,如

this.array.length = 2;

但是如果是直接修改数组中元素,而元素是个对象,可以直接修改对象的值 来自动更新视图。如下:
数组对象直接修改属性,可以触发视图更新

数组对象直接修改属性,可以触发视图更新
 
this.array[0].show = true;
this.array.forEach(function(item){
    item.show = true;
});

触发视图更新的方法有如下几种

1. Vue.set
可以设置对象或数组的值,通过key或数组索引,可以触发视图更新

数组修改

Vue.set(array, indexOfItem, newValue)
this.array.$set(indexOfItem, newValue)

对象修改

Vue.set(obj, keyOfItem, newValue)
this.obj.$set(keyOfItem, newValue)

2. Vue.delete
删除对象或数组中元素,通过key或数组索引,可以触发视图更新

数组修改

Vue.delete(array, indexOfItem)
this.array.$delete(indexOfItem)

对象修改

Vue.delete(obj, keyOfItem)
this.obj.$delete(keyOfItem)

3. 数组对象直接修改属性,可以触发视图更新

this.array[0].show = true;
this.array.forEach(function(item){
    item.show = true;
});

4. splice方法修改数组,可以触发视图更新

this.array.splice(indexOfItem, 1, newElement)

5. 数组赋值为新数组,可以触发视图更新

this.array = this.array.filter(...)
this.array = this.array.concat(...)

6. 用Object.assign或lodash.assign可以为对象添加响应式属性,可以触发视图更新

//Object.assign的单层的覆盖前面的属性,不会递归的合并属性
this.obj = Object.assign({},this.obj,{a:1, b:2})
 
//assign与Object.assign一样
this.obj = _.assign({},this.obj,{a:1, b:2})
 
//merge会递归的合并属性
this.obj = _.merge({},this.obj,{a:1, b:2})

7.Vue提供了如下的数组的变异方法,可以触发视图更新

push()
pop()
shift()
unshift()
splice()  
sort()
reverse()

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

vue-loader会解析文件,提取出每个语言块,如果有必要会通过其他loader处理,最后将他们组装成一个commonjs模块;module.exports出一个vue.js组件对象

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

1、v-if:判断是否隐藏
2、v-for:数据循环
3、v-bind:class:绑定一个属性
4、v-model:实现数据双向绑定
5、v-if v-show
6、v-once
7、v-html:
8、v-cloak:

v-on可以监听多个方法吗

可以

Vue 几种传值方式

1 父传子 父组件在子组件标签上传递一个 :自定义事件="传的值" 子组件用 props 接收父组件传递的内容 然后用this调用
2 子传父 子组件在某个方法里 this.emit(事件,传递的数据)父组件@事件名=事件<br/>3vue兄弟组件中传值传兄弟组件eventBus.emit('事件名',传递的数据) 父组件 @事件名='事件名' <br />3 vue兄弟组件中传值 传兄弟组件 eventBus.emit('事件名',传递的数据) 接兄弟组件eventBus.$on('事件名')
4 vuex 组件创建store创建数据 可以计算属性computed 或created 初始化钩子
5 provide/inject 依赖注入
祖先组件中通过provide来提供变量,然后在子孙组件中通过inject来注入变量
爷孙组件传值方法 parent组件使用provide提供一个inject 数据,son组件通过inject获取到parent注入的数据,以上就是它的最简用法。

Vue组件如何进行传值?

1、父组件向子组件传递数据

父组件内设置要传的数据,在父组件中引用的子组件上绑定一个自定义属性并把数据
绑定在自定义属性上,在子组件添加参数 props 接收即可

2、子组件向父组件传递数据

子组件通过 vue 实例方法$emit 进行触发并且可以携带参数,父组件监听使用@(v-on)
进行监听,然后进行方法处理

3、非父子组件之间传递数据

3.1) 引入第三方 new vue 定义为 eventBus
3.2)在组件中 created 中订阅方法 eventBus.on("自定义事件名",methods中的方法名)<br/>3.3)在另一个兄弟组件中的methods中写函数,在函数中发布eventBus订阅的方法eventBus.on("自定义事件名",methods 中的方法名)<br />3.3) 在另一个兄弟组件中的 methods 中写函数,在函数中发布 eventBus 订阅的方法eventBus.emit("自定义事件名”)
3.4) 在组件的 template 中绑定事件(比如 click)

事件总线

eventBus 非父子组件之间传值(数据通讯),通过一个vue实例来绑定事件和触发事件
组件的自定义事件,只能由组件自己来触发
A组件想传值给B组件,AB组件的非父子。其实也是依赖自定义事件来传递

  • 在A组件触发一个自定义事件(myEvent),触发的时候可以传参,参数可以是A组件数据
  • 在B组件触发绑定自定义事件(myEvent),事件的函数接收传参,参数其实是A组件数据
  • 触发事件和绑定事件由另外一个组件负责,A导入它触发事件,B导入它绑定事件,满足自定义事件触发绑定条件
  • 另外一个组件:我们称为 事件总线 或者 eventBus

Vue数据双向绑定的原理

浅层次解释就是:在vue中,使用指令v-model来实现双向绑定,双向绑定的意思是:表单改变,数据也改变,数据改变,表单也改变,
深层次,其实就是问你vue数据绑定的原理:
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,其中比较关键的是数据劫持
vue实现数据双向绑定的原理就是用Object.defineproperty()重新定义(set方法)对象设置属性值和(get方法)获取属性值的操纵来实现的。

Vue 响应式原理

整体思路是数据劫持+观察者模式
对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

vue 的优势:

组件化、双向数据绑定、数据和结构分离、虚拟DOM、运行速度快、轻量级框架。

什么是vue生命周期

Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。

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

vue生命周期的作用

它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

Vue生命周期总共分几个阶段

初始化 (create)--- 组件挂载(mount)-----组件更新 (update)--- 销毁(destroy)
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载 Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
1.beforeCreate
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
2. created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer)属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,el属性目前不可见<br/>3.beforeMount<br/>在挂载开始之前被调用:相关的render函数首次被调用<br/>4.mounted<br/>el被新创建的vm.el 属性目前不 可见 <br />3. beforeMount <br />在挂载开始之前被调用:相关的 render 函数首次被调用 <br />4. mounted<br />el 被新创建的 vm.el 替换,并挂载到实例上去之后调用该钩子,如果 root 实例挂载了一 个文档内元素,当 mounted 被调用时 vm.$el 也在文档内
5. beforeUpdate
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器,该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行
6. updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
7. activated keep-alive
组件激活时调用。该钩子在服务器端渲染期间不被调用
8. deactivated keep-alive
组件停用时调用。该钩子在服务器端渲染期间不被调用
9. beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用
10. destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监 听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用
11. errorCaptured(2.5.0+ 新增)
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生 错误的组件实例以及一个包含错误来源信息的字符串,此钩子可以返回 false 以阻止该错误继续 向上传播
image.png

第一次加载页面会触发那几个钩子函数

当页面第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子函数

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

答:beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框
nextTick : 更新数据后立即操作dom

DOM渲染在哪个周期中已经完成?

答:mounted

的作用是什么

包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染

vue中computed和watch区别

computed不会立马取值,用到的时候才会取值. 并且有缓存,依赖数据不改变不更新结果
watch 「立即执行」,会先算出来一个老值.数据变化就执行函数

computed
1、computed是计算属性,也就是依赖某个值或者props通过计算得来得数据;
2、 computed的值是在getter执行之后进行缓存的,只有在它依赖的数据发生变化,会重新调用getter来计算;
3、 不支持异步,当computed内有异步操作时无效,无法监听数据的变化;

watch
1、watch是监听器,可以监听某一个数据,然后执行相应的操作;
2、不支持缓存,数据变直接会触发相应的操作;
3、监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
4、支持异步操作;

什么时候用computed 什么时候用watch 比较合适?
当多个属性影响一个属性的时候,建议用computed,比如:
fullName() {return this.firstName + this.secondName}
fullName是由firstName和secondName影响的,这种情况就适合computed;
当一个值发生变化之后,会引起一系列的操作,这种情况就适合用watch;

created和mounted区别

created 和 mounted 的区别:组件内容是否已经在页面渲染完毕
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
挂载到节点上的初始化方法通常用mounted去操作,主动调起的用methods里面封装方法。
数据初始化一般放到created里面,这样可以及早发请求获取数据,如果有依赖dom必须存在的情况,就放到mounted(){this.$nextTick(() => { /* code */ })}里面
Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。

v-if 和v-show 的区别

相同点:v-if与v-show都可以动态控制dom元素显示隐藏
不同点:v-if显示隐藏是将dom元素整个添加或删除,而v-show隐藏则是为该元素添加css--display:none,dom元素还在。
如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-for 和 v-if

当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过 v-if 移动到容 器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次, 且不会在 v-if 为否的时候运算 v-for。

v-for key的作用

key的作用主要是为了高效的更新虚拟DOM
v-for遍历时,用id,uuid之类作为key,唯一标识节点加速虚拟DOM渲染 ,响应式系统没有监听到的数据,用+new Date()生成的时间戳作为key,手动强制触发重新渲染
遍历数组或元素中的唯一标识,增加或删减元素时,通过这个唯一标识key判断是否是之前的元素,并且该元素key的值是多少。这个唯一标识是保持不变的。
key属性可以用来提升v-for渲染的效率!vue不会去改变原有的元素和数据,而是创建新的元素然后把新的数据渲染进去

在使用v-for的时候,vue里面需要我们给元素添加一个key属性,这个key属性必须是唯一的标识
给key赋值的内容不能是可变的
1. 在写v-for的时候,都需要给元素加上一个key属性
2. key的主要作用就是用来提高渲染性能的!
3.key属性可以避免数据混乱的情况出现 (如果元素中包含了有临时数据的元素,如果不用key就会产生数据混乱)

VUE中虚拟DOM原理

用JS对象表示DOM结构。
DOM很慢,而javascript很快。
用javascript对象可以很容易地表示DOM节点。
DOM节点包括标签、属性和子节点。根据虚拟DOM树构建出真实的DOM树。
具体思路:根据虚拟DOM节点的属性和子节点递归地构建出真实的DOM树。
用JS对象表示DOM结构,那么当数据状态发生变化而需要改变DOM结构时,我们先通过JS对象表示的虚拟DOM计算出实际DOM需要做的最小变动,然后再操作实际DOM,从而避免了粗放式的DOM操作带来的性能问题

虚拟dom 就是用来优化浏览器缓存的,虚拟DOM不会立即操作DOM,而是将更新的diff内容保存到本地一个js中,最终将这个js对象一次性映射 到DOM树上

diff算法

我们在进行dom操作的时候可能会出现需要更新某一个dom元素,但如果不更新整个组件就无法生效,其实我们使用diff算法配合虚拟dom即可
实现情况1:同级比较:如果根元素有变化-整个Dom树重建 ;如果根元素不变-属性改变-元素复用-更新属性情况
2:根元素没变,子元素/元素内容发生改变: 没有key:就地更新 -性能不高有key:按key比较,尝试复用,找到本次DOM需要更新的节点,其他的不更新

Vue-router跳转和location.href有什么区别

使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用history.pushState('/url'),无刷新页面,静态跳转;
引进router,然后使用router.push('/url')来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。

①vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
②vue-router使用diff算法,实现按需加载,减少dom操作
③vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
④vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载

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

const router = new VueRouter({})
router.beforeEach((to, from, next) = {
// to do somethings
})
to:Route,代表要进入的目标,它是一个路由对象。
from:Route,代表当前正要离开的路由,也是一个路由对象
next:Function,必须需要调用的方法,具体的执行效果则依赖next方法调用的参数
next():进入管道中的下一个钩子,如果全部的钩子执行完了,则导航的状态就是comfirmed(确认的)
next(false):终端当前的导航。如浏览器URL改变,那么URL会充值到from路由对应的地址。
next(’/’)||next({path:’/’}):跳转到一个不同的地址。当前导航终端,执行新的导航。
next 方法必须调用,否则钩子函数无法resolved
全局后置钩子
router.afterEach((to, from) = {
// to do somethings
})
后置钩子并没有next函数,也不会改变导航本身。
beforEnter
const router = new VueRouter({
routes: [
{
path: ‘/home’,
component: HomebeforeEnter: (to, from, next) = {
// to do somethings
// 参数与全局守卫参数一样
}
}
]
})
const Home = {
template: <div</div,
beforeRouteEnter(to, from, next){
// 在渲染该组件的对应路由被 confirm 前调用
// 不能获取组件实例 ‘this’,因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next){
// 在当前路由改变,但是该组件被复用时调用
// 例:对于一个动态参数的路径 /home/:id,在/home/1 和 /home/2 之间跳转的时候
// 由于会渲染同样的 Home 组件,因此组件实例会被复用,而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 ‘this’
},
beforeRouteLeave(to, from, next){
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 ‘this’
}
}
beforeRouterEnter不能访问this,因为守卫在导航确认前被调用,因此新组建还没有被创建,可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并把实例作为回调的方法参数。
const Home = {
template: <div</div,
beforeRouteEnter(to, from, next){
next( vm = {
// 通过 ‘vm’ 访问组件实例
})
}
}

vue-router响应路由参数的变化

• 用watch 检测

// 监听当前路由发生变化的时候执行
watch: {undefined
$route(to, from){undefined
console.log(to.path)
// 对路由变化做出响应
}
}

• 组件内导航钩子函数

beforeRouteUpdate(to, from, next){undefined
// to do somethings
}


导航守卫的执行顺序

beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < beforeRouteEnter < beforeResolve < afterEach

导航守卫中的next的用处

next的作用,使导航守卫队列的继续向下迭代

为什么afterEach守卫没有next

afterEach根本不在导航守卫队列内,没有迭代的next

beforeEach是否可以叠加

beforeEach是可以叠加的,所有的全局前置守卫按顺序存放在beforeHooks的数组里面

路由跳转经历了哪几部分

路由跳转的核心方法是transitionTo,在跳转过程中经历了一次confirmTransition,
(beforeRouteLeave < beforeEach < beforeRouteUpdate < beforeEnter < 异步组件加载)这样顺序的queue为第一个,在第一个queue迭代完毕后,执行第二个(beforeRouteEnter < beforeResolve)这样顺序的queue,在执行完毕后,开始执行updateRoute,之后执行全局的afterEach守卫。最后完成路由的跳转。

动态生成路由,怎么获取传过来的值

使用VueRouter的router.addRoutes(routes: Array)方法
大概思路是: 1. 后端返回树状结构的数据; 2. 前端通过在router里面的菜单分成静态、动态的菜单; 3. 动态菜单按照url和后端的数据进行匹配过滤,拿到有权限访问的菜单; 4. 然后把有权限访问的菜单存储Vuex 5. 显示菜单
在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。

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

动态路由的创建,主要是使用path属性过程中,使用动态路径参数,以冒号开头,

{
  path: '/details/:id'
  name: 'Details'
  components: Details
}

访问details目录下的所有文件,如果details/a,details/b等,都会映射到Details组件上。
当匹配到/details下的路由时,参数值会被设置到this.r o u t e . p a r a m s 下 , 所 以 通 过 这 个 属 性 可 以 获 取 动 态 参 数 c o n s o l e . l o g ( t h i s . route.params下,所以通过这个属性可以获取动态参数 console.log(this.route.params下,所以通过这个属性可以获取动态参数console.log(this.route.params.id)

页面跳转

声明式导航router-link
编程式导航router.push()
a标签

页面传参,获取页面传递过参数

query要用path来引入,params要用name来引入,接收参数分别是this.route.query.namethis.route.query.name和this.route.params.name。

name+params 跳转页面参数不会在地址栏中看到,相当于post。
path+query 跳转页面参数在地址栏中可以看到,相当于get。

vue给对象动态添加属性后,页面不动态更新的问题

原因:
image.png
解决方法:
一、利用Vue.set(object,key,val)
例如:Vue.set(vm.obj,'k1','v1')
二、利用this.set(this.obj,key,val),这是Vue.set()的全局实例<br/>例如:this.set(this.obj,key,val),这是Vue.set()的全局实例<br />例如:this.set(this.obj,'k1','v1')
三、利用Object.assign({},this.obj)创建新对象
this.obj.k1='v1';
this.obj = Object.assign({}, this.obj)
// 或者下面这种
this.obj = Object.assign({}, this.obj,{'k1','v1'})
this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})
四、也可以使用$forceUpdate()进行强制刷新,但是 不建议这种方式

vue如何获取并操作DOM元素

方法一:
直接给相应的元素加id,然后再document.getElementById("id");获取,然后设置相应属性或样式;
方法二:
使用ref,给相应的元素加ref=“name” 然后再this.$refs.name获取到该元素,并且可以直接调用子组件中定义的方法;
注意:
1、在获取相应元素之前,必须在mounted生命周期进行挂载,否则获取到的值为空;
2、如果是给子组件加id并修改自定义属性,则直接会加载该子组件对应的外层div上,并不会改变该子组件原本的自定义属性的值;
3、如果给子组件加ref,然后获取到该DOM元素之后改变相应的自定义属性的值,vue会报错:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed

vue如何获取并操作DOM元素

方法一:
直接给相应的元素加id,然后再document.getElementById(“id”);获取,然后设置相应属性或样式;
方法二:
使用ref,给相应的元素加ref=“name” 然后再this.$refs.name获取到该元素,并且可以直接调用子组件中定义的方法;

v-html 和 插值语法的区别

image.png

vue如何兼容ie的问题

babel-polyfill插件

promise和acync await 的区别

promise和async/await都是处理异步请求

1 promise是ES6,async/await是ES7
2 async/await相对于promise来讲,写法更加优雅
3 reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误,
2)async/await既可以用.then又可以用try-catch捕捉

使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。
错误处理:
Async/Await 让 try/catch 可以同时处理同步和异步错误。在promise中,try/catch 不能处理 JSON.parse 的错误,因为它在Promise中。我们需要使用 .catch,这样错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂。

路由懒加载

整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。

按需去加载路由对应的资源,提高首屏加载速度(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)。

实现原理:将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容。

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

• 把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应的组件即为路由的懒加载,可以加快项目的加载速度,提高效率

const router = new VueRouter({undefined
routes: [
{undefined
path: ‘/home’,
name: ‘Home’,
component:() = import(’…/views/home’)
}
]
})


Vue单向数据流与双向数据绑定的理解

一:单向数据流
Vue 在不同组件间强制使用单向数据流,父组件可以向子组件传递数据,但是子组件不能直接修改父组件的状态
单向数据流的意思是指数据的改变只能从一个方向修改。
举个栗子:如一个父组件有两个子组件,分别为1和2。父组件向子组件传递数据,两个组件都接收到了父组件传递过来的数据,在组件1中修改父组件传递过来的数据,子组件2和父组件的值不会发生变化。这就是单向的数据流,子组件不能直接改变父组件的状态。但是如果父组件改变相应的数据,两个子组件的数据也会发生相应的改变。
二:双向数据绑定
主要由MVVM框架实现,主要由三部分组成View、ViewModel和Model组成,其中view和model不能直接进行通信,他们通过中间件ViewModel来进行通信。
当Model部分数据改变时,由于vue中Data Binding将底层数据和DOM进行了绑定,ViewModel会通知view更新视图;
当视图view数据变化也会同步到Model中,View和Model之间的同步完全是自动的,不需要人手动操作DOM。
vue中使用Object.defineProperty()函数的set,get函数完成数据双向绑定
举个栗子:例如,当Model部分数据发生改变时,由于vue中Data Binding将底层数据和Dom层进行了绑定,ViewModel通知View层更新视图;当在视图 View数据发生变化也会同步到Model中。View和Model之间的同步完全是自动的,不需要人手动的操作DOM。

封装Vue组件的过程

● 首先,使用Vue.extend()创建一个组件 在compoents文件夹中新建一个vue文件
● 然后,使用Vue.component()方法注册组件
● 接着,如果子组件需要数据,可以在props中接收定义
● 最后,子组件修改好数据之后,想把数据传递给父组件,可以使用$emit()方法

$nextTick

1,对 nextTick 的理解
Vue 的视图更新是通过数据驱动的,当数据发生改变的时候,将当前的数据更改保存在队列中,然后异步的更新视图
由于 Vue 的视图更新是异步的,所以我们在修改完成数据之后,不一定当前的 View 已经发生改变,于是就有了 nextTick
nextTick 是在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

2,nextTick 的使用场景
如果要在 created()钩子函数中进行的 DOM 操作,由于 created()钩子函数中还未对 DOM 进行任何渲染,所以无法直接操作,需要通过nextTick()来完成。在created()钩子函数中进行的DOM操作,不使用nextTick()来完成。在 created()钩子函数中进行的 DOM 操作,不使用nextTick()会报错

更新数据后,想要使用 js 对新的视图进行操作时
在使用某些第三方插件时 ,这些插件需要 dom 动态变化后重新应用该插件,这时候就需要使用nextTick()来重新应用插件的方法。<br/>nextTick()来重新应用插件的方法。<br />nextTick 作用:处理vue中DOM的异步更新
因为 vue 执行DOM更新是异步的
当数据更新时,在DOM渲染之后,会自动执行callback函数
实际开发的时候 nextTick 会在的场景:在修改数据,需要获取到最新的DOM的时候可以使用
d9458e0e83e6d977463db7d1d8fe73b.png

Vue.js中this.nextTick( )的使用 ?

this.nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

vue和jquery的区别:

jQuery是使用选择器()选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。比如需要获取label标签的内容:("lable").val();,它还是依赖DOM元素的值。
Vue则是通过Vue对象将数据和View完全分离开来了。对数据进行操作不再需要引用相应的DOM对象,可以说数据和View是分离的,他们通过Vue对象这个vm实现相互的绑定。这就是传说中的MVVM。

MVVM和MVC区别?哪些场景合适?

MVC 是 Model View Controller 的缩写

  • Model:模型层,是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  • View:视图层,用户界面渲染逻辑,通常视图是依据模型数据创建的。
  • Controller:控制器,数据模型和视图之间通信的桥梁,通常控制器负责从事图读取数据,控制用户输入,并向模型发送数据。

MVC的思想:Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。
MVC的应用:主要用于中大型项目的分层开发
MVVM是Model-View-ViewModel的简写,即模型-视图-视图模型。

  • Modal:模型,指的是后端传递的数据。
  • View:视图,指的是所看到的页面。
  • ViewModal:视图模型,mvvm模式的核心,它是连接view和model的桥梁。主要用来处理业务逻辑

它有两个方向:

  • 一是将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
  • 二是将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。

MVVM适用场景: 适合数据驱动的场景,数据操作比较多的场景

Object.defineproperty()和Proxy的区别

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


混入mixin

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
混入的作用:可以省很多代码(高类聚低耦合),还方便维护;
个人理解:混入就是在一个公共的实例中写入公共的数据或者方法,这样的话vue会自动注入到(全局混入会注入到每一个实例组件中)、(单页面引入的会注入到引入的组件中)。混入的对象也就是mixin中可以写入的内容包含任意组件选项- - - - - -(也就是说你在export default { } 里边的内容都是可以混入的)有一种类似公共方法的感觉,但是可以写公共的data里面的数据,这一点比较方便
在全局注册混入对象的时候是Vue.mixin(mixin)
注意事项:
1、而对于混入对象以及组件自身的created钩子函数呢,它们都将被调用。但混入对象的钩子会在组件自身的钩子之前调用。
2、如果组件自身定义了与钩子对象中同名的数据或方法,为了避免冲突,vue将优先使用组件自身的数据或方法 (也就是重名了就只会调用组件内部的数据或方法)。

组件中写name选项有什么作用?

项目使用keep-alive时,可搭配组件name进行缓存过滤
DOM做递归组件时需要调用自身name
vue-devtools调试工具里显示的组件名称是由vue中组件name决定的

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

1、data 组件都是 Vue 的实例
2、组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他
3、组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果

组件的命名规范

一种是使用链式命名 my-component (字母全小写且必 须包含一个连字符),避免和当前以及未来的 HTML 元素相冲突 ,一种是使用大驼峰命名

怎么在组件中监听路由参数的变化?

方式一:监听 route <br />`watch:{'route'(to, from) {} }<br />方式二:通过组件内的导航守卫,beforeRouteUpdate , (和created(){}生命周期函数同级别);<br />beforeRouteUpdate(to ,from,next){}`

怎么捕获 Vue 组件的错误信息

1、errorCaptured 是组件内部钩子,当捕获一个来自子孙组件的错误时被调用,接收error、vm、info 三个参数,return false 后可以阻止错误继续向上抛出
2、errorHandler 为全局钩子,使用 Vue.config.errorHandler 配置,接收参数与errorCaptured 一致,2.6 后可捕捉 v-on 与 promise 链的错误,可用于统一错误处理与错误兜底

Vue 组件里的定时器要怎么销毁

可以在beforeDestroy里写清除函数 const timer = setInterval(() =>{ // 某些定时器操作 }, 500);
通过once来监听定时器,在beforeDestroy钩子可以被清除this.once来监听定时器,在beforeDestroy钩子可以被清除 this.once('hook:beforeDestroy', () => { clearInterval(timer); })

DOM 渲染在 哪个周期中就已经完成

DOM 渲染在 mounted 中就已经完成了。

vue路由的钩子函数

首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

to:route即将进入的目标路由对象,

from:route当前导航正要离开的路由

next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。

route和router的区别

route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而router是“路由实例”对象包括了路由的跳转方法,钩子函数等。

路由原理 history 和 hash 两种路由方式的特点

hash 模式

  1. location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
  2. 可以为 hash 的改变添加监听事件

window.addEventListener("hashchange", funcRef, false);
每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash 的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了
特点:兼容性好但是不美观
history 模式
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。
这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。
特点:虽然美观,但是刷新会出现 404 需要后端进行配置

Vue的路由实现:hash模式 和 history模式

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

Vue与Angular以及React的区别?

(版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟)

1.与AngularJS的区别

相同点:
都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不同点
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。

2.与React的区别

相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
不同点:
React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。