一、说下MVVM?
MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象
在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
二、mvvm 和 mvc 区别
mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。和当 Model 频繁发生变化,开发者需要主动更新到 View 。
三、Vue2.x双向绑定原理?
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
** 第一步:** 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
第二步: compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步: Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个 update()方法
- 待属性变动 dep.notice()通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。
**第四步:**MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。
四、Vue3.x响应式数据原理?
proxy方法
五、Object.defineProperty缺点?
- 1.深度监听,需要递归到底,一次性计算量大
- 2.无法监听新增属性/删除属性(vue2.0可以使用vue.set vue.delete)
- 3.无法原生监听数组,需要特殊处理
六、Proxy 的优势
- Proxy 可以直接监听对象而非属性
- Proxy 可以直接监听数组的变化
- Proxy 有多达 13 种拦截方法,不限于apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
- Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改
- Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
七、nextTick知道吗,实现原理是什么
在下次 DOM 更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用
PromiseMutationObserversetImmediate如果以上都不行则采用setTimeout
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。
八、Vue的生命周期
beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
created 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom
beforeMount 发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated
mounted 在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
九、每个生命周期适合哪些场景?
生命周期钩子的一些使用方法:
- beforecreate : 可以在这加个loading事件,在加载实例时触发
- created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
- mounted : 挂载元素,获取到DOM节点
- updated : 如果对数据统一处理,在这里写上相应函数
- beforeDestroy : 可以做一个确认停止事件的确认框
- nextTick : 更新数据后立即操作dom
十、说一下Computed和Watch
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
1.当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
2.当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
十一、组件中的data为什么是一个函数
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
十二、Vue模版编译原理?
简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:
- 生成AST树
- 优化
- codegen
首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。
Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。 编译的最后一步是将优化后的AST树转换为可执行的代码。
十三、keep-alive了解吗
keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
常用的两个属性include/exclude,允许组件有条件的进行缓存。
两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态。
keep-alive的中还运用了LRU(Least Recently Used)算法。
(又是数据结构与算法,原来算法在前端也有这么多的应用)
十四、组件通信有哪些方式?
组件通信分为:父子组件通信、隔代组件通信、兄弟组件通信。
- props 和 $emit:适用于父子组件,这种方法是 Vue 组件的基础。
- ref 和 children 适用父子组件通信
- EventBus 总线 :适用于父子、隔代、兄弟组件通信。
- VUEX:适用于父子、隔代、兄弟组件通信。
- provide 和 inject:适用于隔代通信。
十五、SSR了解吗?
十六、你都做过哪些Vue的性能优化?
编码阶段
- 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
- v-if和v-for不能连用
- 如果需要使用v-for给每项元素绑定事件时使用事件代理
- SPA 页面采用keep-alive缓存组件
- 在更多的情况下,使用v-if替代v-show
- key保证唯一
- 使用路由懒加载、异步组件
- 防抖、节流
- 第三方模块按需导入
- 长列表滚动到可视区域动态加载
- 图片懒加载 SEO优化
- 预渲染
- 服务端渲染SSR 打包优化
- 压缩代码
- Tree Shaking/Scope Hoisting
- 使用cdn加载第三方模块
- 多线程打包happypack
- splitChunks抽离公共文件
- sourceMap优化 用户体验
- 骨架屏
- PWA
十七、hash路由和history路由实现原理说一下
location.hash的值实际就是URL中#后面的东西。
history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。
十八、Vue 中的 key 有什么作用?
Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
十九、为何组件data 必须是函数?
因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。
所以一个组件的 data必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
new Vue 的实例是不会被复用的,因此不存在以上问题。
二十、router的区别是什么?
- router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
- route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
作者:Arthas_Xue 链接:juejin.cn/post/684490… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二十一vue3.0 有那些改进?
- vue3采用TS来编写
- 支持 composition API 解决了mixin的问题,让代码更有条理性
- vue3 中的响应式数据原理改成proxy
- vdom的对比算法更新,只更新vdom的绑定来动态数组部分
二十二、vuex 中action 和mutation 有何区别?
mutaction 是同步更新数据(内部会进行是否为异步方式更新数据检测) $watch 在严格模式下会报错
action 异步操作,可以获取数据后调用mutaction 提交最终数据
二十三、vue-loader是什么?使用它的用途有哪些
解析.vue文件的一个加载器,跟template/js/style转换成js模块。 用途:js可以写es6、style样式可以scss或less、template可以加jade等
二十四、vue-router 有哪几种导航钩子?
- 全局导航钩子
router.beforeEach(to, from, next),
router.beforeResolve(to, from, next),
router.afterEach(to, from )
- 组件内钩子
beforeRouteEnter,
beforeRouteUpdate,
beforeRouteLeave
- 单独路由独享组件
beforeEnter
二十五、vuex 有哪几种属性?
有 5 种,分别是 state、getter、mutation、action、module
二十六、vuex 原理
vuex 仅仅是作为 vue 的一个插件而存在,不像 Redux,MobX 等库可以应用于所有框架,vuex 只能使用在 vue 上,很大的程度是因为其高度依赖于 vue 的 computed 依赖检测系统以及其插件系统,
vuex 整体思想诞生于 flux,可其的实现方式完完全全的使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对对象 Property set get 方法的代理劫持。最后一句话结束 vuex 工作原理,vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;
二十七、vuex是什么?怎么使用?哪种功能场景使用它?
vue框架中状态管理。在main.js引入store,注入。
新建了一个目录store.js,….. export 。
场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
二十八、params和query的区别?
用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是 this.route.params.name 。
url地址显示:query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意点:query刷新不会丢失query里面的数据 params刷新 会 丢失 params里面的数据。