0


TypeScript快速上手语法+结合vue3用法

TypeScript快速上手语法+结合vue3用法

**    前言:  **  

    本篇内容不涉及TypeScript安装以及配置,具体安装及配置篇可以看下面目录,本篇只涉及TypeScript语法相关内容,及结合vue3的用法。不讲废话,简单直接直接开撸。
 TypeScript的具体安装及配置

1、定义原始类型

const a: string = 'foo'
const b: number = 100
const c: boolean = true
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symlol()

2、定义object 类型

const foo: object = function () {} // [] // {}
const obj: { foo: number,bar: string } = { foo: 123, bar: 'string' }

3.定义数组类型

// 第一种定义方式,元素类型设置为 *number*
const arr1: Array<number> = [1, 2, 3]
// 第二种定义方式,较为常见
const arr2: number[] = [1, 2, 3]

// 例子
function sum (...args: number[]) {
    // 传统做法是要判断传入的参数是否是数字,
       而TypeScript中只需要像上面这样对参数做一个类型注解,就行了
    return args.reduce(prev, current) => prev + current, 0)
}

sum(1, 2, 3, 'foo') // 这里传入了一个非数字的值就会报错

4.定义元祖类型

const tuple: [number, string] = [18, 'foo']
// const tuple: [number, string] = [18, 18] 类型不匹配,会报错
// const tuple: [number, string] = [18, 'foo', 'xxx'] 数量不匹配,会报错

// 访问
const age = tuple[0]
const name = tuple[1]

// 解构
const [age, name] = tuple

5.定义enum 类型

// enum 对象的属性可以不用赋值,默认从0开始递增,
   也可以赋值Draft = 5,后面的就从5开始递增
   也可以给具体的值,比如 Draft = 'xxx',这样后面的属性都要给具体的值
enum PostStatus {
    Draft = 0,
    Unpublished = 1,
    Published = 2
}

const post = {
    title: 'Hello TypeScript',
    content: 'TypeScript is a typed superset of JavaScript.',
    status: PostStatus.Draft // 0 // 1 // 2
}

6.定义函数类型

// 声明式函数
// 参数a和b是number类型,函数返回是string类型,
// 参数后带问号代表是可选参数
// 当参数数量不固定的时候可以使用rest运算符来接受参数,类型是一个值为number的数组
function func1 (a: number, b?: number, ...rest: number[]): string {
    return 'func1'
}

// 函数表达式定义函数
const func2 = function (a: number, b: number): string {
    return 'func2'
}

// 如果把一个函数作为参数传递,类似callback函数。
function fntD(callback: (bl: boolean) => boolean) {
    callback(true)
}
function callback(bl: boolean): boolean {
    console.log(bl)
    return bl
}
const dResult = fntD(callback)

7.定义任意类型

// value 可以接受任意类型
function stringfy (value: any) {
    return JSON.stringify(value)
}

stringify('string')
stringify(10)
stringify(true)

// foo 可以任意赋值
let foo: any = 'string'
foo = 100

8.隐式类型推断

// age 赋值为 number 类型
let age = 18 // number

age = 'string' // 会警告错误,因为age是number类型

let foo // 没有赋值,就是any类型

foo = 100
foo = 'string'

9.类型断言

// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]

// 这里TypeScript推断res的类型为 number|undefined
// 因为它并不知道这个i到底在数组中有没有
const res = nums.find(i => i > 0)

// 这里就会报错警告
const square = res * res

// 如果我们直接 断言 这个 res 就是 number 类型
const num1 = res as number

// 这里就不会报错了
const square = res * res

10、 接口 interface

接口用来约定对象的结构,一个对象要实现一个接口,就必须拥有这个接口中所包含的所有成员

interface Post {
    title: string
    content: string
}

function printPost (post: Post) {
    console.log(post.title)
    console.log(post.content)
}

printPost({
    title: 'Hello TypeScript',
    content: 'A JavaScript superset'
})

// 特殊的接口成员 可选成员 只读成员
interface Post{
    title: string
    content: string
    subtitle?: string // 加问号就是可选成员
    readonly summary: string // 加 readonly 就是只读成员
}

const hello: Post = {
    title: 'Hello TypeScript',
    content: 'A javascript superset',
    summary: 'a javascript'
}

hello.summary = 'other' // 会报错,因为 summary 是只读成员

// 动态成员
interface Cache {
    [prop: string]: string
}

const cache: Cache = {}

cache.foo = 'value1'
cache.bar = 'value2'

11、类 Class

Class Person {
    // 在这里赋值,和在构造函数中初始化必须两者选其一
    name: string // = 'init name' 这里可以直接初始化
    private age: number // 这里定义 age 为私有属性
    protected gender: boolean // 受保护的类型
    readonly national: string // 只读属性,一经初始化,不可更改

    constructor (name: string, age: number) {
        // 需要在上面标注出构造函数中属性的类型
        this.name = name
        this.age = age
        this.gender = true
        this.national = national
    }

    sayHi (msg: string): void {
        console.log(`I am ${this.name}, ${msg}`)
        console.log(this.age)
    }
}

const tom = new Person('tom', 18)
console.log(tom.name) // tom
console.log(tom.age) // 报错,因为 age 是私有属性,所以访问不到
console.log(tom.gender) // 报错,因为 gender 是受保护的属性,这里访问不到

// 在下方新声明一个类 student 继承与 Person
class Student extends Person {
    constructor (name: string, age: number) {
    super(name, age)
    console.log(this.gender) // 这里就一个访问到 受保护的属性 gender
}

12.类与接口

interface Eat {
    eat (food: string): void
}

interface Run {
    run (distance: number): void
}

// Person类,实现了 Eat 和 Run 两个接口
class Person implements Eat, Run {
    eat (food: string): void {
        console.log(`优雅的进餐:${food}`)
    }
    
    run (distance: number) {
        console.log(`直立行走:${distance}`)
    }
}

// Animal类,实现了 Eat 和 Run 两个接口
class Animal implements Eat, Run {
    eat (food: string): void {
        console.log(`饥不择食的吃:${food}`)
    }
    
    run (distance: number) {
        console.log(`爬行:${distance}`)
    }
}

13.抽象类

abstract 定义抽象类,抽象类只能被继承,不能通过 new 的方式创建实例对象
// 定义一个抽象类 Animal
abstract class Animal {
    eat (food: string): void {
        console.log(`饥不择食的吃:${food}`)
    }
    
    // 定义一个抽象方法 run,可以不需要方法体。
    // 定义了抽象方法之后,子类中必须实现这个抽象方法
    abstract run (distance: number): void
}

class Dog extends Animal {
    run(distance: number): void {
        console.log('四脚爬行', distance)
    }
}

const d = new Dog()
d.eat('嘎嘎') // 饥不择食的吃:嘎嘎
d.run(100) // 四脚爬行 100

14. 泛型 Generics

泛型是指在定义接口函数类的时候,没有指定具体的类型,等到我们在使用的时候再去指定具体的类型的这种特征
// 这里声明一个创建 number 类型数组的函数 creatNumberArray
function createNumberArray (length: number, value: number): number[] {
    // 这里的Array是 any 类型,所以要给它指定一个 Number 类型
    const arr = Array<number>(length).fill(value)
    return arr
}

// 这里声明一个创建 String 类型数组的函数 createStringArray
function createStringArray (length: number, value: string): string[] {
    const arr = Array<string>(length).fill(value)
    return arr
}

// 因为上面的两个函数代码有冗余,所以这里我们可以使用 泛型
// 一般我们使用 T 来作为泛型参数的名称,然后把函数中不明确的类型都改为 T 来做代表
function createArray<T> (length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}

// 然后使用泛型的时候 传递 T 的类型
const res = creatArray<string>(3,'foo')

// const res = createNumberArray(3, 100)
// res => [100, 100, 100]

==============================分割线=============================

下面是结合vue3的项目写法

Vue3+TS基础语法

🧨🧨🧨定义data

  • script标签上lang="ts"
  • 定义一个类型type或者接口interface来约束data
  • 可以使用ref或者toRefs来定义响应式数据
  • 使用ref在setup读取的时候需要获取xxx.value,但在template中不需要
  • 使用reactive时,可以用toRefs解构导出,在template就可以直接使用了
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';

type Todo = {
  id: number,
  name: string,
  completed: boolean
}

export default defineComponent({
  const data = reactive({
    todoList: [] as Todo[]
  })
  const count = ref(0);
  console.log(count.value)
  return {
    ...toRefs(data)
  }
})
</script>

🧨🧨🧨定义props

props需要使用PropType泛型来约束。
<script lang="ts">
import { defineComponent, PropType} from 'vue';

interface UserInfo = {
  id: number,
  name: string,
  age: number
}

export default defineComponent({
  props: {
    userInfo: {
      type: Object as PropType<UserInfo>, // 泛型类型
      required: true
    }
  },
})
</script>
复制代码

defineProps 和 defineEmits

注意:defineProps 和 defineEmits 都是只在

为了声明 props 和 emits 选项且具备完整的类型推断,可以使用 defineProps 和 defineEmits API,它们在 <script setup> 中都是自动可用的:
  • defineProps 和 defineEmits 都是只在 <script setup> 中才能使用的****编译器宏。他们不需要导入,且会在处理 <script setup> 的时候被编译处理掉。
    
  • defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的值。
    
  • defineProps 和 defineEmits 在选项传入后,会提供恰当的类型推断。
    
  • 传入到 defineProps 和 defineEmits 的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。
    

父组件

//父组件

<script setup lang="ts">
    import TestPropsPmit from './components/test-props-emit/index.vue';
    import { ref } from 'vue';

    // 定义字符串变量
    const msg = ref('欢迎使用vite!')
    // 调用事件
    const handleChange = (params:string) =>{
        console.log(params);
    }
</script>
<template>
    <TestPropsPmit :msg="msg" @on-change="handleChange"></TestPropsPmit>
</template>

子组件

//子组件

<template>
    <p>{{props.msg}}</p>
    <button @click="handleClick">点击我调用父组件方法</button>
</template>
<script setup lang="ts">
    const props = defineProps({
         msg:{
            type: String,
            default: () => '默认值'
         }
    })
    const  emit = defineEmits(['on-change', 'update'])
    const handleClick = () =>{
        emit('on-change', '父组件方法被调用了')
    }
</script>

子组件暴露属性和方法,给父组件引用

<script setup lang="ts">
function testChild():void{
    console.log('子组件方法testChild被调用了');
}
const b = ref(2)
// 统一暴露属性
defineExpose({
    obj:{name: '张三', age: 2300},
    b,
    testChild
})
</script>

父组件调用子组件方法和属性

<template>
    <TestPropsEmit ref="propsEmitRef" :msg='msg' @on-change="handleChange">             </TestPropsEmit>
</template>
<script setup lang="ts">
    import TestPropsEmit from './components/test-props-emit/index.vue';
    import {ref, onMounted} from 'vue';

    const msg = ref('欢迎学习vite')

    const handleChange = (params:string)=>{
        console.log(params);
    }

    const propsEmitRef = ref()
    onMounted(()=>{
        console.log(propsEmitRef.value.child);
    })
</script>

🧨🧨🧨定义methods

<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';

type Todo = {
  id: number,
  name: string,
  completed: boolean
}

export default defineComponent({
  const data = reactive({
    todoList: [] as Todo[]
  })
  // 约束输入和输出类型
  const newTodo = (name: string):Todo  => {
    return {
      id: this.items.length + 1,
      name,
      completed: false
    };
  }
  const addTodo = (todo: Todo): void => {
    data.todoList.push(todo)
  }
  return {
    ...toRefs(data),
    newTodo,
    addTodo
  }
})
</script>

vue-router

  • **createRouter**创建router实例
    
  • ** router**的模式分为:
  • **createWebHistory** -- **history**模式
    
  • **createWebHashHistory** -- **hash**模式
    
  • ** routes的约束类型是RouteRecordRaw**
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import Home from '../views/Home.vue';
const routes: Array< RouteRecordRaw > = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;
扩展路由额外属性

在实际项目开发中,常常会遇到这么一个场景,某一个路由是不需要渲染到侧边栏导航上的,此时我们可以给该路由添加一个hidden属性来实现。

在ts的强类型约束下,添加额外属性就会报错,那么我们就需要扩展RouteRecordRaw类型。

在setup中使用

需要导入useRouter创建一个router实例。
<script lang="ts">
import { useRouter } from 'vue-router';
import { defineComponent } from 'vue';
export default defineComponent({
  setup () {
    const router = useRouter();
    goRoute(path) {
       router.push({path})
    }
  }
})
</script>

🧨🧨🧨 vuex

使用this.$store
import { createStore } from 'vuex';
export type State = {
  count: number
}

export default createStore({
  state: {
    count: 0
  }
});
需要创建一个声明文件vuex.d.ts
// vuex.d.ts
import {ComponentCustomProperties} from 'vue';
import {Store} from 'vuex';
import {State} from './store'
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $store: Store<State>
    }
}

在setup中使用

  • 定义InjecktionKey
  • 在安装插件时传入key
  • 在使用useStore时传入
import { InjectionKey } from 'vue';
import { createStore, Store } from 'vuex';

export type State = {
  count: number
}
// 创建一个injectionKey
export const key: InjectionKey<Store<State>> = Symbol('key');
// main.ts
import store, { key } from './store';
app.use(store, key);
<script lang="ts">
import { useStore } from 'vuex';
import { key } from '@/store';
export default defineComponent({
  setup () {
    const store = useStore(key);
    const count = computed(() => store.state.count);
    return {
      count
    }
  }
})
</script>

模块

新增一个todo模块。导入的模块,需要是一个vuex中的interface Module的对象,接收两个泛型约束,第一个是该模块类型,第二个是根模块类型。
// modules/todo.ts
import { Module } from 'vuex';
import { State } from '../index.ts';

type Todo = {
  id: number,
  name: string,
  completed: boolean
}

const initialState = {
  todos: [] as Todo[]
};

export type TodoState = typeof initialState;

export default {
  namespaced: true,
  state: initialState,
  mutations: {
    addTodo (state, payload: Todo) {
      state.todos.push(payload);
    }
  }
} as Module<TodoState, State>; //Module<S, R> S 该模块类型 R根模块类型
// index.ts
export type State = {
  count: number,
  todo?: TodoState // 这里必须是可选,不然state会报错
}

export default createStore({
  state: {
    count: 0
  }
  modules: {
    todo
  }
});
使用: 
setup () {
  console.log(store.state.todo?.todos);
}

🧨🧨🧨 在setup如何定义变量(字符串,对象,数组)

<template>
    <h2>{{count}} {{user.name}}</h2>
    <span v-for="(item, index) in arr" :key="index">{{item}}</span>
    <button @click="setName">点击我增加</button>
</template>
<script setup lang="ts">
    import { ref, reactive } from 'vue';
    // 字符串变量
    const count = ref(0)
    // 对象
    let user = reactive({
        name: '张三'
    })
    // 数组
    let arr = reactive(['1', '2', '3'])
    
    // 综合定义方案
    const originData = reactive({
        count: 0,
        user:{
            name: '张三'
        },
        arr: ['1', '2', '3']
    })
    
    // 方法
    const setName = ()=>{
        count.value++
        user.name = '李四'
    }
</script>

Watch和WatchEffect

1、基本使用方法:
<template>
    <p>{{originData.count}}  {{originData.user.name}}</p>
    <p v-for="(item, index) in originData.arr" :key="index">{{item}}</p>
    <button @click="incriment">点击我count增加</button>
</template>
<script setup lang="ts">
    import { ref, reactive, watchEffect, watch } from 'vue';

    const count = ref(0)
    const user = reactive({name: '张三'})
    const arr = reactive([1,2,3,4])

    // 综合定义方案
    const originData = reactive({
        count: 0,
        user:{
            name: '张三'
        },
        arr:[1,2,3,4]
    })
    const incriment = ()=>{
        originData.count++
        count.value++
        originData.user.name = '李四'
    }
    // 默认页面更新之前立即执行监听,懒执行开始
    watchEffect(() => console.log(count.value))
    // 默认监听数据变化后的值,页面更新后不会立即执行
    watch(count, (n, o) => {
        console.log('watch', n, o);
    })
    
    // 监听多个值
    watch([count, originData.user], (newValues, prevValues) => {
        console.log(newValues[0], newValues[1].name)
    })

    // 立即监听
    watch([count, originData.user], (newValues, prevValues) => {
        console.log(newValues[0], newValues[1].name)
    }, {deep: true, immediate: true})
   
</script>

提示:

watch与 watchEffect 比较,推荐watch监听

watch: 页面更新后不会立即执行,而watchEffect 它会执行;

🧨🧨🧨Vue3生命周期调用

在 setup () 内部调用生命周期钩子:

  • 选项式 API Hook inside setup
    
  • beforeCreate Not needed* 不需要
    
  • created Not needed* 不需要
    
  • beforeMount onBeforeMount 挂载之前
    
  • mounted onMounted 页面加载完成时执行
    
  • beforeUpdate onBeforeUpdate
    
  • updated onUpdated
    
  • beforeUnmount onBeforeUnmount
    
  • unmounted onUnmounted 页面销毁时执行
    
  • errorCaptured onErrorCaptured
    
  • renderTracked onRenderTracked
    
  • renderTriggered onRenderTriggered
    
  • activated onActivated
    
  • deactivated onDeactivated
    
<script setup lang="ts">
import { onMounted, onActivated, onUnmounted, onUpdated, onDeactivated } from 'vue';
// 读取环境变量
const mode = import.meta.env;
//   import HeadMenu from '@/components/head-menu/index.vue';
 onMounted(() => {
 console.log("组件挂载")
 })

 onUnmounted(() => {
 console.log("组件卸载")
 })

 onUpdated(() => {
 console.log("组件更新")
 })
 onActivated(() => {
 console.log("keepAlive 组件 激活")
 })

 onDeactivated(() => {
 console.log("keepAlive 组件 非激活")
 })
</script>

加🧨🧨🧨为必用的东西,可以多看看

** 最后:**

** 待续....精力有限 持续更新中....**


本文转载自: https://blog.csdn.net/zhangxueyou2223/article/details/129216810
版权归原作者 痴心阿文 所有, 如有侵权,请联系我们删除。

“TypeScript快速上手语法+结合vue3用法”的评论:

还没有评论