一、Vuex
关于 Vuex
Vuex 是⼀个专为 Vue.js 应⽤程序开发的模式。
把组件的共享状态抽取出来,以⼀个全局单例模式管理。在这种模式下,我们的组件树构成了⼀个巨⼤的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发⾏为!这就是“状态管理模式”。
应⽤场景有:单页应⽤中,组件之间的数据状态。应⽤实例:
1、购物车功能;
2、下单页⾯有选择优惠券按钮,点击进⼊优惠券页⾯,选择后返回到下单页,数据会绑定回来,显⽰已选择的优惠券;3、登录状态等等;
Vuex有哪⼏种属性?
有五种,分别是 State
、 Getter
、Mutation
、Action
、 Module
Vuex的State特性
1、Vuex就是⼀个仓库,仓库⾥⾯放了很多对象。其中state就是数据源存放地,对应于⼀般Vue对象⾥⾯的data
2、state⾥⾯存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发⽣改变,依赖这个数据的组件也会发⽣更新
3、它通过mapState
把全局的 state
和 getters
映射到当前组件的 computed
计算属性中
Vuex的Getter特性
1、getters
可以对State进⾏计算操作,它就是Store的计算属性
2、虽然在组件内也可以做计算属性,但是getters
可以在多组件之间复⽤
3、如果⼀个状态只在⼀个组件内使⽤,是可以不⽤getters
Vuex的Mutation特性
用于变更 $store
中的数据。Action 类似于 mutation
,不同在于:Action 提交的是 mutation
,⽽不是直接变更状态;Action 可以包含任意异步操作。
Vuex的Module特性
Module 可以让每⼀个模块拥有⾃⼰的state
、mutation
、action
、getters
,使得结构⾮常清晰,⽅便管理。
使⽤Vuex的好处?
1、多层嵌套的组件、兄弟组件间的状态会更好管理维护。
2、缓存⼀些当前要使⽤请求远程或本地的数据集(刷新后会⾃⼰销毁)。
3、有了第⼆条,就可以减少向服务器的请求,节省资源。如果你的⽤户⾜够多,那么每多出⼀个请求,对公司来说,都是⼀⼤笔钱。
4、对开发者来说,如果你的项⽬⾜够复杂,团队的规模也不仅是⼀个⼈,数据集中处理更利于程序的稳定和维护。
二、双向绑定原理
VUE数据的双向绑定是通过数据劫持和观察者模式(订阅者模式)的方式来实现的。
(1)数据劫持:vue2.x使用Object.defineProperty(); Vue3使用的是proxy。
当你把一个普通的 JavaScript 对象(json)传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
目的:感知属性的变化。当给属性赋值时,程序是能够感知的(知道的)。如果知道的话,就可以控制属性值的有效范围,也可以改变其它属性的值等,在Vue中也可以去改变模板上的显示。
(2)观察者模式(订阅者模式)
Observer(数据监听器) : Observer的核心是通过Object.defineProprtty()来监听数据的变动,这个函数内部可以定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher
Watcher(订阅者) : Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:
① 在自身实例化时往属性订阅器(dep)里面添加自己
② 自身必须有一个update()方法
③ 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调
Compile(指令解析器) : Compile主要做的事情是解析模板指令,将模板中变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新视图。
目的:当属性发生变化时,所有使用该数据地方跟着变化。
三、组件间传递数据
1、⽗组件向⼦组件传递数据,使⽤props
属性;⼦组件向⽗组件中传递数据,在⼦组件中使⽤$emit
派发事件,⽗组件中使⽤v-on
监听事件;缺点:组件嵌套层次多的话,传递数据⽐较⿇烦。
2、祖先组件通过依赖注⼊(inject
/ provide
)的⽅式,向其所有⼦孙后代传递数据;缺点:⽆法监听数据修改的来源,不⽀持响应式。
3、通过属性$root
/ $parent
/ $children
/ ref
,访问根组件、⽗级组件、⼦组件中的数据;缺点:要求组件之间要有传递性。
4、通过事件总线(event bus)的⽅式,可以实现任意两个组件间进⾏数据传递;缺点:不⽀持响应式,这个概念是vue1.0版本中的,现在已经废弃。
5、通过 VueJs 的状态管理模式Vuex
,实现多个组件进⾏数据共享,推荐使⽤这种⽅式进⾏项⽬中各组件间的数据传递。
四、Vue项⽬优化
1、懒加载模块
比如Echarts图标的包和编辑器的包,都是很大的有45百kb,但并不是所有的页面都需要图表和编辑器,只有少量页面需要,所以我们可以把Echarts和编辑器放到需要的页面中去加载,不要放到全局里面,这样其他页面就省去加载他们的时间。
2、路由懒加载拆分
1
2
3
4
5// routing.js
const routes = [
{ path: '/', component: () => import('./Dashboard.vue') },
{ path: '/contact', component: () => import('./Contact.vue') }
];
以这种函数的形式加载路由,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会加载路由组件。
3、prefetch预渲染组件
prefetch是一种利用浏览器的空闲时间加载页面将来可能用到的资源的一种机制;通常可以用于加载非首页的其他页面所需要的资源,以便加快后续页面的首屏速度;通过webpack我们可以非常方便的实现组件的预渲染,通过加入/ webpackPrefetch: true /,webpack会自动在页面中加入 的标签。1
2
3components: {
ModalView: () => import(/* webpackPrefetch:true */ './ModalView.vue')
}
4、优化三方库依赖
一般我们在项目中都会用到一些三方的依赖库,比如lodash; 如果我们全量的引入的话会很大,但我们可能只用到一两个函数,我们只需要引用我们需要的就好:1
import isEmpty from 'lodash/isEmpty';
5、善用浏览器缓存
一般我们打包后的文件像这样
main.[hash].js – 根组件
common.[hash].js – 公共组件
dashboard.[hash].js – dashboard页面
contact.[hash].js – contact页面
其实我们可以把一些很长时间都不需要的公共代码依赖放到common.[hash].js中,这样一次缓存后,以后再次访问就不会去发请求。
6、优化压缩图片
图片的大小对于项目的性能也至关重要,一般webpack会帮助我们把小的图片直接转为base64来减少网络请求。对其他图片来说我们也要进行压缩,一般压缩方式有两种
- 使用软件进行压缩
- 使用CDN进行文件压缩
使用软件压缩我推荐使用TinyPNG,一个在线网站,使用它压缩几乎不损失清晰度而且压缩效果特别好。
使用CDN压缩,一般专业的文件存储都会提供图片的处理功能比如京东的图片地址,中间的s280x280可以去修改图片宽高,后面的.webp后缀把jpg图片转换成webp格式,进一步的来缩小文件的大小。
7、静态文件上CDN
一般小公司可能为了方便直接把CSS、JS、图片等文件直接传到服务器上进行访问,使用CDN的优势在于CDN时全国的各个地方都会有服务节点,而且CDN也会缓存文件,所以通过CDN访问静态文件和直接访问服务器文件要快上几倍。
淘宝的图片访问,有98%的流量都走了CDN缓存。只有2%会回源到源站,节省了大量的服务器资源。
五、Vue父子组件生命周期钩子函数执行顺序
Vue父子组件的生命周期钩子函数执行顺序主要分为四个部分:
- 首次加载渲染:父组件beforeCreate -> 父组件created -> 父组件beforeMount -> 子组件beforeCreate -> 子组件created -> 子组件beforeMount -> 子组件mounted -> 父组件mounted。
- 子组件更新:父组件beforeUpdate -> 子组件beforeUpdate -> 子组件updated -> 父组件updated。
- 父组件更新(不会影响子组件): 父组件beforeUpdate -> 父组件updated。
- 销毁事件:父组件beforeDestroy -> 子组件beforeDestroy -> 子组件destroyed -> 父组件destroyed。
六、Vue属性的执行顺序
我们先看一下源码的初始化:
从源码我们可以看出属性的执行顺序为:props
-> methods
-> data
-> computed
-> watch
。
七、自定义指令的原理及钩子函数
指令的原理:
1、在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性。
2、通过 genDirectives 生成指令代码。
3、在 patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子。
4、当执行指令对应钩子函数时,调用对应指令定义方法。
自定义指令有五个生命周期(也叫钩子函数),分别是:
1、 bind
:只调用一次,指令第一次绑定到元素时调用。
2、inserted
:被绑定元素插入父节点时调用。
3、update
:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。
4、componentUpdated
:被绑定元素所在模板完成一次更新周期时调用。
5、unbind
:只调用一次,指令与元素解绑时调用。
八、vue-router 中常用的路由模式和实现原理
hash模式: location.has 的值实际就是 URL 中 # 后面的东西。它的特点在于:hash虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。可以为 hash 的改变添加监听事件 window.addEventListener(“hashchange”,funcRef,false);每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一 个记录,利用hash的以上特点,就可以实现前端路由“更新视图但不重新请求页面”的功能.
history模式: 利用 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go 的基础上,他们提供了对历史记录进行修改的功能。这两个方法有个共同点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页面应用前端路由“更新视图但不重新请求页面”提供了基础。