Vue
Vue 路由实现方式?
路由:通过一定的机制,监听用户的行为动作,从而做出对应的变化。
常见两种方式:
hash
模式浏览器暴露一个现成的方法
hashchange
,在hash
改变的时候,触发该事件。有了监听事件,且改变hash
页面并不刷新,这样我们就可以在监听事件的回调函数中,执行我们展示和隐藏不同 UI 显示的功能,从而实现前端路由。hash
路由的核心实现,主要就是监听hash
的变化,渲染不同的组件代码。总结:
hash
模式所有的工作都是在前端完成的,不需要后端服务的配合hash
模式的实现方式就是通过监听URL
中hash
部分的变化,从而做出对应的渲染逻辑hash
模式下,URL
中会带有#
,看起来不太美观
history
模式history
路由模式的实现,是要归功于HTML5
提供的一个history
全局对象,可以将它理解为其中包含了关于我们访问网页(历史会话)的一些信息。同时它还暴露了一些有用的方法,比如:window.history.go
可以跳转到浏览器会话历史中的指定的某一个记录页window.history.forward
指向浏览器会话历史中的下一页,跟浏览器的前进按钮相同window.history.back
返回浏览器会话历史中的上一页,跟浏览器的回退按钮功能相同window.history.pushState
可以将给定的数据压入到浏览器会话历史栈中window.history.replaceState
将当前的会话页面的url
替换成指定的数据
而
history
路由的实现,主要就是依靠于pushState
与replaceState
实现的- 特点:
- 都会改变当前页面显示的
url
,但都不会刷新页面 pushState
是压入浏览器的会话历史栈中,会使得history.length
加 1,而replaceState
是替换当前的这条会话历史,因此不会增加history.length
history.pushState
和history.replaceState
方法是不会触发popstate
事件的- 但是浏览器的某些行为会导致
popstate
,比如 go、back、forward popstate
事件对象中的state
属性,可以理解是我们在通过history.pushState
或history.replaceState
方法时,传入的指定的数据
重点:重写
pushState
和replaceState
方法
provide & inject
小型状态管理器
自定义指令
生命周期
过滤器
Vue.set
forceUpdate
Object.freeze
长列表渲染
常用指令
常用事件
图片懒加载
按需引入
语法糖
nextTick
key 的作用
常见问题
data
为什么是函数?
1、组件中 因为当一个组件被引用多次时,相当于创建了多个实例。这些实例用的都是同一个构造函数。若data
是对象的话,属于引用类型,修改其他组件属性时会影响到其他组件的实例。故,为了保证组件不同的实例之间 data 不冲突,data 必须是一个函数!
v-if
和v-show
的区别?
2、区别如下:
v-if
和v-show
用于视图层进行条件判断视图展示v-if
根据判断条件来动态的进行增删 DOM 元素,v-show
是判断条件来动态的进行显示和隐藏元素
总结:
- 若需要非常频繁地切换,则使用 v-show 较好
- 若如果在运行时条件很少改变,则使用 v-if 较好
v-model
的原理?
3、v-model
本质就是一个语法糖,可以看成是value
+ input
方法的语法糖。 可以通过 model 属性的prop
和event
属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。
Vue
模版编译原理?
4、本质:
将template模板 转换为 render函数
,经历以下阶段:
- 生成 AST 树
- 优化
- codegen
第一、首先解析模版,生成 AST 语法树(一种用 JavaScript 对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时都会执行对应的钩子进行相关处理。
第二、Vue 的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的 DOM 也不会变化。那么优化过程就是深度遍历 AST 树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。
keep-alive
?
5、谈一谈keep-alive
是Vue
的内置组件, 可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
常用属性:1)、
include
2)、exclude
, 允许组件有条件的进行缓存。生命周期:1)、
activated
2)、deactivated
, 用来得知当前组件是否处于活跃状态。
keep-alive
的中还运用了LRU(Least Recently Used)
算法。
hash
路由和history
路由实现原理?
6、location.hash
的值实际就是 URL 中#后面的东西。history
实际采用了HTML5
中提供的 API 来实现,主要有history.pushState()
和history.replaceState()
。
Vue2.x
和Vue3.x
渲染器的diff
算法分别说一下?
7、
diff
算法有以下过程:
- 同级比较,再比较子节点
- 先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)
- 比较都有子节点的情况(核心 diff)
- 递归比较子节点
正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 Vue 将 Diff 进行了优化,从 O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。
Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 React 的 Diff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。
Vue3.x 借鉴了 ivi算法
和 inferno算法
在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。
该算法中还运用了动态规划的思想求解最长递归子序列。
vue2.x
中如何监测数组变化?
8、使用了函数劫持的方式,重写了数组的(push、pop、shift、unshift、splice、sort、reverse)方法,Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
Vue2.x
响应式数据原理?
9、Vue 在初始化数据时,会使用 Object.defineProperty 重新定义 data 中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的 watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。
流程如下:
- 当一个 JavaScript 对象作为 Vue 的 data 项被传入的时候,Vue 会遍历该对象。并使用 Object.defineProperty 把每个属性转为 getter/setter。就是把 data 里的数据转为图上紫色的部分。
- Vue 使用 getter/setter,在内部追踪收集的依赖。在属性被访问或修改的时候,发出变更通知。
- 每个组件中都有一个 watcher,在组件渲染的时候,watcher 会把每个 touch 过的数据属性记录依赖,也就是会触发 getter 然后收集依赖(Collect as Dependency)。在属性被 setter 的时候,由依赖 dep 通知 watcher,重新渲染关联的组件。
10、Vue3.x 响应式数据原理?
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
问:Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?
答:判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。
问:监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?
答:可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。
Vue2.x 与 Vue3.0 对比
- Vue2.x 采用 flow 进行编写,而 3.0 源码全部采用 TypeScript 进行开发,对 TypeScript 支持友好
- 源码体积优化 移除部分 api 使用 tree-shaking
- 数据劫持优化 Vue3 采用 Proxy,性能大大提升
- 编译优化:Vue3 实现了静态模板分享,重新 diff 算法
- CompositionAPI 整合业务代码的逻辑,提取公共逻辑(Vue2.0 采用-Mixin - 命名冲突,数据来源不清晰)
- 自定义渲染器,可以用来创建自定义的渲染器,改写 Vue 底层渲染逻辑
- 新增 Fragment Teleport Suspense 组件