Vue知识点查缺补漏
对 Vue 框架中一些重要的特性、框架的原理进行整理汇总、有写的不对的或者不够充分的还望指教。
文章目录
- SPA单页面
- Vue—-单向数据流
- Vue—-生命周期
- v-if 与 v-show的区别
- computed 与 watch的区别
- Vue—-双向数据绑定原理
- keep-alive
- Vue—-组件间通信
- Vuex
- Vue—-SSR(服务端渲染)
- vue-router
- Vue—-虚拟DOM
- Vue—-key的作用
- Vue—-性能优化
SPA单页面
1、
SPA
( single-page application )是一种特殊的Web应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript、CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用JavaScript动态的变换HTML(采用的是div切换显示和隐藏)以及利用路由机制,从而实现UI与用户的交互,避免页面的重新加载。2、简单来说SPA的网页只有一个页面,而这个网页的实际方式要能够回应使用者所使用的各种装置并且赋值使用者在电脑上使用软件的体验,让使用者可以更容易和有效的使用网站。按照正常情况下,我们会在一个页面中链接到其他的很多个页面,进行页面的跳转,但是如果使用单页面应用的话,我们始终在一个页面中,通常使用a标签的描点来实现。
优点 :
用户体验好
、页面内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染- 基于上面一点,SPA 相对
对服务器压力小
- 前后端职责分离,
架构清晰
,前端进行交互逻辑,后端负责数据处理
缺点 :
初次加载耗时多 (首屏加载)
:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;前进后退路由管理麻烦
:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;SEO 比较差
:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
Vue—单向数据流
所有的
prop
都使得其父子prop
之间形成了一个单向下行绑定
:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
prop
是父组件
传过来的原始数据,但是我们试图通过子组件
直接 去改变父组件
的数据(而不是通过发送事件的方式),这是不允许的,因为Vue 是单向数据流
—— 也就是说,数据总是从父组件传到子组件 ,子组件没有权利修改父组件传过来的数据, 只能请求 父组件对原始数据进行修改。
Vue—生命周期
Vue实例有一个完整的生命周期,也就是从
开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等
一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期
。
异步请求调用???
可以在钩子函数 created、beforeMount、mounted
中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created钩子函数中调用异步请求
。
why???
- 能更快获取到服务端数据,减少页面 loading 时间
- ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性
访问操作DOM???
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted
中可以访问操作 DOM。
v-if 与 v-show的区别
v-if
是真正的
条件渲染,因为它会确保在切换过程中条件块内的事件监听器
和子组件
适当地被销毁
和重建
。同时它也是惰性的
,渲染条件为假
,什么都不做。直到条件变成真
,才会开始渲染条件块。
v-show
不管什么条件,元素是会被渲染,并且只是简单的基于css的 display属性 进行切换
so,出于性能考虑,需要经常切换或操作频繁的使用
v-show,否则用 v-if
computed 与 watch的区别
computed
: 是计算属性
,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
应用场景 :
- 进行
数值计算
,并且依赖于其它数据
时,应该使用computed
,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算 - 处理props或$emit的传值
watch
: 更多的是观察
的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作。
应用场景 :
- 在数据变化时
执行异步
或开销较大
的操作时,应该使用watch
,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。 - 监听props,$emit或当前组件的值执行异步操作
Vue—双向数据绑定原理
vue.js 采用
数据劫持
结合发布者-订阅者
模式的方式,通过Object.defineProperty()
来劫持各个属性的setter,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。
数据监听器
—Observer
,能够对数据对象的所有属性进行监听
,如有变动可拿到最新值并通知订阅者
指令解析器
—Compile
,对每个元素节点的指令进行扫描和解析
,根据指令模板替换数据,以及绑定相应的更新函数Watcher
,作为连接Observer
和Compile
的桥梁,能够订阅
并收到
每个属性变动的通知
,执行指令绑定的相应回调函数,从而更新视图
keep-alive
keep-alive
是 Vue 内置的一个组件,可以使被包含的组件保留状态
,避免重新渲染。
- 一般结合路由和动态组件一起使用,用于缓存组件
- 提供
include
和exclude
属性,两者都支持字符串
或正则表达式
,include
表示只有名称匹配的组件会被缓存
,exclude
表示任何名称匹配的组件都不会被缓存 ,其中exclude
的优先级比include
高 - 对应两个钩子函数
activated
和deactivated
,当组件被激活时
,触发钩子函数activated
,当组件被移除时
,触发钩子函数deactivated
。
Vue—组件间通信
Vue 组件间通信
主要指以下 3 类通信:父子组件通信
、兄弟组件通信
、隔代组件通信
。
1 、props / $emit—适用于父子组件通信
- 父组件的数据需要通过 props 把数据传给子组件
- 子组件向父组件传递数据就需要通过
$emit()
再通过里传递的事件来传递数据。子组件可以通过$emit()
触发父组件中的自定义事件。
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 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。改变 store 中的状态
的唯一途径就是显式地提交mutation
。这样使得我们可以方便地跟踪每一个状态的变化。
Vuex
Vuex
是一个专为 Vue.js 应用程序开发的状态管理
(它采用集中式存贮管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化)。每一个 Vuex 应用的核心就是store
(仓库)。store
基本上就是一个容器,它包含着你的应用中大部分的状态state
。
Vuex的五大核心属性: State、Getter、Mutations、Actions、Module
- State : 存储
数据/状态
,在根实例注册store,用this.$store
来访问,对应的data是响应式
的,从store读取数据,若发生变化,组件对应更新。 - Getter : store的
计算属性
,返回值根据依赖
被缓存
,只有当依赖发生变化
才会被重新计算
。 - Mutations :
更改
vuex的store中状态
的唯一方法就是提交mutations
(同步操作
)。 - Actions : 含任意
异步操作
,通过提交mutations间接改变状态。 - Module : 将store
分割模块
,允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
由于
传参
的方法对于多层嵌套
的组件会非常繁琐,并且对于兄弟组件
间的状态传递
也无能为力。我们经常用父子组件直接引用
或通过事件
来变更/同步状态
,这样导致后期代码维护花销太大。
所以要把组件的共享状态
抽取出来,以一个全局的单例模式管理,在这种模式下,不管在任意组件任意位置都能获取状态
或触发行为
。另外通过定义
和隔离
状态管理中的各种概念,后续的代码会更结构化
且易维护
。
Vue—SSR(服务端渲染)
Vue.js
是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记”激活”为客户端上完全可交互的应用程序。即:SSR 大致的意思就是vue在客户端将标签渲染成的整个html片段的工作在服务端完成,服务端形成的html片段直接返回给客户端这个过程就叫做服务端渲染
。
优点 :
- 利于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) 下使用,请准备相应的服务器负载
,并明智地采用缓存策略
。
vue-router
前端路由的核心
:改变试图的同时不会向服务器端发起请求。
vue-router的路由模式是 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,路由会自动强制进入这个模式。
主要说说 hash 和 history
hash:
之前前端路由的实现是基于
location.hash
来实现的。其原理很简单,location.hash的值
就是URL
中#
后面的内容。hash路由模式的实现主要有几个特性:
URL
中hash值
只是客户端的一种状态,也就是说当向服务器端发出请求
时,hash 部分不会被发送
。hash 值的改变
,都会在浏览器的访问历史
中增加一个记录。因此我们能通过浏览器的回退、前进
按钮控制hash 的切换
。- 可以通过
a
标签,并设置href
属性,当用户点击这个标签后,URL
的hash
值会发生改变;或者使用 JavaScript 来对loaction.hash
进行赋值,改变 URL 的hash
值。 - 可以使用
hashchange
事件来监听hash
值的变化,从而对页面进行跳转(渲染)
。
history:
利用了
HTML5 History Interface
中新增的pushState()
和replaceState()
方法。(需要特定浏览器支持
)
这两个方法应用于浏览器的历史记录栈,在当前已有的back
、forward
、go
的基础之上,它们提供了对历史记录进行修改
的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即
向后端发送请求
。history路由模式的实现主要有几个特性:
pushState
和repalceState
两个 API 来操作实现URL
的变化- 可以使用
popstate
事件来监听 url 的变化
,从而对页面进行跳转(渲染) history.pushState()
或history.replaceState()
不会触发popstate
事件,这时我们需要手动触发页面跳转(渲染)
可以说,hash和history模式都属于浏览器自身的特性,vue-router只是利用这两个特性(通过调用浏览器提供的接口)来实现前端路由
注:history不怕前进,不怕后退,就怕刷新(f5)。 所以要想玩好history,后端必须做到路由全覆盖处理,前端配置404页面,这样就不至于一刷新就404.
Vue—虚拟DOM
虚拟 DOM 的实现原理主要包括以下 3 部分:
- 用
JavaScript
对象模拟真实DOM
树,对真实DOM
进行抽象 diff 算法
— 比较两棵虚拟DOM
树的差异patch 算法
— 将两个虚拟DOM
对象的差异应用到真正的DOM
树。
优点:
- 保证性能下限: 框架的
虚拟 DOM
需要适配任何上层 API 可能产生的操作,它的一些DOM
操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM
操作性能要好很多,因此框架的虚拟 DOM
至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限。 - 无需手动操作 DOM: 我们不再需要手动去操作
DOM
,只需要写好View-Model
的代码逻辑,框架会根据虚拟DOM
和数据双向绑定
,帮我们以可预期的方式更新视图,极大提高我们的开发效率。 - 跨平台:
虚拟 DOM
本质上是JavaScript
对象,而DOM
与平台强相关,相比之下虚拟 DOM
可以进行更方便地跨平台操作,例如服务器渲染
、weex 开发
等等。
缺点:
- 无法进行极致优化: 虽然
虚拟 DOM
+ 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM
无法进行针对性的极致优化。
Vue—key的作用
key
是为Vue
中vnode
的唯一标记
,通过这个 key,我们的diff
操作可以更准确、更快速。
看源码
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
}
更准确:
因为带key
就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
更快速:
利用key
的唯一性生成map
对象来获取对应节点,比遍历方式更快。主要是为了提升diff【同级比较】
的效率。自己想一下自己要实现前后列表的diff,如果对列表的每一项增加一个key,即唯一索引,那就可以很清楚的知道两个列表谁少了谁没变。而如果不加key的话,就只能一个个对比了。
Vue—性能优化
代码层面的优化
v-if
和v-show
区分使用场景computed
和watch
区分使用场景v-for
遍历必须为item
添加key
,且避免同时使用v-if
- 长列表性能优化
- 事件的销毁
- 图片资源懒加载
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染
SSR
or 预渲染
Webpack 层面的优化
Webpack
对图片进行压缩- 减少
ES6
转为ES5
的冗余代码 - 提取公共代码
- 模板预编译
- 提取组件的
CSS
- 优化
SourceMap
- 构建结果输出分析
Vue
项目的编译优化
基础的 Web 技术的优化
- 开启
gzip
压缩 - 浏览器缓存
CDN
的使用- 使用
Chrome Performance
查找性能瓶颈
还没有评论,来说两句吧...