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>
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+移动键