Vue3
组合式API
1.钩子函数steup
//简写使用setup
<script setup>
</script>
<template>
</template>
2.响应式API
//ref
<script setup>
import { ref } from 'vue'
const state = ref(0)
function increment() {
state.value++
}
</script>
<template>
<button @click="increment">
{{ state }}
</button>
</template>
//reactive函数
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
3.计算属性API
<script setup>
import { computed,reactive } from 'vue'
const Person=reactive({X:'张',M:'三'})
Person.AXM=computed({
get(){
return Person.X+'-'+Person.M
},
set(value){
const arr=value.split('-')
Person.X=arr[0]
Person.M=arr[1]
}
})
</script>
<template>
姓:<input v-model="Person.X"><br>
名:<input v-model="Person.M"><br>
双向响应:<input v-model="Person.AXM">
</template>
4.监听属性API
1.监听整个对象
<!-- // 监听整个对象,由于是浅拷贝,他们新旧指向的是通一个对象 -->
<script setup>
import {reactive,watch} from 'vue'
const Person=reactive({name:'张三',age:18, job:{salary:20}})
watch(Person,(newVal,oldVal)=>{
console.log('用户信息发生了变化',newVal,oldVal);
})
</script>
<template>
<h2>年龄:{{Person.age}}</h2>
<button @click="Person.age++">+1</button>
</template>
2.监听对象中单个属性
<!-- 监听对象中单个属性,监听单个属性可以检测到新旧值 -->
<script setup>
import {reactive,watch} from 'vue'
const Person=reactive({name:'张三',age:18, job:{salary:20}})
watch(()=>Person.age,(newVal,oldVal)=>{
console.log('用户年龄发生了变化',newVal,oldVal);
})
</script>
<template>
<h2>年龄:{{Person.age}}</h2>
<button @click="Person.age++">+1</button>
</template>
3.监听多个对象
<!-- 监听对象中多个个属性,监听单个属性可以检测到新旧值 -->
<script setup>
import {reactive,watch} from 'vue'
const Person=reactive({name:'张三',age:18, job:{salary:20}})
watch([()=>Person.name,()=>Person.age],(newValue,oldValue)=>{
console.log('person.name或者person.age的值变化了',newValue,oldValue);
})
</script>
<template>
<h2>姓名:{{Person.name}}</h2>
<button @click="Person.name+='~'">修改</button>
<h2>年龄:{{Person.age}}</h2>
<button @click="Person.age++">+1</button>
</template>
4.监听对象中对象(深度监听)
<!-- 监听对象中对象,必须开启深度监听,一般情况不监听对象 -->
<script setup>
import {reactive,watch} from 'vue'
const Person=reactive({name:'张三',age:18, job:{salary:20}})
watch(()=>Person.job,(newValue,oldValue)=>{
console.log('person.job的值变化了',newValue,oldValue);
},{
deep:true
})
</script>
<template>
<h2>薪资:{{Person.job.salary}}K</h2>
<button @click="Person.job.salary++">+1</button>
</template>
5.高级监听API
1.基本使用(默认执行一次)
<!-- watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 -->
<script setup>
import {reactive,watchEffect} from 'vue'
const Person=reactive({
name:'张三'
})
watchEffect(()=>{
Person.name
console.log('姓名发送了变化');
})
</script>
<template>
<h2>姓名:{{Person.name}}</h2>
<button @click="Person.name+='~'">修改</button>
</template>
2.监听御前处理oninvalidate参数
<script setup lang="ts">
import { reactive, watchEffect } from "vue";
const Person = reactive({
name: "张三",
});
watchEffect((oninvalidate) => {
oninvalidate(() => {
console.log("before");
});
Person.name;
console.log("姓名发送了变化");
});
</script>
<template>
<h2>姓名:{{ Person.name }}</h2>
<button @click="Person.name += '~'">修改</button>
</template>
3.停止监听
<script setup lang="ts">
import { reactive, watchEffect } from "vue";
const Person = reactive({
name: "张三",
});
const stop = watchEffect((oninvalidate) => {
oninvalidate(() => {
console.log("before");
});
Person.name;
console.log("姓名发送了变化");
</script>
<template>
<h2>姓名:{{ Person.name }}</h2>
<button @click="Person.name += '~'">修改</button>
<button @click="stop">停止</button>
</template>
6.响应式对象解构API
1.toRef函数
<script setup>
import {reactive,toRef} from 'vue'
const person=reactive({A:1,B:2})
const A=toRef(person,'A')
</script>
<template>
<h2>姓名:{{A}}</h2>
<button @click="person.A+='~'">修改</button>
</template>
2.toRefs
<script setup lang="ts">
import {reactive,toRefs} from 'vue'
const person=reactive({A:1,B:2})
const {A,B}=toRefs(person)
</script>
<template>
<h2>姓名:{{A}}</h2>
<button @click="A+=1">修改</button>
</template>
7.生命周期API
<script setup>
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref} from "vue";
onBeforeMount(()=>{
console.log('---挂载之前---');
})
onMounted(()=>{
console.log('---挂载---');
})
onBeforeUpdate(()=>{
console.log('---更新之前---');
})
onUpdated(()=>{
console.log('---更新---');
})
onBeforeUnmount(()=>{
console.log('---卸载之前---');
})
onUnmounted(()=>{
console.log('---卸载---');
})
</script>
8.ref获取dom
<template>
<div>
<div ref="box">我是div</div>
</div>
</template>
<script>
import { ref,onMounted } from "vue";
export default {
setup() {
let box = ref(null); //本质是reactive({value:null})
// 需要在生命周期获取
onMounted(()=>{
// 当界面挂载出来后就会自动执行
console.log(box.value);
})
//接受的是null,原因是setup执行时机比mounted早,dom还没形成
console.log(box.value);
return { box };
},
};
</script>
9.Hooks
(1)官方hooks
1.useAttrs()
<!-- 父组件 -->
<template>
<Acom a="456" title="789" />
</template>
<!-- 子组件 -->
<!-- 获取父组件传过来的全部参数 -->
<script setup lang="ts">
import { useAttrs } from 'vue'
let attr = useAttrs()
console.log(attr)
</script>
(2)自定hooks
1.自定义hooks转换图片
import { onMounted } from 'vue'
type Options = {
el: string
}
export default function (options: Options): Promise<{ baseUrl: string }> {
return new Promise(resolve => {
onMounted(() => {
const img: HTMLImageElement = document.querySelector(
options.el
) as HTMLImageElement
img.onload = () => {
resolve({
baseUrl: base64(img)
})
}
})
const base64 = (el: HTMLImageElement) => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = el.width
canvas.height = el.height
ctx?.drawImage(el, 0, 0, canvas.width, canvas.height)
return canvas.toDataURL('image/jpg')
}
})
}
2.使用hooks
<script setup lang="ts">
import BASE64 from './hooks'
BASE64({ el: '#img' }).then(resolve => {
console.log(resolve.baseUrl)
})
</script>
(3)第三方hooks
1.安装依赖yarn add @vueuse/core
2.简单使用
<script setup lang="ts">
import { ref } from 'vue'
import { useDraggable } from '@vueuse/core'
const el = ref<HTMLElement | null>(null)
// `style` will be a helper computed for `left: ?px; top: ?px;`
const { x, y, style } = useDraggable(el, {
initialValue: { x: 40, y: 40 }
})
</script>
<template>
<div ref="el" :style="style" style="position: fixed">
Drag me! I am at {{ x }}, {{ y }}
</div>
</template>
组件间通讯
1.props父传子
<!--父组件-->
<script setup >
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<HelloWorld msg="1"/>
</template>
<!--子组件-->
<script setup>
// const props=defineProps(['msg'])
const props=defineProps({msg:String})
console.log(props.msg)
</script>
2.emit子传父
<!--父组件-->
<script setup >
import HelloWorld from './components/HelloWorld.vue'
const getuser=(a)=>{
console.log(a)
}
</script>
<template>
<HelloWorld @getuser="getuser"/>
</template>
<!--子组件-->
<script setup lang="ts">
const emit = defineEmits(['getuser'])
function buttonClick() {
emit('getuser',1)
}
</script>
<template>
<button @click="buttonClick">传输</button>
</template>
3.自定义事件事件校检
<script setup>
const emit = defineEmits({
// 没有校验
click: null,
// 校验 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
</script>
插槽通讯
1.匿名插槽
<!-- 子组件 -->
<template>
<!-- slot插槽占位 -->
<slot></slot>
</template>
<!-- 父组件 -->
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
<template>
<HelloWorld>
插槽传递
</HelloWorld>
</template>
2.具名插槽
<!-- 父组件 -->
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
<template>
<HelloWorld>
<!-- v-slot:简写# -->
<template v-slot:btn>
<button>具名插槽</button>
</template>
</HelloWorld>
</template>
<!-- 子组件 -->
<template>
<!-- slot插槽占位 -->
<slot name="btn"></slot>
</template>
3.作用域插槽
理解:数据在子组件的自身,但根据数据生成的结构需要父组件决定。
<!-- 父组件 -->
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
const person=[{name:'小明',age:18},{name:'小红',age:20}]
</script>
<template>
// 父组件将信息传递给子组件
<HelloWorld :person="person">
// 子组件接收父组件的插槽中传的值
<template #tab="scope">
<tr v-for="(item,index) in scope.person" :key="index">
<th>{{item.name}}</th>
<th>{{item.age}}</th>
<th><button >编辑</button></th>
</tr>
</template>
</HelloWorld>
</template>
<script setup lang="ts">
const props=defineProps<{person:{name:string,age:number}[]}>()
</script>
<!-- 子组件 -->
<template>
<table border="1">
<tr>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
<!-- 作用域插槽命名 -->
// 向作用插槽中传值
<slot name="tab" :person="props.person"></slot>
</table>
</template>
依赖注入
<!-- 依赖注入传的参可以在子组件中改变 -->
<!-- 父组件(祖先组件)-->
<template>
<div class="App">
<button>我是App</button>
<A></A>
</div>
</template>
<script setup lang="ts">
import { provide, ref } from 'vue'
import A from './components/Acom.vue'
let flag = ref<number>(1)
provide('flag', flag)
</script>
<!-- 子组件(后代组件)-->
<template>
<div>
我是B
<div>{{ flag }}</div>
<button @click="flag++">+1</button>
</div>
</template>
<script setup lang="ts">
import { inject, ref } from 'vue'
// 注入值,默认值(让其可以进行类型推断)
const flag = inject('flag', ref(1))
</script>
兄弟传参
(1)父组件当成一个桥梁
(2)发布订阅模式
Bus传递
type BusClass = {
emit: (name: string) => void
on: (name: string, callback: Function) => void
}
type PramsKey = string | number | symbol
type List = {
[key: PramsKey]: Array<Function>
}
class Bus implements BusClass {
list: List
constructor() {
this.list = {}
}
emit(name: string, ...args: Array<any>) {
const evnentName: Array<Function> = this.list[name]
evnentName.forEach(fn => {
fn.apply(this, args)
})
}
on(name: string, callback: Function) {
const fn: Array<Function> = this.list[name] || []
fn.push(callback)
this.list[name] = fn
}
}
export default new Bus()
A组件传递数值
<script setup lang="ts">
import { ref } from 'vue'
import Bus from '../utils/Bus'
const flag = ref(1)
const Pass = () => {
Bus.emit('pass', flag)
}
</script>
<template>
<div>
我是A
<div>{{ flag }}</div>
<button @click="Pass">Pass</button>
</div>
</template>
<style scoped lang="less"></style>
B组件接收数值
<script setup lang="ts">
import Bus from '../utils/Bus'
import { ref, type Ref } from 'vue'
const flag = ref(0)
Bus.on('pass', (Flag: Ref<number>) => {
console.log(Flag)
flag.value = Flag.value
})
</script>
<template>
<div>
我是B
<div>{{ flag }}</div>
<button @click="flag++">+</button>
</div>
</template>
<style scoped lang="less"></style>
第三方库mitt
安装yarn add mitt
全局挂载mit
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import './assets/main.css'
import mitt from 'mitt'
const Mit = mitt()
const app = createApp(App)
// 类型声明
declare module 'vue' {
export interface ComponentCustomProperties {
$Bus: typeof Mit
}
}
app.use(createPinia())
app.config.globalProperties.$Bus = Mit
app.mount('#app')
A组件传递数值
<script setup lang="ts">
import { getCurrentInstance, ref } from 'vue'
const instance = getCurrentInstance()
const flag = ref(1)
const Pass = () => {
instance?.proxy?.$Bus.emit('pass', flag)
}
</script>
<template>
<div>
我是A
<div>{{ flag }}</div>
<button @click="Pass">Pass</button>
</div>
</template>
<style scoped lang="less"></style>
B组件接收数值
<script setup lang="ts">
import { getCurrentInstance, ref, type Ref } from 'vue'
const instance = getCurrentInstance()
const flag = ref(0)
instance?.proxy?.$Bus.on('pass', Flag => {
flag.value = (Flag as Ref<number>).value
})
</script>
<template>
<div>
我是B
<div>{{ flag }}</div>
<button @click="flag++">+</button>
</div>
</template>
<style scoped lang="less"></style>
*监听事件
<script setup lang="ts">
import { getCurrentInstance, ref, type Ref } from 'vue'
const instance = getCurrentInstance()
const flag = ref(0)
/**
* type:事件名称
* Flag:传递参数
*/
instance?.proxy?.$Bus.on('*', (type, Flag) => {
flag.value = (Flag as Ref<number>).value
})
</script>
取消监听事件
<script setup lang="ts">
import { getCurrentInstance, ref, type Ref } from 'vue'
const instance = getCurrentInstance()
const flag = ref(0)
instance?.proxy?.$Bus.off('pass', Flag => {
flag.value = (Flag as Ref<number>).value
})
</script>
取消全部监听事件
<script setup lang="ts">
import { getCurrentInstance, ref, } from 'vue'
const instance = getCurrentInstance()
instance?.proxy?.$Bus.all.clear()
</script>