Vue3 + setup + ts 使用总结

news/2024/5/20 3:17:59 标签: vue3, setup, ts

在这里插入图片描述

阅读vue的英文官网

中文的vue官网比vue的英文官网差很多,这个其实很容易理解,毕竟vue是服务于全球的开源项目之一。

所以程序员的第一生产力还是英语

不管学什么都要去获取第一手资料,不要看中文官网,直接去看英文官网

vite初始化项目

1、pnpm create vite
在这里插入图片描述
2、选择vue,再选择typescript
3、启动项目

  cd test1
  pnpm install
  pnpm run dev

vscode的vue代码片段

根据自己的使用习惯,设置vscode的vue代码片段,推荐使用snippet-generator.app

"vue3模版": {

"prefix": "vue3",

"body": [

   "<template>",

       " <div class='${1:box}'></div>",

   "</template>",

   " ",

   "<script setup lang='ts'>",

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

   " ${3}",

   "</script>",

   " ",

   "<style lang=\"scss\" scoped>",

   " .${2:box} {",

   " }",

   "</style>"

],

"description": "vue3模版"

}

另外vscode的不仅可以设置vue的代码片段,理论上你在vscode上写的任何代码,都可以设置成代码片段,方便自己以后使用。

这个自己根据自己的个人习惯,自己挖掘。

另外因为使用vscode开发vue的typescript项目,vscode还需要安装对应的插件,比如TypeScript Vue Plugin

在这里插入图片描述

3. 组件引入

当使用setup的时候,组件直接引入就可以了,不需要再自己手动注册

4. ref和reactive

ref一般用于基本的数据类型,比如string,boolean
reactive一般用于对象
使用reactive的注意事项:

1、reactive不能用于string,number,boolean
vue官方网站说明如下:It cannot hold primitive types such as string, number or boolean
2、不能修改reactive设置的值
比如:

let state = reactive({ count: 0 }) 
// the above reference ({ count: 0 }) is no longer being tracked (reactivity connection // is lost!) 
// 这里state如果重新赋值以后,vue就不能双向绑定
state = reactive({ count: 1 })

ref的底层实现,其实也是调用的reactive实现的,有点类似react hooks的useState和useReducer;

tsdefineProps_96">5. defineEmits和defineProps获取父组件传过来值和事件

// 第一种不带默认值props
const props = defineProps<{
  foo: string
  bar?: number
}>()
// 第二种带默认值props

export interface ChildProps {
  foo: string
  bar?: number
}

const props = withDefaults(defineProps<ChildProps>(), {
   foo: "1qsd"
   bar?: 3
})

// 第一种获取事件
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 第二种获取事件
 
const emit = defineEmits(["dosth"])

ts_128">6. 使用useAttrs和useSlots

useAttrs 可以获取父组件传过来的id和class等值。
useSlots 可以获得插槽的内容。
例子中,我们使用useAttrs获取父组件传过来的id和class,useSlots获取插槽的内容。
父组件:

<template>

    <div class="father">{{ fatherRef }}</div>

    <Child :fatherRef="fatherRef" @changeVal="changeVal" class="btn" id="111">

        <template #test1>

        <div>1223</div>

        </template>

    </Child>

</template>

<script setup lang="ts">

import { ref } from "vue";

import Child from "./Child.vue";

const fatherRef = ref("1");

function changeVal(val: string) {

    fatherRef.value = val;

}

</script>

<style lang="scss" scoped>

.father {

    margin-top: 40px;

    margin-bottom: 40px;

}

.btn {

    font-size: 20px;

    color: red;

}

</style>

子组件:

<template>

    <!-- <div class="child">{{ props.fatherRef }}</div> -->

    <div v-bind="attrs">

        <slot name="test1">11</slot>

        <input type="text" v-model="inputVal" />

    </div>

</template>

<script setup lang="ts">

import { computed, useAttrs, useSlots } from "vue";

const props = defineProps<{

    fatherRef: string;

}>();

const emits = defineEmits(["changeVal"]);

const slots = useSlots();

const attrs = useAttrs();

console.log(122, attrs, slots);

const inputVal = computed({

    get() {

        return props.fatherRef;

    },

    set(val: string) {

        emits("changeVal", val);

    },

});

</script>


<style lang="scss" scoped>

.child {

}

</style>

使用自定义指令

setup里边自定义指令的时候,只需要遵循vNameOfDirective 这样的命名规范就可以了

比如如下自定义focus指令,命名就是vMyFocus,使用的就是v-my-focus

自定义指令

<script setup lang="ts">
const vMyFocus = {
  onMounted: (el: HTMLInputElement) => {
    el.focus();
    // 在元素上做些操作
  },
};
</script>
<template>
  <input v-my-focus value="111" />
</template>


7. 使用defineExpose子组件传父组件

子组件

<template>

    <div class="child"></div>

</template>


<script setup lang="ts">

import { ref, reactive } from "vue";

function doSth() {

    console.log(333);

}

defineExpose({ doSth });

</script>


<style lang="scss" scoped>

.child {

}

</style>

父组件

<template>

<div class="father" @click="doSth1">222</div>

    <Child ref="childRef"></Child>

</template>

<script setup lang="ts">

import { ref, reactive } from "vue";

import Child from "./Child.vue";

const childRef = ref();

function doSth1() {

    childRef.value.doSth();

}

</script>

<style lang="scss" scoped>

.father {

}

</style>

8. 父组件传子组件

父组件

<template>

    <div class="father"></div>

    <Child @click="doSth"></Child>

</template>

<script setup lang="ts">

import { ref, reactive } from "vue";

import Child from "./Child.vue";

function doSth() {

    console.log(112);

}

</script>

<style lang="scss" scoped>

.father {

}

</style>

9. toRefs

当从父组件向子组件传props的时候,必须使用toRefs或者toRef进行转一下,这是为什么呢?
这里是因为如果不使用toRefs转一次的话,当父组件中的props改变的时候,子组件如果使用了Es6的解析,会失去响应性。
可以看下如下例子
父组件

<template>

<div class="father" @click="changeVal">{{ fatherRef }}</div>

    <Child :fatherRef="fatherRef"></Child>

</template>

<script setup lang="ts">

import { ref, reactive } from "vue";

import Child from "./Child.vue";

const fatherRef = ref(1);

function changeVal() {

    fatherRef.value = 2;

}

</script>

<style lang="scss" scoped>

.father {

    margin-bottom: 40px;

}

</style>

子组件

<template>

    <div class="child" @click="changeVal">{{ fatherRef }}</div>

</template>

<script setup lang="ts">

import { ref, reactive, onMounted, toRefs } from "vue";

const props = defineProps<{

    fatherRef: any;

}>();

const { fatherRef } = props;

function changeVal() {

    fatherRef.value = 34;
}

</script>

<style lang="scss" scoped>

.child {

}

</style>

可以看到当父组件如果点击之后,因为使用const { fatherRef } = props;进行解析,就失去了响应性
所以当父组件变成2的时候,子组件还是1。
这里有两种解决办法

使用const { fatherRef } = toRefs(props);
在模版中中使用props.fatherRef

10. 子组件使用v-model

10.1 可以在子组件中使用computed,实现双向绑定
父组件

<template>

    <div class="father">{{ fatherRef }}</div>

    <Child :fatherRef="fatherRef" @changeVal="changeVal"></Child>

</template>

<script setup lang="ts">

import { ref } from "vue";

import Child from "./Child.vue";

const fatherRef = ref("1");

function changeVal(val: string) {

    fatherRef.value = val;

}

</script>


<style lang="scss" scoped>

.father {

    margin-top: 40px;

    margin-bottom: 40px;

}

</style>

子组件

<template>

    <!-- <div class="child">{{ props.fatherRef }}</div> -->

    <input type="text" v-model="inputVal" />

</template>

<script setup lang="ts">

import { computed } from "vue";

const props = defineProps<{

    fatherRef: string;

}>();

const emits = defineEmits(["changeVal"]);


const inputVal = computed({

    get() {

        return props.fatherRef;

    },

    set(val: string) {

        emits("changeVal", val);

    },

});

</script>

<style lang="scss" scoped>

.child {

}

</style>

10.2 可以从父组件传递值和改变值的方法,然后子组件也可以使用v-model
例子中父组件传递 modelValue和update:modelValue方法 父组件:

<template>

    <Child :modelValue="searchText" @update:modelValue="changeVal"> </Child>

</template>

<script setup lang="ts">

import { ref } from "vue";

import Child from "./Child.vue";

const searchText = ref(1);

function changeVal(val: number) {

    searchText.value = val;

}

</script>

<style lang="scss" scoped>

.father {

    margin-top: 40px;

    margin-bottom: 40px;

}

.btn {

    font-size: 20px;

    color: red;

}

</style>

子组件:

<template>

    <!-- <div class="child">{{ props.fatherRef }}</div> -->

    <!-- <div v-bind="attrs">

        <slot name="test1">11</slot>

        <input type="text" v-model="inputVal" />

    </div> -->

    <input v-model="modelValue" />

</template>


<script setup lang="ts">

import { computed, useAttrs, useSlots } from "vue";

const props = defineProps<{

    modelValue: number;

}>();

// const emits = defineEmits(["changeVal"]);

</script>


<style lang="scss" scoped>

.child {

}

</style>

11. 递归组件

组件本身是可以调用组件自身的,也就是递归。 比如名为 Child.vue 的组件可以在其模板中用 引用它自己。这里需要注意的是需要设置条件语句,用来中断递归,不然递归会无限递归下去。

父组件

<template>
  <Child :modelValue="searchText" @update:modelValue="changeVal"> </Child>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const searchText = ref(1);
function changeVal(val: number) {
  searchText.value = val;
}
</script>

<style lang="scss" scoped>
.father {
  margin-top: 40px;
  margin-bottom: 40px;
}
.btn {
  font-size: 20px;
  color: red;
}
</style>

子组件

<template>
  <input v-model="modelValue" />
  <Child
    :modelValue="test"
    @update:modelValue="changeTest"
    v-if="modelValue > 2"
  ></Child>
</template>

<script setup lang="ts">
import { computed, useAttrs, useSlots, ref } from "vue";
const props = defineProps<{
  modelValue: number;
}>();
const test = ref(0);
function changeTest(val: number) {
  test.value = val;
}

// const emits = defineEmits(["changeVal"]);
</script>

<style lang="scss" scoped>
.child {
  position: relative;
}
</style>


http://www.niftyadmin.cn/n/113260.html

相关文章

C#开发的OpenRA的界面布局数据加载

C#开发的OpenRA的界面布局数据加载 当显示完成加载界面之后,就是进行其它内容处理。 因为后面内容的加载会比较长时间,所以首先显示加载界面是一种非常友好的方法。 因此在软件设计里,尽可能先显示界面,让用户先看到程序正在运行, 然后再处理时间长的加载。如果不这样做,…

深度包检测(DPI)详细介绍

以前不了解这个&#xff0c;一个应聘职位是这个方面的&#xff0c;就在网上收集这个资料&#xff0c;了解了这个方面的资料&#xff0c;其实&#xff0c;这个核心是自然语言识别。 目录 简介背景 流量识别 常用功能具体功能 做法特征识别架构举例部署方式 串接方式并接方式存…

美团2面:如何保障 MySQL 和 Redis 数据一致性?这样答,让面试官爱到 死去活来

美团2面&#xff1a;如何保障 MySQL 和 Redis 的数据一致性&#xff1f; 说在前面 在尼恩的&#xff08;50&#xff09;读者社群中&#xff0c;经常遇到一个 非常、非常高频的一个面试题&#xff0c;但是很不好回答&#xff0c;类似如下&#xff1a; 如何保障 MySQL 和 Redis…

金三银四了,准备跳槽的可以看看....

前两天跟朋友感慨&#xff0c;去年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经3月了&#xff0c;具体的金三银四也已经到来了。 对于想跳槽的职场人来说&#xff0c;绝对要已经提前做准备了。这时候&#xff0c;很多高薪技术岗、管理岗的缺口和市场需求也出来了。…

这么简单的 CSS 动效,快来瞧瞧

前言 这几天逛网站浏览网页的时候&#xff0c;看到一个不错的CSS效果&#xff0c;便想来实现一下。整个效果实现起来比较简单&#xff0c;但是并不缺少交互感&#xff0c;因此来分享一下这个CSS效果。 效果展示 HTML 搭建 HTML部分一如既往地简单&#xff0c;认清楚它的布局…

项目设计模式和规范

1、责任链模式 自己的理解:避免发生方与接收方解耦 优点:①降低发送方与接收方的耦合 ②简化他们对象 ③方便扩展新增 处理者 缺点:①不方便排错 ②性能问题,且使用不当容易搞出死循环 应用场景:拦截器 Interceptor和过滤器 filter:符合模式的进行拦截或者过滤到,然…

SQLSERVER数据库质疑解决方案

ALTER DATABASE ICYQSHSF SET EMERGENCY --1、修改数据库为紧急模式 ALTER DATABASE ICYQSHSF SET SINGLE_USER --2、使数据库变为单用户模式 DBCC CheckDB (ICYQSHSF, REPAIR_ALLOW_DATA_LOSS) --3、修复数据库日志重新生成&#xff0c;此命令检查的分配&#xff0c;结构&a…

Flink 异常 - 13.getSideOutput 侧输出流无数据

目录 一.引言 二.异常分析 1.问题描述 2.官方 API 代码示例 3.问题定位 4.代码复现