SPA(single-page application)优缺点
原文SPA的优缺点
优点
1、良好的交互体验(不用重新加载页面)
2、前后端分离,效率更高
3、减轻服务器压力(只需返回数据)
4、共用一套后端代码,只需改样式适配pc、移动
缺点
1、不利于SEO
2、初次加载耗时
SPA首屏优化
原文面试官:SPA(单页应用)首屏加载速度慢怎么解决?
1、动态加载路由(减小入口文件体积)component: () => import(‘./components/xxx.vue’)
2、静态资源本地缓存(http缓存)
3、UI框架按需加载
4、图片资源压缩(在线icon、精灵图)
5、开启GZip
6、SSR(服务端渲染)Nuxt.js
对vue的理解
1、MVVM架构,渐进式架构
2、组件化(低耦合,维护性高)
3、指令系统(v-if、v-model、v-for等)
vue2数据双向绑定原理
原文vue双向绑定原理及实现
1、vue双向绑定是通过数据劫持(Object.defineProperty)结合发布订阅模式的当时来实现的,也就是数据和视图同步。
2、Observer:核心就是Object.defineProperty()方法;
用Object.defineProperty()重写对象属性的get和set方法。来监听model数据的改变,通知Watcher
3、Compile:实现指令解析,订阅view数据变化,绑定好更新函数
3、每个组件实例都对应一个watcher实例,Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update
vue3数据双向绑定原理
原文面试官:Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
1、Object.defineProperty() 是通过遍历对象的属性进行数据劫持的,导致新增或删除对象属性时无法监听到对象的改变,只能用其他方法进行辅助( $ set、$ delete、重写数组方法)
2、ES6中的Proxy(12种拦截方法)可以直接劫持整个对象,并返回一个新对象,proxy可以监听数组的变化
vue3的性能提升
原文面试官:Vue3.0 性能提升主要是通过哪几方面体现的?
1、静态提升(diff算法优化)
vue3中对不参与更新的元素会做静态提升(给静态元素标记-1),只被创建一次,在渲染时复用;
2、响应式系统(proxy)
3、源码体积较小(移除某些API、tree-shaking机制打包时没用到的ref、reactive等不打包)
4、SSR优化(大量静态内容时会用createStaticVNode方法生成静态node,不要用创建对象再根据对象渲染)
vue2、vue3的区别
原文vue3.0 到底提升了什么?(值得收藏)
1、vue3完全支持typescript,提高项目的可维护性
2、Fragment:模板更简单。
vue3用到了一个虚拟的父级,
vue2只能有一个父级节点
3、 vue2用 new Vue(); vue3用createApp();
多个实例时,vue2中的vue实例共享一个Vue构造函数,无法做到完全隔离
createApp方法返回一个提供上下文的应用实例,应用实例挂载的整个组件共享同一个上下文
vue3的静态标记和静态提升(diff优化)
原文Vue3.0学习 - 第一节,diff算法、静态标记
静态标记:在vue3的render方法里会对动态数据做标记,更新虚拟DOM时只对静态标记对象进行比较
<div>{{msg}}</div>
_createVNode("p", null, _toDisplayString(_ctx.kkl), 1 /* TEXT */),
静态提升:在render方法外面将静态内容进行提取,后面可以直接复用。
vue2中template标签只能有一个节点
vue实例会将template标签下的节点作为入口(多个节点时无法确认入口)
vue2中 $set用法
this.$set(obj, key, value)
$nextTick()
等dom更新后再执行回调(v-if显示元素,等dom更新后再执行获取元素)
路由两种传参方式params、query
params: 和name匹配(刷新无数据)
this.$router.push({
name: '详情', //匹配route里面配置的name
params: {
detailFrom: 1 //传递的参数名和值
}
});
this.$route.params.detailFrom;
query: 和path匹配(对象要转json再传递)
this.$router.push({
path: '/order/chargedtail',
query: {
//将对象转换为json传递
detailFrom: JSON.stringify(row)
}
});
JSON.parse(this.$route.query.detailFrom);
动态组件Component
<component :is="currentView"></component>
this.currentView = {template: `<div>我是存档页</div>`}
keep-alive
组件用keep-alive时,组件存在activated()和deactivated两个钩子函数
keep-alive会将组件保留在内存中,以便下次使用
路由中配置
{
path:'ratings',
name: 'ratings',
component: Ratings,
meta: {
keepAlive: true // 需要缓存
}
}
页面中标签配置
<keep-alive>
<component></component>
</keep-alive>
slot(插槽)
父组件:
<template>
<div class="father">
<h3>这里是父组件</h3>
<child>
具名插槽
<div v-slot:header='scope' >
<span>{{scope.userName}}</span>
</div>
匿名插槽
<div v-slot='scope' >
<span>{{scope.userName}}</span>
</div>
</child>
</div>
</template>
子组件
<template>
// 具名插槽
<slot name="header"></slot>
// 匿名插槽
<slot></slot>
<template>
vue3的作用域插槽写法
<template #default="scope">
<span :title="scope.userName" > {{scope.userName}} </span>
</template>
computed和watch
computed: 计算结果并返回,只有当被计算的值发生改变时会触发;(日期变化,格式化返回)
每一个计算属性都会被缓存起来,当计算属性依赖的属性变更是重新计算当前技术属性的值。
watch:监听某个值的变化并做对应操作。(监听boolean显隐元素)
immediate: true //初始化执行一遍
deep:ture //监听对象
Diff算法
原文简单粗暴的React Diff算法将传统O(n^3)Diff算法的时间复杂度降为O(n)
老版算法0(n^3):
更新前的树,与更新后的树 遍历进行比较 O(n^2)
再进行树的编辑(插入、替换、删除) O(n^1)
vue:
认为不同类型的元素,不同层级的元素,大概率内容不会重复,就直接删除,添加新节点 O(n)
vue自定义过滤器
用法
<div>{{date | formatDate}}</div>
<div :id = "id | formatId"></div>
自定义写法:
//组件内部过滤器
//与methods同级
filters: {
formatDate: function(value) {
let date = '2022-'+value+'-02'
return date;
}
}
//全局过滤器
//在main.ts中写
Vue.filter("upperCase", function(value) {
if (!value) return "";
value = value.toString();
return value.toUpperCase(); // 小写转为大写
});
vue自定义指令
局部注册
//与data同级
directives: {
focus:{
inserted: function(el) {
el.focus();
}
}
}
全局注册
Vue.directive('focus',{
inserted: function(el){
el.focus();
}
}
)
vue2生命周期钩子函数
beforeCreate:不能用this调用data中的数据
created:调接口
beforeMount
mounted
beforeUpdate
updated
( activated deactivated //keep-alive缓存的组件)
beforeDestroy
destroyed
errorCaptured:报错处理(2.5.0新增)
vue3生命周期钩子函数
setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
onBeforeMount() : 组件挂载到节点上之前执行的函数;
onMounted() : 组件挂载完成后执行的函数;
onBeforeUpdate(): 组件更新之前执行的函数;
onUpdated(): 组件更新完成之后执行的函数;
onBeforeUnmount(): 组件卸载之前执行的函数;
onUnmounted(): 组件卸载完成后执行的函数;
onActivated(): 被包含在 中的组件,会多出两个生命周期钩子函数,被激活时执行;
onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
父子组件钩子函数执行顺序
子组件在父组件挂载(mounted)前加载完成
father:beforecreated、created、beforemount、
son:beforecreated、created、beforemount、mounted
father: mounted、xxxx
data需要return
防止数据污染
一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立拷贝。
vuex(全局状态管理)
原文Vuex的全面用法总结
1.state – 存放状态 store.state.xxx
2.getters – state的计算属性 store.getters.xxx
3.mutations – 更改状态的逻辑,同步操作 store.commit(’ funName ‘, param)
4.actions – 提交mutation,异步操作 store.dispath(’ funName ',param)
5.mudules – 将store模块化 modules: { a : moduleA, b : moduleB }
可以直接this.$store.state.xxx = xxx
但是不建议,
vuex直接修改state,异步直接用mutation修改
vuex是单向数据流,只能通过mutation进行修改。
如果直接修改不利于对state里的数据状态的跟踪(vue调试工具),
如果是异步的修改,在异步执行完之前别的地方修改,程序就会存在问题
vuex数据状态持久化
原文Vuex数据状态持久化
1、手动存储
state从storage里拿,mutations修改时改变storage里的值
2、利用vuex-persistedstate(原理也是利用storage存储)
vue的两种路由模式( hash 、history)
原文vue-router的两种模式及区别
hash:地址栏 URL 中携带 # 符号,但不包括在请求地址中;
history:利用了HTML5 History Interface中新增的pushState()和replaceState()方法;
history.go(-1);//后退一次
history.back(); //后退一次
history.forward(); //前进
路由导航守卫
1、全局守卫
前置守卫 router.beforeEach((to,from,next)=>{})
后置守卫 router.afterEach((to,from)=>{})
在main.ts中设置全局守卫,判断是否登录(next() 或 next(‘/login’))
2、组件内守卫(vm是应为data数据未加载完成)
beforeRouteEnter:(to,from,next)=>{
next(vm=>{
alert("hello" + vm.name);
})
}
beforeRouteLeave:(to,from,next)=>{}
3、路由独享守卫
beforeEnter:(to,from,next)=>{},用法与全局守卫一致。只是,将其写在路由(router)文件里,只在这个路由下起作用。
vue2兄弟传值
创建busEvent.js
import Vue from 'vue'
let VueEvent = new Vue()
export default VueEvent
发送数据
import bus from '../assets/busEvevt'
bus.$emit('infoMsg',this.info)
接收数据
bus.$on('infoMsg',(msg) => {
this.mssage = msg
})
vue3兄弟传值
npm install mitt,
main.js挂载到全局
app.config.globalProperties.$mitt = new mitt();
发送数据
proxy.$mitt.emit('mittFn', money.value)
接收数据
proxy.$mitt.on('mittFn', (res) => {
console.log(res);
})
vue3
代码层面主要是Composition -api
setup是组合式(Composition)编程的入口,setup接受两个值:props 和 context
props(父组件向子组件传值)
context 上下文(context.emit(‘xxx’);//调用父方法)
父组件 helloworld
<template>
<button @click="btnClick">调用子组件方法</button>
<div class="hello">
<helloSon @myFun="myFun" ref="helloSonRef" :toSon="fatherInfo"></helloSon>
</div>
</template>
<script>
import { reactive } from '@vue/reactivity';
import helloSon from "./helloSon.vue"
export default {
name: "HelloWorld",
components: {helloSon},
setup(props,context) {
const fatherInfo = reactive({name:'son',age:'20'})
const myFun = (val)=> {
console.log(val);
}
//注册子ref
let helloSonRef = ref<any>();
//调用子方法
const btnClick = () => {
helloSonRef.value.sonFun();
}
return {fatherInfo, myFun };
},
mounted() {},
};
</script>
子组件 helloSon
<template>
<div>
<button @click="getFather">son</button>
</div>
</template>
<script>
export default {
name: 'Vue3DemoHelloson',
// props: ['name'],
props: {
toSon: {
type: String,
default: '12'
}
},
setup(props,context) {
console.log(props.toSon);
//调用父方法
const getFather = ()=> {
context.emit("myFun",'hello')
}
const sonFun = () => {
console.log('我是子');
}
return { getFather ,sonFun };
}
};
</script>
1、子组件重复使用时的Ref
//html
<child-component
:ref="(refItem) => childRef[index] = refItem"
v-for="(item,index) in 12">
</child-component>
//ts
const childRef = ref<any>([]);
return {childRef}
VUE3 watch 和watchEffect
watch
watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
可监听:
一个函数,返回一个值
一个 ref
一个响应式对象
…或是由以上类型的值组成的数组
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
副作用清除:watchEffect的参数是一个函数,这个函数的参数也是一个函数,可用来清除副作用
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 取消之前未完成的请求
onCleanup(cancel)
data.value = await response
})
watchEffect返回一个停止监听函数,用于停止监听
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
区别
options中的flush配置:sync(dom更新前)post(dom更新后)
watch()
懒执行副作用;
更加明确是应该由哪个状态触发侦听器重新执行;
可以访问所侦听状态的前一个值和当前值。
watchEffect()
它会自动追踪函数内部使用的响应式数据
由于watchEffect的自动追踪机制,它通常用于处理副作用,例如执行异步操作或者更新UI。
watch(
() => state.count,
(newVal, oldVal) => {
console.log(`count 变化,新值为 ${newVal},旧值为 ${oldVal}`);
}
);
// 使用 watchEffect 响应式地追踪 count
watchEffect(() => {
state.double = state.count * 2;
});