如果准备一场vue面试
# 对SSR的理解
SSR页就是服务端渲染,也就是将Vue客户端把渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端
SSR的优势:
- 更好的SEO
- 首屏加载速度更快
SSR的缺点
- 开发条件会受到限制,服务器端渲染只支持beforeCreate和create两个钩子;
- 当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于NODE.js环境;
- 更多的服务端负载.
# Vue的优点
- 轻量级框架:只关注视图层,是一个构建数据的视图合集,大小只有几十
kb
- 简单易学:国人开发,中文文档,不存在语言障碍,易于理解和学习;
- 双向数据绑定:保留
angular
的特点,在数据操作方面更简单; - 组件化:保留了
react
的优点,实现了html
的封装和重用,在构建但页面应用方面有着独特的优势; - 视图,数据,结构分离:使数据的更改更简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
- 虚拟DOM:
dom
操作是非常耗费性能的,不再使用原生的dom
操作节点,极大解放dom
操作,但具体操作的还是dom
不过是换了另一种方式; - 运行速度更快:相比较于
react
而言,同样是操作虚拟dom
,就性能而言,vue
存在很大的优势.
# computed的实现原理
- computed 本质是一个惰性求值的观察者
- computed 内部实现了一个惰性的watcher,也就是computed watcher,computed watcher 不会立刻求值,同时持有一个dep示例.
- 其内部通过this.dirty属性标记计算属性是否需要重新求值.
- 当computed的依赖状态发生改变时,就会通知这个惰性的watcher,computed watcher通过 this.dep.subs.length判断有没有订阅者,
- 有的话,会重新计算,然后对比新旧值,如果变化了,才会重新渲染,(Vue想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染watcher重新渲染,本质上是一种优化.)
- 没有的话,仅仅把this.dirty = true.(当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,他才会真正的计算,寄具备lazy(懒计算)特性.)
# 能说下vue-router中常用的hash和history路由模式的实现原理吗?
- hash模式的实现原理
早期的前端路由实现就是基于localhost.hash来实现的,其实现原理很简单,localhost.hash的值就是URL中#后面的内容.比如
https://www.baidu.com#search
,它的localhost.hash的值为‘#search’ hash路由模式的实现主要是基于下面几个特性:- URL中的hash值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash部分不会被发送;hash值的改变,都会在浏览器的访问历史中增加一个记录,因此我们能通过浏览器的回退、前进按钮控制hash的切换‘
- 可以通过a标签,并设置href属性,当用户点击这个表亲啊后,URL的hash值会发生改变;或者使用javascript来对localhost.hash进行赋值,改变URL的hash值;
- 我们可以使用hashchange事件来监听hash的变化,从而对页面进行跳转(渲染);
- history模式的实现原理
HTML5提供了Histoty API来实现URL的变化,其中最主要的两个API有以下两个: history.pushState()和
history.replaceState()
.这两个API可以在不进行刷新的情况下,操作浏览器的历史记录.唯一不同的时,前者是增加一个历史记录,后者是直接替换当前的历史记录,如下所示:history 路由模式的实现主要基于存在下面几个特性:window.history.pushState(null,null,path); window.history.repalceState(null,null,path);
1
2- pushState 和replaceState 两个API来操作实现URL的变化;
- 我们可以使用popstate事件来监听url的变化,从而对页面进行跳转(渲染);
- history.pushState()或history.replaceState()不会出发popstate事件,这时候我们需要手动触发页面跳转(渲染);
参考:前端vue面试题详细解答 (opens new window)
# 对React和Vue的理解,它们的异同
- 相似之处:
- 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理相关的库;
- 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模版;
- 都使用了virtual DOM (虚拟DOM)提高重绘性能;
- 都有props的概念,允许组件间的数据传递;
- 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性;.
- 不同之处:
数据流 Vue默认支持数据双向绑定,而React一直提倡单向数据流
虚拟DOM
Vue2.x开始引入‘Virtual DOM’,消除了和React在这方面的差异,但是在具体的细节还是有各自的特点.- Vue宣称可以更快的计算出 Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.
- 对于React而言,每当应用的状态被改变时,全部自组件都会重新渲染.当前,这可以通过PureComponent/shouldComponentUpdate这个生命周期方法来进行控制,但Vue将此视为默认的优化.
组件化
React与Vue最大的不同是模版的编写.- Vue鼓励写近似常规的html模版,写起来很接近标准html元素,只是多写了一些属性.
- React推荐你所有的模版通用Javascript的语法扩展——JSX书写.
具体来讲:React中的rrender函数是支持闭包特性的,所以import的组件在render中可以直接调用.但是在Vue中,由于模版中使用的数据都必须在this上进行一次中转,所以import一个组建完了之后,还需要在components中在声明下.
监听数据变化的实现原理不同
- Vue 通过getter/setter以及一些函数的劫持,能精确知道数据变化,不需要特变的优化就能达到很好的性能.
- React默认时通过比较引用的方式进行的,如果不优化(pureComponents/shouldComponentUpdate)可能导致大量不必要的DOM的重新渲染.这是因为Vue使用的是可变数据,而React更强调数据的不可变.
高阶组件 React可以通过高阶组件(HOC)来扩展,而Vue需要通过mixins来扩展.
高阶组件就是高阶函数,而React的组件本身就是纯粹的函数,所以高阶函数对React来说易如反掌.相反Vue.js使用HTML模版创建视图组件,这时模版无法有效的变异,因此Vue不能采用HOC来实现.
构建工具 两者都有自己的构建工具:
- React==> Create React APP
- Vue ==> vue-cli
跨平台
- React ==> React Native
- Vue ==> Weex
# Vue 的生命周期方法有哪些 一般在哪一步发请求
# beforeCreate
- 是在实例初始化之后,数据观测(data observer)和event/watcher 事件配置之前被调用.
- 在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问.
# created
- 实例已经创建完成之后被调用.
- 在这一步,实例已经完以下的配置:数据观测(data observer),属性和方法的运算,watch/event事件回调,这里没有$el,如果非想要与DOM交互,可以通过vm.$nextTick来访问DOM.
# befeoreMounted
- 在挂载之前被调用:相关的render函数首次被调用.
# mounted
- 在挂载完成后发生,在当前阶段,真实的DOM挂载完毕,数据完成双向绑定,可以访问到DOM节点.
# beforeUpdate
- 数据更新时调用,发生在虚拟DOM重新渲染和打补丁(patch)之前.可以在这个狗子中进一步的更改状态,这不会出发附加的重新渲染过程
# updated
- 发生在更新完成之后,当前阶段组件DOM已完成更新.要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器渲染期间不被调用.
# beforeDestroy
- 实例销毁之前调用.在这一步,实例仍然完全可用.我们可以在这时候进行善后和收尾工作,比如清除定时器.
# destroyed
- Vue实例销毁后调用.调用后,Vue实例指示的所有东西都会解绑定,所有事件监听器会被移除,所有的子实例也会被销毁.该钩子在服务器端渲染期间不被调用.
# activated
- keep-alive 专属,组件被激活时调用
# deactivated
- keep-alive 专属,组件被销毁时调用
异步请求在哪一步发起?
可以在钩子函数create、beforeMount,mounted、中进行异步请求,因为在这三个钩子函数中,data已经创建,可以讲服务端返回的数据进行赋值.
如果异步请求不需要依赖DOM推荐在created钩子函数中调用异步请求,因为在created钩子函数中调用异步请求有以下优点:
- 能更快的获取到服务端的数据,减少页面loading事件;
- ssr(服务端渲染)不支持beforeMount,mounted钩子函数,所以放在created中有助于一致性;
# Vue路由hash模式和history模式
# hash
模式
早期的前端路由实现就是基于localhost.hash来实现的,其实现原理很简单,localhost.hash的值就是URL中#后面的内容.比如https://www.baidu.com#search
,它的localhost.hash的值为‘#search’
hash路由模式的实现主要是基于下面几个特性
URL
中hash
值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash
部分不会被发送;hash
值的改变,都会在浏览器的访问历史中增加一个记录.因此我们能通过浏览器的回退、前进按钮控制hash
的切换;- 可以通过
a
标签,并设置href
属性,当用户点击这个标签后,URL
的hash
值后发生改变;或者使用javascript来对localhost.hash进行赋值,改变URL的hash值; - 我们可以使用hashchange事件来坚挺hash的变化.从而对页面进行跳转(渲染)
window.addEventListener('hashchange',funcRef,false)
每一次改变hash(window.localhost.hash),都会在浏览器的访问历史中增加一个记录利用hash的以上特点,就可以来实现前端路由’更新视图但不重新请求页面‘的功能了;
特点 : 兼容性好但不美观;
# history
模式
history采用HTML5的新特性;且提供了两个方法:pushState()
,repalceState()
可以对浏览器历史记录栈进行修改,以及popState()
事件的监听到状态变更
window.history.pushState(null,null,path)
window.history.replaceState(null,null,path)
2
这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL改变了.但浏览器不会刷新页面,这就为单页面应用前端路由‘更新视图但不重新请求页面’提供了基础.
history 路由模式的实现主要基于存在下面几个特性:
pushState()
和replaceState()
两个api来操作实现URL 的变化;- 我们可以使用
popState()
事件来监听URL的变化,从而对页面进行跳转(渲染) - history.pushState()或
history.replace()
不会触发popState
事件,这时我们需要手动触发页面跳转(渲染)
特点: 虽然美观,但是刷新会出现404,需要后端进行配置
# 什么是MVVM?
Model-View-ViewModel(MVVM)是一个软件架构设计模式,由微软WPF和Silverlight的架构师.....开发,是一种简化用户界面的事件驱动编程方式.由John Gossman()于2005年子啊他的博客上发表
MVVM源自经典的Model-View-Controller(MVC)模式,MVVM的出现促进了前端开发与后端业务逻辑的分离,极大的提高了前端开发效率,MVVM的核心是ViewModel层,它就像是一个中转站,(value Converter),负责转换Model中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与Model层通过接口请求进行数据交互,起承上启下的作用
- View层
- View 是视图层,也就是用户界面.前端主要是有HTML与CSS来构建.
- Model层
- Model是指数据模型.泛指后端进行各种业务逻辑处理和数据操控,对于前端来说就是后端提供的api接口.
- ViewModel层
- ViewModel 是由前端开发人员组织生成和维护的视图数据层.在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成View层使用预期的视图数据模型.需要注意的是ViewModel所封装出来的数据模型包括视图的状态和行为两部分,而Model层的数据模型指示包含状态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了ViewModel里.这样的封装使得ViewModel可以完整的去描述View层.
- MVVM框架实现了双向绑定,这样ViewModel,的内容会实时展示在View层,前端开发者再也不必低效又麻烦的通过DOM层去更新视图,MVVM框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新.这样View层展现的不是Model层的数据,而是ViewModel的数据,由ViewModel负责与Model层交互,这样就完全解藕了View和Model层,这个解藕是至关重要的,它是前后端分类方案实施的重要一环.
- 我们通过一个Vue实例来说明MVVM的具体实现
- View层
<div id="app"> <p>{{message}}</p> <button v-on:click="showMessage()">Click me</button> </div>
1
2
3
4 - ViewModel层
var app = new Vue({ el: '#app', data: { // 用于描述视图状态 message: 'Hello Vue!', }, methods: { // 用于描述视图行为 showMessage(){ let vm = this; alert(vm.message); } }, created(){ let vm = this; // Ajax 获取 Model 层的数据 ajax({ url: '/your/server/data/api', success(res){ vm.message = res; } }); } })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 - Model层
{ "url": "/your/server/data/api", "res": { "success": true, "name": "IoveC", "domain": "www.cnblogs.com" } }
1
2
3
4
5
6
7
8
- View层
# 谈谈Vue和React组件话的思想
- 我们在各个页面开发的时候,会产生很多重复的功能,比如element中的xxx,像这种纯粹非页面的UI,便成为我们常用的UI组件,最初的前端组件也就仅仅指的是UI组件
- 随着业务逻辑变的越来越多,我们就想要我们的组件可以处理很多事情,这就是我们常说的组件化,这个组件就不是UI组件,而是包含具体业务的业务组件
- 这种开发思想就是分而治之.最大程度的降低开发难度和维护成本的效果.并且可以多人协作,每个人写不同的组件,最后像搭积木一样把它构成一个页面
# Vue Complier 实现
- 模版解析这种事,本质是将数据转化为一段html,最开始出现在后端,经过各种处理吐给前端.随着各种
MV*
的兴起,模版解析交由前端处理. - 总的来说,Vue Complier 是将template转换成一个render 字符串.
可以简单理解为以下步骤
parse
过程,将templat
利用正则转换成AST
语法树.optimise
过程,标记静态节点,后diff
过程跳过静态节点,提升性能.generate
过程,生成render
字符串.
# Vue3 Watch、watchEffect区别
watch
是惰性执行,也就是只有监听的值发生变化的时候才会执行,但是watchEffect不同,每次代码加载watchEffect都会执行(忽略watch第三个参数的配置,如果修改配置项也可以实现立即执行)- watch需要船体监听的对象,watchEffect不需要
- watch智能监听响应式数据:ref定义的属性和reactive定义的对象,如果直接监听reactive定义对象中的属性是不允许的(会报警告),除非使用函数转换一下.其实官网上说的监听一个getter
- watchEffect如果监听reactive定义的对象时不起作用的,只能监听对象中的属性.
看一下watchEffect的代码
```js
```
总结:
- 如果定义了reactive的数据,想去使用watch监听数据改变,则无法正确获取旧值,并且deep属性配置无效,自动强制开启了深层监听.
- 如果使用ref初始化一个对象或者数组类型的数据,会被自动转成reacitve的实现方式,生成proxy代理对象.也会变得无法正确取旧值.
- 用任何方式生成的数据,如果接受的变量时一个pxory代理对象,就会导致watch回调无法正确获取旧值.
- 所以当大家使用watch监听对象时,如果在不需要使用旧值的情况,可以正常监听对象没关系;但是如果当监听改变函数里面需要用到的旧值时,只能监听对象.xx属性的方式才行 异同总结;