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 @ 2024-07-25 01:08  芊嵛  阅读(32)  评论(0编辑  收藏  举报