VueUse文档
ref, isRef, shallowRef, triggerRef, customRef
<template>
<div class="home">
Ref: {{student1.name}}<br>
ShallowRef: {{student2.name}}<br>
CustomRef: {{student3}}<br>
<div ref="myDiv">我是一个div</div>
<button @click="updateStudentName">修改</button>
</div>
</template>
<script setup lang="ts">
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue'
const student1 = ref({ name: '小花1' })
const student2 = shallowRef({ name: '小花2' })
function myRef<T> (value:T) {
return customRef((track, trigger) => {
let timer:any
return {
get () {
track() // 收集依赖
return value
},
set (newValue:T) {
clearInterval(timer)
timer = setTimeout(() => {
timer = null
console.log('触发一次调用一次')
value = newValue // 触发依赖
trigger()
}, 500)
}
}
})
}
const student3 = myRef<string>('小花3')
const myDiv = ref<HTMLDivElement>() // 变量名需要与dom一致
// console.log(myDiv.value?.innerText) dom未渲染,打印无数据
const updateStudentName = () => {
// student.value.name = '小明1'
student2.value.name = '小明2' // 这里如果响应函数里有ref,则shallowRef也会同时刷新 (student,注释掉就不会更新)
// student2.value = { name: '小明2' } // 如果想要更新视图需要在value层修改
triggerRef(student2) // 强制更新shallowRef
student3.value = '小明3'
console.log(myDiv.value?.innerText)
// console.log(student1)
// console.log('判断是否是ref', isRef(student1))
// console.log(student2)
}
</script>
reactive, readonly, shallowReactive
<template>
<div class="about">
<form>
姓名:<input v-model="student1.name">
</form>
<ul>
<li :key="index" v-for="(item,index) in student2">{{item}}</li>
</ul>
shallowReactive:{{student5}}
<button @click="submitStudent">修改</button>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, readonly, shallowReactive } from 'vue'
const student1 = reactive({ name: '小花' })
const student2 = reactive<string[]>([])
const student3 = reactive<{arr:string[]}>({ arr: [] })
const student4 = reactive({ name: '小红' })
const readonlyS4 = readonly(student3)
// readonlyS4.name = ''
const student5 = shallowReactive({
obj1: {
obj2: {
name: '小花'
}
}
})
const submitStudent = () => {
// student1.name = '小明'
// setTimeout(() => {
// const res = ['1', '2', '3']
// // student2 = res //不是响应式对象,视图不会更新
// student2.push(...res) // 响应式对象,视图更新
// student3.arr = res // 响应式对象,视图更新
// }, 2000)
student5.obj1.obj2.name = '小明' // 视图不会改变(不能和reactive一起使用)
student5.obj1 = { obj2: { name: '小明' } } // 视图改变
console.log(student5)
}
</script>
toRaw, toRef, toRefs
<template>
<div class="about">
<h1>{{student1}}</h1>
<h1>{{name}}--{{age}}--{{sex}}</h1>
<button @click="updateStudent">修改</button>
</div>
</template>
<script setup lang="ts">
import { reactive, toRaw, toRef, toRefs } from 'vue'
// toRef 只能修改响应式对象的值,非响应式视图不会变化
// toRefs 解构
// toRaw 响应式改成非响应式
const student1 = reactive({ name: '小花', age: 12, sex: '女' })
const age = toRef(student1, 'age')
const { name, sex } = toRefs(student1)
const student2 = reactive({ name: '小花', age: 12, sex: '女' })
const updateStudent = () => {
age.value = 13
name.value = '小明'
sex.value = '男'
console.log(student1, toRaw(student2))
}
</script>
computed
<input v-model="name1"><br>
<input v-model="name2"><br>
<input v-model="name3.obj1.obj2.name"><br>
<input v-model="name4.obj1.obj2.name"><br>
const one = ref('')
const tow = ref('')
const split1 = computed(() => {
return `${one.value}--${tow.value}`
})
const split2 = computed({
get () {
return `${one.value}--${tow.value}`
},
set () {
//
}
})
watch
const name1 = ref('小花1')
const name2 = ref('小花2')
const name3 = ref({
obj1: {
obj2: {
name: '小花3'
}
}
})
const name4 = reactive({
obj1: {
obj2: {
name: '小花4'
}
}
})
// 监听单个
watch(name1, (n, o) => {
console.log('watch', n, o)
})
// 监听多个 newValue oldValue 也是数组对应监听数组
watch([name1, name2], (n, o) => {
console.log('watch', n, o)
})
// 深度监听 立即执行
watch(name3, (n, o) => {
console.log('watch', n, o)
},
{
deep: true, // 深度监听
immediate: true // 立即执行
})
// 监听对象单一属性(回调函数)
watch(() => name4.obj1.obj2.name, (n, o) => {
console.log('watch', n, o)
})
// 监听对象单一属性 (toRef)
const toName = toRef(name4.obj1.obj2, 'name')
watch(toName, (n, o) => {
console.log('watch', n, o)
})
watchEffect
<input id="message1" v-model="message1"><br>
<input v-model="message2"><br>
<button @click="stopWatchEffect">停止监听</button>
const message1 = ref('watchEffect1')
const message2 = ref('watchEffect2')
const stopWatchEffect = watchEffect((oninvalidate) => {
oninvalidate(() => {
console.log('我最先执行,一开始不执行,监听值变化才执行')
})
const message1Dom:HTMLInputElement = document.getElementById('message1') as HTMLInputElement
console.log('message1Dom', message1Dom)
console.log('message1', message1.value)
console.log('message2', message2.value)
}, {
flush: 'post', // pre 组件更新前执行 sync强制效果始终同步触发 post组件更新后执行
onTrigger (e) {
console.log('进入调试模式', e)
}
})
在使用 defineProps、defineEmits、defineExpose、withDefaults 编译器宏
ESLint报错 ‘defineEmits’ is not defined.(no-undef)
先检查ESlint版本 npm list eslint-plugin-vue
module.exports = {
// 版本为 v8.0.0或以上
env: {
node: true,
'vue/setup-compiler-macros': true
},
// 版本为 v8.0.0以下
globals: {
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly",
}
}
父组件
<template>
<div>
<h1>我是父亲</h1>
<button @click="getChild">获取子组件暴露的方法和属性</button>
<hr>
<Child ref="childRef" :sendChildVal="sendChildVal" @sendFather="sendFather" :arr="[1,2,3]"></Child>
</div>
</template>
<script setup lang="ts">
import Child from '@/components/Child.vue'
import { nextTick, onMounted, ref } from 'vue'
const sendChildVal = '我是父组件定义的数据'
const sendFather = (value:string) => {
console.log(value)
}
// 获取子组件暴露的方法和属性
const childRef = ref<InstanceType<typeof Child>>() // 必须先定义
const getChild = () => {
console.log(childRef.value.childName)
console.log(childRef.value.open())
}
onMounted(() => { // nextTick onMounted 中调用
console.log(childRef.value.childName)
console.log(childRef.value.open())
})
</script>
子组件
<template>
<div>
<h1>我是儿子</h1>
<h1>{{ sendChildVal }}</h1>
<h1>{{arr}}</h1>
<h1>{{defaultValue}}</h1>
<button @click="sendFather">传值给父组件</button>
</div>
</template>
<script setup lang="ts">
// 接收值
// const props = defineProps({ //普通写法
// sendChildVal: {
// type: String,
// default: '默认值'
// }
// })
// let props = defineProps<{ //ts写法
// sendChildVal:string,
// arr:number[],
// defaultValue:string
// }>()
// console.log(props.sendChildVal)
withDefaults(defineProps<{
sendChildVal:string,
arr:number[],
defaultValue:string
}>(), {
defaultValue: () => '默认值'
})
// 传值
// const emit = defineEmits(['sendFather']) //普通写法
const emit = defineEmits<{
(e:'sendFather', message:string):void
}>()
const sendFather = () => {
emit('sendFather', '我是子组件发送过来的值')
}
// 暴露属性方法给父组件
defineExpose({
childName: '我是子组件属性',
open: () => console.log('我是子组件方法')
})
</script>
递归组件
// 父组件定义传值并使用
<Tree :data="TreeData"></Tree>
interface TreeInterface {
name:string,
checked:boolean,
children?:TreeInterface[]
}
const TreeData = reactive<TreeInterface[]>([
{
name: '1-1',
checked: false,
children: [
{
name: '1-1-1',
checked: false
},
{
name: '1-1-2',
checked: false,
children: [{
name: '1-1-2-1',
checked: false
}]
}
]
},
{
name: '1-2',
checked: false
},
{
name: '1-3',
checked: false,
children: [
{
name: '1-3-1',
checked: false
}
]
}
])
//子组件递归
<template>
<div>
<div class="tree" v-for="(item,i) in data" :key="i">
<input type="checkbox" v-model="item.checked"> <span>{{item.name}}</span>
<Tree v-if="item?.children?.length" :data="item.children"></Tree>
</div>
</div>
</template>
<script setup lang="ts">
interface TreeInterface {
name:string,
checked:boolean,
children?:TreeInterface[]
}
withDefaults(defineProps<{
data:TreeInterface[]
}>(), {
data: () => [
{
name: '1-1',
checked: false
}
]
})
</script>
<style>
.tree{
margin-left: 30px;
}
</style>
动态组件
<div @click="handleClick(item.component)" v-for="(item,i) in tabList" :key="i">
<div>{{item.name}}</div>
</div>
<component class="active-tab" :is="activeTab"></component>
const activeTab = shallowRef(Child) // 节约性能
const tabList = reactive([
{
name: '普通组件',
component: markRaw(Child) // 不做Proxy代理
},
{
name: '递归组件',
component: markRaw(Tree)
}
])
const handleClick = (com) => {
activeTab.value = com
}
异步组件
应用场景:骨架屏
涉及知识点 defineAsyncComponent
,Suspense
需要异步加载数据的组件
<template>
<div>
<div style="width: 100px;height: 20px;">{{data}}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const axios = {
get <T> (url:string) {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
// JSON.parse(xhr.responseText)
setTimeout(() => {
resolve('成功')
}, 1000)
}
}
xhr.send()
})
}
}
const data = ref('')
await axios.get('https://img2.baidu.com/it/u=3202947311,1179654885&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1678035600&t=3bc1ca76995dd635b10b7e1d5d648cfa')
data.value = 'hello'
</script>
<style>
</style>
数据还在加载中显示的组件
<template>
<div>
<div style="width: 100px;height: 20px;background-color: red"></div>
</div>
</template>
<script setup lang="ts">
</script>
<style>
</style>
调用异步组件
<template>
<div>
<!-- vue3内置组件 -->
<Suspense>
<template #default>
<Sync></Sync> //需要异步加载数据的组件
</template>
<template #fallback>
<Show></Show> // 数据还在加载中显示的组件
</template>
</Suspense>
</div>
</template>
<script setup lang="ts">
const Sync = defineAsyncComponent(() => import('@/components/Sync.vue')) // 需要异步加载数据的组件
// const SyncVue = defineAsyncComponent({
// loadingComponent: () => import('@/components/Sync.vue'),
// errorComponent:'',
// timeout:''
// })
</script>
构造函数是大写的,小写是构造函数的实例化对象
number: 可以定义 NaN、Infiniy(无穷大)、各种进制
void:可以定义 null(在TS的严格模式中回报错)、undefined,一般用于函数
null和undefined可以穿插赋值(需关闭严格模式)
any:可赋值任意类型
unknown(未知):只能赋值给自身或者是any,没有办法读任何属性,方法也不可以调用
interface
1、遇到重名的会合并属性
2、继承也会合并属性
interface Test{
name:string
age:number
sex?:string //可选属性
readonly cb:()=>boolean // 只读
readonly id:number // 只读
[propName:string]:any // 索引签名,用于未确定的属性
}
// 约束函数
interface Fu{
(name:string):number[]
}
let fu:Fu = (name:string)=>{
return [1]
}
function test (...args:number[]){
console.log(args) // [1,2,3]
}
test(1,2,3)
// 默认参数和可选参数不可在同一参数只使用
let test = ( a:number = 10, b?:number)=> a + b
test()
// typeScript可以定义this的类型 在js中无法使用 必须是第一个参数定义this类型
interface Obj{
num:number
add:(this:Obj,num:number)=>void
}
let obj:Obj = {
num:1,
add(this:Obj, val:number){
this.num += val
}
}
obj.add(1)
interface People{
name:string,
age:number
}
interface Student{
sno:number
}
let fu = (student:People & Student)=> ''
fu({name:'小明',age:12,sno:18023569})
定义DOM
let div:NodeListOf<HTMLInputElement | HTMLDivElement> = document.querySelectorAll('div')
安装npm install --save pinia
mian.js中引入
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
import { createPinia } from "pinia";
const pinia = createPinia();
let app = createApp(App)
app.use(Antd)
app.use(pinia);
app.mount('#app')
创建userStore(/src/store/user.js)
import { defineStore } from 'pinia'
export const useUserStore = defineStore("user",{
state:()=>{
return{
name:'小明',
age:21,
sex:'男',
}
},
getters:{
getAge:(state)=>{
return (num)=> state.age + num
}
},
actions:{
updateName(name){
this.name = name
}
}
})
组件A
<script setup lang="ts">
import { useUserStore } from './store/user.js'
import Child from "./view/ComponentB.vue";
let userStore = useUserStore()
</script>
<template>
<div>组件A</div>
<span>{{userStore.name}}</span>-
<span>{{userStore.age}}</span>-
<span>{{userStore.sex}}</span>-
<!-- 计算属性 -->
<span>{{userStore.getAge(20)}}</span>
<ComponentB></ComponentB>
</template>
<style scoped>
</style>
组件B
<script setup>
import {useUserStore} from "../store/user.js";
let userStore = useUserStore()
let handleClick = ()=>{
userStore.name = '张三'
console.log(userStore.name)
}
// 全部重置
let reset = ()=>{
userStore.$reset()
}
// 批量修改
let batchUpdate = ()=>{
userStore.$patch({
name:'粒子',
age:100,
sex:'女',
})
}
let updateName = ()=>{
userStore.updateName('小花')
}
</script>
<template>
<div>组件B</div>
<a-button @click="handleClick">点击修改</a-button>
<a-button @click="reset">重置数据</a-button>
<a-button @click="batchUpdate">批量修改数据</a-button>
<a-button @click="updateName">修改名字</a-button>
</template>
<style scoped>
</style>