Mvvm 与 mvc 的区别
- MVVM
MVVM 分为三部分:分别是 M(Model,模型层),V(View,视图层),V(ViewModel,V 与 M 连接的桥梁,也可以看作为控制器MVC 的 C 层)- 1、M:模型层,主要负责业务数据相关
- 2、V:视图层,负责视图相关,细分下来就是 html+css 层
- 3、VM:V 与 M 沟通的桥梁,负责监听 M 或者 V 的修改,是实现MVVM 双向绑定的要点;因此开发者只需关注业务逻辑,不需要手动操作 DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
- MVC
MVC 封装与业务无关的重复代码,形成框架,是模型(model)-视图(view)-控制器(controller)的缩写。- 1、模型(Model)数据的储存和处理,再传递给视图层相应或者展示
- 2、视图(View)前端的数据展示
- 3、控制器(Controller)对数据的接收和触发事件的接收和传递
- 两者在思想上的区别
- 1、MVC 思想:一种将数据层与视图层进行分离的设计思想。
- 2、MVVM 思想:MVVM 的一个重要特性,双向绑定,意思就是当 M 层数据进行修改时,VM 层会监测到变化,并且通知 V 层进行相应的修改,反之相同 mvc 和 mvvm 都是一种设计思想。主要就是 mvc 中Controller 演变成 mvvm 中的 viewModel。
- mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
虚拟dom原理
- 定义
- 虚拟DOM其实就是用一个原生的JS对象去描述一个DOM节点,实际上它只是对真实 DOM 的一层抽象。最终可以通过一系列操作使这棵树映射到真实环境上。
- 相当于在js与DOM之间做了一个缓存,利用patch(diff算法)对比新旧虚拟DOM记录到一个对象中按需更新, 最后创建真实的DOM
- 虚拟dom原理流程
- 模板 ==> 渲染函数 ==> 虚拟DOM树 ==> 真实DOM
- vuejs通过编译将模板(template)转成渲染函数(render),执行渲染函数可以得到一个虚拟节点树
- 在对 Model 进行操作的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。
- 虚拟 DOM 的实现原理主要包括以下 3 部分:
- 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
diff 算法— 比较两棵虚拟 DOM 树的差异;pach 算法— 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
渲染函数: 渲染函数是用来生成Virtual DOM的。
VNode虚拟节点: 它可以代表一个真实的dom节点。通过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点
patch(也叫做patching算法): 虚拟DOM最核心的部分,它可以将vNode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新
- 虚拟DOM好处
- 具备跨平台的优势–由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。
- 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
- 提升渲染性能 Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新
- 虚拟DOM就是为了解决浏览器性能问题而被设计出来的
vue渐进式
Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
- 简单地说,渐进式的概念是分层设计,每层可选,不同层可以灵活接入其他方案架构模式
- 渐进式的最大好处就是灵活,可以根据不同场景做定制
vue生命周期
从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
beforeCreate(创建前) :组件实例被创建之初,组件的属性生效之前
//beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
created(创建后) :组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
// data 和 methods都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
beforeMount(挂载前) :在挂载开始之前被调用:相关的 render 函数首次被调用
//执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
mounted(挂载后) :在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
//到mounted周期的时候,Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate(更新前) :组件数据更新之前调用,真实DOM还没被渲染
// 当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步
update(更新后) :组件数据更新之后
//页面显示的数据和data中的数据已经保持同步了,都是最新的
activated(激活前) :keep-alive专属,组件被激活时调用
//当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。
deactivated(激活后) :keep-alive专属,组件被销毁时调用
//当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期
beforeDestory(销毁前) :组件销毁前调用
//Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
destoryed(销毁后) :组件销毁前调用
//这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
Vue子组件和父组件执行顺序
父组件created→父组件beforeMounted→子组件created→子组件beforeMounted→子组件mounted→父组件mounted;
Vue的el属性和$mount优先级
new Vue({
router,
store,
el: '#app',
render: h => h(App)
}).$mount('#ggg')
从下面的官方图片我们可以看出来, el的优先级是高于$mount的,因此以el挂载节点为准
Vue双向绑定的原理
-
vue双向数据绑定原理,又称vue响应式原理,是vue的核心,双向数据绑定是通过数据劫持结合发布者订阅者模式的方式来实现的,通过Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变
-
vue实现双向数据绑定的核心是Object.defineProperty()方法
-
Object.defineProperty(obj, prop, descriptor)方法,接收三个参数,分别为obj(定义其上属性的对象)prop(定义或修改的属性)descriptor(具体的改变方法),就是用这个方法来定义一个值,当调用时我们使用了它里面的get方法,当我们给这个属性赋值时,又用到了它里面的set方法
具体步骤:
第一步: 需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步: compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步: Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个update()方法
- 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
第四步: MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
// 简单的数据响应劫持
var demo={
name:'张三'
}
//Object.defineProperty(obj,prop,descriptor)
//obj:目标对象,prop:需要新增,或者修改的属性名,descriptor:定义的特性
Object.defineProperty(demo,'name',{
set(value){
console.log('重新设为:'+value)
name=value
},
get(){
return name
}
})
demo.name='李四'
console.log(demo.name)//李四
- Vue3.x是通过proxy(代理)实现的数据的双向绑定
- 跟Object.defineProperty一样也可以给对象的属性添加两个方法get&set
let pObj = new Proxy(target, handler);
// `target` 代表需要添加代理的对象
// `handler` 用来自定义对象中的操作
- 区别:
- Object.defineProperty一次性只能给对象的一个属性添加get&set方法
- Proxy:一次性给对象所有属性都设置get&set方法用法:
- 与Object.defineProperty不同 的是Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。 因为proxy是对整个对象进行代理,所以可以监听对象某个属性值的变化,还可以监听对象属性的新增和删除,而且还可以监听数组。
- proxy是对整个对象进行代理,不关心对象里面具体有什么属性,而defineProperty只能响应首次渲染时候的属性,但是proxy的兼容性不好,IE无法兼容,vue2是兼容到了IE8,但是vue3使用了proxy说明放弃了对IE的兼容考虑。
如何实现data里面的数据不想做响应式
1、数据放在vue实例外(vue template中访问不到数据)
2、created, mounted钩子函数中定义(注意data中不要声明该变量名)
3、自定义Options
4、Object.freeze()
如何将获取data中某一个数据的初始状态
data() {
return {
num: 10
},
mounted() {
this.num = 1000
},
methods: {
countNum() {
// 可以通过this.$options.data().keyname来获取初始值
// 计算出num增加了多少
console.log(1000 - this.$options.data().num)
}
}
vue给对象新增属性页面不会动态更新
- 原因:vue2使用
Object.defineProperty实现数据响应式- 组件初始化时,对data中的obj进行递归遍历,对obj的每一个属性进行劫持,添加set,get方法。我们后来新加的newProperty属性,并没有通过Object.defineProperty设置成响应式数据,所以修改后不会视图更新
- 解决:
- 1、如果为对象添加少量的新属性,可以直接采用Vue.set()
- 2、如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
- 3、如果你需要进行强制刷新时,可采取$forceUpdate() (不建议)
注意:
vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式
vue组件传值方法
1、父向子传值使用props;
2、子向父传值使用“attrs/$listeners”方法进行跨级传值(父组件向深层的子组件传值和接收)。
也可以总结为:
- 父子通信:父向子传递数据是通过 props,子向父是通过 parent / ref 也可以访问组件实例;provide / inject ;
- 兄弟通信: EventBus;Vuex;
- 跨级通信: EventBus;Vuex;provide / inject ;listeners;
vue路由2种方式:hash和history
- hash模式: 在浏览器中符号“#”,#以及#后面的字符称之为hash,用 window.location.hash 读取。特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面,而是会
触发 hashchange 事件。- 只需要前端配置路由表, 不需要后端的参与
- 兼容性好, 浏览器都能支持
- hash值改变不会向后端发送请求, 完全属于前端路由
- hash值前面需要加#, 不符合url规范,也不美观(缺点)
- history模式: history采用HTML5的新特性;且提供了两个新方法: pushState(), replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
- 符合url地址规范, 不需要#, 使用起来比较美观(优点)
- 兼容性不如 hash,且需要服务端支持重定向,否则一刷新页面就404了
- 兼容性比较差, 利用了 HTML5 History对象中新增的 pushState() 和 replaceState() 方法,需要特定浏览器的支持.
传统路由跟前端路由不同
传统的路由指的是:当用户访问一个url时,对应的服务器会接收这个请求,然后解析url中的路径,从而执行对应的处理逻辑。这样就完成了一次路由分发。
而前端路由不涉及服务器,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换。
vue路由跳转方式
1、用<router-link :to="{..}">语句;
2、用this.$router.push()语句;
3、用this.$router.replace()语句;
4、用this.$router.go(n)语句;
Vue 路由钩子函数
全局前置守卫 router.beforeEach
全局解析守卫 router.beforeResolve
全局后置钩子 router.afterEach
路由独享的守卫 beforeEnter
组件内的守卫 beforeRouteEnter beforeRouteUpdate beforeRouteLeave
- 全局守卫
- router.beforeEach 全局前置守卫 进入路由之前
- router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
- router.afterEach 全局后置钩子 进入路由之后
// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => {
next();
});
router.beforeResolve((to, from, next) => {
next();
});
router.afterEach((to, from) => {
console.log('afterEach 全局后置钩子');
});
to,from,next 这三个参数:
to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。
next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。
-
next() 进入该路由。
-
next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。
-
next 跳转新路由,当前的导航被中断,重新开始一个新的导航。
我们可以这样跳转:next('path地址')或者next({path:''})或者next({name:''})
且允许设置诸如 replace: true、name: 'home' 之类的选项
以及你用在router-link或router.push的对象选项。
- 路由独享守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖
// ...
}
}
]
})
- 路由组件内的守卫:
- beforeRouteEnter 进入路由前
- beforeRouteUpdate (2.2) 路由复用同一个组件时
- beforeRouteLeave 离开当前路由时
beforeRouteEnter (to, from, next) {
// 在路由独享守卫后调用 不!能!获取组件实例 `this`,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用 可以访问组件实例 `this`
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用,可以访问组件实例 `this`
}
路由配置项常用的属性及作用
- path :
路由Hash地址 - component :
互相对应的组件 - name:
路由命名 - children:
子路由 - props:
props自定义传递参数 - redirect :
重定向
vue-router的动态路由
- 动态路由指的就是path路径上传智, 前提需要路由规则了提前配置/path/:key名, 可以写多个用/隔开;
- 获取使用$route.params.key名来提取对应用路径传过来的值
$route和$router的区别
- $route是路由信息对象,包括‘path,hash,query,fullPath,matched,name’等路由信息参数;
- $router是路由实例对象,包括了路由的跳转方法,钩子函数等
vue-router是什么?有哪些组件?
- Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
<router-link>和<router-view>和<keep-alive>
active-class 是哪个组件的属性
active-class是router-link终端属性,用来做选中样式的切换,当router-link标签被点击时将会应用这个样式
vue-router 传参
- Params
- 只能使用name,不能使用path
- 参数不会显示在路径上
- 浏览器强制刷新参数会被清空,
// 传递参数
this.$router.push({
name: Home,
params: {
number: 1 ,
code: '999'
}
})
// 接收参数
const p = this.$route.params
- Query:
- 参数会显示在路径上,刷新不会被清空
- name 可以使用path路径
// 传递参数
this.$router.push({
name: Home,
query: {
number: 1 ,
code: ``'999'
}
})
// 接收参数
const q = this.$route.query
$nextTick原理及运用
- 原理
- Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作,这样可以提高渲染效率。
- 如果要获取更新后的DOM元素,可以使用vue内置的$nextTick方法,参数是一个函数。它的作用类似setTimeout,进行执行异步的操作。
this.$nextTick()的使用场景- 官方用法:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
- 在Vue生命周期的
created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。 - 在数据变化后要执行的某个操作,当你设置 vm.someData = ‘new value’,DOM并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。
为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数在 DOM 更新完成后就会调用。 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
compute和watch区别和应用场景
- computed 计算属性具有缓存,计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值。 只要计算属性的依赖没有改变,那么调用它就会直接返回之前的缓存。 同时computed对于其中变量的依赖时多个 的时候,只要其中一个发生了变化都会触发这个函数
- 应用场景:当一个变量的值受多个变量的值影响
//计算属性中的属性需要在data中定义,而且必须有return
data(){
return{
firstname:"张",
lastname:"三"
}
}
computehd(){
fullname(){
return this.firstname+this.lastname
}
}
- watch watch的依赖是单个的,它每次只可以对一个变量进行监控,并且区别于computed属性,监听器watch可以是异步的而computed则不行
- 应用场景:当一个变量的值影响着多个变量的值
//监听器watch中的值需要在data中定义,且函数有参数,newval和oldval
data: {
firstName: '张',
lastName: '三',
fullName: '张三r'
},
watch: {
firstName: function (oval,nval) {
this.fullName = nval + ' ' + this.lastName
},
lastName: function (oval,nval) {
this.fullName = this.firstName + ' ' + nval
},
immediate: true,// 代表在wacth里声明了firstName之后立即先去执行其函数方法
deep: true //深度监听
}
Vuex
- Vuex是: Vuex是一个专为 Vue.js 应用程序开发的状态管理模式,它由五部分组成:
- state: 基本数据
- actions:可以包含异步操作
- mutations: 唯一可以修改state数据的场所
- getters: 类似于vue组件中的计算属性,对state数据进行计算(会被缓存)
- modules:模块化管理store(仓库),每个模块拥有自己的 state、mutation、action、getter
- 高级用法 ----- 数据持久化
- 问题:存储在vuex中的状态,刷新页面,会丢失。
- 最简单的做法就是利用插件 vuex-persistedState。
- 为了解决刷新页面数据丢失,才有了数据持久化。
v-show 与 v-if 的区别
-
v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏;
-
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果;
- 使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。
-
优先级 v-for优先级比v-if高(vue2.x中)
-
注意事项 不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断) 正确的做法应该是再v-for的外面新增一个模板标签template,在template上使用v-if也能结合filters或者是computed属性对数据进行加工,避免v-if判断,更好的渲染
-
注意: 在vue2.x中,v-for的优先级会比v-if的优先高,但在vue3.x中这一判断优先级被换了过来,在3.x的环境下v-if优先级更高,除此之外v-for和v-if在同一标签中使用也会报错,解决办法可以在外面套一个template标签,或者使用computed来对数组进行过滤然后渲染
vue中key的原理及其必要性
- vue中key作用: key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点 key是每一个节点的唯一标识
- 必要性: 当我们对数据进行更新的时候,譬如在数组中插入、移除数据时,设置的key值能让vue底层高效的对新旧vnode进行diff,然后将比对出的结果用来更新真实的DOM
共享组件将不会重新渲染问题
我们有时候开发中会把多个路由解析为同一个Vue组件。问题是,Vue默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化,此时我们需要传递key来区分,达到刷新的目的
const routes = [
{
path: "/a",
component: MyComponent
},
{
path: "/b",
component: MyComponent
},
];
<template>
<router-view :key="$route.path"></router-view>
</template>
mixins
- mixins是一种分发Vue组件中可复用功能的一种灵活方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
mixins是一个JavaScript对象,可以包含组件中的任意选项,比如Vue实例中生命周期的各个钩子函数,也可以是data、components、methods或directives等
- 1、在组件A对混入的数据做出更改后组件B获取到的仍是混入初始设置的数据,组件间操作互不污染。
- 2、值为对象的如methods,components等,选项会被合并,组件会覆盖混入对象的方法。 比如混入对象里有个方法A,组件里也有方法A,这时候在组件里调用的话,执行的是组件里的A方法。
- 3、created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用, 同一个钩子函数里,会先执行混入对象的东西,再执行本组件的。
- 4、在mixins里面包含异步请求函数的时候,通过直接调用异步函数获取返回数据
//mixin文件
export const myMixin={
data(){
return{
msg:1
}
},
created(){
console.log('myMixin')
},
methods:{
Fn(){
console.log('myMixin')
}
}
}
//引入
<template>
<div>运用mixin的组件</div>
</template>
<script>
import {myMixin} from'目标文件路径'
export default{
mixins:[myMixin]
}
</script>
keep-alive
- keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive是一个抽象组件,它自身不会渲染一个DOM元素,也不会出现在父组件中。
- 作用: 在组件切换过程中 把切换出去的组件保留在[内存]中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
- 原理: 在 created钩子函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode实例进行渲染。
- 参数(Props):
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
- max - 数字。最多可以缓存多少组件实例
- 对生命周期的变化
1.activated- 在 keep-alive 组件激活时调用
- 该钩子函数在服务器端渲染期间不被调用 \
- 在 keep-alive 组件停用时调用
- 该钩子在服务器端渲染期间不被调用
- 被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
- 使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。
- 注意: 只有组件被 keep-alive 包裹时,这两个生命周期函数才会被调用,如果作为正常组件使用,是不会被调用的,以及在 2.1.0 版本之后,使用 exclude 排除之后,就算被包裹在 keep-alive 中,这两个钩子函数依然不会被调用!另外,在服务端渲染时,此钩子函数也不会被调用。
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
/*匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称
(父组件 components 选项的键值),匿名组件不能被匹配*/
SSR
- 什么是ssr: Server-Side Rendering 我们称其为SSR,意为服务端渲染,展开说就是通过服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
- ssr作用:
- seo:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务相关联的HTML,有利于seo
- 首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端)
- 缺点:
- 项目复杂度高
- 需要库的支持性,代码兼容
- 服务器负载变大,相对于前后端分离务器只需要提供静态资源来说,服务器负载更大
Composition API
Vue2与Vue3 最大的区别 — Vue2使用选项类型API(Options API)对比Vue3合成型API(Composition API)
什么是hook?什么是自定义hook函数
- 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
- 类似于vue2.x中的mixin。
- 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
Composition API 和 React Hook区别
从 React Hook 从实现的角度来看,React Hook 是基于 useState 的调用顺序来确定下一个 re 渲染时间状态从哪个 useState 开始,所以有以下几个限制
- 不在循环中、条件、调用嵌套函数 Hook
- 你必须确保它总是在你这边 React Top level 调用函数 Hook
- 使用效果、使用备忘录 依赖关系必须手动确定
和 Composition API 是基于 Vue 的响应系统,和 React Hook 相比
- 在设置函数中,一个组件实例只调用一次设置,而 React Hook 每次重新渲染时,都需要调用 Hook,给 React 带来的 GC 比 Vue 更大的压力,性能也相对 Vue 对我来说也比较慢
- Compositon API 你不必担心调用的顺序,它也可以在循环中、条件、在嵌套函数中使用
- 响应式系统自动实现依赖关系收集,而且组件的性能优化是由 Vue 内部完成的,而 React Hook 的依赖关系需要手动传递,并且依赖关系的顺序必须得到保证,让路 useEffect、useMemo 等等,否则组件性能会因为依赖关系不正确而下降。
虽然Compoliton API看起来像React Hook来使用,但它的设计思路也是React Hook的参考
pinia
-
pinia是如何做到类型安全,摒弃魔法字符串?
因为pinia将state、getter、action都绑定到store的属性,通过composition可以获取到对应的store实例。 -
为什么pinia不需要mutation?
在vuex中使用mutation记录数据的更新,action进行异步操作,而pinia在action执行完成后会自动发布给订阅者,所以就不需要mutation。 -
为什么$patch可以批量更新?
在更新期间通过isListening标识暂停触发回调函数。
pinia和vuex的区别?
- (1)它没有mutation,他只有state,getters,action【同步、异步】使用他来修改state数据
- (2)他默认也是存入内存中,如果需要使用本地存储,在配置上比vuex麻烦一点
- (3)语法上比vuex更容易理解和使用,灵活。
- (4)pinia没有modules配置,每一个独立的仓库都是definStore生成出来的
- (5)state是一个对象返回一个对象和组件的data是一样的语法
vite为啥比webpack快?
-
1、webpack是先打包在启动开发服务器,请求服务器时给予打包的结果
-
2、vite是直接启动开发服务器,需要哪个模块就对哪个模块进行实时编译
-
3、现代浏览器中exmodules会主动获取所需文件,vite就是利用这一个特性将开发文件变成浏览器所需的执行文件,而不是像webpack一样交给浏览器是打包后的文件
-
4、vite不需要打包,因此不需要模块依赖、编译,所以启动速度快,浏览器需要哪个模块在对哪个模块进行按需动态编译
-
5、在内容改变的时候,vite会直接去请求该模块,而webpack会把该模块相关的依赖和本身再重新执行一遍