Vue.js
是构建客户端应用程序的框架。默认状况下,能够在浏览器中输入Vue
组件,进行生成DOM
和操作DOM
。然而,也能够将同一个组件渲染为服务端的HTML
字符串,将它们间接发送到浏览器,最初将这些动态标记”激活”为客户端上齐全可交互的应用程序。
SSR
也就是服务端渲染,也就是将Vue
在客户端把标签渲染成HTML
的工作放在服务端实现,而后再把html
间接返回给客户端
长处 :SSR
有着更好的 SEO
、并且首屏加载速度更快
SPA
页面的内容是通过 Ajax
获取,而搜索引擎爬取工具并不会期待 Ajax
异步实现后再抓取页面内容,所以在 SPA
中是抓取不到页面通过 Ajax
获取到的内容;而 SSR
是间接由服务端返回曾经渲染好的页面(数据曾经蕴含在页面中),所以搜索引擎爬取工具能够抓取渲染好的页面SPA
会期待所有 Vue
编译后的 js
文件都下载实现后,才开始进行页面的渲染,文件下载等须要肯定的工夫等,所以首屏渲染须要肯定的工夫;SSR
间接由服务端渲染好页面间接返回显示,无需期待下载 js 文件及再去渲染等,所以 SSR 有更快的内容达到工夫毛病 : 开发条件会受到限制,服务器端渲染只反对 beforeCreate
和 created
两个钩子,当咱们须要一些内部扩大库时须要非凡解决,服务端渲染应用程序也须要处于 Node.js
的运行环境。服务器会有更大的负载需要
server
更加大量占用CPU
资源 (CPU-intensive – CPU 密集),因而如果你意料在高流量环境 ( high traffic ) 下应用,请筹备相应的服务器负载,并明智地采纳缓存策略其根本实现原理
app.js
作为客户端与服务端的专用入口,导出 Vue
根实例,供客户端 entry
与服务端 entry
应用。客户端 entry
次要作用挂载到 DOM
上,服务端 entry
除了创立和返回实例,还进行路由匹配与数据预获取。webpack
为客服端打包一个 Client Bundle
,为服务端打包一个 Server Bundle
。url
,加载相应组件,获取和解析异步数据,创立一个读取 Server Bundle
的 BundleRenderer
,而后生成 html
发送给客户端。DOM
与本人的生成的 DOM 进行比照,把不雷同的 DOM
激活,使其能够可能响应后续变动,这个过程称为客户端激活 。为确保混合胜利,客户端与服务器端须要共享同一套数据。在服务端,能够在渲染之前获取数据,填充到 stroe
里,这样,在客户端挂载到 DOM
之前,能够间接从 store
里取数据。首屏的动态数据通过 window.__INITIAL_STATE__
发送到客户端
Vue SSR
的实现,次要就是把Vue
的组件输入成一个残缺HTML
,vue-server-renderer
就是干这事的
Vue SSR
须要做的事多点(输入残缺 HTML),除了complier -> vnode
,还需如数据获取填充至 HTML
、客户端混合(hydration
)、缓存等等。相比于其余模板引擎(ejs
, jade
等),最终要实现的目标是一样的,性能上可能要差点
事件修饰符
v-model 的修饰符
键盘事件的修饰符
零碎润饰键
鼠标按钮修饰符
v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
v-show 会被编译成指令,条件不满足时管制款式将对应节点暗藏 (display:none)
Proxy 的劣势如下:
Proxy 作为新规范将受到浏览器厂商重点继续的性能优化,也就是传说中的新规范的性能红利;
Object.defineProperty 的劣势如下:
Vue 组件间通信是面试常考的知识点之一,这题有点相似于凋谢题,你答复出越多办法当然越加分,表明你对 Vue 把握的越纯熟。Vue 组件间通信只有指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,上面咱们别离介绍每种通信形式且会阐明此种办法可实用于哪类组件间通信。
(1)props / $emit
实用 父子组件通信
这种办法是 Vue 组件的根底,置信大部分同学耳闻能详,所以此处就不举例开展介绍。
(2)ref
与 $parent / $children
实用 父子组件通信
ref
:如果在一般的 DOM 元素上应用,援用指向的就是 DOM 元素;如果用在子组件上,援用就指向组件实例$parent
/ $children
:拜访父 / 子实例(3)EventBus ($emit / $on)
实用于 父子、隔代、兄弟组件通信
这种办法通过一个空的 Vue 实例作为地方事件总线(事件核心),用它来触发事件和监听事件,从而实现任何组件间的通信,包含父子、隔代、兄弟组件。
(4)$attrs
/$listeners
实用于 隔代组件通信
$attrs
:蕴含了父作用域中不被 prop 所辨认 (且获取) 的个性绑定 ( class 和 style 除外 )。当一个组件没有申明任何 prop 时,这里会蕴含所有父作用域的绑定 ( class 和 style 除外 ),并且能够通过 v-bind="$attrs"
传入外部组件。通常配合 inheritAttrs 选项一起应用。$listeners
:蕴含了父作用域中的 (不含 .native 润饰器的) v-on 事件监听器。它能够通过 v-on="$listeners"
传入外部组件(5)provide / inject
实用于 隔代组件通信
先人组件中通过 provider 来提供变量,而后在子孙组件中通过 inject 来注入变量。 provide / inject API 次要解决了跨级组件间的通信问题,不过它的应用场景,次要是子组件获取下级组件的状态,跨级组件间建设了一种被动提供与依赖注入的关系。
(6)Vuex 实用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。每一个 Vuex 利用的外围就是 store(仓库)。“store” 基本上就是一个容器,它蕴含着你的利用中大部分的状态 ( state )。
简而言之,就是先转化成AST树,再失去的render函数返回VNode(Vue的虚构DOM节点),具体步骤如下:
首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的形象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创立编译器的。另外compile还负责合并option。
而后,AST会通过generate(将AST语法树转化成render funtion字符串的过程)失去render函数,render的返回值是VNode,VNode是Vue的虚构DOM节点,外面有(标签名、子节点、文本等等)
受古代 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 | 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] = val
return val
}
const ob = (target: any).__ob__
// target 自身就不是响应式数据, 间接赋值
if (!ob) {
target[key] = val
return val
}
// 对属性进行响应式解决
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
咱们浏览以上源码可知,vm.$set 的实现原理是:
(1)hash 模式的实现原理
晚期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简略,location.hash 的值就是 URL 中 # 前面的内容。比方上面这个网站,它的 location.hash 的值为 ‘#search’:
https://www.word.com#search
hash 路由模式的实现次要是基于上面几个个性:
hash 值的扭转,都会在浏览器的拜访历史中减少一个记录。因而咱们能通过浏览器的回退、后退按钮管制hash 的切换;
(2)history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变动。其中做最次要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 能够在不进行刷新的状况下,操作浏览器的历史纪录。惟一不同的是,前者是新增一个历史记录,后者是间接替换以后的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的实现次要基于存在上面几个个性:
咱们在 vue 我的项目中次要应用 v-model 指令在表单 input、textarea、select 等元素上创立双向数据绑定,咱们晓得 v-model 实质上不过是语法糖,v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:
以 input 表单元素为例:
相当于
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
子组件:
{{value}}
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
参考:前端vue面试题具体解答
官网解释:Vue.extend 应用根底 Vue 结构器,创立一个“子类”。参数是一个蕴含组件选项的对象。
其实就是一个子类结构器 是 Vue 组件的外围 api 实现思路就是应用原型继承的办法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并
相干代码如下
export default function initExtend(Vue) {
let cid = 0; //组件的惟一标识
// 创立子类继承Vue父类 便于属性扩大
Vue.extend = function (extendOptions) {
// 创立子类的构造函数 并且调用初始化办法
const Sub = function VueComponent(options) {
this._init(options); //调用Vue初始化办法
};
Sub.cid = cid++;
Sub.prototype = Object.create(this.prototype); // 子类原型指向父类
Sub.prototype.cOnstructor= Sub; //constructor指向本人
Sub.optiOns= mergeOptions(this.options, extendOptions); //合并本人的options和父类的options
return Sub;
};
}
key 是给每一个 vnode 的惟一 id,依附 key,咱们的 diff 操作能够更精确、更疾速 (对于简略列表页渲染来说 diff 节点也更快,但会产生一些暗藏的副作用,比方可能不会产生过渡成果,或者在某些节点有绑定数据(表单)状态,会呈现状态错位。)
diff 算法的过程中,先会进行新旧节点的首尾穿插比照,当无奈匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.
更精确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 比照中能够防止就地复用的状况。所以会更加精确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。
更疾速 : key 的唯一性能够被 Map 数据结构充分利用,相比于遍历查找的工夫复杂度 O(n),Map 的工夫复杂度仅仅为 O(1),源码如下:
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;
}
=> 相同点:
1. 数据驱动页面,提供响应式的试图组件
2. 都有virtual DOM,组件化的开发,通过props参数进行父子之间组件传递数据,都实现了webComponents标准
3. 数据流动单向,都反对服务器的渲染SSR
4. 都有反对native的办法,react有React native, vue有wexx
=> 不同点:
1.数据绑定:Vue实现了双向的数据绑定,react数据流动是单向的
2.数据渲染:大规模的数据渲染,react更快
3.应用场景:React配合Redux架构适宜大规模多人合作简单我的项目,Vue适宜小快的我的项目
4.开发格调:react举荐做法jsx + inline style把html和css都写在js了
vue是采纳webpack + vue-loader单文件组件格局,html, js, css同一个文件
Vue 组件间通信是面试常考的知识点之一,这题有点相似于凋谢题,你答复出越多办法当然越加分,表明你对 Vue 把握的越纯熟。Vue 组件间通信只有指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,上面咱们别离介绍每种通信形式且会阐明此种办法可实用于哪类组件间通信。
(1)props / $emit
实用 父子组件通信 这种办法是 Vue 组件的根底,置信大部分同学耳闻能详,所以此处就不举例开展介绍。
(2)ref 与 $parent / $children
实用 父子组件通信
ref
:如果在一般的 DOM 元素上应用,援用指向的就是 DOM 元素;如果用在子组件上,援用就指向组件实例$parent / $children
:拜访父 / 子实例(3)EventBus ($emit / $on)
实用于 父子、隔代、兄弟组件通信 这种办法通过一个空的 Vue 实例作为地方事件总线(事件核心),用它来触发事件和监听事件,从而实现任何组件间的通信,包含父子、隔代、兄弟组件。
(4)$attrs/$listeners
实用于 隔代组件通信
$attrs
:蕴含了父作用域中不被 prop 所辨认 (且获取) 的个性绑定 ( class 和 style 除外 )。当一个组件没有申明任何 prop
时,这里会蕴含所有父作用域的绑定 ( class 和 style 除外 ),并且能够通过 v-bind="$attrs"
传入外部组件。通常配合 inheritAttrs
选项一起应用。$listeners
:蕴含了父作用域中的 (不含 .native 润饰器的) v-on
事件监听器。它能够通过 v-on="$listeners"
传入外部组件(5)provide / inject
实用于 隔代组件通信 先人组件中通过 provider
来提供变量,而后在子孙组件中通过 inject
来注入变量。 provide / inject API
次要解决了跨级组件间的通信问题,不过它的应用场景,次要是子组件获取下级组件的状态,跨级组件间建设了一种被动提供与依赖注入的关系。 (6)Vuex
实用于 父子、隔代、兄弟组件通信 Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。每一个 Vuex 利用的外围就是 store(仓库)。“store” 基本上就是一个容器,它蕴含着你的利用中大部分的状态 ( state )。
computed:
computed
是计算属性,也就是计算值,它更多用于计算值的场景computed
具备缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值扭转之后,下一次获取computed的值时才会从新调用对应的getter来计算computed
实用于计算比拟耗费性能的计算场景watch:
props
$emit
或者本组件的值,当数据变动时来执行回调进行后续操作小结:
数组就是应用 object.defineProperty
从新定义数组的每一项,那能引起数组变动的办法咱们都是晓得的, pop
、 push
、 shift
、 unshift
、 splice
、 sort
、 reverse
这七种,只有这些办法执行改了数组内容,我就更新内容就好了,是不是很好了解。
vue3:改用 proxy
,可间接监听对象数组的变动。
(1)代码层面的优化
(2)Webpack 层面的优化
(3)根底的 Web 技术的优化
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简略得多——不论初始条件是什么,元素总是会被渲染,并且只是简略地基于 CSS 的 “display” 属性进行切换。
所以,v-if 实用于在运行时很少扭转条件,不须要频繁切换条件的场景;v-show 则实用于须要十分频繁切换条件的场景。
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简略得多——不论初始条件是什么,元素总是会被渲染,并且只是简略地基于 CSS 的 “display” 属性进行切换。
所以,v-if 实用于在运行时很少扭转条件,不须要频繁切换条件的场景;v-show 则实用于须要十分频繁切换条件的场景。
工夫复杂度: 个树的齐全 diff
算法是一个工夫复杂度为 O(n*3)
,vue进行优化转化成 O(n)
。
了解:
最小量更新, key
很重要。这个能够是这个节点的惟一标识,通知 diff
算法,在更改前后它们是同一个DOM节点
v-for
为什么要有 key
,没有 key
会暴力复用,举例子的话轻易说一个比方挪动节点或者减少节点(批改DOM),加 key
只会挪动缩小操作DOM。diff算法的优化策略:四种命中查找,四个指针