V3+TS,90%的项目场景

231 阅读3分钟

cn.vuejs.org/guide/intro…

1.ref + ts

<div ref="btn">btn</div>

import { ref } from 'vue'
import type { Ref } from 'vue'

const xxx = ref('')   //ts存在类型推导     xxx:Ref<string>

//第一种
const xxx = ref<string>('')

//第二种
const xxx: Ref<string> = ref('')

// 第三种
const btn = ref<HTMLDivElement>()

HTMLDivElement
HTMLSpanElement
HTMLElement

2.reactive + ts

// 第一种 使用type
type S = {
    name:string
}
const readonlyObj = reactive<S>({
    name:'1111'
})

// 第二种  使用interface
interface IType {
    
}
 const obj1 = reactive<IType>({

})

//第三种  泛型约束
reactive<T extends object>( target: T )  
// 为什么ref可以接收简单数据类型,reactive不可以,这就是里面使用了泛型约束

type 和 interface 的异同

1.编译后产生的代码, type 其实是声明别名,然后去赋值地址,后续引用的操作,而 interface 只存在于编译阶段,在编译后生成的 JS 代码中不包含任何接口代码

2.type 能够表示非对象类型,  而 interface 则只能表示对象类型

type type1 = boolean | string | number;
type func = (data: string) => void

// 也可以使用泛型
type Lazy<T> = T | ( () => T )


interface IType {
    
}
  1. interface可以继承其他的接口、类等对象类型,  type 不支持继承。
  2. typeinterface都支持扩展
// type扩展
type foo = {
    name: string
}

type foo2 = foo & {
    age: number
}

// interface扩展
interface IFoo {
    name: string
}

interface IFoo2 extends IFoo {
    age: number
}

//交叉使用
type foo = {
    name: string
}
interface IFoo2 extends foo {
    age: number
}

interface IFoo {
    name: string
}

type foo2 = IFoo & {
    age: number
}

  1. 同名interface会自动合并,type则是会报错

image.png

3.计算属性 + ts

计算属性利用了 computed 里面的泛型去对自身的返回值做了约束

使用:

computed<T>

// 函数重载
1。重载签名 和 实现签名
重载签名:
function greet(person: string): string
function greet(person: string[]): string[]
// 因为有多种参数,所以类型是不确定的
function greet<T>(person: T): unknown{
    if( typeof person === 'string'){
        return person
    }else if(Array.isArray(person)){
        return person.join('')
    }
    throw new Error('unable to greet')
}

export declare function computed<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions): ComputedRef<T>;

export declare function computed<T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions): WritableComputedRef<T>;

const computed: {  

<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions | undefined): 
ComputedRef<T>; 

<T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions | undefined):
WritableComputedRef<...>;  

}
<template>
    {{ computedName }}
    <button @click="changeName">changeName</button>
</template>

<script setup lang='ts'>
import { computed, ref, reactive } from 'vue'

let F1 = ref<string>('11')
let F2 = ref<string>('22')

// let computedName = computed<string>({
//     get(){
//         return  F1.value + F2.value
//     },
//     set(newValue){
//         F1.value = newValue.split('-')[0]
//         F2.value = newValue.split('-')[1]
//     }
// })

const changeName = () => {
    computedName.value = '333-4444'
}
let computedName = computed<string>(() => {
    return F1.value + F2.value
})
</script>

4. 父子组件通信 + ts


<template>
    father
    <hr>
    <children name="1231" :list="[1,2,3,4]" @update:list="updateList" ref="childrenRef"></children>
</template>

<script setup lang='ts'>
import children from './Echildren.vue';
import { ref, onMounted } from 'vue'

const updateList = (list: number[]) => {
    console.log(list);
}

// childrenRef 组件的ref
// children    组件
const childrenRef = ref<InstanceType<typeof children>>()
   
onMounted(()=>{
    console.log( childrenRef.value?.name);
    console.log( childrenRef.value?.open());
})

</script>

<template>
    children
    {{ name }}
    {{ list }}
    <button @click="handleEmit">handleEmit</button>
</template>

<script setup lang='ts'>

// 接收父组件传值  没有使用TS
// const props = defineProps({
//     name:{
//         type: String,
//         default:''
//     },
//     list:{
//         type: Array,
//         default: []
//     }
// })

// 如果在这里使用 props 的话, 需要接收
// console.log(props.name);
// console.log(props.list);



// 使用TS, 使用泛型字面量
// const props = defineProps<{
//     name:string,
//     list: number[]
// }>()

// console.log(props);


// TS +  默认值   TS的宏函数 withDefaults
const props = withDefaults(defineProps<{
    name: string,
    list: number[]
}>(),
    {
        name: '',
        arr: () => [],
    }
)

// 子传父  JS方式
// const emit = defineEmits(['update:list'])
// const handleEmit = () => {
//     let list: number[] = [1,1,1,1,,1,1]  as number[]
//     emit('update:list',list)
// }

// 子传父  TS方式
const emit = defineEmits<{
    (e: 'update:list', list: number[]): void
}>()

const handleEmit = () => {
    let list: number[] = [1,1,1,1,2,1,1]  as number[]
    emit('update:list',list)
}

// 暴露
// 使用场景, 就是父访子的方法,  类似于 this.$refs.xxxxxx.xxxx
defineExpose({
    name:'xxxxxxx',
    open: ()=> 1
})

</script>

5.import 与 import type 区别

  1. import 导入值
  2. import type 导出类型,并且不参与编译

6.keyof操作符

keyof 相当于 Object.keys(xxxx) 返回的是联合类型

function getProperty<T extends object, K extends keyof T>(obj: T, key: K){
    return obj[key]
}

7.css深度选择器等的使用

vue2 是 ::v-deep

vue3 是 :deep(类名/Id/标签名....)

8.vue3中的生命周期

image.png

所有的生命周期方法

常用

不常用

  • onErrorCaptured( )在捕获了后代组件传递的错误时调用
  • onRenderTracked( )当组件渲染过程中追踪到响应式依赖时调用这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
  • onRenderTriggered( )当响应式依赖的变更触发了组件渲染时调用这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
  • onServerPrefetch( )在组件实例在服务器上被渲染之前调用

keepAlive组件使用

9.watch

<template>
    {{ msg }}
    {{ msg1 }}
    {{ list }}
    {{ list2 }}
    <button @click="handleWatch">handleWatchRef</button>
    <button @click="handleWatchReactive">handleWatchReactive</button>
</template>

<script setup lang='ts'>
// 监听响应式数据的变化  比如使用 ref ,reactive 进行侦听
import { ref, reactive, watch } from 'vue'
import type { Ref } from 'vue'

// ref 模式
// let msg: Ref<string> = ref('msg')
// watch(msg, (nv,ov)=>{
//     console.log(nv,ov);
// })
// const handleWatch = () => {
//     msg.value = 'watch'
// }

// 多个 ref
let msg: Ref<string> = ref('msg')
let msg1: Ref<string> = ref('msg1')
watch([msg, msg1], (nv, ov) => {
    console.log(nv, ov);
})
const handleWatch = () => {
    msg.value = 'watch'
    msg1.value = 'watch'
}

// reactive watch 自动开启deep
let list = reactive({ name: '1111' })
let list2 = reactive({ name: '22222' })

watch([list,list2], (nv)=>{
    console.log(nv);
})

const handleWatchReactive = () => {
    list.name = '222222'
    list2.name = '33333333'
}


// 监听单一属性
watch(()=>list.name, (nv,ov)=>{
    console.log(nv);
})

</script>


10.vue-router的使用

导航守卫以及其他的功能: router.vuejs.org/zh/guide/ad…

如果需要跳转

import { useRouter, useRoute } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()

    function pushWithQuery(query) {
      router.push({
        name: 'search',
        query: {
          ...route.query,
          ...query,
        },
      })
    }
  },
}

11.组件的引入与使用(懒加载等)

懒加载使用 defineAsyncComponent

import { defineAsyncComponent } from 'vue'

// 第一种方式
const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

// 第二种方式
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./components/MyComponent.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})