目录
1. 谈一谈你对 SPA 单⻚面的理解,它的优缺点分别是什么
12. Vuedata中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?
21. v-if和v-for哪个优先级更高?如果同时出现,应如何优化?
39. 为什么 Vuex 的 mutation 中不能做异步操作?
1. 谈一谈你对 SPA 单⻚面的理解,它的优缺点分别是什么
SPA( single-page application )仅在 Web ⻚面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦⻚面加载完成,SPA 不会因为用户的操作而进行⻚面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免⻚面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个⻚面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 首屏(初次)加载慢:为实现单⻚ Web 应用功能及显示效果,需要在加载⻚面的时候将JavaScript、CSS 统一加载,部分⻚面按需加载;
- 不利于 SEO:由于所有的内容都在一个⻚面中动态替换显示,所以在 SEO 上其有着天然的弱势。
2 SPA 单⻚面应用的实现方式有哪些
- hash 模式
- history 模式
在hash模式中,在window上监听hashchange事件(地址栏中hash变化触发)驱动界面变化;
在history模式中,在window上监听popstate事件(浏览器的前进或后退按钮的点击触发)驱动界面变化,监听 a 链接点击事件用history.pushState、history.replaceState方法驱动界面变化;直接在界面用显示隐藏事件驱动界面变化
3 使用过 Vue SSR 吗?说说 SSR?
Server Side Renderer
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。
服务端渲染 SSR 的优缺点如下
(1)服务端渲染的优点:
更好的 SEO:因为 SPA ⻚面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取⻚面内容,所以在 SPA 中是抓取不到⻚面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的⻚面(数据已经包含在⻚面中),所以搜索引擎爬取工具可以抓取渲染好的⻚面;
更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行⻚面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好⻚面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
(2) 服务端渲染的缺点:
更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单⻚面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
4 谈一谈你对 MVVM 的理解
传统的服务端 MVC 架构模型:
models数据模型,专⻔提供数据支持的
controllers控制器模块,处理不同的⻚面请求的或者处理接口请求
views视图文件
MVVM 模型:
MVVM 是 Model-View-ViewModel 的缩写,MVVM 是一种设计思想。
- Model 层代表数据模式,也可以在 Model 中定义数据修改和操作的业务逻辑
- View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来
- ViewModel 是一个同步 View 和 Model 的对象。
MVVM 模式常⻅于用于构建用户界面的客户端应用
我的理解:
- 前端开发早期的时候都是操作 DOM
- 后来使用 jQuery 让我们提高了操作 DOM 的效率,但从开发⻆度还是在大量的手动操作 DOM
- MVVM 模式让以往手动操作 DOM 的方式彻底解脱了,它不要用户自己操作 DOM,而是将普通数据绑定到 ViewModel 上,会自动将数据渲染到⻚面中,视图变化会通知 ViewModel 层更新数据,数据变化也会通过 ViewModel 层更新视图,因此 MVVM 中最重要的⻆色就是 ViewModel,真正意义上把视图和数据实现了解耦,提高了开发效率。
- MVVM 模式主要用于构建用户界面的前端应用
微软的 WPF,构建客户端应用的
手机应用,iOS APP、Android App
Web
5 谈一谈你对 Vue.js 的响应式数据的理解
当数据发生变化后,使用到该数据的视图也会相应进行自动更新
当我们使用目前主流的前端框架 Vue.js ,只需关注页面数据如何变化,因为数据变化后,视图也会自动更新,这让我们从繁杂的 DOM 操作中解脱出来,提高开发效率。
在 Vue 2.x:
- 当你把一个普通的 JavaScript 对象传入 Vue 实例作为data选项,Vue 将遍历此对象所有的property,并使用Object.defineProperty把这些 property 全部转为getter/setter
- 当使用这些数据属性时,会进行依赖收集(收集到当前组件的 watcher
- 每个组件都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据记录为依赖
- 之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染
Vue 3.x:
vue3用Proxy的优势如下
- 可以直接监听整个对象而非属性。
- 可以直接监听数组的变化。
- 有13中拦截方法,如ownKeys、deleteProperty、has 等是 Object.defineProperty 不具备的。
- 返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改;
Object.defineProperty 的优势如下
-
兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平。
Object.defineProperty 不足在于:
Object.defineProperty 只能劫持对象的属性(key值.Object.key()),因此我们需要对每个对象的每个属性进行遍历。
Object.defineProperty不能监听数组。是通过重写数据的那7个可以改变数据的方法来对数组进行监听的。
Object.defineProperty 也不能对 es6 新产生的 Map,Set 这些数据结构做出监听。
Object.defineProperty也不能监听新增和删除操作,通过 Vue.set()和 Vue.delete来实现响应式的。
简而言之就是两件事儿:
- 拦截属性的获取进行依赖收集
- 拦截属性的修改对相关依赖进行通知更新
6 浏览器和 Node.js 中的事件循环
l
浏览器的事件循环机制
JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我们称为事件循环过程。
一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。
任务队列又分为macro-task(宏任务)与micro-task(微任务)
宏任务包括:scrip整体代码、setTimeout、setInterval、setImmediate、I/O操作、UI 渲染
微任务包括:process.nextTick、Promise、Async/Await、MutationObserver
总结: 先执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。
当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务;如果没有,会读取宏任务中排在最前面的任务。
执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后再读取微任务队列里的任务,以此类推。
Node的事件循环机制
浏览器中有事件循环,Node中也有,但是与浏览器中的事件循环完全不一样。
宏任务包括:scrip整体代码、setTimeout、setInterval、setImmediate、I/O操作
微任务包括:process.nextTick(在微任务队列执行之前执行)、new Promise().then(回调)
Node.js采用V8作为js的解析引擎,I/O处理使用自己设计的libuv。
libuv是基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的API,事件循环也是它里面的 实现。
libuv引擎中的事件循环分为六个阶段,它们会按照顺序反复执行。每当进入某一个阶段后,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。
- 定时器检测阶段(timers): 在这个阶段执行timer的回调,即setTimeout、setInterval里面的回调函数。
- I/O事件回调阶段(I/O callbacks): 此阶段会执行几乎所有的回调函数,除了close callbacks(关闭回调)和那些由timers与setImmediate()调度的回调。
- 限制阶段(idel,prepare):仅系统内部使用。(内部处理的事务,暂时不需要关注)
- 轮询阶段(poll): 检索新的I/O事件、执行与I/O相关的回调,其余情况nide将在适当的时候在此堵塞。
- 检查阶段(check):setImmediate()回调函数在这里执行。
- 关闭事件回调阶段(close callback):一些关闭的回调函数。
两者的区别
浏览器事件循环机制中,微任务的任务队列是在每个宏任务执行完之后执行。
Node事件循环机制中,微任务会在事件循环的各个阶段之间执行,也就是说,一个阶段执行完毕,就会去执行微任务队列的任务。
7 MVVM、MVC、MVP的区别
MVC、MVP和MVVM是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。
MVVM分为Model、View、ViewModel:Model代表数据模型,数据和业务逻辑都在Model层中定义;View代表UI视图,负责数据的展示;ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
MVP的模式通过使用Presenter来实现对View层和Model层的解耦。MVC中的Controller只知道Model的接口,因此它没有办法控制View层的更新,MVP模式中,View层的接口暴露给了Presenter因此可以在Presenter中将Model的变化和View的变化绑定在一起,以此来实现View和Model的同步更新。这样就实现了对View和Model的解耦,Presenter还包含了其他的响应逻辑。
8 slot是什么?有什么作用?原理是什么?
slot又名插槽,是Vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。
slot又分三类,默认插槽,具名插槽和作用域插槽。
默认插槽:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
- 作用域插槽在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件
slot-scope
接收的对象上
实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在vm.$slot中,默认插槽为vm.$slot.default,具名插槽为vm.$slot.xxx,xxx为插槽名,当组件执行渲染函数时候,遇到slot标签,使用$slot中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。
9.$nextTick原理及作用
原理
Vue的nextTick其本质是对JavaScript执行原理EventLoop的一种应用。nextTick的核心是利用了如Promise、MutationObserver、setImmediate、setTimeout的原生JavaScript方法来模拟对应的微/宏任务的实现,本质是为了利用JavaScript的这些异步回调任务队列来实现Vue框架中自己的异步回调队列。
作用
Vue采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作DOM。有时候,可能遇到这样的情况,DOM1的数据发生了变化,而DOM2需要从DOM1中获取数据,那这时就会发现DOM2的视图并没有更新,这时就需要用到了nextTick了。
由于Vue的DOM操作是异步的,所以,在上面的情况中,就要将DOM2获取数据的操作写在$nextTick中。
所以,在以下情况下,会用到nextTick:
1 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在nextTick()的回调函数中。
在vue生命周期中,如果在created()钩子进行DOM操作,也一定要放在nextTick()的回调函数中。
因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。
2、在数据变化后要执行某个回调函数,而这个操作需要使用随数据改变而改变的DOM结构的时候, 这个操作都应该放进Vue.nextTick () 回调函数中。
3、Vue的官方文档中详细解释:
vue 执行dom 更新是异步的, 只要是观察到数据变化, vue 将会开启一个队列, 并缓冲在同一事件循环中发生的所有数据改变。 如果同一个watcher 被多次触发,只会被推如到队列中一次。 在这种缓冲时去除重复数据对于避免不必要的计算, 和DOM 操作非常重要。 然后下一个事件循环 “tick”中, vue 刷新队列执行实际(去重)工作。 在Vue 内部尝试对异步队列使用原生的promise.then 和 MessageChange,如果执行环境不支持, 会采用setTime(()=> {} , 0)代替。
10.Vue单页应用与多页应用的区别
概念:
SPA单页面应用(SinglePageWebApplication),指只有一个主页面的应用,一开始只需要加载一次js、css等相关资源。所有内容都包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换相关组件,仅仅刷新局部资源。
MPA多页面应用(MultiPageApplication),指有多个独立页面的应用,每个页面必须重复加载js、css等相关资源。多页应用跳转,需要整页资源刷新。
11. Vue中封装的数组方法有哪些,其如何实现页面更新
1.vue中封装的数组方法有这些:具体的可以去查看vue的官方网址里面的基础中的列表渲染中的数组更新检测
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
其如何实现页面更新
从上面可以看出array.js中重写了数组的push、pop、shift、unshift、splice、sort、reverse七种方法,重写方法在实现时除了将数组方法名对应的原始方法调用一遍并将执行结果返回外,还通过执行ob.dep.notify()将当前数组的变更通知给其订阅者,这样当使用重写后方法改变数组后,数组订阅者会将这边变化更新到页面中。
重写完数组的上述7种方法外,我们还需要将这些重写的方法应用到数组上,因此在Observer构造函数中,可以看到在监听数据时会判断数据类型是否为数组。当为数组时,如果浏览器支持__proto__,则直接将当前数据的原型__proto__指向重写后的数组方法对象arrayMethods,
如果浏览器不支持__proto__,则直接将arrayMethods上重写的方法直接定义到当前数据对象上;当数据类型为非数组时,继续递归执行数据的监听。
vue中是如何监听数组变化?_xm2by的博客-CSDN博客_vue2监听数组变化
- Object.defineProperty 和 Proxy 本质差别是,defineProperty 只能对属性进行劫持,所以出现了需要递归遍历,新增属性需要手动 Observe 的问题。
- Proxy 作为新标准,浏览器厂商势必会对其进行持续优化,但它的兼容性也是块硬伤,并且目前还没有完整的polifill方案。
12. Vuedata中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?
不会立即同步执行重新渲染。Vue实现响应式并不是数据发生变化之后DOM立即变化,而是按一定的策略进行DOM的更新。Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。
13. 简述mixin、extends的覆盖逻辑
mixin和extends均是用于合并、拓展组件的,两者均通过mergeOptions方法实现合并。
mixins接收一个混入对象的数组,其中混入对象可以像正常的实例对象一样包含实例选项,这些选项会被合并到最终的选项中。Mixin钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。extends主要是为了便于扩展单文件组件,接收一个对象或构造函数。
vue高频面试题(附答案)_helloworld1024fd的博客-CSDN博客_vue的高级面试题
2022Vue经典面试题及答案汇总(持续更新)_每天都要进步哦的博客-CSDN博客_vue面试题
14. 子组件可以直接改变父组件的数据吗?
Vue提倡单向数据流,即父级props的更新会流向子组件,但是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解,导致数据流混乱。如果破坏了单向数据流,当应用复杂时,debug的成本会非常高。
只能通过$emit派发一个自定义事件,父组件接收到后,由父组件修改。
子组件不可以直接改变父组件的数据。这样做主要是为了维护父子组件的单向数据流。每次父级组件发生更新时,子组件中所有的prop都将会刷新为最新的值。如果这样做了,Vue会在浏览器的控制台中发出警告。
父级给子组件是啥,子组件可以用,也可以不用,但是不能修改。这就保证了数据可控性
如果不希望改变父级,可以在子组件中深拷贝数据。简单点直接
JSON .parse( JSON .stringify( data ))
为什么要单向 :
因为Vue是单向数据流,为了保证数据的单向流动,便于对数据的追踪,出现了错误可以更加迅速的定位到错误发生的位置。
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
15. Vue的优点
- 优点:轻量级的框架、双向数据绑定、组件化开发、单页面路由、学习成本低、虚拟dom、渐进式框架、数据和结构的分离、运行速度快、插件化
- 缺点:不支持ie8以下、社区没有angular和react丰富、缺乏高阶教程和文档、单页面应用不利用seo优化、首次加载时耗时多
16. assets和static的区别
相同点:assets和static两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点
不相同点:assets中存放的静态资源文件在项目打包时,也就是运行npmrunbuild时会将assets中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。
而压缩后的静态资源文件最终也都会放置在static文件中跟着index.html一同上传至服务器。
static中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议:将项目中template需要的样式文件js文件等都可以放置在assets中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css等文件可以放置在static中,因为这些引入的第三方文件已经经过处理,不再需要处理,直接上传。
17. delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了empty/undefined其他的元素的键值还是不变。
Vue.delete直接删除了数组改变了数组的键值
18. Vue模版编译原理
vue中的模板template无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所有需要将template转化成一个JavaScript函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。
模板编译又分三个阶段,解析parse,优化optimize,生成generate,最终生成可执行函数render。
1 解析阶段:使用大量的正则表达式对template字符串进行解析,将标签、指令、属性等转化为抽象语法树AST。
2 优化阶段:遍历AST,找到其中的一些静态节点并进行标记,方便在页面重渲染的时候进行diff比较时,直接跳过这一些静态节点,优化runtime的性能。
3 生成阶段:将最终的AST转化为render函数字符串。
AST 全称为 Abstract Syntax Tree,译为抽象语法树。在 JavaScript 中,任何一个对象(变量、函数、表达式等)都可以转化为一个抽象语法树的形式。抽象语法树本质就是一个树形结构的对象。
19. vue初始化页面闪动问题
使用vue开发时,在vue初始化之前,由于div是不归vue管的,所以我们写的代码在还没有解析的情况下会容易出现花屏现象,看到类似于{{message}}的字样,虽然一般情况下这个时间很短暂,但是还是有必要让解决这个问题的。
首先:在css里加上以下代码
如果没有彻底解决问题,则在根元素加上style="display:none;":style="{display:'block'}"
20. vue自定义指令使用例子
主要用来给原生的HTML元素增加功能
例子 有 : 表单聚焦 获取焦点
列表数据的字体放大
21. v-if和v-for哪个优先级更高?如果同时出现,应如何优化?
v-for优先于v-if被解析,如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能。
要避免出现这种情况,则在外层嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环。如果条件出现在循环内部,可通过计算属性提前过滤掉那些不需要显示的项
22. 对Vue组件化的理解
1.组件是独立和可复用的代码组织单元。组件系统是Vue核心特性之一,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
2.组件化开发能大幅提高应用开发效率、测试性、复用性等;
3.组件使用按分类有:页面组件、业务组件、通用组件;
4.vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
5.vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;6合理的划分组件,有助于提升应用性能;
6.组件应该是高内聚、低耦合的;
7.遵循单向数据流的原则。
23. 对vue设计原则的理解
1.渐进式JavaScript框架:与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页应用提供驱动。
2.易用性:vue提供数据响应式、声明式模板语法和基于配置的组件系统等核心特性。这些使我们只需要关注应用的核心业务即可,只要会写js、html和css就能轻松编写vue应用。
3.灵活性:渐进式框架的最大优点就是灵活性,如果应用足够小,我们可能仅需要vue核心特性即可完成功能;随着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是学习难度都是一个逐渐增加的平和曲线。
4.高效性:超快的虚拟DOM和diff算法使我们的应用拥有最佳的性能表现。追求高效的过程还在继续,vue3中引入Proxy对数据响应式改进以及编译器中对于静态内容编译的改进都会让vue更加高效。
24. 说一下Vue的生命周期
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom->渲染、更新->渲染、卸载等一系列过程,称这是Vue的生命周期。
1.beforeCreate(创建前):数据观测和初始化事件还未开始,此时data的响应式追踪、event/watcher都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2.created(创建后):实例创建完成,实例上配置的options包括data、computed、watch、methods等都配置完成,但是此时渲染得节点还未挂载到DOM,所以不能访问到$el属性。
3.beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4.mounted(挂载后):在el被新创建的vm.$el替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
5.beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实DOM还没有被渲染。
6.updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时DOM已经根据响应式数据的变化更新了。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7.beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this仍能获取到实例。
8.destroyed(销毁后):实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
还有keep-alive独有的生命周期,分别为activated和deactivated。用keep-alive包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行deactivated钩子函数,命中缓存渲染后会执行activated钩子函数
25. keep-alive中的生命周期哪些
keep-alive是Vue提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
如果为一个组件包裹了keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy和destroyed就不会再被触发了,因为组件不会被真正销毁。
当组件被换掉时,会被缓存到内存中、触发deactivated生命周期;当组件被切回来时,再去缓存里找这个组件、触发activated钩子函数。
26. Vue子组件和父组件执行顺序
加载渲染过程:
1.父组件beforeCreate
2.父组件created
3.父组件beforeMount
4.子组件beforeCreate
5.子组件created
6.子组件beforeMount
7.子组件mounted
8.父组件mounted
更新过程:
1.父组件beforeUpdate
2.子组件beforeUpdate
3.子组件updated
4.父组件updated
销毁过程:
1.父组件beforeDestroy
2.子组件beforeDestroy
3.子组件destroyed
4.父组件destoryed
27. created和mounted的区别
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
28. 一般在哪个生命周期请求异步数据
我们可以在钩子函数created、beforeMount、mounted中进行调用,因为在这三个钩子函数中,data已经创建,可以将服务端端返回的数据进行赋值。
推荐在created钩子函数中调用异步请求,因为在created钩子函数中调用异步请求有以下优点:能更快获取到服务端数据,减少页面加载时间,用户体验更好;
SSR不支持beforeMount、mounted钩子函数,放在created中有助于一致性。(服务器加载)
29. 路由的hash和history模式的区别
安装vue-router
npm install vue-router
第一步:安装vue-router
npm install vue-router --save
第二步:在模块化工程中使用它(因为是一个插件,所以可以通过Vue.use()来安装路由功能)
导入路由对象,并且调用Vue.use(VueRouter)
创建路由实例,并且传入路由映射配置
在Vue实例中挂载创建的路由实例
使用vue-router的步骤:
第一步:创建路由组件
第二步:配置路由映射:组件和路径映射关系
第三步:使用路由:通过<router-link>和<router-view>
vue-router 详解_星海少年的博客-CSDN博客_vue-router
Vue-router跳转和location.href有什么区别
- 使用location.href=/url来跳转,简单方便,但是刷新了页面;
- 使用history.pushState(/url),无刷新页面,静态跳转;
- 引进router,然后使用router.push(/url)来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。
1.hash模式
简介:hash模式是开发中默认的模式,它的URL带着一个#,例如:http://www.abc.com/#/vue,它的hash值就是#/vue。特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。原理:hash模式的主要原理就是onhashchange()事件
使用onhashchange()事件的好处就是,在页面的hash值发生变化时,无需向后端发起请求,window就可以监听事件的改变,并按规则加载相应的代码。除此之外,hash值变化对应的URL都会被浏览器记录下来,这样浏览器就能实现页面的前进和后退。虽然是没有请求后端服务器,但是页面的hash值和对应的URL关联起来了。
2 history模式
history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。
包括了HTML5HistoryInterface中新增的pushState()和replaceState()方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。如果要做到改变url但又不刷新页面的效果,就需要前端用上这两个API。
30. Vue3.0有什么更新
(1)监测机制的改变3.0将带来基于代理Proxy的observer实现,提供全语言覆盖的反应性跟踪。
消除了Vue2当中基于Object.defineProperty的实现所存在的很多限制:
(2)只能监测属性,不能监测对象
检测属性的添加和删除;
检测数组索引和长度的变更;
支持Map、Set、WeakMap和WeakSet。
(3)模板作用域插槽,2.x的机制导致作用域插槽变了,
父组件会重新渲染,而3.0把作用域插槽改成了函数的方式,
这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于render函数的方面,vue3.0也会进行一系列更改来方便习惯直接使用api来生成vdom。
(4)对象式的组件声明方式
vue2.x中的组件是通过声明的方式传入一系列option,和TypeScript的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。
3.0修改了组件的声明方式,改成了类式的写法,这样使得和TypeScript的结合变得很容易
(5)其它方面的更改
支持自定义渲染器,从而使得weex可以通过自定义渲染器的方式来扩展,而不是直接fork源码来改的方式。
支持Fragment(多个根节点)和Protal(在dom其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。
31. defineProperty和proxy的区别
Vue在实例初始化时遍历data中的所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter。这样当追踪数据发生变化时,setter会被自动调用。
1.添加或删除对象的属性时,Vue检测不到。因为添加或删除的对象没有在初始化进行响应式处理,只能通过$set来调用Object.defineProperty()处理。
2.无法监控到数组下标和长度的变化。Vue3使用Proxy来监控数据的变化。Proxy是ES6中提供的功能,其作用为:用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。相对于Object.defineProperty(),其有以下特点:
3 Proxy直接代理整个对象而非对象属性,这样只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。
2.Proxy可以监听数组的变化。
Vue3.0为什么要用proxy?
0bject.defineProperty会改变原始数据,而Proxy是创建对象的虚拟表示,并提供set、get和deleteProperty等处理器,这些处理器可在访问或修改原始对象上的属性时进行拦截,
有以下特点∶
- 不需用使用Vue.$set或Vue.$delete触发响应式。
- 全方位的数组变化检测,消除了Vue2无效的边界情况。
- 支持Map,Set,WeakMap和WeakSet。
Proxy实现的响应式原理与Vue2的实现原理相同,实现方式大同小异∶
get收集依赖
Set、delete等触发依赖
对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执行依赖相关的收集或触发逻辑。
32. 谈谈 diff算法
在新老虚拟DOM对比时:
1 首先,对比节点本身,判断是否为同一节点,如果不为相同节点,则删除该节点重新创建节点进行替换
2 如果为相同节点,进行patchVnode,判断如何对该节点的子节点进行处理,先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
比较如果都有子节点,则进行updateChildren,判断如何对这些新老节点的子节点进行操作(diff核心)。
匹配时,找到相同的子节点,递归比较子节点
在diff中,只对同层的子节点进行比较,放弃跨级的节点比较,使得时间复杂从O(n3)降低值O(n),也就是说,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
33. Vue中key的作用
vue中key值的作用可以分为两种情况来考虑:
第一种情况是v-if中使用key。由于Vue会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。因此当使用v-if来实现元素切换的时候,如果切换前后含有相同类型的元素,那么这个元素就会被复用。如果是相同的input元素,那么切换前后用户的输入不会被清除掉,这样是不符合需求的。因此可以通过使用key来唯一的标识一个元素,这个情况下,使用key的元素不会被复用。这个时候key的作用是用来标识一个独立的元素。
第二种情况是v-for中使用key。用v-for更新已渲染过的元素列表时,它默认使用“就地复用”的策略。如果数据项的顺序发生了改变,Vue不会移动DOM元素来匹配数据项的顺序,而是简单复用此处的每个元素。因此通过为每个列表项提供一个key值,来以便Vue跟踪元素的身份,从而高效的实现复用。这个时候key的作用是为了高效的更新渲染虚拟DOM。
key是为Vue中vnode的唯一标记,通过这个key,diff操作可以更准确、更快速更准确:
因为带key就不是就地复用了,在sameNode函数a.key===b.key对比中可以避免就地复用的情况。
所以会更加准确。更快速:利用key的唯一性生成map对象来获取对应节点,比遍历方式更快
34. 虚拟DOM的解析过程
虚拟DOM的解析过程:
1 首先对将要插入到文档中的DOM树结构进行分析,使用js对象将其表示出来,比如一个元素对象,包含TagName、props和Children
这些属性。然后将这个js对象树给保存下来,最后再将DOM片段插入到文档中。
2 当页面的状态发生改变,需要对页面的DOM的结构进行调整的时候,首先根据变更的状态,重新构建起一棵对象树,然后将这棵新的对象树和旧的对象树进行比较,记录下两棵树的的差异。
3 最后将记录的有差异的地方应用到真正的DOM树中去,这样视图就更新了。
35. Vuex的原理
Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation提交修改信息,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。
vue
框架中状态管理。在main.js
引入store
注入。新建一个目录store
。场景有:单页应用中,组件之间的状态,音乐播放、登录状态、加入购物车等。
36. Vuex有哪几种属性?
有五种,分别是State、Getter、Mutation、Action、Module
state=>基本数据(数据源存放地)
getters=>从基本数据派生出来的数据
mutations=>提交更改数据的方法,同步
actions=>像一个装饰器,包裹mutations,使之可以异步。
modules=>模块化Vuex
37. Vuex和单纯的全局对象有什么区别?
Vuex的状态存储是响应式的。当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新。
不能直接改变store中的状态。改变store中的状态的唯一途径就是显式地提交(commit)mutation。这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
38. Vuex和localStorage的区别、
1)最重要的区别vuex存储在内存中
localstorage则以文件的方式存储在本地,只能存储字符串类型的数据,存储对象需要JSON的stringify和parse方法进行处理。读取内存比读取硬盘速度要快
(2)应用场景
Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex用于组件之间的传值。
localstorage是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用。
Vuex能做到数据的响应式,localstorage不能
(3)永久性刷新页面时vuex存储的值会丢失,localstorage不会。注意:对于不变的数据确实可以用localstorage可以代替vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage无法做到,原因就是区别1。
39. 为什么 Vuex 的 mutation 中不能做异步操作?
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。