Vue3+TypeScript+pinia

VueUse文档

一、Ref

ref, isRef, shallowRef, triggerRef, customRef

  1. ref返回的是es6的一个class类,取值和修改都要加上.value
  2. ref 和 shallowRef不能一起写,会引起shallowRef的视图更新
  3. ref = shallowRef + triggerRef
<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

reactive, readonly, shallowReactive

  1. ref支持所有类型 reactive只支持引用类型 Array Object Map Set
  2. reactive 取值赋值不需要 .value
  3. reactive 是proxy代理,不能直接赋值,否则会破坏响应式对象
  4. readonly只读,不可修改readonly赋值的变量值,原始变量可以修改,修改只会readonly赋值的变量值会跟着改变
  5. 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>

三、To系列

toRaw, toRef, toRefs

  1. toRef 只能修改响应式对象的值,非响应式视图不会变化
  2. toRefs 解构
  3. toRaw 响应式改成非响应式
<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,Watch,WatchEffect

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

  1. watch只能监听响应式对象
  2. watch监听引用类型监听的新值和旧值是一样的
  3. watch监听reactive包裹的深层对象,可以不加deep
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

  1. watchEffect回调函数内部写了什么就监听什么
  2. 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>

七、TypeScript

构造函数是大写的,小写是构造函数的实例化对象

基础类型

number: 可以定义 NaN、Infiniy(无穷大)、各种进制
void:可以定义 null(在TS的严格模式中回报错)、undefined,一般用于函数
nullundefined可以穿插赋值(需关闭严格模式)

任意类型

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')

八、Pinia

安装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>

猜你喜欢

转载自blog.csdn.net/qq_42048638/article/details/129319452
今日推荐