Vue3+TypeScript+ピニア

VueUseDocumentation

1.参照

ref, isRef, shallowRef, triggerRef, customRef

  1. ref は es6 のクラスを返します。値と変更には .value を追加する必要があります。
  2. Ref とshallowRef を一緒に書き込むことはできません。その場合、 shallowRef のビューが更新されます。
  3. ref = 浅い参照 + トリガー参照
<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 は参照型のみをサポートします配列オブジェクト マップ セット
  2. リアクティブな値の割り当てには.valueは必要ありません
  3. reactive はプロキシであるため、直接割り当てることはできません。そうでないと、応答性の高いオブジェクトが破棄されます。
  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>

3.シリーズへ

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

計算された

<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 () {
    
    
  //
  }
})

時計

  1. watch はリアクティブなオブジェクトのみを監視できます
  2. 監視監視参照型で監視する新値と旧値が同じである
  3. watch は、reactive でラップされた深いオブジェクトを監視します。深いオブジェクトを追加することはできません
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)
})

ウォッチエフェクト

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

5. 親コンポーネントと子コンポーネント間で値を渡す

defineProps、defineEmits、defineExpose、および withDefaults を使用すると、コンパイラ マクロ
ESLint は、「defineEmits' が定義されていません。(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>

6. 再帰コンポーネント、動的コンポーネント、非同期コンポーネント

再帰的なコンポーネント

// 父组件定义传值并使用
<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 の厳密モードでエラーが報告されます)、未定義を定義できます。一般に関数に使用されます。
null未定義には代入を散在させることができます (厳密モードでは、オフになります)

いかなるタイプ

any: 任意の型に割り当てることができます。
不明(unknown): それ自体または any にのみ割り当てることができ、属性を読み取る方法がなく、メソッドを呼び出すことができません。

インターフェースとオブジェクトタイプ

インターフェイス
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')

8. ピニア

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