标题
- 1、双向绑定原理,手撕简单源码(vue响应式原理)
- 2、vue的优点
- 3、请谈谈Vue中的MVVM模式
- 4、渐进式框架的理解
- 5、vue中双向数据绑定是如何实现的
- 6、虚拟DOM
- 7、created和mounted的区别
- 8、computed和watch的区别
- 9、vue3新特性,proxy和defineProperty的区别
- 10、MVVM和mvc的差别
- 11、父子通信(传值
- 12、非父子通信(多个vue实例传值)(vuex)
- 13、生命周期
- 14、vue中key的作用
- 15、v-if和v-show区别
- 16、vue-router原理,路由如何跳转
- 17、 vuex数据流向,mutation和action
- 18、 Vue中的nextTick
- 19 scope原理
- 20、v-if和v-for为什么不能一起使用
- 21、vue中provide和injected用法
- 23、history和hash路由的区别
- 24、单页面路由原理
- 25、Vue 异步更新原理
- 29、keep-alive
- 30、package.json和package-lock.json
- 34、路由的懒加载
- 35 、data为什么是函数
- 36、有没有封装过组件?使用过Slot吗?
- 37、vue Array长度变化是否能监测到
- 38、你用过vue自定义指令吗?让你实现埋点的功能怎么做?
- 39、单页面应用和多页面应用的区别
- 40、vue如果同一个数据,很短的时间内连续更新 会怎么样。
- 40、盘点 Vue3 与 Vue2 的区别
- 41、vite与webpack
1、双向绑定原理,手撕简单源码(vue响应式原理)
Vue的双向数据绑定是通过数据劫持结合发布者订阅者模式来实现的
- 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可以拿到最新值并通知订阅者
- 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
- 实现一个watcher,作为连接Oberver和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行绑定的相应回调函数,从而更新视图
- MVVM入口函数,整合以上三者
描述
Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图
通过Object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时通知Vue实例,触发相应的getter和setter回调函数。
当把一个普通 Javascript 对象传给Vue 实例来作为它的 data 选项时, Vue 将遍历它的属性,用Object.defineProperty将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
2、vue的优点
- 轻量级的框架
- 双向数据绑定
- 组件化
- 视图,数据,结构分离
- 虚拟dom
3、请谈谈Vue中的MVVM模式
MVVM全程是Model-View-ViewModel
vue是以数据为驱动的,vue自身将DOM和数据进行绑定,一旦创建绑定,dom和数据将保持同步。每当数据发生变化,dom会跟着变化。ViewModel是Vue的核心,它是Vue的一个实例。
DomListeners和DataBindings是实现双向绑定的关键。DOMListenners监听页面所有View层DOM元素的变化,当发生变化,Model层的数据随之改变;DataBindings监听Model层的数据,当数据发生变化,View层的DOM元素随之变化。
4、渐进式框架的理解
“渐进式框架”就是用你想用或者能用的功能特性,你不想用的部分功能可以先不用。vue不强求你一次性接受并使用它的全部功能特性。
5、vue中双向数据绑定是如何实现的
view更新data其实可以通过时间监听即可,比如input标签监听“input”事件就可以实现了
通过Object.defineProperty()对属性设置一个set函数,当数据改变了就会触发这个函数,所以我们只要将一些需要更新的方法放在里面就可以实现data更新view了。
6、虚拟DOM
virtual dom 其实就是一颗以js对象(VNode节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实DOM的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。
6.1diff算法
找出有必要更新的节点更新,没有更新的节点就不要动。这其中的核心就是如何找出哪些更新哪些不更新,这个过程就需要diff算法来完成
怎么做到复杂度从o(n^3)降低到o(n)
标准diff算法:o(n^3) 树的最小距离编辑算法(找到最少的改动路径)算出来的(待了解)考虑到前端操作很少跨级别修改节点,通常都是修改节点属性,及调整子节点,所以只比较同层级的节点。就将复杂度降到了n
patch过程:
1 对比新节点是否存在,不存在插入新节点
2 对比老节点是否存在,不存在删除老节点
3 对比新节点与老节点是否相同,如果不同则删除老节点,插入新节点
4 如果新老节点相同(tag,key等),则执行patchVNode,如果是文本的话就处理,然后对比children。
diff重点
对比children,若老的有新的没则删除,若新的有老的没,则新增。
若都有children,先头头比较,然后尾尾比较,头尾,尾头比较(主要是为了快速处理reverse的情况),
如果相同,则执行patchVNode进行children的对比,如果不同,则会对老节点进行map处理,在里面按照key来找新节点,如果找到了,则patchvnode,否则新建,没有key的话就新建
3.0中diff的差异
预处理及最长递增子序列
7、created和mounted的区别
created
实例创建完成,可以访问data、computed、watch、methods上的方法和数据,未挂载到DOM,不能访问到$el属性, $ref属性内容为空数组。
mounted
实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问,常用与获取VNode信息和操作,Ajax请求。
8、computed和watch的区别
computed
computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed的值在getter执行之后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值才会重新调用对应的getter来计算。
watch
watcher更像是一个data的数据监听回调,当依赖的data的数据变化,执行回调,在方法中会传入newVal和oldVal。可以提供输入值无效,中间值等场景。Vue实例将会在实例化的时候调用$watch(),遍历watch对象的每一个属性。如果你需要在某个数据变化时候做一些事情,使用watch
总结
如果一个数据需要经过复杂计算就用computed。
如果一个数据依赖于其他数据,那么把这个数据设计为computed的
如果一个数据需要被监听并且对数据做一些操作就用watch。
如果你需要在某个数据变化的时候做一些事情,使用watch来观察这个数据变化。
9、vue3新特性,proxy和defineProperty的区别
-
Object.defineProperty只能劫持对象的属性,而 Proxy 是直接代理对象。
由于 Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,
如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。 -
Object.defineProperty对新增属性需要手动进行 Observe。
由于 Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象,
对其新增属性再使用 Object.defineProperty 进行劫持。
也正是因为这个原因,使用 Vue 给 data 中的数组或对象新增属性时,
需要使用 vm.$set 才能保证新增的属性也是响应式的。
9.1proxy应用实例
1. 数据验证
在设置对象属性时,使用 Proxy 可以对输入的值进行验证,确保其符合特定的规则。
解释:创建了一个 person 对象的代理 personProxy,在 set 拦截器中对 age 和 name 属性的赋值进行验证。如果赋值不符合规则,会抛出错误。
2. 访问控制
可以使用 Proxy 限制对对象某些属性的访问,只有满足特定条件的用户才能访问。
解释:代码创建了一个 secretData 对象的代理 secretProxy,在 get 拦截器中根据 restrictedAccess.canAccess 的值来决定是否允许访问 password 属性。
3. 日志记录
在对象属性被访问或修改时,使用 Proxy 记录相关的日志信息。
解释:通过 get 和 set 拦截器,在访问和修改 data 对象的属性时,会记录相应的日志信息。
4. 函数调用拦截
Proxy 不仅可以用于对象,还可以用于函数,对函数的调用进行拦截和增强。
解释:创建了函数 add 的代理 addProxy,在 apply 拦截器中记录函数调用的参数和返回值。
5. 数组边界检查
对于数组,可以使用 Proxy 检查访问的索引是否越界。
解释:创建了数组 arr 的代理 arrProxy,在 get 和 set 拦截器中检查索引是否越界,如果越界会输出提示信息。
10、MVVM和mvc的差别
- MVC中Controller演变成MVVM中的ViewModel
- MVVM通过数据来显示视图层而不是节点操作
- MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验等问题。
11、父子通信(传值
通信
一.父组件向子组件传递值使用props;
二.子组件向父组件传递值用this. $emit()
三.组件与组件值引用可采用vuex.
四.vuex是一个专为vue. js应用程序开发状态管理模式。集中式管理所有组件的状态。
五.利用插槽子组件向父组件传值
12、非父子通信(多个vue实例传值)(vuex)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说就是:应用遇到多个组件共享状态时,使用vuex。
场景:多个组件共享数据或者是跨组件传递数据时
vuex的流程
页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation,mutation会修改state中对应的值。最后通过getter把对应值跑出去,在页面的计算属性中,通过,mapGetter来动态获取state中的值
12.1为什么要commit改变状态
通过commit 提交 mutation 的方式来修改 state 时,vue的调试工具能够记录每一次state的变化,这样方便调试。但是如果是直接修改state,则没有这个记录。
12.2同级传参的两种方式
1.query穿参,或者params传参
使用 this.KaTeX parse error: Expected '}', got 'EOF' at end of input: … '参数值'}) this.router.push({name: ‘/’, params: {参数名: ‘参数值’})
注意1: 使用params时不能使用path
接收: var a = this. r o u t e . q u e r y . 参数名 v a r b = t h i s . route.query.参数名 var b = this. route.query.参数名varb=this.route.params.参数名
注意2:实用params去传值的时候,在页面刷新时,参数会消失,用query则不会有这个问题。
13、生命周期
概念
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM-渲染、更新-渲染、卸载等一系列的过程,我们称这是 Vue 的生命周期。
初始化显示
beforeCreated
created
beforeMounted
mounted
更新
beforeUpdate
updated
销毁
beforeDestrory
destrory
14、vue中key的作用
key的作用是为了在diff算法执行时更快找到对应的节点,提高diff速。
key具有唯一性
vue中循环需加 :key = “唯一标识“,唯一表示可以是item里面id index等,因为vue组件高度复用增加key可以标识组件的唯一性,为了更好的区别各个组件key的作用主要是为了高校的更新虚拟DOM
15、v-if和v-show区别
- 控制手段不同
- 编译过程不同
- 编译条件不同
控制手段:v-show隐藏则是为该元素添加css–display:none,dom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除
编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换
编译条件:v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
v-show 由false变为true的时候不会触发组件的生命周期
v-if由false变为true的时候,触发组件的beforeCreate、create、beforeMount、mounted钩子,由true变为false的时候触发组件的beforeDestory、destoryed方法
性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
使用场景
v-if 相比 v-show 开销更大的(直接操作dom节点增加与删除)
如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好
#参考文献
16、vue-router原理,路由如何跳转
window.location的hash方法是干嘛用的
改变location.hash会在浏览器的访问历史中增加一个记录,使用后退键时可以回到上一个浏览位置。利用这一点就可以解决ajax中无访问状态的问题,配合#和历史记录,就可以在无刷新的ajax中顺利往返于各个访问状态。
我们都知道,单页面应用(SPA)的核心之一是: 更新视图而不重新请求页面;vue-rouetr在实现单页面前端路由时,提供了两种方式:Hash模式和History模式;根据mode参数来决定采用哪一种方式。
1、Hash模式:
hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中也不会不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;
2、History模式:
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面
通常情况下,我们会选择使用History模式,原因就是Hash模式下URL带着‘#’会显得不美观
但当用户直接在用户栏输入地址并带有参数时: Hash模式:xxx.com/#/id=5 请求地址为 xxx.com,没有问题; History模式: xxx.com/id=5 请求地址为 xxx.com/id=5,如果后端没有对应的路由处理,就会返回404错误;
在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。 给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果你使用 Node.js 服务器,你可以用服务端路由匹配到来的 URL,并在没有匹配到路由的时候返回 404,以实现回退。
17、 vuex数据流向,mutation和action
1、流程顺序
“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation。
2、角色定位
基于流程顺序,二者扮演不同的角色。
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:业务代码、异步请求。
3、限制