vue3+typescript学习

200 阅读4分钟

vue脚手架

一、vite简介

官网:开始 | Vite 官方中文文档 (vitejs.dev)

二、创建

npm create vite

最后的生成目录结构:

image.png

注:Volar 取代了我们之前为 Vue 2 提供的官方 VSCode 扩展 Vetur。如果你之前已经安装了 Vetur,请确保在 Vue 3 的项目中禁用它。

vue3

一、简介

官网:cn.vuejs.org/api/applica…

二、基础

1、简单示例

<template>
  <p>计算:{{ num }}</p>
  <button @click="toAdd"></button>
  <p>复杂计算:{{ objNum.num }}</p>
  <p ref="op">我是p标签</p>
</template>

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

export default defineComponent({
    setup(){
        /**
         * ref基本数据类型
         */
        // let num = 10;//不是响应式数据,做不到双向数据绑定,影响页面变化
        let num = ref(10);//这是vue内部封装的一个响应式对象数据;num.value才能真正拿到10这个值
        //所以,以后在js中操作这种响应式数据,需要 .value;但是在模板中不需要,直接使用num(原因是在vue编译的时候已经帮我们用.value获取了一次)。


        /**
         * reactive:复杂类型
         */
        let objNum  = ref({
            num:20
        })
        console.log(objNum.value.num);
        let objNum2 = reactive({
            num:10
        })
        console.log(objNum2.num)//不需要.value

        //获取绑定了ref属性,且值为op的标签
        let op = ref();

        //方法
        const toAdd = () => {
            num.value++;
            objNum.value.num++;
        }

        //生命周期
        onMounted(() => {
            console.log(op.value);//挂载完成后,才能获取到元素
        })
        nextTick(() => {
            console.log(op.value);//在下一个dom更新的时候执行(只会执行1次)
        })

        return {
            num,
            objNum,
            op,
            toAdd
        }
    }
});
</script>
<style lang="scss" scoped>
</style>

Tip:定义快捷代码片段:设置-用户代码片段-新建全局代码片段文件。(其中prefix中定义的名字,就是在使用时直接输入vue3,可以快速生成代码片段)

{
        "demo":{
                "prefix":"vue3",
                "body":["<template>",
                "\t",
                "</template>",
                "",
                "<script lang='ts'>",
                "import { defineComponent, ref } from 'vue';",
                "",
                "export default defineComponent({",
                "\tsetup(){\n",
                "\t\t",	
                "\t\treturn {",
                "",
                "\t\t}",
                "\t}",
                "});",
                "</script>",
                "",
                "<style lang='scss' scoped>",
                "",
                "</style>"]
        },
        "description": "定义vue3的代码片段"
}

2.setup语法糖

 <template>
  <p>复杂计算:{{ num }}</p>
  <button @click="toAdd"></button>
</template>

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

let objNum = reactive({
  num: 20,
});

// let {num} = objNum;//结构后,不是响应式数据
//解构成响应式数据,依旧需要通过.value的方式操作
let {num} = toRefs(objNum);

const toAdd = () => {
    num.value++
}
</script>
<style lang="scss" scoped></style>

3.watch和watchEffect

import { reactive, ref, toRefs, watch, watchEffect } from "vue";

let num = ref(10);

const toAdd = () => {
    num.value++
}

watch(num, (newVal, oldVal) => {
    console.log(newVal, oldVal)
})

//监听复杂类型中的属性
let obj = reactive({
    age:10
});
let {age} = toRefs(obj);
watch(age, (newVal, oldVal) => {
    console.log(newVal, oldVal)
})

watch(() => obj.age, (newVal, oldVal) => {
    console.log(newVal, oldVal)
})

watch([() => obj.age], (newVal, oldVal) => {
    console.log(newVal, oldVal)
})

//页面刷新的时候就立即监听
watchEffect(() => {

})

4.computed计算属性

import { computed, reactive, ref } from "vue";

let num = ref(10);
let doubleNum = computed(() => {
  return num.value * 2;
});

let obj = reactive({
  age: 10,
});

let doubleAge = computed(() => {
  return obj.age * 2;
});

let name=ref('张三');
let checkAll = computed({
    get(){
        return name.value;
    },
    set(val){
        name.value = "我是:"+val;
    }
});
checkAll.value = '李四';
console.log(checkAll.value);

5.父子组件

//------第一种写法

父组件:

<script setup lang="ts">
import {ref} from 'vue';
//子组件
import child from './child.vue'

let num = ref(1000);

const submit = (n:number)=>{
  num.value = n;
}
</script>

<template>
  <child :num="num" @submit="submit"></child>
</template>

子组件:

<template>
  <p>参数:{{ num }}</p>
  <button @click="toSubmit">提交</button>
</template>

<script lang="ts" setup>
//子组件接受参数
defineProps({
  num: {
    type: Number,
    default: 10,
  },
});

//ts中子传父
const emits = defineEmits<{(event: "submit", id: number): void }>();

const toSubmit = () => {
  emits("submit", 12);
};
</script>
<style lang="scss" scoped></style>

//------第二种写法

父组件:

<script setup lang="ts">
import {ref} from 'vue';
//子组件
import child from './child.vue'

let num = ref(1000);
</script>

<template>
  <child v-model:num="num"></child>
</template>

<style scoped>
</style>

子组件:

<template>
  <p>参数:{{ num }}</p>
  <button @click="toSubmit">提交</button>
</template>

<script lang="ts" setup>
//子组件接受参数
defineProps({
  num: {
    type: Number,
    default: 10,
  },
});

//ts中子传父,update:固定写法,后面的变量就是父组件中的v-model:后面的这个变量
const emits = defineEmits<{(event: "update:num", id: number): void }>();

const toSubmit = () => {
  emits("update:num", 12);
};
</script>
<style lang="scss" scoped></style>

6.匿名插槽和具名插槽

父组件:

</script>

<template>
  <child>
    <p>我是匿名插槽的内容</p>
    <!-- <template v-slot:first> -->
    <!-- 简写# -->
    <template #first>
      我是具名插槽的内容
    </template>
  </child>
</template>

子组件:

  <template>
    <slot name="first">
    </slot>

    <p>插槽测试</p>
    <slot></slot>
</template>

7.全局配置

需求:把userType接口定义为全局。

实现步骤:

7.1 新建一个userType.d.ts文件,文件内容:

interface UserType{
    name:string,
    age:number
}

7.2 修改tsconfig.json文件中的include,添加userType.d.ts的文件路径(其中**表示所有文件夹)。在inlucde中配置的文件内容都是全局引用的。

image.png

8.Teleport传送门,可以把组件传送到指定位置

  <!-- 能够把里面的内容跟传送到指定位置的最后的位置 -->
  <Teleport to="#app">
    <p>测试传送</p>
  </Teleport>
  

9.类型增强

declare var globalVar:string;//声明全局变量
declare var globalObj:UserType;
declare function globalFun(a:number):void;//声明全局方法

declare class 声明全局类;
declare enum 声明全局枚举类型;
declare type 声明全局类型;
declare interface 声明全局接口;
declare namespace 声明(含有某方法的)全局对象;

9.1 在index.html中添加:

<script>
  var globalVar = "test";
</script>
    

9.2 在vue中使用会飘红,但是实际是有值的。(声明下就可以不会飘红了)

image.png

10.指令

hasPermi指令:

import {userStore} from '@/store/user'

export default {
  mounted(el, binding, vnode) {
    const { value } = binding
    const all_permission = "*:*:*";

    const store = userStore();
    const permissions = store.permissions
    if (value && value instanceof Array && value.length > 0) {
      const permissionFlag = value

      const hasPermissions = permissions.some(permission => {
        return all_permission === permission || permissionFlag.includes(permission)
      })

      if (!hasPermissions) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置操作权限标签值`)
    }
  }
}

绑定到全局:

import hasPermi from './hasPermi'
app.directive('hasPermi', hasPermi)

三、配置项目路径别名

1.tsconfig.json文件中配置baseUrl和path

image.png

2.vite.config.ts文件中修改

image.png