一、vue3快速上手
初始须知:要使用vue-cli来创建项目必须要保证vue-cli的版本在4.5.0以上。
vue3相比较vue2明显的变化:速度更快,打包之后的体积更小。
二、常用Composition API
1.setup(是一个函数)
setup里面能写data,methods,computed,watch,生命周期钩子,等等东西,全都往setup里面写
上面这句话我们这么理解,以前我们vue2是这样写的
export default {
name: 'App',
components: {},
data(){
return {}
},
methods: {},
watch: {},
//生命周期
created(){},
//还有若干个生命周期...
}
现在我们vue3这样写
export default {
name: 'App',
components: {
HelloWorld
},
//暂时不考虑响应式,只是测试一下setup
setup(){
//数据
let name = '张三'
let age = 18
//方法
function sayHello(){
alert(`我叫${name}今年${age}岁了`)
}
return {
name,
age,
sayHello
}
}
}
2.ref函(函数)
它的作用是将一个数据变成响应式数据,其具体的使用方法如下:
import {ref} from 'vue'
export default {
setup(){
let name = ref('张三')
let obj = ref({
type = '职位',
salary = '10k'
})
name.value = '李四'
obj.value.type = '前端开发工程师'
}
}
2.1 unref
如果参数是一个ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。
import { unref } from "vue";
let msgref = ref('你好')
console.log(unref(msgref)) // 你好
let msg = '你好'
console.log(unref(msg)) // 你好
3.reactive(函数)
它的作用是对于非基本类型的数据实现响应式,使用方法如下
import {ref,reactive} form 'vue'
export default {
setup(){
let obj = reactive({
type: '前端开发工程师',
salary: '30k'
})
obj.type = '后端开发工程师'
let hobby = reactive(['抽烟','喝酒','烫头'])
hobby[0] = '学习'
}
}
4.vue2存在的一些问题
vue2bug1
如下图,vue2中的对象新增一个sex属性检测不到它的变化,这是vue2的一个bug
data(){
return {
person:{
name: '张三',
age: 18
}
}
methods:{
addSex(){
//能新增成功,但是界面不更新,原因是它不是响应式的
//console.log(this.person.sex)
//this.person.sex = '女'
//console.log(this.person.sex)
//新增成功,且界面更新,是响应式的
this.$set(this.person,'sex','女')
//Vue.set(this.person,'sex','女')
}
}
vue2bug2
vue2删除对象属性界面监测不到bug
data(){
return {
person:{
name: '张三',
age: 18
}
}
}
methods:{
deleteName(){
//能删除成功但是界面不响应
//console.log(this.person.name)
//delete this.person.name
//console.log(this.person.name)
//删除成功,且界面响应,是响应式的
this.$delete(this.person,'name')
}
}
5.vue2响应式原理
下面例子触发方式:控制台p.name = '李四'
let person = {
name: '张三',
age: 18
}
let p = {}
Object.defineProperty(p,'name',{
configurable: true,
get() {//有人读取name时调用
return person.name
},
set(value) {//有人修改name时调用
console.log('有人修改了name属性'
person.name = value
}
})
6.vue3响应式原理
下面例子触发方式:控制台p.name = '李四'
let person = {
name: '张三',
age: 18
}
const p = new Proxy(person,{
//有人读取p身上的某个属性时调用
get(target, propName){
console.log(`有人修改了p身上的${propName}属性`)
//return target[propName]
return Reflect.get(target,propName)
},
//有人修改p的某个属性,或给p追加属性时调用
set(target, propName, value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了`)
//target[propName] = value
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target, propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了`)
return Reflect.deleteProperty(target,propName)
}
})
7.setup的props,content。
export default {
//在vue3中使用自定义事件需要声明
props: ['msg','school'],
emits: ['hello'],
setup(props, context){
//props就是我们vue2中的props
//context里面有一项是emit其意思其实跟vue2的有一句是一样的
this.$emit('test',value)
context.emit('test',value)
//context.attrs
//context.emit
//context.slot
}
}
8.vue3中computed
import {computed} from 'vue'
setup() {
...
//计算属性-简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性-完整
//vue3中的computed属性必须要有返回值
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
person.fullName = fullName
}
9.vue3中watch监听reactive数据
情景一:监听ref定义的基本数据类型
import {watch} from 'vue'
export default {
setup(){
//监听单个的值
watch(sum,(newval,oldval)=>{
console.log(newval,oldval)
})
//立即执行监听
watch(sum,(newval,oldval)=>{
console.log(newval,oldval)
},{immediate: true,deep: true})
}
}
情景二:监听ref定义的复杂数据类型
setup(){
//监听多个值
watch([sum,msg],(newval,oldval)=>{
//[],[]
console.log(newval,oldval)
})
}
情景三:监听reactive定义的一个响应式数据的全部属性
注意1:此处无法正确的获取oldValue 注意2:强制开启了深度监听(deep配置无效)
let person = reactive({
name: '张三',
age: 18
})
setup(){
//监听多个值
watch(person,(newval,oldval)=>{
console.log('person变化了',newval,oldval)
})
}
情景四:监听reactive定义的复杂数据类型的某个数据
let person = reactive({
name: '张三',
age: 18
})
setup(){
//监听多个值
watch(()=>person.age,(newval,oldval)=>{
console.log('person的age变化了',newval,oldval)
})
}
情景五:监听reactive定义的某些响应式数据
setup(){
watch([()=>person.name,()=>person.age],(newval,oldval)=>{
console.log('person的age或者name变化了',newval,oldval)
})
}
特殊情况
setup(){
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
watch(()=>person.job, (newval,oldval)=>{
console.log('person的age或者name变化了',newval,oldval)
},{deep: true})
//此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效,不配置监听不到
}
10.vue3中watch监听ref数据
import {watch,ref} from 'vue'
export default {
let sum = ref(0)
let person = ref({
name: '张三',
age: 18,
job:{
j1: {
salary:20
}
}
})
setup(){
watch(sum,(newValue, oldValue)=>{
console.log('sum的值变化了',newValue, oldValue)
})
watch(person,(newValue, oldValue)=>{
console.log('person的值变化了',newValue, oldValue)
},{deep:true})
}
}
11.watchEffect函数
watchEffect不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
import {watchEffect} from 'vue'
export default {
setup(){
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
}
}
12.vue3里面的生命周期
有两个生命周期发生了改变
- beforeDestroy ===> beforeUnmount
- destroyed ===> unmounted
12.1 api形式的生命周期钩子,与vue2中钩子的对应关系如下
- beforeCreate ===> setup()
- created ===> setup()
- beforeMount ===> onBeforeMount
- mounted ===> onMounted
- beforeUpdate ===> onBeforeUpdate
- updated ===> onUpdated
- beforeUnmount ===> onBeforeUnmount
- unmounted ===> onUnmounted
注意:组合式生命周期比配置项生命周期要快一点。
13.自定义hook函数
什么是hook?--本质是一个函数,把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。 自定义hook的优势:复用代码,让setup中的逻辑更清晰易懂
example: hooks文件夹下usePoint.js文件
import {reactive, onMounted, onBeforeUnmount} from 'vue'
export default function() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX, event.pageY)
}
onMounted(()=>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
另一个文件引入使用:
import usePoint from '../hook/usePoint'
export default {
setup(){
const point = usePoint()
return {point}
}
}
14.toRef,toRefs
- 作用:创建一个ref对象,其value值指向另一个对象中的某个属性。
- 应用:要将响应式对象中的某个属性单独提供给外部使用时。
- 拓展:toRefs跟toRef功能一致,但可以批量创建多个ref对象。
example:
import {reactive, toRef, toRefs}
setup(){
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
return {
person,
//name: toRef(person, 'name'),
//age: toRef(person, 'age'),
//salary: toRef(person.job.j1, 'salary'),
...toRefs(person)//name,age,job
}
}
15.nextTick
import { nextTick } from 'vue';
nextTick(() => {
//这里写逻辑
})
JS合集
1.Promise
function getTreeselect() {
return new Promise((resolve, reject) => {
listMenuOptions().then(res => {
resolve(res.data)
}).catch(()=>{
reject()
})
})
}
getTreeselect().then(()=>{})
async created() {
let res = await this.getTreeselect()
console.log('getTreeselect回来的结果', res)
}
2.try{}catch{}
try {
this.getBusinessFn().then(res => {
console.log('getBusinessFn回来的结果', res)
}) // 从事业务
} catch (exception) {
console.log(exception)
}
3.throw new Error()
4.在控制台抛出错误
throw new Error(`跑错`);
5.callback
function A(callback){
let a = 5
console.log('A')
if (callback) callback(a);
}
function B(b){
console.log('B')
console.log('B中打印的a为:',b)
}
A(B)
//A B B中打印的a为: 5
6.实现定隔多久执行几次定时器然后清除定时器
let second = 3;
const timer = setInterval(() => {
second--;
if (second) {
toast.message = `倒计时 ${second} 秒`;
} else {
clearInterval(timer);
}
}, 1000);
三、其Composition API
1.shallowReactive与shallowRef
- shallowReactive:只处理最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本类型的响应式,不进行对象类型的响应式处理
- 什么时候使用?
- 如果有一个对象数据,结构比较深,但变化时只是外层属性的变化 ===>shoallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
example:
import {reactive, shallowReactive, shallowRef}
setup(){
//只考虑第一层数据的响应式
let person = shallowReactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//基本类型数据与ref效果相同,但是引用类型数据就不一样了
//let x = shallowRef(0)
let x = shallowRef({
y: 0
})
//替换,不能修改,只能替换
//<button @click="x = {y:888}" />
}
2.readonly, shallowReadonly
这种使用场景可以是从别人那里拿过来这么一个数据,然后不能改,先readonly然后再开始用
import {ref, reactive, toRefs, readonly, shallowReadonly} from 'vue'
export default {
setup(){
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//person = readonly(person)
//这个对象里面的所有数据都只能只读
person = shallowReadonly(person)
//只是name,age不能改,但是可以改深层次的数据job里面的数据
//sum = readonly(sum)
sum = shallowReadonly
return {
sum,
...toRefs(person)
}
}
}
3.toRaw与markRaw
- toRaw:
- 作用:将一个由reactive生成的响应式对象转为普通对象。
- 使用场景:用于读取响应式对象的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
1.有些值不应该设置为响应式的,例如复杂的第三方类库等。 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
toRow只能处理reactive制造的响应式对象
import {ref, reactive, toRefs, toRaw, markRaw} from 'vue'
export default {
setup(){
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
function showRawPerson() {
const p = toRaw(person)
console.log(p)
//将proxy对象变为普通对象,不能响应
}
function addCar() {
let car = {name: '奔驰',price: 40}
person.car = markRaw(car)
//car添加到proxy对象上会变成响应式,经此处理挂载上去之后不会再变成响应式。
}
return {
sum,
...toRefs(person),
showRawPerson
}
}
}
4.customRef
- 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
- 实现防抖效果
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
<template/>
<script>
import {ref,customRef} from 'vue'
export default {
name: 'Demo',
setup(){
//let keyword = ref('hello')//使用vue准备好的内置ref
//自定义一个myRef
function myRef(value, delay){
let timer
//通过customRef去实现自定义
return customRef((track, trigger)=>{
return {
get(){
track()//告诉vue这个value值需要被'追踪'的
return value
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger()//告诉vue去更新界面
}, delay)
}
}
})
}
let keyword = myRef('hello')
return {
keyword
}
}
}
</script>
5.provide与inject
provide: 提供 inject:注入
-
作用:实现祖孙组件间通信。
-
套路:父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这些数据。
-
具体写法:
1.祖组件中:
import {provide, reactive} from 'vue'
setup(){
...
let car = reactive({name: '奔驰',price: '40万'})
provide('car',car)
...
}
2.孙组件中:
import {inject} from 'vue'
setup(props,context){
...
const car = inject('car')
return {car}
}
6.响应式数据的判断
- isRef: 检查一个值是否为一个ref对象。
- isReactive: 检查一个对象是否是由reactive创建的响应式代理。
- isReadonly: 检查一个对象是否是由readonly创建的只读代理。
- isProxy: 检查一个对象是否是由reactive或者readonly方法创建的代理。
example:
setup(){
let car = reactive({name: '奔驰',price: '40万'})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum))
console.log(isReactive(car))
console.log(isReadonly(car2))
console.log(isProxy(car))
console.log(isProxy(car2))
//需要注意这一点,car2虽然被限制只读了,但是它任然是一个Proxy对象。
}
四、Composition API的优势
Opitions API存在的问题
使用传统Options API时,新增或者修改一个需求,就需要分别在data,methods,computed里修改。
Composition API的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
五、新的组件
1.Fragment
- 在Vue2中:组件必须有一个根标签
- 在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减小内存占用
2.Teleport
teleport:传送(译)
- 什么是Teleport? ——Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
<template to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</template>
3.Suspense
- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
- 使用步骤:
- 异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))- 使用Suspense包裹组件,并配置好default与fallback
<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中.....</h3> </template> </Suspense> </div> </template>
六、其它
1.全局api的转义
- Vue3.0中对这些API做出了调整:
- 将全局的API,即:Vue.xxx调整到应用实例(app)上
2.其它改变
-
移除keyCode作为v-on的修饰符,同时也不再支持config.keyCodes
-
移除v-on.native修饰符
-
父组件中绑定事件
<my-component
v-on: close="handleComponentEvent"
v-on:click="handleNativeclickEvent"
/>
- 子组件中声明自定义事件
<script>
export default {
emits: ['close']
}
</script>
- 移除过滤器(filter) ...
七、父子组件通信
1.vue3中父子组件通信
//子组件接收参数
const props = defineProps({
pagination: {
type: Boolean,
required: false,
default: false
},
appUnitSelect: {
type: Number,
required: false,
default: 0
}
})
//子组件接收参数简化写法
const props = defineProps({
roleId: {
type: [Number, String]
}
});
//不带参数
@ok="handleQuery"
const emit = defineEmits(["ok"]);
emit("ok");
//父组件传递定义方法,带参数function getTabHeightFn(data: heightData)
@getTabHeightFn="getTabHeightFn"
//子组件接收方法
const emits = defineEmits(['getTabHeightFn'])
//子组件调用方法
emits('getTabHeightFn', { baseHeight, optionHeight });
2.vue2中父子组件通信
//子组件接收参数
props: {
childMsg: {
type: Array,
default: [0,0,0] //这样可以指定默认的值
}
}
//父组件传递事件
<child @on-result-change="mychangHandle" :msg="msg">
//子组件调用事件
this.$emit('resultChange','hehe');
3.vue2中任意组件间通信
如果2个组件不是父子组件那么如何通信呢?这时可以通过eventHub来实现通信.
所谓eventHub就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件.
使用一个空的 Vue 实例作为中央事件总线:
let Hub = new Vue(); //创建事件中心,注意Hub要放在全局
组件1触发:
<div @click="eve">
</div>
methods: { eve() { Hub.$emit('change','hehe'); //Hub触发事件 }}
组件2接收:
<div></div>
created() { Hub.$on('change', () => { //Hub接收事件 this.msg = 'hehe'; });}
4.vue3中页面缓存
参考:www.cnblogs.com/zhilu/p/138…
被keep-alive>裹的组件其会被缓存
例如:动态添加到UI上的一个li
没有被keep-alive>裹的话,ctivated不起作用的。
挂载后和更新前被调用的。
import { onActivated } from 'vue';
// 页面缓存
onActivated(() => {
getList();
})
5.vue3中获取ref元素
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as any;
proxy.$refs['pipe-echart']
scss常见合集
<div class="colour-display" :style="{'--color': scope.row.colour }"></div>
background-color: var(--color);