赞
踩
。
流程:
初始化传入 data 数据执行 initData
将数据进行观测 new Observer
将数组原型方法指向重写的原型
深度观察数组中的引用类型
有两种情况无法检测到数组的变化
。
当利用索引直接设置一个数组项时,例如 vm.items[indexOfItem] = newValue
当修改数组的长度时,例如 vm.items.length = newLength
不过这两种场景都有对应的解决方案。
利用索引设置数组项的替代方案
//使用该方法进行更新视图
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
加载渲染过程
父
beforeCreate ->
父
created ->
父
beforeMount
->
子
beforeCreate
->
子
created
->
子
beforeMount ->
子
mounted ->
父
mounted
子组件更新过程
父
beforeUpdate ->
子
beforeUpdate ->
子
updated ->
父
updated
父组件更新过程
父
beforeUpdate ->
父
updated
销毁过程
父
beforeDestroy ->
子
beforeDestroy ->
子
destroyed ->
父
destroyed
总结:
父组件先开始 子组件先结束
v-model
本质
就是 :
value + input 方法的语法糖
。可以通过 model 属性的 prop 和 event 属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。
例如:
text 和 textarea 元素使用 value 属性和 input 事件
checkbox 和 radio 使用 checked 属性和 change 事件
select 字段将 value 作为 prop 并将 change 作为事件
以输入框为例,当用户在输入框输入内容时,会触发 input 事件,从而更新 value。而 value 的改变同样会更新视图,这就是 vue 中的双向绑定。双向绑定的原理,其实现思路 如下:
首先要对
数据进行劫持监听
,所以我们需要设置
一个监听器 Observe
r,用来
监听
所有属 性。如果属性发上变化了,就需要告
诉订阅者 Watcher 看是否需要更新
。
因为订阅者是有很多个,所以我们需要有一个
消息订阅器 Dep 来专门收集这些订阅者
,然 后在监听器
Observer 和订阅者 Watcher 之间
进行统一管理的。
接着,我们还需要有一个
指令解析器 Compile
,对每个节点元素进
行扫描和解析
,将相关指令对应初始化成一个订阅者 Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
因此接下去我们执行以
下 3 个步骤,实现数据的双向绑定
:
实现一个
监听器 Observer
,用来
劫持并监听所有属
性,如果有变动的,就通知订阅 者。
实现一个
订阅者 Watcher
,可以
收到属性的变化通知并执
行相应的函数,从而更新视 图。
实现一个
解析器 Compile
,可以
扫描和解析每个
节点的相关指令,并根据
初始化模板数
据以及初始化相应的订阅器。
Vue3.x 响应式数据原理是什么?
Proxy 只会代理对象的第一层,那么
Vue3
又是怎样处理这个问题的呢
?
监
测数组的时候可能触发多次
get/set
,那么如何
防止触发多
次呢?
参考答案:
Vue3.x 响应式数据原理是什么? 在
Vue 2
中,响应式原理就是使用的
Object.defineProperty
来实现的。但是在 Vue 3.0 中采用了
Proxy,抛弃了 Object.defineProperty
方法。 究其原因,主要是以下几点:
Object.defineProperty
无法监控
到数组
下标的变化
,导致通过数组下标添加元素,不
能实时响应 Object.defineProperty 只能
劫持对象
的属性,从而需要对每个对象,每
个属性进行遍历,如果,属性值是对象,还需要深度遍历。
Proxy 可以劫持整个对象,
并返回一个新的对象
。
P
roxy 不仅可以代理对象,还可以代理数组。还可以代理动态
增加的属性。 Proxy 有多达 13 种拦截方法 Proxy作为新标准将受到浏览器厂商重点
持续的性能优化 Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的
呢? 判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方
法做代理, 这样就实现了深度观测。 监测数组的时候可能触发多次 get/set,那么
如何防止触发多次呢? 我们可以判断 key 是否为当前被代理对象 target 自身属性,
也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行
trigger。
参考答案:
简单来说,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 有了提 升
。该算法中还运用了动态规划的思想求解最长递归子序列。
hash 值的变化
,
不会
导致浏览器
向服务器发出请
求,浏览器不发出请求,就不会刷新页面;通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。
history 模式
的实现,主要是
HTML5
标准发布的
两个 API
,
pushState
和
replaceState
,这两API 可以在改变 URL,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。
两种模式的区别:
hash
模式
1
、
location.has
的值实际就是
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
需要后端进行配置。
路由钩子的执行流程,钩子函数种类有:全局守卫、路由守卫、组件守卫。
完整的导航解析流程:
1
、导航被触发。
2
、在失活的组件里调用
beforeRouterLeave
守卫。
3
、调用全局的
beforeEach
守卫。
4
、在重用的组件调用
beforeRouterUpdate
守卫(
2.2+
)。
5
、在路由配置里面
beforeEnter
。
6
、解析异步路由组件。
7
、在被激活的组件里调用
beforeRouterEnter
。
8
、调用全局的
beforeResolve
守卫(
2.5+
)。
9
、导航被确认。
10
、调用全局的
afterEach
钩子。
11
、触发
DOM
更新。
12
、调用
beforeRouterEnter
守卫中传给
next
的回调函数,创建好的组件实例会作为回
调函数的参数传入。
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个
User
组件,对于所有
ID
各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用
“
动态路径参数
”
(
dynamic segment
)来达到这个效果:
1 const User = {
2 template: "
3 User", };
4 const router = new VueRouter({
5 routes: [
6 // 动态路径参数 以冒号开头
7 { path: "/user/:id", component: User },
8 ],
9 });
问题
:
vue-router
组件复用导致路由参数失效怎么办?
解决方案
:
1
、通
过
watch
监听
路由参数再发请求
1 watch:{
2 "router":function(){
3 this.getData(this.$router.params.xxx)
4 }
5 }
2
、用
:key
来阻止复用
router-view :key=“$route.fullPath”
vuex 是什么
vuex 是一个专为 Vue 应用程序开发
的状态管理器,
采用集中式
存储管理
应用的所有组件的状态。每 一个 vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着应用中大部分 的状态 (state)。
为什么需要 vuex
由于组件只维护自身的状态(data),组件创建时或者路由切换时,组件会被初始化,从而导致 data 也 随之销毁。
使用方法
在 main.js 引入 store,注入。只用来读取的状态集中放在 store 中, 改变状态的方式是提交
mutations,这是个同步的事物,异步逻辑应该封装在 action 中。
什么场景下会使用到 vuex
如果是 vue 的小型应用,那么没有必要使用 vuex,这个时候使用 vuex 反而会带来负担。组件之间的 状态传递使用 props、自定义事件来传递即可。 但是如果
涉及到 vue 的大型应用
,那么就需要类似于
vuex 这样的集中管
理状态的状态机来管理所有 组件的状态。例如登录状态、加入购物车、音乐播放等,总之只要是开发 vue 的大型应用,都推荐使 用 vuex 来管理所有组件状态
主要包括以下几个模块:
需要做
vuex
数据持久化
,一般使
用本地储存的方案
来保存数据,可以自己设计存储方
案,也可以使用第三方插件。
推荐使用
vuex-persist (
脯肉赛斯特
)
插件,它是为
Vuex
持久化储存而生的一个插件。
不需要你手动存取
storage
,而是直接将状态保存至
cookie
或者
localStorage
中。
这里只列举针对
Vue
的性能优化,整个项目的性能优化是一个大工程。
作用
:vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成,
nextTick的回调是在下次 DOM 更新循环结束之后执行的延迟回调
。
实现原理
:nextTick 主要使用了
宏任务和微任务
。根据执行环境分别尝试采用
keep-alive 组件是 vue 的内置组件
,用于
缓存内部组件
实例。这样做的目的在于,keep
alive 内部的组件切回时,
不用重新创建
组件实例,而直接使用缓存中的实例,一方面能够
避免创建组件带来的开销,另一方面可以保留组件的状态
。
keep-alive 具有 include 和 exclude 属性,通过它们可以控制哪些组件进入缓存。另外它 还提供了 max 属性,通过它可以设置最大缓存数,当缓存的实例超过该数时,vue 会移除最久没有使用的组件缓存。
受keep-alive的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是activated 和 deactivated,它们分别在组件激活和失活时触发。第一次 activated 触发是在 mounted 之后
在具体的实现上,keep-alive 在内部维护了一个 key 数组和一个缓存对象
1 // keep-alive 内部的声明周期函数
2 created () {
3 this.cache = Object.create(null)
4
5 this.keys = []
6 }
key 数组记录目前缓存的组件 key 值,如果组件没有指定 key 值,则会为其自动生成一个
唯一的 key 值 cache 对象以 key 值为键,vnode 为值,用于缓存组件对应的虚拟 DOM
在 keep-alive 的渲染函数中,其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存,
如果有,从缓存中读取到对应的组件实例;如果没有则将其缓存。、
当缓存数量超过 max 数值时,keep-alive 会移除掉 key 数组的第一个元素。
了解
Vue
响应式原理的同学都知道在两种情况下修改
Vue
是不会触发视图更新的。
Vue.set
或者说是
$set
原理如下
因为响应式数据 我们给对象和数组本身新增了
__ob__
属性,代表的是
Observer
实例。
当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象
__ob__
的
dep
收集到的
watcher
去更新,当修改数组索引时我们调用数组本身的
splice
方法去
更新数组。
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了
组件 key 值,如果组件没有指定 key 值,则会为其自动生成一个
唯一的 key 值 cache 对象以 key 值为键,vnode 为值,用于缓存组件对应的虚拟 DOM
在 keep-alive 的渲染函数中,其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存,
如果有,从缓存中读取到对应的组件实例;如果没有则将其缓存。、
当缓存数量超过 max 数值时,keep-alive 会移除掉 key 数组的第一个元素。
了解
Vue
响应式原理的同学都知道在两种情况下修改
Vue
是不会触发视图更新的。
Vue.set
或者说是
$set
原理如下
因为响应式数据 我们给对象和数组本身新增了
__ob__
属性,代表的是
Observer
实例。
当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象
__ob__
的
dep
收集到的
watcher
去更新,当修改数组索引时我们调用数组本身的
splice
方法去
更新数组。
总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了
[外链图片转存中…(img-vLL4M8Tp-1714240405206)]
[外链图片转存中…(img-eiDODDpU-1714240405206)]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。