Vue学习

vite

1.创建

npm create vue@latest

2.命名

不能出现中文和特殊字符

建议纯小写字母数字下划线

3.其余选项根据项目需要进行选择

4.建议安装的插件

TypeScript Vue Plugin(Volar)
Vue Language Features(Volar)

5.文件与文件夹说明

1.vscode
建议安装的插件

2.public
存放页签图表

3.src
源代码文件

main.ts

createApp创建App组件挂载到id为app的标签上

4.gitignore
git的忽略文件

5.env.d.ts
安装依赖后就不暴红了,用来让vue认识各种文件

npm i

引入完成最好重启

6.index.html
打开展示的页面

7.package-lock.json\package.json
包管理文件

8.README.md
对项目的介绍

9.tsconfig.app.json\tsconfig.json\tsconfig.node.json
ts配置文件

10.vite.config.ts
整个工程的配置文件

2.给组件命名(别名)

方法一:全用vue2方法

<script lang="ts">
	export default{
		name:"名字"
	}
</script>

方法二:采用vue2加vue3

<script lang="ts">
	export {
		name:"名字"
	}
</script>
<script lang="ts" setup>

</script>

方法三:引入依赖

npm i vite-plugin-vue-setup-extend -D

vite.config.ts

// 一
import VueSetupExtend from "vite-plugin-vue-setup-extend";
// 二在plugins中追加
VueSetupExtend()
// 在script中加入name属性例如
<script lang="ts" setup name="person132"></script>

3.响应式数据

3.1ref

3.1.1定义基本类型数据

1.引入ref

import { ref } from "vue";

2.通过ref给基本数据类型赋值

let name = ref("李四");

3.如果要赋值需要用.value去取出对象中的值,原因是通过ref,该基本数据类型变成了RefImpl对象

name.value = "张三";
<template>
<div class="person">
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="showTel">查看联系方式</button>
    </div>
</template>

<script lang="ts" setup name="person132">
    // 引入ref
    import { ref } from "vue";
    let name = ref("李四");
    let age = ref(18);
    let tel = "1354687955";
    // 这里要改变必须要用.value,上面的插值语法不需要,上面会制动解析对象
    function changeName() {
        name.value = "张三";
    }
    function changeAge() {
        age.value = age.value + 1;
    }
    function showTel() {
        alert(tel);
    }
</script>

image

3.1.2定义对象类型的响应式数据

<template>
<div class="car">
   <h2>一辆{{ car.brand }}车,价值{{ car.price }}W</h2>
   <button @click="changePrice">修改汽车的价值</button>
   </div>
</template>

<script lang="ts" setup name="person132">
   import { ref } from "vue";
   let car = ref({ brand: "奔驰", price: 100 });
   // 查看一下被ref修饰的对象,底层还是用了reactive,被套了两层
   console.log(car);
   function changePrice() {
       car.value.price += 10;
       console.log(car.value.price);
   }
</script>

3.2reactive

只能定义对象类型的响应式布局

1.引入reactive

import { reactive } from "vue";

2.通过reactive给对象赋值

let car = reactive({ brand: "奔驰", price: 100 });
<template>
<div class="car">
   <h2>一辆{{ car.brand }}车,价值{{ car.price }}W</h2>
   <button @click="changePrice">修改汽车的价值</button>
   </div>
</template>

<script lang="ts" setup name="person132">
   import { reactive } from "vue";
   let car = reactive({ brand: "奔驰", price: 100 });
   // 查看一下被reactive修饰的对象,又被Proxy修饰一层
   console.log(car);
   function changePrice() {
       car.price += 10;
       console.log(car.price);
   }
</script>

3.3ref对比reactive

3.3.1宏观角度

1.ref用来定义:基本数据类型、对象数据类型
2.reactive只能用来定义对象类型数据

3.3.2区别

1.用ref定义必须写.value才能修改值
2.用reactive重新分配一个新对象,会失去响应式式,可以用Object.assign去整体替换

<template>
<div class="car">
   <h2>一辆{{ car.brand }}车,价值{{ car.price }}W</h2>
   <button @click="changeCar">修改整个汽车</button>
   </div>
</template>

<script lang="ts" setup name="person132">
   import { reactive } from "vue";
   let car = reactive({ brand: "奔驰", price: 100 });
   function changeCar() {
       // 错误
       // car = { brand: "特斯拉", price: 99 };
       // 错误
       // car = reactive({ brand: "特斯拉", price: 99 });
       // 正确
       Object.assign(car, { brand: "特斯拉", price: 99, name: "啊哈哈" });
   }
</script>

3.如果用ref不会出现2中的问题,我觉得问题在于用ref修改的是其中一个属性,reactive是整个替换

car.value={ brand: "特斯拉", price: 99 };

3.3.3使用规则

1.若需要一个基本类型的响应式数据,必须使用ref 。
2.若需要一个响应式对象,层级不深ref 、 reactive都可以。
3.若需要一个响应式对象,且层级较深,推荐使用reactive。

4.toRefs与toRef

都是用来解构reactive

toRefs

<template>
<div class="car">
    <h2>姓名{{ name }}</h2>
    <h2>年龄{{ age }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    </div>
</template>

<script lang="ts" setup name="person132">
    import { reactive, toRefs } from "vue";
    let Person = reactive({
        name: "张三",
        age: 18,
    });
    // 解构:写法一
    let { name, age } = toRefs(Person);
    function changeName() {
        name.value += "~";
        console.log(name);
    }
    function changeAge() {
        age.value += 1;
        console.log(age);
    }
</script>

toRef

<template>
<div class="car">
    <h2>姓名{{ name }}</h2>
    <h2>年龄{{ age }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    </div>
</template>

<script lang="ts" setup name="person132">
    import { reactive, toRef } from "vue";
    let Person = reactive({
        name: "张三",
        age: 18,
    });
    // 解构:写法二
    let name = toRef(Person, "name");
    let age = toRef(Person, "age");

    function changeName() {
        name.value += "~";
        console.log(name);
    }
    function changeAge() {
        age.value += 1;
        console.log(age);
    }
</script>

5.computed计算属性

计算属性有缓存,方法没缓存
计算属性是只读的不能修改

<template>
<div class="car">
    姓:<input type="text" v-model="firstName" /> <br />
    名:<input type="text" v-model="lastName" /> <br />
    全名:<span>{{ fullName }}</span>
    </div>
</template>

<script lang="ts" setup name="person132">
    import { ref, computed } from "vue";
    let firstName = ref("zhang");
    let lastName = ref("san");
    // 计算属性
    let fullName = computed(() => {
        return (
            firstName.value.slice(0, 1).toUpperCase() +
            firstName.value.slice(1) +
            "-" +
            lastName.value
        );
    });
</script>

如何让计算属性可读可写

<template>
<div class="car">
    姓:<input type="text" v-model="firstName" /> <br />
    名:<input type="text" v-model="lastName" /> <br />
    全名:<span>{{ fullName }}</span>
    <button @click="changeFullName">修改姓名</button>
    </div>
</template>

<script lang="ts" setup name="person132">
    import { ref, computed } from "vue";
    let firstName = ref("zhang");
    let lastName = ref("san");
    // 计算属性
    let fullName = computed({
        get() {
            return (
                firstName.value.slice(0, 1).toUpperCase() +
                firstName.value.slice(1) +
                "-" +
                lastName.value
            );
        },
        set(val) {
            console.log(val);
            const [a, b] = val.split(",");
            firstName.value = a;
            lastName.value = b;
        },
    });
    function changeFullName() {
        fullName.value = "li,ming";
    }
</script>

6.watch

6.1作用

监视数据的变化(同Vue2中的watch作用一致)

6.2特点

Vue3中的watch只能监视一下四种数据

1.ref定义的数据
2.reactive定义的数据
3.函数返回一个值(getter函数)
4.一个包含上述内容的数组

6.3可能遇到的五种情况

6.3.1情况一:监视ref定义的基本类型数据

<template>
<div class="person">
    <h2>当前求和数{{ sum }}</h2>
    <button @click="changeSum">点击加一</button>
    </div>
</template>
<script lang="ts" setup>
    import { ref, watch } from "vue";
    let sum = ref(0);
    function changeSum() {
        sum.value += 1;
    }
    // watch是一个函数,第一个参数不写.value
    const stopWatch = watch(sum, (newValue, oldValue) => {
        console.log("sum变化了", newValue, oldValue);
        if (newValue >= 10) {
            // 调用watch就停止监视了
            stopWatch();
        }
    });
</script>

6.3.2情况二:监视ref定义的对象类型数据

<template>
<div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
    </div>
</template>
<script lang="ts" setup>
    import { ref, watch } from "vue";
    let person = ref({
        name: "张三",
        age: 18,
    });
    function changeName() {
        person.value.name += "~";
    }

    function changeAge() {
        person.value.age += 1;
    }

    function changePerson() {
        person.value = {
            name: "李四",
            age: 20,
        };
    }
    // 监视,情况二:监视ref定义的对象类型数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
    // watch的第一个参数是:被监视的数据
    // watch的第二个参数是:监视的回调
    // watch的第三个参数是:配置对象(deep、immediate等)
    watch(
        person,
        (a, b) => {
            console.log("person变化了", a, b);
        },
        {
            deep: true,
        }
    );
</script>

6.3.3情况三:监视reactive定义的对象类型数据

<template>
<div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
    </div>
</template>
<script lang="ts" setup>
    import { reactive, watch } from "vue";
    let person = reactive({
        name: "张三",
        age: 18,
    });
    function changeName() {
        person.name += "~";
    }

    function changeAge() {
        person.age += 1;
    }

    function changePerson() {
        Object.assign(person, { name: "李四", age: 20 });
    }

    // reactive定义的对象,默认开启深度监视,并且关不掉
    // 新值和旧值相同因为地址并没有改变
    watch(person, (newValue, oldValue) => {
        console.log("person修改了", newValue, oldValue);
    });
</script>

6.3.4情况四:监视reactive定义的对象类型数据,中的基本类型数据或对象类型数据中的一个属性

<template>
<div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeCar1">修改第一台车</button>
    <button @click="changeCar2">修改第二台车</button>
    <button @click="changeCar">修改全部车</button>
    </div>
</template>
<script lang="ts" setup>
    import { reactive, watch } from "vue";
    let person = reactive({
        name: "张三",
        age: 18,
        car: {
            c1: "奔驰",
            c2: "宝马",
        },
    });

    function changeName() {
        person.name += "~";
    }
    function changeAge() {
        person.age += 1;
    }
    function changeCar1() {
        person.car.c1 = "大牛";
    }
    function changeCar2() {
        person.car.c2 = "特斯拉";
    }
    function changeCar() {
        person.car = {
            c1: "红旗",
            c2: "五菱宏光",
        };
    }
    // 1.监视reactive中的一个属性,并且该属性是基本类型
    // watch(
    //   () => {
    //     return person.name;
    //   },
    //   (a, b) => {
    //     console.log("person.name变化了", a, b);
    //   }
    // );

    // 2.监视reactive中的一个属性,并且该属性是一个对象
    // 2.1可以直接写person.car,这样写内监视其中一个值变化时进行监控,整个地址变化时不能监视
    // watch(person.car, (a, b) => {
    //   console.log("person.car变化了", a, b);
    // });

    // 2.2写成函数式(建议),不开启深度监视时,只能监视地址变化,不能监视其中具体值的变化,开启后可以
    watch(
        () => person.car,
        (a, b) => {
            console.log("person.car变化了", a, b);
        },
        { deep: true }
    );
</script>

6.3.5情况五:监视多个数据(用到了数组)

<template>
<div class="person">
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改姓名</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeCar1">修改第一台车</button>
    <button @click="changeCar2">修改第二台车</button>
    <button @click="changeCar">修改全部车</button>
    </div>
</template>
<script lang="ts" setup>
    import { reactive, watch } from "vue";
    let person = reactive({
        name: "张三",
        age: 18,
        car: {
            c1: "奔驰",
            c2: "宝马",
        },
    });

    function changeName() {
        person.name += "~";
    }
    function changeAge() {
        person.age += 1;
    }
    function changeCar1() {
        person.car.c1 = "大牛";
    }
    function changeCar2() {
        person.car.c2 = "特斯拉";
    }
    function changeCar() {
        person.car = {
            c1: "红旗",
            c2: "五菱宏光",
        };
    }
    // 监视名和车
    watch(
        [() => person.name, () => person.car],
        (a, b) => {
            console.log("person.car变化了", a, b);
        },
        { deep: true }
    );
</script>

7.watchEffect

开始就执行,不需要像watch那样需要传入具体要监视的值,直接写需要监视的内容就行

<template>
<div class="person">
    <h2>需求水温达到15℃或水位达到30cm发送请求</h2>
    <h2>当前水温:{{ temp }}℃</h2>
    <h2>当前水位:{{ height }}cm</h2>
    <button @click="changeTemp">水温加1</button>
    <button @click="changeHeight">水位加10</button>
    </div>
</template>
<script lang="ts" setup>
    import { ref, watch, watchEffect } from "vue";
    let temp = ref(10);
    let height = ref(2);

    function changeTemp() {
        temp.value += 1;
    }

    function changeHeight() {
        height.value += 10;
    }

    // watch实现
    // watch([temp, height], (value) => {
    //   let [newTemp, newHeight] = value;
    //   console.log(newTemp, newHeight);
    //   if (newTemp >= 15 || newHeight >= 30) {
    //     console.log("到了");
    //   }
    // });

    // watchEffect实现
    watchEffect(() => {
        if (temp.value >= 15 || height.value >= 30) {
            console.log("到了");
        }
    });
</script>

8.接口泛型

8.1接口

export interface PersonInter {
  id: string;
  name: string;
  age: number;
}

// 一个自定义类型写法一
// export type Persons = Array<PersonInter>;

// 一个自定义类型写法二
export type Persons = PersonInter[];

8.2使用

// @会找顶层,默认找index.ts
import { type PersonInter, type Persons } from "@/types";
// 只写一个实体
// let person:PersonInter = { id: "afdsljk01", name: "张三", age: 60 };

// 数组写法1
// let personList: Array<PersonInter> = [
//   { id: "afdsljk01", name: "张三", age: 60 },
//   { id: "afdsljk02", name: "李四", age: 60 },
//   { id: "afdsljk03", name: "王五", age: 60 },
// ];

// 数组写法2
let personList: Persons = [
    { id: "afdsljk01", name: "张三", age: 60 },
    { id: "afdsljk02", name: "李四", age: 60 },
    { id: "afdsljk03", name: "王五", age: 60 },
];

9.组件通信

9.1父子传递通过props

案例一:

<!-- 父对象 -->
<template>
<WoodenFish a="你好" :list1="personList" />
</template>

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

    // 导入子组件
    import WoodenFish from "./components/WoodenFish.vue";
    import { type Persons } from "@/types";

    let personList = reactive<Persons>([
        { id: "asdf01", name: "张三", age: 18 },
        { id: "asdf02", name: "李四", age: 19 },
        { id: "asdf03", name: "王五", age: 20 },
    ]);
</script>

<style scoped></style>
<!-- 子对象 -->
<template>
<div class="person">
    <!-- <h2>{{ a }}</h2> -->
    <ul>
        <li v-for="p in list1" :key="p.id">{{ p.name }} -- {{ p.age }}</li>
    </ul>
    </div>
</template>
<script lang="ts" setup>
    import { withDefaults } from "vue";
    import { type Persons } from "@/types";
    // 只接受list1
    // let x = defineProps(["a", "list1"]);
    // console.log(x.a);

    // 接收list1 + 限制类型
    // defineProps<{ list1: Persons }>();

    // 接收list1 + 限制类型 + 限制必要性 + 指定默认值
    withDefaults(defineProps<{ list1?: Persons }>(), {
        list1: () => [{ id: "fasdffas011", name: "浩浩", age: 19 }],
    });
</script>

<style scoped></style>

案例二

<!-- 父 -->
<template>
<div class="father">
    <h3>父组件</h3>
    <h4>汽车:{{ car }}</h4>
    <h4 v-show="toy">子给父:{{ toy }}</h4>
    <!-- 在这里传过去 -->
    <Son1 :car="car" :sendToy="getToy" />
    </div>
</template>
<script setup lang="ts">
    import Son1 from "./Son1.vue";
    import { ref } from "vue";
    // 父传子直接传就行
    let car = ref("特斯拉");
    let toy = ref("");
    // 通过props子传父需要父给子一个函数
    function getToy(value: string) {
        toy.value = value;
    }
</script>
<style scoped>
    .father {
        background-color: rgb(165, 164, 164);
        padding: 20px;
        border-radius: 10px;
    }
</style>

<!-- 子 -->
<template>
<div class="son1">
    <h3>子组件</h3>
    <h4>玩具:{{ toy }}</h4>
    <h4>父亲的车:{{ car }}</h4>
    </div>
</template>
<script setup lang="ts">
    import { ref } from "vue";
    let toy = ref("八哥");
    // 接收数据
    const x = defineProps(["car", "sendToy"]);
    x.sendToy(toy.value);
</script>
<style scoped>
    .son1 {
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
    }
</style>

9.2子传父emit

<!-- 父 -->
<template>
<div class="father">
    <h3>父组件</h3>
    <h4 v-show="toy">子给父:{{ toy }}</h4>
    <!-- 在这里传过来,xxx-xxx规范 -->
    <Son1 @send-toy="saveToy" />
    </div>
</template>
<script setup lang="ts">
    import Son1 from "./Son1.vue";
    import { ref } from "vue";
    // 子传父
    let toy = ref("");
    // 通过props子传父需要父给子一个函数
    function saveToy(value: string) {
        toy.value = value;
    }
</script>
<style scoped>
    .father {
        background-color: rgb(165, 164, 164);
        padding: 20px;
        border-radius: 10px;
    }
</style>
<!-- 子 -->
<template>
<div class="son1">
    <h3>子组件</h3>
    <h4>玩具:{{ toy }}</h4>
    </div>
</template>
<script setup lang="ts">
    import { ref } from "vue";
    let toy = ref("八哥");
    // 给父传递数据
    const emit = defineEmits(["send-toy"]);
    // 调用
    emit("send-toy", toy.value);
</script>
<style scoped>
    .son1 {
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
    }
</style>

9.3mitt

安装

npm i mitt

配置

utils emitter.ts

// 引入mitt
import mitt from "mitt";

// 调用mitt得到emitter,emitter能绑定事件、触发事件
const emitter = mitt();


// 举例开始,举例的东西不写
// 绑定事件
emitter.on("test1", () => {
  console.log("test1被调用");
});
// 触发事件
setInterval(() => {
  emitter.emit("test1");
}, 1000);

// 解绑事件
setTimeout(() => {
  emitter.off("test1");
}, 5000);

// 全部解绑
// emitter.all.clear();
// 举例结束


// 暴露emitter
export default emitter;

用上面的举例采写,不用不需要引用main.ts

// 引入mitt,不需要调用
import emitter from "@/utils/emitter";

举例二

<!-- 哥哥 -->
<template>
<div class="son1">
    <h3>子组件1</h3>
    <h4>玩具:{{ toy }}</h4>
    <!-- <button @click="emitter.emit('send-toy', toy)">ann</button> -->
    </div>
</template>
<script setup lang="ts">
    import { ref } from "vue";
    import emitter from "@/utils/emitter";

    let toy = ref("坦克");
    emitter.emit("send-toy", toy.value);
    console.log("");
</script>
<style scoped>
    .son1 {
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
    }
</style>
<!-- 弟弟 -->
<template>
<div class="son2">
    <h3>子组件2</h3>
    <h4>自己的{{ toy }}</h4>
    <h4>兄弟的{{ ftoy }}</h4>
    </div>
</template>
<script setup lang="ts">
    import emitter from "@/utils/emitter";
    import { ref, onUnmounted } from "vue";
    let toy = ref("飞机");
    let ftoy = ref("");
    // 给emitter 绑定send-toy事件

    emitter.on("send-toy", (value: any) => {
        ftoy.value = value;
    });
    onUnmounted(() => {
        emitter.off("send-toy");
    });
</script>
<style scoped>
    .son2 {
        background-color: skyblue;
        padding: 10px;
        box-shadow: 0 0 10px black;
        border-radius: 10px;
        margin-top: 20px;
    }
</style>

10.生命周期

创建、挂载、更新、销毁

10.1vue2

<!-- 父 -->
<template>
<Person v-if="isShow" />
</template>

<script>
    import Person from "./components/Person.vue";
    export default {
        name: "App",
        components: { Person },
        data() {
            return {
                isShow: true,
            };
        },
    };
</script>
<!-- 子 -->
<template>
<div class="person">
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="add">点我加1</button>
    </div>
</template>

<script>
    export default {
        /* eslint-disable */
        name: "Person",
        data() {
            return {
                sum: 1,
            };
        },
        methods: {
            add() {
                this.sum += 1;
            },
        },
        // 创建前的钩子
        beforeCreate() {
            console.log("创建前");
        },
        // 创建完毕的钩子
        created() {
            console.log("创建完毕");
        },
        //挂载前
        beforeMount() {
            console.log("挂载前");
        },
        // 挂载完毕
        mounted() {
            console.log("挂载完毕");
        },
        // 更新前
        beforeUpdate() {
            console.log("更新前");
        },
        // 更新完毕
        updated() {
            console.log("更新完毕");
        },
        // 销毁前
        beforeDestroy() {
            console.log("销毁前");
        },
        // 销毁完毕
        destroyed() {
            console.log("销毁完毕");
        },
    };
</script>

<style>
    .person {
        background-color: skyblueS;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

10.2vue3

<!-- 父 -->
<template>
<WoodenFish v-if="isShow" />
</template>

<script lang="ts" setup>
    import { ref } from "vue";

    // 导入子组件
    import WoodenFish from "./components/WoodenFish.vue";
    let isShow = ref(true);
</script>

<style scoped></style>
<!-- 子 -->
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="add">点击加1</button>
</template>
<script lang="ts" setup>
    import {
        ref,
        onBeforeMount,
        onMounted,
        onBeforeUpdate,
        onUpdated,
        onBeforeUnmount,
        onUnmounted,
    } from "vue";
    let sum = ref(0);
    function add() {
        sum.value += 1;
    }

    // 创建前
    console.log("创建前");
    // 挂在前
    onBeforeMount(() => {
        console.log("挂载前");
    });
    // 挂载后
    onMounted(() => {
        console.log("挂载后");
    });
    // 更新前
    onBeforeUpdate(() => {
        console.log("更新前");
    });
    // 更新后
    onUpdated(() => {
        console.log("更新后");
    });
    // 卸载前
    onBeforeUnmount(() => {
        console.log("卸载前");
    });
    // 卸载后
    onUnmounted(() => {
        console.log("卸载后");
    });
</script>

<style scoped></style>

11.axios

11.1安装axios

npm i axios

12.hooks

把script中的东西再细分

<template>
<h2>当期求和为:{{ sum }}</h2>
<button @click="add">点我加一</button>
<hr />
<img v-for="(dog, index) in dogList" :key="index" :src="dog" alt="" />
<hr />
<button @click="getDog">再来一只</button>
</template>

<script lang="ts" setup>
    import useSum from "@/hooks/useSum";
    import useDog from "@/hooks/useDog";
    const { sum, add } = useSum();
    const { dogList, getDog } = useDog();
</script>

<style scoped>
    .li {
        font-size: 20px;
    }
    img {
        width: 100px;
    }
</style>
import { reactive } from "vue";
import axios from "axios";

export default function () {
    let dogList = reactive([
        "https://images.dog.ceo/breeds/finnish-lapphund/mochilamvan.jpg",
    ]);

    async function getDog() {
        try {
            let result = await axios.get("https://dog.ceo/api/breeds/image/random");
            console.log(result.data.message);
            dogList.push(result.data.message);
        } catch (error) {
            alert(error);
        }
    }
    return { dogList, getDog };
}

import { ref } from "vue";
export default function () {
    let sum = ref(0);
    function add() {
        sum.value += 1;
    }
    return { sum, add };
}

13.路由

13.1路由的配置

npm i vue-router

main.ts

// 引用路由器
import router from "./router";
// 创建一个应用
const app = createApp(App);
// 使用路由器
app.use(router);
// 挂载整个app到容器中
app.mount("#app");

创建router文件夹及index.ts

// 创建一个路由器,并暴露出去

// 第一步:引入createRoutrt
import { createRouter, createWebHistory } from "vue-router";

// 引入一个一个可能要呈现的组件
import Home from "@/pages/Home.vue";
import News from "@/pages/News.vue";
import About from "@/pages/About.vue";

// 第二部:创建路由器
const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    routes: [
        {
            path: "/home",
            component: Home,
        },
        {
            path: "/news",
            component: News,
        },
        {
            path: "/about",
            component: About,
        },
    ],
});

// 暴露
export default router;

13.2路由的工作模式

history模式

优点:URL更加美观,不带有#,更接近传统的网站URL
缺点:后期项目上线,需要服务器端配合处理路径问题,否则刷新会有404错误

// Vue2
mode:"history"
// Vue3
history:createWebHistory()
// React
BrowserRouter

hash模式

优点:兼容性更好,因为不需要服务器端处理路径。
缺点:URL带有#不太美观,且在SEO优化方面相对较差

13.3路由跳转

import { RouterView, RouterLink } from "vue-router";
<!-- 字符串写法 -->
<RouterLink to="/home" active-class="hh">首页</RouterLink>
<!-- 对象写法 -->
<RouterLink :to="{ name: 'news1' }" active-class="hh">新闻</RouterLink>
<!-- 对象写法 -->
<RouterLink :to="{ path: '/about' }" active-class="hh">关于</RouterLink>

13.4query参数

<!-- 子 -->
<template>
<div class="news">
    <ul>
        <li v-for="news in newsList" :key="news.id">
            <!-- 方法一,用模板字符串 -->
            <!-- <RouterLink
:to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`"
>
{{ news.title }}</RouterLink
> -->
            <!-- 方法二 -->
            <RouterLink
                        :to="{
                             path: '/news/detail',
                             query: {
                             id: news.title,
                             title: news.title,
                             content: news.content,
                             },
                             }"
                        >
                {{ news.title }}</RouterLink
                >
    </li>
    </ul>
    <div class="news-content"><RouterView></RouterView></div>
    </div>
</template>
<script setup lang="ts" name="News">
    import { reactive } from "vue";
    import { RouterView, RouterLink } from "vue-router";

    const newsList = reactive([
        { id: "asdf01", title: "好啊后1", content: "ni" },
        { id: "asdf02", title: "好啊后2", content: "ni" },
        { id: "asdf03", title: "好啊后3", content: "ni" },
        { id: "asdf04", title: "好消息", content: "ni" },
    ]);
</script>
<style scoped>
    .news-content {
        height: 200px;
        width: 300px;
        background-color: aqua;
    }
</style>
<!-- 孙 -->
<template>
<ul class="news-list">
    <!-- 解构前 -->
    <!-- <li>编号:{{ route.query.id }}</li> -->
    <li>编号:{{ query.id }}</li>
    <li>标题:{{ query.title }}</li>
    <li>内容:{{ query.content }}</li>
    </ul>
</template>

<script setup lang="ts" name="Detail">
    // 解构对象用 toRefs 否则会失去响应式
    import { toRefs } from "vue";
    import { useRoute } from "vue-router";
    let route = useRoute();
    // 解构
    let { query } = toRefs(route);
</script>

<style scoped></style>

13.5params参数

<!-- 子 -->
<template>
<div class="news">
    <ul>
        <li v-for="news in newsList" :key="news.id">
            <!-- 方法一 -->
            <!-- <RouterLink
:to="`/news/detail/${news.id}/${news.title}/${news.content}`"
>
{{ news.title }}</RouterLink
> -->

            <!-- 方法二,这里的name用的是子标签的name -->
            <RouterLink
                        :to="{
                             name: 'xiang',
                             params: {
                             id: news.id,
                             title: news.title,
                             content: news.content,
                             },
                             }"
                        >
                {{ news.title }}</RouterLink
                >
    </li>
    </ul>
    <div class="news-content"><RouterView></RouterView></div>
    </div>
</template>
<script setup lang="ts" name="News">
    import { reactive } from "vue";
    import { RouterView, RouterLink } from "vue-router";

    const newsList = reactive([
        { id: "asdf01", title: "好啊后1", content: "ni" },
        { id: "asdf02", title: "好啊后2", content: "ni" },
        { id: "asdf03", title: "好啊后3", content: "ni" },
        { id: "asdf04", title: "好消息", content: "ni" },
    ]);
</script>
<style scoped>
    .news-content {
        height: 200px;
        width: 300px;
        background-color: aqua;
    }
</style>
<!-- 孙 -->
<template>
<ul class="news-list">
    <li>编号:{{ route.params.id }}</li>
    <li>标题:{{ route.params.title }}</li>
    <li>内容:{{ route.params.content }}</li>
    </ul>
</template>

<script setup lang="ts" name="Detail">
    import { useRoute } from "vue-router";
    const route = useRoute();
    console.log(route);
</script>

<style scoped></style>
<!-- 路由 -->
{
    name: "news",
        path: "/news",
            component: News,
                children: [
                    {
                        // ?参数的必要性
                        name: "xiang",
                        path: "detail/:id?/:title?/:content?",
                        component: Detail,
                    },
                ],
},

13.6props配置建议

<!-- 子 -->
<template>
<div class="news">
    <ul>
        <li v-for="news in newsList" :key="news.id">
            <!-- 方法一 -->
            <!-- <RouterLink
:to="`/news/detail/${news.id}/${news.title}/${news.content}`"
>
{{ news.title }}</RouterLink
> -->

            <!-- 方法二,这里的name用的是子标签的name -->
            <RouterLink
                        :to="{
                             name: 'xiang',
                             query: {
                             id: news.id,
                             title: news.title,
                             content: news.content,
                             },
                             }"
                        >
                {{ news.title }}</RouterLink
                >
    </li>
    </ul>
    <div class="news-content"><RouterView></RouterView></div>
    </div>
</template>
<script setup lang="ts" name="News">
    import { reactive } from "vue";
    import { RouterView, RouterLink } from "vue-router";

    const newsList = reactive([
        { id: "asdf01", title: "好啊后1", content: "ni" },
        { id: "asdf02", title: "好啊后2", content: "ni" },
        { id: "asdf03", title: "好啊后3", content: "ni" },
        { id: "asdf04", title: "好消息", content: "ni" },
    ]);
</script>
<style scoped>
    .news-content {
        height: 200px;
        width: 300px;
        background-color: aqua;
    }
</style>
<!-- 孙 -->
<template>
<ul class="news-list">
    <li>编号:{{ id }}</li>
    <li>标题:{{ title }}</li>
    <li>内容:{{ content }}</li>
    </ul>
</template>

<script setup lang="ts" name="Detail">
    defineProps(["id", "title", "content"]);
</script>

<style scoped></style>
<!-- 路由 -->
{
    name: "news",
        path: "/news",
            component: News,
                children: [
                    {
                        // ?参数的必要性
                        name: "xiang",
                        path: "detail/:id?/:title?/:content?",
                        component: Detail,

                        // 第一种写法:将路由收到的所有params参数作为props传给路由组件
                        // props: true,

                        // 第二种写法:函数写法,如果接收params可以直接用第一种写法,可以自己决定将什么作为参数传给路由组件
                        // props(route) {
                        //   return route.query;
                        // },

                        // 第三中写法:对象写法,写死
                        props: {
                            id: 100,
                            title: "标题",
                            content: "内容",
                        },
                    },
                ],
},

13.7replace属性

不写跳转后能回退(push),写了就被替代不能回退了(replace)

13.8编程式路由跳转

import { useRouter } from "vue-router";
const router = useRouter();
<!-- 举例1 -->
onMounted(() => {
    setTimeout(() => {
        router.push("/news");
    }, 3000);
});
<!-- 举例2 -->
function showNews(news: any) {
    router.push({
        // 这里面的东西与to里面的一样
        name: "xiang",
        query: {
            id: news.id,
            title: news.title,
            content: news.content,
        },
    });
}

13.9重定向

// 路由里面
{
    path: "/",
        redirect: "/home",
},

14.Pinia

npm install pinia

main.ts

// 引入pinia
import { createPinia } from "Pinia";
// 创建pinia
const pinia = createPinia();
// 安装pinia
app.use(pinia);

建store

// 注意后面是大写的Pinia
import { defineStore } from "Pinia";

// 第一个参数是id
export const useCountStore = defineStore("count", {
    // 真正存储数据的地方
    state() {
        return {
            sum: 6,
        };
    },
    // actions放方法
    actions: {
        increment(value: number) {
            console.log("increment被调用", value);
            // 调用本类里的用this
            this.sum += value;
        },
    },
});

vue中的用法

// 引用
import { useCountStore } from "@/store/count";
// 创建
const countStore = useCountStore();
// 点里面的方法就行

// 如果要解构
import { storeToRefs } from "Pinia";
const { sum } = storeToRefs(countStore);
// 不要用toRefs应为会把不需要的都解构出来

14.1subscribe,相当于watch

<template>
<div class="talk">
    <button @click="talkStore.getLoveTalk">获取一句土味情话</button>
    <ul>
        <li v-for="talk in talkStore.talkList" :key="talk.id">
            {{ talk.title }}
    </li>
    </ul>
    </div>
</template>
<script setup lang="ts">
    import { useLoveStore } from "@/store/loveTalk";
    const talkStore = useLoveStore();
    // 更新时调用,mutate本次修改的信息,state跟新后的数据
    talkStore.$subscribe((mutate, state) => {
        localStorage.setItem("talkList", JSON.stringify(state.talkList));
    });
</script>
<style scoped>
    .talk {
        background-color: skyblue;
        padding: 10px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>
import { defineStore } from "Pinia";
import axios from "axios";
// 生成id
import { nanoid } from "nanoid";
export const useLoveStore = defineStore("loveTalk", {
    // 真正存储数据的地方
    state() {
        return {
            talkList: JSON.parse(localStorage.getItem("talkList") as string) || [],
        };
    },
    // 函数
    actions: {
        async getLoveTalk() {
            let {
                data: { content: title },
            } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
            // 把请求回来的字符串,包装成一个对象
            let obj = { id: nanoid(), title };
            // 向数组中添加数据,不能向null里添加,可以向[]中添加
            this.talkList.unshift(obj);
        },
    },
});

14.2store组合式写法

import { defineStore } from "Pinia";
import axios from "axios";
// 生成id
import { nanoid } from "nanoid";

import { reactive } from "vue";
export const useLoveStore = defineStore("loveTalk", () => {
    const talkList = reactive(
        JSON.parse(localStorage.getItem("talkList") as string) || []
    );
    async function getLoveTalk() {
        let {
            data: { content: title },
        } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
        // 把请求回来的字符串,包装成一个对象
        let obj = { id: nanoid(), title };
        // 向数组中添加数据,不能向null里添加,可以向[]中添加
        talkList.unshift(obj);
    }
    // 一定要导出
    return { talkList, getLoveTalk };
});
// 生成id
npm i nanoid

import { nanoid } from "nanoid";
nanoid()

其他

快捷键

复制:shift+alt+↓
移动当前行:alt+移动键
posted @   芊嵛  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
  1. 1 イエスタデイ(翻自 Official髭男dism) 茶泡饭,春茶,kobasolo
  2. 2 世间美好与你环环相扣 柏松
世间美好与你环环相扣 - 柏松
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 尹初七

作曲 : 柏松

编曲 : 彭圣杰

偏偏秉烛夜游

偏偏秉烛夜游

午夜星辰 似奔走之友

爱你每个结痂伤口

酿成的陈年烈酒

入喉尚算可口

入喉尚算可口

怎么泪水 还偶尔失守

邀你细看心中缺口

裂缝中留存 温柔

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

知道你不能 还要你感受

知道你不能 还要你感受

让星光加了一点彩虹

让樱花偷偷 吻你额头

让世间美好 与你环环相扣

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

此时已莺飞草长 爱的人正在路上

此时已莺飞草长 爱的人正在路上

我知他风雨兼程 途经日暮不赏

穿越人海 只为与你相拥

此刻已皓月当空 爱的人手捧星光

我知他乘风破浪 去了黑暗一趟

感同身受 给你救赎热望

知道你不能 还要你感受

知道你不能 还要你感受

让星光加了一点彩虹

当樱花开的纷纷扬扬

当世间美好 与你环环相扣

特别鸣谢:槿葵,我们的海报制作妹妹。

原唱:柏松

吉他:柏松

和声:柏松

录音:柏松

混音:张强

点击右上角即可分享
微信分享提示