Vue3+TS教程(一)

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>

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容