1、MVVM
介绍一下MVVM,和MVC有什么区别
- MVVM:不需要手动操作dom 关注model变化,让mvvm框架自动更新dom状态
- Model:数据模型,泛指后端进行的各种业务逻辑处理,主要围绕数据库系统展开
- View:视图层,主要由html与css来构建
- ViewModel:视图数据层,连接view与model的桥梁,对后端获取的model数据进行转换处理,做二次封装,其封装后的数据模型包括视图的状态和行为两部分(而model的数据模型只包含状态),状态是展示什么,行为是点击这一块发生什么。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层。完全解耦了view层与viewmodel层
- MVC:
- View:数据显示,检测用户的键盘、鼠标等行为,传递调用Controller执行应用逻辑,View更新需要重新获取Model的数据
- Controller:处理消息,View与Model之间协作的应用逻辑或业务逻辑处理
- Model:掌管数据源,Model变更后,通过观察者模式通知View更新视图
ViewModel有什么好处
- 双向绑定,只需关注model的变化,无需手动操作dom,会自动更新
- 控制器的功能大都移动到view上,对控制器进行了瘦身
- 提高可维护性,可测试性(因为可以针对viewmodel来写)
- 低耦合可重用,viewmodel彻底解耦了view层与viewmodel层
MVVM是如何实现的
-
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的监听回调
Vue是一个典型的MVVM框架,模型(Model)只是普通的javascript对象,修改它则试图(View)会自动更新。这种设计让状态管理变得非常简单而直观Observer(数据监听器) : Observer的核心是通过Object.defineProprtty()来监听数据的变动,这个函数内部可以定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher
Watcher(订阅者) : Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个update()方法
- 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调
Compile(指令解析器) : Compile主要做的事情是解析模板指令,将模板中变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
-
数组是通过遍历每个元素监听的
双向绑定
定义:Vue 中的双向数据绑定指的是当一个数据发生变化时,视图也会随之更新;而当用户在视图中修改了数据时,数据模型也会相应地更新
实现:v-model是v-bind和v-on的语法糖。
v-bind即model=>view,当model数据发生变化,在setter中,去触发对应组件重新生成Vnode,对比新旧虚拟树,更新差异。
v-on即view=>model,view操作后,触发事件,调用回调函数,在回调函数中更新model
2、生命周期
渲染:将状态作为输入,并生成DOM输出到页面上显示出来,这个过程叫做渲染,gpt给的答案是将数据动态地呈现在页面上
模版:{{message}}
挂载:vue实例挂载到dom,也就是模版渲染到指定的dom元素中
el:通过使用el
选项,Vue实例知道应该在哪个DOM元素内渲染内容,这是Vue应用与特定HTML元素关联的关键。在 Vue 实例初始化的过程中,Vue 会将模板编译成渲染函数,并将渲染函数生成的虚拟 DOM(Virtual DOM)挂载到实例的根元素上。
$el属性:表示当前vue实例所关联的根DOM元素
r e f 属性:在 v u e 组件中通过获取子组件或 d o m 元素, t h i s . ref属性:在vue组件中通过获取子组件或dom元素,this. ref属性:在vue组件中通过获取子组件或dom元素,this.ref.childRef
虚拟dom:当数据发生变化时,框架首先会创建一个虚拟 DOM 树的副本,并在内存中对其进行操作。然后,通过比较新旧虚拟 DOM 树的差异(称为“diffing”),找出发生变化的部分,并将这些变化批量更新到真实 DOM 上。由于虚拟 DOM 是在内存中操作的轻量级对象,因此更新速度更快,减少了性能开销。
DOM(Document Object Model)树:是指HTML或XML文档在浏览器中被解析后所形成的树状结构。它表示了文档的层次结构,每个文档元素都被表示为树中的一个节点
- 初始化阶段
- 模版编译阶段
- 挂载阶段
- 卸载阶段
或者
- 创建阶段
- 挂载阶段
- 更新阶段
- 销毁阶段
3、proxy与Object.defineproperty
proxy优势:
- 直接监听对象而非属性,因此还可监听数组
- 拦截方式多样:不仅可拦截set与get,还可拦截in、delete、函数调用apply、new操作
- proxy返回新对象
- proxy是新标准,会得到重点持续的性能优化
Object.defineProperty:
- 兼容性好,proxy存在兼容问题
4、状态管理
5、组件通信
6、Virtual Dom
-
为什么有虚拟dom:变动dom会导致回流或重绘,因此需要抽象,尽可能一次性将差异更新到dom
-
更新虚拟dom:需要将虚拟dom与真实dom对应起来,将vnode化为真实dom
-
实现与原理:
- 用JavaScript对象模拟真实DOM树,对真实DOM进行抽象
- diff算法——比较两颗虚拟DOM树的差异(如果不采用虚拟DOM,根本不需要对比,但这样粒度太大,需要很多个watcher,组件级别的watcher实例)
- 定性:同层比较,实际上是深度优先算法
- 过程:
- patch算法——将两个虚拟DOM对象的差异应用到真正的DOM树上,也就是渲染
- patch的过程其实就是创建新增的结点、删除废弃的结点、修改需要更新的结点
-
评价:
- 保证性能下限,一定会比直接操作dom快
- 无需手动操作dom
- 跨平台:因为不直接依赖与浏览器的真实dom
- 无法进行极致优化,因为没法针对性地优化,因为是全量对比
-
如果两者都有子节点,则执行
updateChildren
函数比较子节点,这一步很重要-
sameVnode:key(是vnode唯一标记)、tag、isComment、data属性是否一样,以及sameInputType;如果不是,才replace,
sameVnode
函数就是看这两个节点是否值得比较,代码相当简单: -
patchVnode
-找到对应的
真实DOM
,称为el
-判断
newVnode
和oldVnode
是否指向同一个对象,如果是,那么直接return
-如果他们都有文本节点并且不相等,那么将
el
的文本节点设置为newVnode
的文本节点。-如果
oldVnode
有子节点而newVnode
没有,则删除el
的子节点-如果
oldVnode
没有子节点而newVnode
有,则将newVnode
的子节点真实化之后添加到el
-如果两者都有子节点,则执行
updateChildren
函数比较子节点,这一步很重要 -
updateChildren:对比newChildren与oldChildren,循环newChildren,每循环一个新子节点,就去oldChildren中找到和当前结点相同的那个旧子节点。如果找不到,则新增结点;如果找到,就更新;如果找到的旧子节点的位置和新子节点不同,则移动结点。整个过程,需要key
–如果四个逻辑都匹配不到,再把所有旧子节点的
key
做一个映射到旧节点下标的key -> index
表,然后用新vnode
的key
去找出在旧节点中可以复用的位置。- 新增
- 更新
- 移动
- 删除
-
7、计算属性computed与侦听属性watch
computed:是计算属性,依赖于其他属性值;只有它依赖的属性值发生变化时,下一次获取computed的值时才会重新计算,主要用于同步数据的处理,能用则用,避免了多个数据影响其中某个数据时多次调用watch的尴尬情况。
watch:更多的是观察作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调函数,进行后续操作,主要用于观测某个值的变化去完成开销较大的复杂业务逻辑
- 深度监听deep:数据对象中的嵌套属性的值,发生变化时也能触发监听器的回调,Vue 不能检测到对象属性的添加或删除
- immediate:声明监听属性之后,就会立刻执行handler
- handler:发生变化时,回调函数
8、组件间通信
根据组件分类:父子组件通信、隔代组件通信、兄弟组件通信
根据实践方法分类:
-
props/$emit——父子
- 父组件向子组件传递信息props,父组件定义属性传递数据:propsName:data,子组件export default中的props属性接收。
props
用于接收父组件传递的数据 - 子组件向父组件传值 e m i t :在父组件中给子组件绑定一个自定义事件,子组件通过 emit:在父组件中给子组件绑定一个自定义事件,子组件通过 emit:在父组件中给子组件绑定一个自定义事件,子组件通过emit触发该事件并传输数据,父组件会监听该自定义事件并执行相应逻辑
- 父组件向子组件传递信息props,父组件定义属性传递数据:propsName:data,子组件export default中的props属性接收。
-
ref与 p a r e n t / parent/ parent/children——父子
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/$children
:访问父 / 子实例- 这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据,const comA = this.$refs.comA;
-
a t t r s / attrs/ attrs/listeners-父传子
$attrs
非props属性:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过v-bind="$attrs"
传入内部组件。通常配合 inheritAttrs 选项一起使用。- this.$attrs就可以访问数据了
- inheritAttrs用于不继承父组件非props属性,不会合并到子组件的根元素上
$listeners
非原生事件:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过v-on="$listeners"
传入内部组件$attrs
里存放的是父组件中绑定的非 Props 属性,$listeners
里存放的是父组件中绑定的非原生事件。
-
provide/inject-跨级组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
-
EventBus( e m i t / emit/ emit/on)-三种都可以
- 通过一个空的vue实例作为事件中心,用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
- 自定义事件名并传输数据:Event.$emit(自定义事件名,数据);
- 监听并接收数据:Event.$on(自定义事件名,data => {});
-
vuex-三种都可以
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是commit(mutation,data)。这样使得我们可以方便地跟踪每一个状态的变化。commit是状态改变提交操作方法,mutation是状态改变操作,只能是同步
- actions操作行为处理,由组件中的
$store.dispatch('action 名称', data1)
来触发。然后由commit()来触发mutation的调用 , 间接更新 state - 组件中使用mapMutation就可以调用mutation了,在组件中直接调用
increment
方法,而无需通过this.$store.commit('increment')
来提交 mutation。 - vuex与LocalStorage:vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
9、v-show与v-if有什么区别
v-if:满足条件会渲染,不满足则销毁
v-show:基于display实现,元素总是会被渲染
10、Class 与 Style 如何动态绑定?
对象语法中属性如果不符合js命名规则,则需要单引号括起来,如text-danger。
数组语法:
11、如何理解Vue的单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
子组件用prop来接受父组件的数据,子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
…… ,子组件用$emit派发一个自定义事件,然后父组件处理该事件。
12、直接给一个数组项赋值,Vue能检测到变化吗
不能
13、Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
-
加载渲染过程:子组件mounted之后,父组件才mounted
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
-
子组件更新过程:子组件update之后,父组件才update
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
-
父组件更新过程
父 beforeUpdate -> 父 updated
-
销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
14、在哪个生命周期内调用异步请求
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面 loading 时间;
- ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
15、父组件可以监听到子组件的声明周期吗
子组件通过$emit,让父组件知道并处理,或者通过@hook:mounted=“dosth”
16、谈谈你对 keep-alive 的了解?
为什么用:在平常开发中,有部分组件没有必要多次初始化,这时,我们需要将组件进行持久化,使组件的状态维持不变,在下一次展示时,也不会进行重新初始化组件。不会被初始化,也就是不会重走生命周期,但有时候希望缓存的组件被再次渲染,因此被包含在 keep-alive
中创建的组件,会多出两个生命周期的钩子: activated
与 deactivated
:
怎么用:被keep-alive包含的组件会被缓存
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
- 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
如果未使用keep-alive
组件,则在页面回退时仍然会重新渲染页面,触发created
钩子,使用体验不好。 在以下场景中使用keep-alive
组件会显著提高用户体验:
- 商品列表页点击商品跳转到商品详情,返回后仍显示原有信息
- 订单列表跳转到订单详情,返回,等等场景。
13、组件中data为什么是一个函数
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响
14、v-model的原理
15、vue-router路由模式
路由模式的理解hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;URL中以#
符号开始的部分被称为哈希部分。在Web开发中,通常使用哈希来实现页面内的导航或锚点定位
。当浏览器的哈希发生变化时,页面不会重新加载,而是触发一个hashchange
事件。
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
Abstract 模式是 Vue Router 提供的一种路由模式,它不依赖浏览器的 URL,而是通过 JavaScript 对路由进行操作。在 Abstract 模式下,路由的改变不会影响浏览器的 URL,也不会触发浏览器的历史记录,因此更适合在非浏览器环境下使用,比如在服务器端渲染中。
//to 将要访问的路径
//from 代表从哪个路径跳转而来
//next 是一个函数,表示放行 next(‘/login’) 强制跳转
16、Vue中的key有什么用
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key
对比中可以避免就地复用的情况。所以会更加准确。
更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快(新前、新后比较的时候。。。可以直接通过map拿到对象)
为什么不能用index作为key
性能消耗:使用 index 做 key,破坏顺序操作的时候, 因为每一个节点都找不到对应的 key,导致部分节点不能复用。
数据错位:当在比较时,发现虽然文本值变了,但是当继续向下比较时发现 DOM 节点还是和原来一摸一样,就复用了,但是没想到 input 输入框残留输入的值,这时候就会出现输入的值出现错位的情况
17、项目优化
(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 查找性能瓶颈
18、路由懒加载
路由懒加载即当路由被访问时才加载对应的组件,所以在routes.js文件中无需引入组
19、hash与history模式
hash特点:
- hash变化,会触发网页跳转,不会产生浏览器历史记录,不能提交到server端
- hash改变url,但不会触发页面重新加载,可用于实现SPA,不利于SEO
history模式:允许开发者直接更改前端路由,即更新浏览器 URL
地址而不重新发起请求。
- 会改变路由,产生历史记录
- 可通过pushState、replaceState来实现无刷新跳转的功能
- 存在的问题是:刷新会发起请求,如果后端未设置,会404
20、vuex的理解
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是commit(mutation,data)。这样使得我们可以方便地跟踪每一个状态的变化。commit是状态改变提交操作方法,mutation是状态改变操作
- actions操作行为处理,由组件中的
$store.dispatch('action 名称', data1)
来触发。然后由commit()来触发mutation的调用 , 间接更新 state - 组件中使用mapMutation就可以调用mutation了,在组件中直接调用
increment
方法,而无需通过this.$store.commit('increment')
来提交 mutation。 - vuex与LocalStorage:vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
21、插槽是什么 怎么使用的
22、单页面SPA
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
23、为什么使用VUEX
为什么使用vuex:
一是,此数据会在应用运行过程中发生变化,因此要求响应式数据进行存储,如localStorage、sessionStrorage都不是响应式的,vuex则是响应式的而且这种vuex也能够通过mutation、action来方便地维护应用的状态;
二是,其他storage只能存储字符串,vuex则没有这个限制,因此vuex能够方便地利用数据类型本身的api操作数据。
三是,vuex更适合应用在组件开发。
24、组件和插件的区别,什么场景下定义为组件,什么场景下是插件
组件定义:组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue
中每一个.vue
文件都可以视为一个组件**,通常封装特定功能或界面元素。**
插件:插件是一种扩展,通常用来为 Vue
添加全局功能,通常作为外部模块导入系统。
25、vue2和vue3的区别
-
选项式API(基于选项组织)-----组合式API(基于逻辑组织)
-
defineProperty-------proxy
-
单根----多根
-
对全局和内部API进行重构,只能被命名导出,因此打包优化了。支持 tree-shaking ,进行了打包优化,主要用于减少 JavaScript 或 TypeScript项目中未使用的代码。其原理是通过静态分析并标记未被引用的模块、函数、变量等,将其从最终构建结果中去除掉,进而达到减小文件大小和提升项目性能的目的。
-
生命周期有所改变,钩子函数名字也变了
-
diff算法优化,patchFlag 帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。
vue3
在diff
算法中相比vue2
增加了静态标记。关于这个静态标记,其作用是为了会发生变化的地方添加一个flag
标记,下次发生变化的时候直接找该地方进行比较下图这里,已经标记静态节点的
p
标签在diff
过程中则不会比较,把性能进一步提高
26、vue的nextTick是什么
Vue的响应式并不是只数据发生变化之后,DOM就立刻发生变化,而是按照一定的策略进行DOM的更新。这样的好处是可以避免一些对DOM不必要的操作,提高渲染性能。
其实一句话就可以把$nextTick
这个东西讲明白:就是你放在$nextTick
当中的操作不会立即执行,而是等数据更新、DOM更新完成之后再执行,这样我们拿到的肯定就是最新的了。
再准确一点来讲就是$nextTick
方法将回调延迟到下次DOM更新循环之后执行。
27、vue-router的钩子
-
导航:用户在应用程序中进行页面之间的切换或跳转的过程。导航可以通过点击链接、按钮,或者其他用户交互方式触发。
-
全局
- 全局前置守卫:router.beforeEach,在每个路由导航前被调用。
- 全局后置守卫:router.afterEach,在导航确认完毕之后调用。
- 全局解析守卫:router.beforeResolve,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
-
独立的一个:beforeEnter,在进入路由之前调用
-
组件内
- beforeRouteEnter,在路由进入前被调用。
- beforeRouteUpdate,在当前路由改变,但是该组件被复用时调用。
- beforeRouteLeave,在路由离开前被调用
1.导航被触发。
2.在失活的组件里调用 beforeRouteLeave
守卫。
3.调用全局的 beforeEach
守卫。
4.在重用的组件里调用 beforeRouteUpdate
守卫 (2.2+)。
5.在路由配置里调用 beforeEnter
。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter
。
8.调用全局的 beforeResolve
守卫 (2.5+)。
9.导航被确认。
10.调用全局的 afterEach
钩子。
11.触发 DOM 更新。
12.调用 beforeRouteEnter
守卫中传给 next
的回调函数,创建好的组件实例会作为回调函数的参数传入。