本文目录
- 1.简述Vue的响应式原理
- 2.delete和Vue.delete删除数组的区别
- 3.v-for循环时为什么要加key
- 4.Vue 组件 data 为什么必须是函数
- 5.Vue 的核心是什么
- 6.Vue 等SPA单页面应用的优缺点
- 7.v-if 和 v-show 有什么区别?
- 8.Vue中如何监控某个属性值的变化?
- 9.Vue中给data中的对象属性添加一个新的属性时会发生什么,如何解决?
- 10.
$route
和$router
的区别 - 11.computed 和 watch 的区别和运用的场景
- 12.路由模式hash和history的区别
- 13.对MVC和MVVM的理解
- 14.vue如何实现数据双向绑定
- 15.简述vue的原理
- 简要说一下vue的生命周期
- 17.vue的diff算法原理
- 18.二次封装axios的基本思路
- vue首屏优化加载时间的方法
- 20.当单页面应用的体量越来越大时,只有一个页面会难以应对负责的业务场景,此时可以进行两个方面的优化:1.将单页面应用拆分成多个页面。 2.将几十甚至上百个路由进行重新规划。讲一下你对这两方面的见解。
- 21.说一下vue3版本的新特性
- 22.actions 和 mutations 有什么区别。
- 23.怎么实现axios的同步请求
- 24.说下常用的组件通信方法
- 25.vue怎么进行seo优化
- 26.data中某个数据想解除双向绑定该怎么做
- 27.页面中定义一个定时器,在哪个阶段清除
- 28.vue中data的属性可以和methods中的方法同名吗
- 29.怎么缓存当前的组件?缓存后怎么更新
- 30.怎么实现路由懒加载
- 31.vuex有哪几种属性
- 32.对于仅仅用来展示的长列表数据怎么进行优化
1.简述Vue的响应式原理
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
2.delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。
var a = [1,2,3,4]
delete a[1]
console.log(a)
//[1, empty, 3, 4]
console.log(a[1])
//undefined
Vue.delete 直接删除了数组对应的项,改变了数组的键值。
var c=[1,2,3,4]
this.$delete(c,1)
console.log(c)
//[1,3,4]
3.v-for循环时为什么要加key
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
总结:当v-for循环出的元素需要进行到动态的变化的时候,是一定要绑定key值的。
4.Vue 组件 data 为什么必须是函数
因为 JS 本身的特性带来的,如果 data 是一个对象,那么由于对象本身属于引用类型,当我们修改其中的一个属性时,会影响到所有 Vue 实例的数据。如果将 data 作为一个函数返回一个对象,那么每一个实例的 data 属性都是独立的,不会相互影响了。
5.Vue 的核心是什么
数据驱动、组件系统。
jQuery 专注视图层,通过操作 DOM 去实现页面的一些逻辑渲染; Vue 专注于数据层,通过数据的双向绑定,最终表现在 DOM 层面,减少了 DOM 操作。
6.Vue 等SPA单页面应用的优缺点
优点
- 良好的交互体验
- 良好的前后端工作分离模式
- 减轻服务器压力
缺点
- SEO 难度较高
- 前进、后退管理逻辑不是那么清晰
- 初次加载耗时多
7.v-if 和 v-show 有什么区别?
v-show 仅仅控制元素的显示方式,将 display 属性在 block 和 none 来回切换;而v-if会控制这个 DOM 节点的存在与否。当我们需要经常切换某个元素的显示/隐藏时,使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
8.Vue中如何监控某个属性值的变化?
watch: {
obj: {
handler (newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改:
watch: {
'obj.a': {
handler (newValue, oldValue) {
console.log('obj changed')
}
}
}
还有一种方法,可以通过computed 来实现,只需要:
computed : {
a1 () {
return this.obj.a
}
}
利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。
9.Vue中给data中的对象属性添加一个新的属性时会发生什么,如何解决?
给data中的对象增加属性需要用到Vue的全局api—— $set():
this.$set(this.obj, 'b', 'obj.b')
这样$set() 方法相当于手动的去把新增加的属性处理成一个响应式的属性,从而自动触发视图改变。利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue,Vue也是不能检测到的,
10.$route
和 $router
的区别
$router
为 VueRouter 实例,想要导航到不同URL,则使用 $router.push
方法。
$route
为当前 router 跳转对象,里面可以获取 name 、 path 、 query 、 params 等。
11.computed 和 watch 的区别和运用的场景
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
12.路由模式hash和history的区别
对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义。前端路由的核心,就在于 => 改变视图的同时不会向后端发出请求。
为了达到这一目的,浏览器当前提供了以下两种支持:
1.hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。
比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
2.history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)
这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
因此可以说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。
hash
hash路由模式是这样的:http://xxx.abc.com/#/xx。 有带#号,后面就是hash值的变化。改变后面的hash值,它不会向服务器发出请求,因此也就不会刷新页面。hash的url中会夹杂#,会让请求变得不是那么优雅。
history
浏览器地址没有#, 比如(http://localhost:3001/a); 它也一样不会刷新页面的。但是url地址会改变。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。
对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式比较多,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
13.对MVC和MVVM的理解
MVC模式是MVVM模式的基础,MVVM模式更像是MVC模式的优化改良版,他们两个的MV即Model(模型),view(视图)相同,不同的是MV之间的纽带部分。本文主要介绍MVC与MVVM的应用与区别。
MVC
MVC允许在不改变视图的情况下改变视图对用户输入的响应方式,用户对View的操作交给了Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,一旦Model发生变化便通知相关视图进行更新。
如果前端没有框架,只使用原生的html+js,MVC模式可以这样理解。将html看成view;js看成controller,负责处理用户与应用的交互,响应对view的操作(对事件的监听),调用Model对数据进行操作,完成model与view的同步(根据model的改变,通过选择器对view进行操作);将js的ajax当做Model,也就是数据层,通过ajax从服务器获取数据。
但是现实开发肯定没有刚才的举例那么简单,但是大体原理是这样的。
MVVM
MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。
那MVVM模式的代表Vue举例看,Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,即Model变化时VIew可以实时更新,View变化也能让Model发生变化。
整体看来,MVVM比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM中,View不知道Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性。
总结
在学习MVC与MVVM架构模式的过程中,经常会对分层的界限叫不准。比如说不清楚js里到底哪里算Model,哪里算Controller,Vue实例里面Model与ViewModel的严格界限在哪,有时候越想越感觉叫不准。当我从头到尾整理完这两种模式特点的时候,发现这个界限没有那么重要。我觉得重要的是,理解两种模式的基本思想,根据应用需求,选择适合自己业务的框架。
14.vue如何实现数据双向绑定
vue的双向绑定是通过数据劫持和发布者-订阅者模式实现的,数据劫持又是通过Object.defineProperty()实现的
mvvm的数据变化更新视图,是通过Object.defineProperty()实现的;视图更新变化数据,是通过事件监听实现的。
发布者-订阅者的实现过程:
- 实现一个监听器Observer,劫持并监听所有属性,如果有变化,就通知订阅者
- 实现一个订阅者Watcher,收到属性的变化通知并执行响应的函数,从而更新视图
- 实现一个解析器Compiler,可以扫描并解析每个节点的相关指令,初始化模板数据和对应的订阅器
15.简述vue的原理
1、建立虚拟DOM Tree,通过document.createDocumentFragment(),遍历指定根节点内部节点,根据{{ prop }}、v-model等规则进行compile;
2、通过Object.defineProperty()进行数据变化拦截;
3、截取到的数据变化,通过发布者-订阅者模式,触发Watcher,从而改变虚拟DOM中的具体数据;
4、通过改变虚拟DOM元素值,从而改变最后渲染dom树的值,通过Object.defineProperty()完成数据的双向绑定。
16. 简要说一下vue的生命周期
答案见《Vue》文集的《组件的生命周期钩子和路由钩子总结》
17.vue的diff算法原理
Diff算法的作用是用来计算出 Virtual DOM(虚拟DOM) 中被改变的部分,然后针对该部分进行原生DOM操作,而不用重新渲染整个页面。
虚拟dom是对真实dom的一种映射,新旧Vnode比较同层级的节点,然后根据两者的差异只更新有差异的部分,生成新的视图,而不是对树进行逐层搜素遍历,因此时间复杂度是O(n)。虚拟dom可以减少页面的回流和重绘,提升性能。
18.二次封装axios的基本思路
1.新建一个axios对象,定义好字段并设置默认值,比如超时时间、请求头
2.定义过滤字符串方法,过滤服务端为空字符串或null的属性
3.请求拦截器调用过滤字符串方法,遍历url上的字段,如果为数组或对象转为JSON对象
4.响应拦截器捕获错误,根据http状态码进行不同的处理,比如401跳转登陆页面,403返回您没有权限,502返回系统正在升级中,请稍后重试,
504返回系统超时,并弹出对应的消息提示框。消息提示框自定义
19.vue首屏优化加载时间的方法
20.当单页面应用的体量越来越大时,只有一个页面会难以应对负责的业务场景,此时可以进行两个方面的优化:1.将单页面应用拆分成多个页面。 2.将几十甚至上百个路由进行重新规划。讲一下你对这两方面的见解。
21.说一下vue3版本的新特性
放弃 Object.defineProperty ,使用更快的原生 Proxy (访问对象拦截器, 也成代理器)
提速, 降低内存使用, Tree-shaking更友好
支持IE11等
使用Typescript
22.actions 和 mutations 有什么区别。
23.怎么实现axios的同步请求
24.说下常用的组件通信方法
详细内容见文件《Vue》=>《组件数据通信方案总结》
25.vue怎么进行seo优化
seo关系到网站排名, vue搭建spa做前后端分离不好做seo, 可通过其他方法解决:
SSR服务端渲染: 将同一个组件渲染为服务器端的 HTML 字符串.利于seo且更快.
vue-meta-info, nuxt, prerender-spa-plugin页面预渲染等
26.data中某个数据想解除双向绑定该怎么做
let obj = JSON.parse(JSON.stringify(this.temp1));
27.页面中定义一个定时器,在哪个阶段清除
首先确定的一点肯定是在 beforeDestroy 中销毁定时器。比如在页面 a 中写了一个定时器,比如每隔一秒钟打印一次 1,当我点击按钮进入页面 b 的时候,会发现定时器依然在执行,这是非常消耗性能的。
代码如下:
mounted(){
this.timer = setInterval(()=>{
console.log(1)
},1000)
},
beforeDestroy(){
clearInterval(this.timer)
}
上面这样的确可以实现功能,但是存在两个缺点:
它需要在这个组件实例中保存这个 timer,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。
我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。
可以通过$once 这个事件侦听器在定义完定时器之后的位置来清除定时器
mounted(){
const timer = setInterval(()=>{
console.log(1)
},1000)
this.$once('hook:beforeDestroy',()=>{
clearInterval(timer)
})
}
28.vue中data的属性可以和methods中的方法同名吗
不可以,因为Vue会把methods和data的东西,全部代理到Vue生成的对象中,会产生覆盖所以最好不要同名,所以这个问题就和对象中的属性和方法可以重名吗是一个性质。
29.怎么缓存当前的组件?缓存后怎么更新
<keep-alive>
<router-view></router-view>
</keep-alive>
<!-- 这里是需要keepalive的 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<!-- 这里不会被keepalive -->
<router-view v-if="!$route.meta.keepAlive"></router-view>
{
path: '',
name: '',
component: ,
meta: {keepAlive: true} // 这个是需要keepalive的
},
{
path: '',
name: '',
component: ,
meta: {keepAlive: false} // 这是不会被keepalive的
}
如果缓存的组件想要清空数据或者执行初始化方法,在加载组件的时候调用activated钩子函数,如下:
activated: function () {
this.data = '';
}
30.怎么实现路由懒加载
第一种(常用):
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
第二种(常用):
const router = new Router({
routes: [
{
path: '/index',
component: (resolve) => {
require(['../components/index'], resolve) // 这里是你的模块 不用import去引入了
}
//或者直接这样写
//component: () =>
//import('../components/index')
}
]
})
第三种(官方推荐):
利用webpack提供的require.ensure(),vue-router配置路由时,使用webpack的require.ensure技术,可以实现按需加载。这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
···
// r就是resolve
const list = r => require.ensure([], () => r(require('../components/list/list')), 'list');
// 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载
const router = new Router({
routes: [
{
path: '/list/blog',
component: list,
name: 'blog'
}
]
})
···
31.vuex有哪几种属性
有五种,分别是 State、 Getter、Mutation 、Action、 Module
state => 基本数据(数据源存放地)
getters => 从基本数据派生出来的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
32.对于仅仅用来展示的长列表数据怎么进行优化
Vue 会通过 Object.defineProperty/Proxy 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
async getUsers(){
const users = await axios.get(/api/users)
this.users =Object.freeze(users)
}
网友评论