1. 简单说下vue
vue是渐变式框架,根据自己的需求添加功能 vue数据驱动采用mvvm模式,m是数据层,v是视图层,vm是调度者 SPA单页面应用,只有一个页面,加载速率快 组件化,复用性强
那么,它有什么缺点?
vue2底层基于Object.defineProperty实现响应式,这个api本身不支持IE8及以下浏览器 csr的先天不足,首屏性能问题(白屏) 由于百度等搜索引擎爬虫无法爬取js中的内容,故spa先天就对seo优化心有余力不足(谷歌的puppeteer就挺牛逼的,实现预渲染底层也是用到了这个工具)
2. vue和react的区别
首先你得说说相同点,两个都是MVVM框架,数据驱动视图,无争议。如果说不同,那可能分为以下这么几点: vue是完整一套由官方维护的框架,核心库主要有由尤雨溪大神独自维护,而react是不要脸的书维护(很多库由社区维护),曾经一段时间很多人质疑vue的后续维护性,似乎这并不是问题。 vue上手简单,进阶式框架,白话说你可以学一点,就可以在你项目中去用一点,你不一定需要一次性学习整个vue才能去使用它,而react,恐怕如果你这样会面对项目束手无策。 语法上vue并不限制你必须es6+完全js形式编写页面,可以视图和js逻辑尽可能分离,减少很多人看不惯react-jsx的恶心嵌套,毕竟都是作为前端开发者,还是更习惯于html干净。 很多人说react适合大型项目,适合什么什么,vue轻量级,适合移动端中小型项目,其实我想说,说这话的人是心里根本没点逼数,vue完全可以应对复杂的大型应用,甚至于说如果你react学的不是很好,写出来的东西或根本不如vue写的,毕竟vue跟着官方文档撸就行,自有人帮你规范,而react比较懒散自由,可以自由发挥 vue在国内人气明显胜过react,这很大程度上得益于它的很多语法包括编程思维更符合国人思想
什么是mvvm
MVVM的核心是数据驱动即ViewModel,ViewModel是View和Model的关系映射。
MVVM本质就是基于操作数据来操作视图进而操作DOM,借助于MVVM无需直接操作DOM,开发者只需编写ViewModel中有业务,使得View完全实现自动化。
什么是 SPA 单页面,它的优缺点分别是什么
SPA( single-page application )即一个web项目就只有一个页面(即一个HTML文件,HTML 内容的变换是利用路由机制实现的。 仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。 优点: 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染; 基于上面一点,SPA 相对对服务器压力小; 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理; 缺点: 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载; 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理; SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
3. 生命周期
基本概念
什么是vue生命周期? Vue 实例从创建到销毁的过程,就是生命周期。 注意:浏览器有8个钩子,但是node中做服务端渲染的时候只有beforeCreate和created
beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。 可以做页面拦截。当进一个路由的时候我们可以判断是否有权限进去,是否安全进去,携带参数是否完整,参数是否安全。使用这个钩子好函数的时候就避免了让页面去判断,省掉了创建一个组建Vue实例。 created 发生在实例创建完成后,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互(因为Dom还没有创建),如果非要想,可以通过vm.refs属性对Dom进行操作。 beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。 updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。 beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器,销毁父组件对子组件的重复监听。beforeDestroy(){Bus.$off("saveTheme")} destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
生命周期调用顺序
组件的调用顺序都是先父后子 渲染完成的顺序是先子后父 组件的销毁操作是先父后子 销毁完成的顺序是先子后父 加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted 子组件更新过程 父beforeUpdate->子beforeUpdate->子updated->父updated 父组件更新过程 父 beforeUpdate -> 父 updated 销毁过程 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
vue生命周期的作用是什么
它的生命周期中有多个事件钩子,让我们控制Vue实例过程更加清晰。
第一次页面加载会触发哪几个钩子
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
每个周期具体适合哪些场景
beforecreate : 可以在这加个loading事件,在加载实例时触发 created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用 mounted : 挂载元素,获取到DOM节点 updated : 如果对数据统一处理,在这里写上相应函数 beforeDestroy : 可以清除定时器 nextTick : 更新数据后立即操作dom
4.v-show 与 v-if 的区别
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建; 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。 所以: v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景; v-show 则适用于需要非常频繁切换条件的场景。
5. Vue 的单向数据流
背景: 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向变的混乱。 每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。 有两种常见的试图改变一个 prop 的情形 : 这个 prop 用来传递一个初始值; 这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在第2情况下,最好定义一个本地的 data属性并将这个 prop 用作其初始值: 这个 prop 以一种原始的值传入且需要进行转换。
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter//定义本地的data属性接收prop初始值
}
}
在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
6.异步请求适合在哪个生命周期调用?
官方实例的异步请求是在mounted生命周期中调用的,而实际上也可以在created生命周期中调用。
本人推荐在 created 钩子函数中调用异步请求,有以下优点:
能更快获取到服务端数据,减少页面 loading 时间; ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
7.Vue2.x组件通信有哪些方式?
父子组件通信
父->子props;子(emit)->父(on) 获取父子组件实例 children 如:直接在子组件的methods方法里面写:this.refs.comA.titles Provide、inject 官方不推荐使用,但是写组件库时很常用,祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量
兄弟组件通信
Event Bus 实现跨组件通信: Vue.prototype.$bus = new Vue Vuex
跨级组件通信
Vuex attrs,listeners Provide、inject
8.什么是SSR
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端。 服务端渲染 SSR 的优缺点如下:
服务端渲染的优点:
更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面; 更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
服务端渲染的缺点:
更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境; 更多的服务器负载: 在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。 9.Vue路由 9-1 vue-router 路由模式有几种? vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, invalid mode: ${mode})
}
}
路由模式的说明如下:
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器; history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式; abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
9.hash路由和history路由实现原理
hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。 比如下面这个网站,它的 location.hash 的值为 '#search': www.word.com#search hash 路由模式的实现主要是基于下面几个特性: URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送; hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换; 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值; 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化,其中做最主要的 API 有以下两个: history.pushState() //新曾历史记录 history.repalceState()。 //替换历史记录 这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示: window.history.pushState(null, null, path); window.history.replaceState(null, null, path); history 路由模式的实现主要基于存在下面几个特性: pushState 和 repalceState 两个 API 来操作实现 URL 的变化; 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染); history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。
10.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 至少有一个已经遍历完了,就会结束比较。 所以 Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速 更准确因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快,源码如下:
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}