🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来Vuet篇专栏内容:vue综合指南
目录
101、Vue 框架怎么实现对象和数组的监听?
102、Proxy 与 Object.defineProperty 优劣对比
103、介绍下vue单页面和多页面区别
104、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
105、虚拟 DOM 的优缺点?
106、虚拟 DOM 实现原理?
107、Vue中key的作用 不加会怎么样
108、Vue的项目优化
109、说说你使用 Vue 框架踩过最大的坑是什么?怎么解决的?
110、vue如何优化页面加载:
111、什么是路由懒加载:
112、vue中有哪些内置组件:
113、ssr是什么
114、vue中router-link和传统a链接的区别
115、vue插槽是什么
116、methods和computed的区别?
117、Vue的传参方法?
118、vue-router路由守卫?
119、vue组件封装?
120、vuex刷新数据页面会丢失吗?咋解决的?
121、虚拟DOM和DIFF算法?
122、vue2和vue3原理?
123、scss是什么?
124、 active-class是哪个组件的属性?
125、说出至少4种vue当中的指令和它的用法?
126、请说出vue.cli项目中src目录每个文件夹和文件的用法?
127、对于即将要来的vue3.0你有什么了解
128、prop 验证,和默认值
129、Vuex和redux有什么区别 他们的共同思想
共同思想
101、Vue 框架怎么实现对象和数组的监听?
如果被问到 Vue 怎么实现数据双向绑定,大家肯定都会回答 通过 Object.defineProperty() 对数据进行劫持,但是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,同理无法对数组进行劫持,但是我们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操作)的变化,那它是怎么实现的呢?我们查看相关代码如下:
/*** Observe a list of Array items.*/observeArray (items: Array<any>) {for (let i = 0, l = items.length; i < l; i++) {observe(items[i]) // observe 功能为监测数据的变化}}
/*** 对属性进行递归遍历*/let childOb = !shallow && observe(val) // observe 功能为监测数据的变化
通过以上 Vue 源码部分查看,我们就能知道 Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。
102、Proxy 与 Object.defineProperty 优劣对比
Proxy 的优势如下:
-
Proxy 可以直接监听对象而非属性;
-
Proxy 可以直接监听数组的变化;
-
Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
-
Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
-
Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
Object.defineProperty 的优势如下:
-
兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
103、介绍下vue单页面和多页面区别
单页面首次加载缓慢,但是后面的加载优势就会超过多页面了。而多页面则是首次加载快,但是后面每次都要进行加载。 vue这种还是比较适合做单页面的,你把webpack配置一下做多页面应该也可以,但是这样就丧失了vue的优势了。 建议如果不需要考虑太多的兼容性就还是用vue来做单页面吧。比如自己用的后台管理系统什么的。 如果要兼容到ie8什么的。。。还是再考虑下。
104、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
来实现为对象添加响应式属性,那框架本身是如何实现的呢?
我们查看对应的 Vue 源码:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {// target 为数组 if (Array.isArray(target) && isValidArrayIndex(key)) {// 修改数组的长度, 避免索引>数组长度导致splcie()执行有误target.length = Math.max(target.length, key)// 利用数组的splice变异方法触发响应式 target.splice(key, 1, val)return val}// key 已经存在,直接修改属性值 if (key in target && !(key in Object.prototype)) {target[key] = valreturn val}const ob = (target: any).__ob__// target 本身就不是响应式数据, 直接赋值if (!ob) {target[key] = valreturn val}// 对属性进行响应式处理defineReactive(ob.value, key, val)ob.dep.notify()return val
}
我们阅读以上源码可知,vm.$set 的实现原理是:
-
如果目标是数组,直接使用数组的 splice 方法触发相应式;
-
如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)
105、虚拟 DOM 的优缺点?
优点:
-
保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
-
无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
-
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
-
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
106、虚拟 DOM 实现原理?
虚拟 DOM 的实现原理主要包括以下 3 部分:
-
用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
-
diff 算法 — 比较两棵虚拟 DOM 树的差异;
-
pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
107、Vue中key的作用 不加会怎么样
key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。Vue 的 diff 过程可以概括为:oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex,如果以上 4 种比较都没匹配,如果设置了key,就会用 key 再进行比较,在比较的过程中,遍历会往中间靠,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了,就会结束比较。具体有无 key 的 diff 过程,
所以 Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速
更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key
对比中可以避免就地复用的情况。所以会更加准确。
更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:
function createKeyToOldIdx (children, beginIdx, endIdx) {let i, keyconst map = {}for (i = beginIdx; i <= endIdx; ++i) {key = children[i].keyif (isDef(key)) map[key] = i}return map
}
108、Vue的项目优化
(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 查找性能瓶颈
109、说说你使用 Vue 框架踩过最大的坑是什么?怎么解决的?
本题为开放题目 把老师项目中 给大家讲的坑说出来即可
110、vue如何优化页面加载:
使用cdn,不要打包一些公共的文件和组件库
111、什么是路由懒加载:
路由懒加载是通过异步的方式来加载对应的路由组件,提高页面相应速度
112、vue中有哪些内置组件:
component slot transtion fliters
113、ssr是什么
服务端渲染
114、vue中router-link和传统a链接的区别
官方中给出的解释是这样的:
<router-link> 比起写死的 <a href="..." rel="external nofollow" rel="external nofollow" > 会好一些,理由如下:
无论是 HTML5 history 模式还是 hash 模式,它的表现行为一致,所以,当你要切换路由模式,或者在 IE9 降级使用 hash 模式,无须作任何变动。
在 HTML5 history 模式下,router-link 会守卫点击事件,让浏览器不再重新加载页面。
当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了。
呃呃,只能说博主刚学时第一次并没有看懂(应该是之前的基础知识不牢固吧,看来之后得补补啦),于是去自行查阅啦:
<a href="..." rel="external nofollow" rel="external nofollow" >
W3C中是这样解释a标签的:
<a> 标签定义超链接,用于从一张页面链接到另一张页面。
从一张页面跳转到另一张页面,但从这里来说就违背了多视图的单页Web应用这个概念
通过a标签进行跳转,页面会被重新渲染,即相当于重新打开一个新的网页,体现为视觉上的“闪烁”(如果是本地的项目基本看不出来)
router-link
<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。
通过router-link进行跳转不会跳转到新的页面,也不会重新渲染,它会选择路由所指的组件进行渲染,避免了重复渲染的“无用功”。
总结:对比<a>,router-link组件避免了不必要的重渲染,它只更新变化的部分从而减少DOM性能消耗
Vue的创新之处在于,它利用虚拟DOM的概念和diff算法实现了对页面的"按需更新",
Vue-router很好地继承了这一点,重渲染是我们不希望看到的,因为无论跳转到哪个页面,只需要渲染一次就够了。<Link>组件帮助我们实现了这个愿望
反观<a>标签,每次跳转都得重渲染一次,在一个浩大的项目里,这多么可怕!我们的"渲染"做了许多"无用功",而且消耗了大量弥足珍贵的DOM性能!
115、vue插槽是什么
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。
116、methods和computed的区别?
都是存放方法的地方;
methods里面存放的方法每次调用都会重新计算(不会缓存)
computed里面的方法调用计算后,当其参数不发生改变时再次调用,直接返回上次计算的结果,不重新计算(会响应数据缓存)
watch主要用来解决 beforeUpdate, updated两个钩子里修改状态导致死循环的问题,采用watch以及 监测可能异步改变值得时候
117、Vue的传参方法?
属性传值、$refs、$parent、通知传值(广播传值)、本地传值、路由传值
父向子传值:v-bind属性绑定(props)
子向父传值:v-on 事件绑定
兄弟组件之间共享数据:EventBus($on数据接收方,$emit发送数据的内个组件)
118、vue-router路由守卫?
全局守卫、路由独享守卫、组件内守卫
119、vue组件封装?
首先,使用Vue.extend()创建一个组件
然后,使用Vue.component()方法注册组件
接着,如果子组件需要数据,可以在props中接受定义
最后,子组件修改好数据之后,想把数据传递给父组件,可以使用emit()方法
120、vuex刷新数据页面会丢失吗?咋解决的?
会用本地存储
121、虚拟DOM和DIFF算法?
将DOM抽象为虚拟DOM, 然后通过新旧虚拟DOM 这两个对象的差异(Diff算法),最终只把变化的部分重新渲染,提高渲染效率的过程;
diff 是通过JS层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染
122、vue2和vue3原理?
1. vue2和vue3双向数据绑定原理发生了改变
vue2 的双向数据绑定是利用ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。
vue3 中使用了 es6 的 ProxyAPI 对数据代理。
相比于vue2.x,使用proxy的优势如下
defineProperty只能监听某个属性,不能对全对象监听
可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化
2、默认进行懒观察(lazy observation)
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
3、更精准的变更通知
比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
4、vue3新加入了TypeScript以及PWA的支持
5、vue2和vue3组件发送改变
123、scss是什么?
预处理css,把css当前函数编写,定义变量,嵌套.
124、 active-class是哪个组件的属性?
vue-router模块的router-link组件。
125、说出至少4种vue当中的指令和它的用法?
v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定
126、请说出vue.cli项目中src目录每个文件夹和文件的用法?
assets文件夹是放静态资源; components是放组件; router是定义路由相关的配置; view视图; app.vue是一个应用主组件; main.js是入口文件
127、对于即将要来的vue3.0你有什么了解
Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:
(1)监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
-
只能监测属性,不能监测对象
-
检测属性的添加和删除;
-
检测数组索引和长度的变更;
-
支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了以下特性:
-
用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
-
默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
-
更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
-
不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
-
更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。
(2)模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。 同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
(3)对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。 此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。
(4)其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:
-
支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
-
支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
-
基于 treeshaking 优化,提供了更多的内置功能。
128、prop 验证,和默认值
我们在父组件给子组件传值得时候,为了避免不必要的错误,可以给prop的值进行类型设定,让父组件给子组件传值得时候,更加准确,prop可以传一个数字,一个布尔值,一个数组,一个对象,以及一个对象的所有属性。组件可以为 props 指定验证要求。如果未指定验证要求,Vue 会发出警告比如传一个number类型的数据,用defalt设置它的默认值,如果验证失败的话就会发出警告。
props: {visible: {default: true,type: Boolean,required: true},
},
129、Vuex和redux有什么区别 他们的共同思想
-
Vuex改进了Redux中的Action和Reducer函数,以mutations变化函数取代Reducer,无需switch,只需在对应的mutation函数里改变state值就可以
-
Vuex由于Vue自动重新渲染的特性,无需订阅重新渲染函数,只要生成新的state就可以
-
Vuex数据流的顺序是:View调用store.commit提交对应的请求到Store中对应的mutation函数 -- store改变(vue检测到数据变化自动渲染)
通俗理解就是:Vuex弱化dispatch,通过commit进行store状态的一次变更;取消了action概念,不必传入特定的action形式进行指定变更;弱化reducer,基于commit参数直接对数据进行转变,使得框架更加建议
共同思想
-
单一的数据源
-
变化可以预测
-
本质上:
Redux和Vuex都是对MVVM思想的服务,将数据从视图中抽离的一种方案 -
形式上:
Vuex借鉴了Redux,将store作为全局的数据中心,进行数据管理