Vue3(Ⅱ)
- 3、 进阶 —— 路由
- 3.1、示例
- 3.2、to 的两种写法
- 3.3、命令路由
- 3.4、嵌套路由
- 3.5、query 参数
- 3.6、params 参数
- 3.7、路由的 props 配置
- 3.8、replace 属性
- 3.9、编程式导航
- 3.10、重定向
- 4、 进阶 —— Pinia
- 4.1、概述
- 4.2、环境搭建
- 4.3、示例
- 4.4、修改数据(三种方法)
- 4.5、storeToRefs
- 4.6、getters
- 4.7、$subscribe
- 4.8、store组合式的写法
- 5、 进阶 —— 组件通信
- 5.1、props(父 ↔ 子)
- 5.2、自定义事件(子 → 父)
- 5.3、mitt(任意组件间进行通信)
- 5.4、$attrs(父 → 子孙)
- 5.5、$refs、\$parent(父 ↔ 子)
- 5.6、v-model(父 ↔ 子)
- 5.7、provide、inject(父 → 子孙)
- 5.8、pinia(任意组件间进行通信)
- 5.9、插槽(父 → 子)
- 5.9.1 匿名插槽
- 5.9.2 具名插槽
- 5.9.3 作用域插槽
- 6、 进阶 —— 其它一些常用的 API
- 6.1、shallowRef 和 shallowReactive
- 6.1.1 shallowRef
- 6.1.2 shallowReactive
- 6.2、readonly 和 shallowReadonly
- 6.2.1 readonly
- 6.2.2 shallowReadonly
- 6.3、toRaw 和 markRaw
- 6.3.1 toRaw
- 6.3.2 markRaw
- 6.4、customRef
- 7、 进阶 —— Vue3 新组件
- 7.1、Teleport
- 7.2、Suspense
- 7.3、全局API
- 7.4、非兼容性改变
3、 进阶 —— 路由
3.1、示例
安装: npm i vue-router
① 在 pages/ (或者 view/ )下创建路由组件
<!-- src/pages/About.vue -->
<template><h1>About</h1>
</template>
<script lang="ts" setup name="About"></script><!-- src/pages/News.vue -->
<template><h1>News</h1>
</template>
<script lang="ts" setup name="News"></script><!-- src/pages/Home.vue -->
<template><h1>Home</h1>
</template>
<script lang="ts" setup name="Home"></script>
② 编写路由配置文件
// src/router/index.ts
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({// 路由器的工作模式选用historyhistory: createWebHistory(),// 创建一个个路由规则routes: [{path: '/home',component: Home},{path: '/news',component: News},{path: '/about',component: About}]
})
export default router
使用 History 模式的 API:createWebHistory
使用 Hash 模式的 API:createWebHashHistory
③ 让 vue 实例来使用这个配置文件
// src/main.ts
// 引入
import { createApp } from 'vue';
import router from './router'
// 组件
import App from './App.vue';
// 创建实例
const app = createApp(App);
// 使用
app.use(router)
// 挂载
app.mount('#app')
④ 编写 App 组件
<!-- src/App.vue -->
<template><h2 class="title">Vue路由测试</h2><!-- 导航区 --><div class="navigate"><span class="rlink"><RouterLink to="/home">首页</RouterLink></span><span class="rlink"><RouterLink to="/news">新闻</RouterLink></span><span class="rlink"><RouterLink to="/about">关于</RouterLink></span></div><!-- 展示区 --><div class="content"><RouterView></RouterView></div>
</template><script lang="ts" setup name="App">
</script><style>.rlink {margin-right: 10px;}.content {background-color: cadetblue;}
</style>
Hash模式的缺点之一:在 SEO 优化方面相对较差,SEO(Search Engine Optimization,搜索引擎优化)是一组策略和技术,用于提高网站在搜索引擎结果页面(SERP)中的排名,从而增加网站的可见性和流量。SEO 涉及多个方面,包括关键词优化、内容质量、网站结构、外部链接和技术因素等
3.2、to 的两种写法
类型 | 写法 |
---|---|
字符串 | to=“/home” |
对象 | :to=“{path:‘/home’}” to 前要加冒号,这样才能把双引号的内容解析为对象,否则只是一个字符串 |
3.3、命令路由
在路由配置文件中,为路由规则添加 name 配置,例如
// src/router/index.ts
{name:'myhome',path:'/home',component:Home
}
在使用的时候,需要配合 to 的对象型写法使用,例如
<RouterLink :to="{name:'myhome'}">首页</RouterLink >
3.4、嵌套路由
在路由配置文件中,为路由规则添加 children 配置,例如
// src/router/index.ts
{path: '/news',component: News,// 相当于一个 routes 也是一个数组children: [{name: 'xiangqing',path: 'detail', // 前面不能加 / 否则这个component: Detail}]
}
相应的在使用时如下
<RouterLink to="/news/detail">Detail</RouterLink>
<!-- 或 -->
<RouterLink :to="{path:'/news/detail'}">Detail</RouterLink>
<!-- 或 -->
<RouterLink :to="{name:'xiangqing'}">Detail</RouterLink>
3.5、query 参数
传递参数有两种写法:字符串、对象
<!-- src/pages/News.vue -->
<template><!-- 写法1:字符串 --><span class="rlink"><RouterLink to="/news/detail?id=SWE19059&name=niki">详情(字符串</RouterLink></span><!-- 写法2:对象 --><span class="rlink"><RouterLink:to="{name: 'xiangqing',query: { id: 'SWE20059', name: 'pyy' },}">详情(对象)</RouterLink></span><div class="content"><RouterView></RouterView></div>
</template>
<script lang="ts" setup name="News"></script>
使用 useRoute() 这个 hook 接收
<!-- src/pages/Detail.vue -->
<template><h1>id:{{ id }}</h1><h1>name:{{ name }}</h1>
</template><script lang="ts" setup name="Detail">import { useRoute } from "vue-router";const route = useRoute();let { id, name } = route.query;
</script>
3.6、params 参数
修改路由配置
// src/router/index.ts
{path: '/news',component: News,children: [{name: 'xiangqing',path: 'detail/:id/:name', //表示在使用 path 时,detail路径后面出现的部分作为 params 参数,id 和 name 是参数名component: Detail}]
},
同样地,传递参数有两种写法:字符串、对象
<!-- src/pages/News.vue -->
<template><!-- 写法1:字符串 --><span class="rlink"><RouterLink to="/news/detail/SWE19059/niki">详情(字符串)</RouterLink></span><!-- 写法2:对象 --><span class="rlink"><RouterLink:to="{name: 'xiangqing',params: { id: 'SWE20059', name: 'pyy' },}">详情(对象)</RouterLink></span><div class="content"><RouterView></RouterView></div>
</template><script lang="ts" setup name="News"></script>
关于对象写法
1、query 可以搭配 path 或 name使用
2、params 只能搭配 name 使用
使用 useRoute() 接收
<!-- src/pages/Detail.vue -->
<template><h1>id:{{ id }}</h1><h1>name:{{ name }}</h1>
</template><script lang="ts" setup name="Detail">import { useRoute } from "vue-router";const route = useRoute();let { id, name } = route.params;
</script>
3.7、路由的 props 配置
在路由配置文件中,为路由规则添加 props 配置,一共有三种写法:对象、布尔值、函数式
{path: '/news',component: News,children: [{name: 'xiangqing',path: 'detail/:id/:name',component: Detail,// 写法1:布尔值 ,为true则把params传给子组件// props: true// 写法2:对象// props:{gender:'male',school:'MIT'}// 写法3:函数props(route) {return route.query //需要返回一个对象}}],
},
在 News 组件中使用 /news/detail/SWE19059/niki?hobby=rap 进行跳转,然后在 Detail 组件中使用 defineProps 进行接收
<!-- src/pages/Detail.vue -->
<template><!-- 对应布尔值写法 --><!-- <h1>id:{{ id }}</h1><h1>name:{{ name }}</h1> --><!-- 对应对象写法 --><!-- <h1>gender:{{ gender }}</h1><h1>school:{{ school }}</h1> --><!-- 对应函数式写法 --><h1>hobby:{{ hobby }}</h1>
</template><script lang="ts" setup name="Detail">// 对应布尔值写法// defineProps(["id", "name"]);// 对应对象写法// defineProps(["gender", "school"]);// 对应函数式写法const { hobby } = defineProps(["hobby"]);console.log(hobby)
</script>
3.8、replace 属性
replace 属性用于控制路由跳转时操作浏览器历史记录的模式,浏览器的历史记录有两种写入方式:分别为 push 和 replace
方式 | 描述 |
---|---|
push(默认) | 追加历史记录 |
replace | 替换当前的记录 |
开启 replace 模式的方法只需要在 RouterLink 标签中进行声明即可,例如
<RouterLink replace ...>xxx</RouterLink>
3.9、编程式导航
跳转要使用 useRouter() 这个 hook 来完成
<!-- src/pages/News.vue -->
<template><button @click="toDetail">跳转</button><RouterView></RouterView>
</template><script lang="ts" setup name="News">import { useRouter } from "vue-router";const router = useRouter();function toDetail() {router.push({ //相应的有 route.replace 这个方法name: "xiangqing",params: {id: "SWE19059",name: "niki",},});}
</script>
Tip
1、Vue2 中的 $route、$routes 在 Vue3 中变成了 useRoute、useRouter 这两个 hook
2、在 Vue2 中使用编程式导航重复跳转时会报错,但是 Vue3 中不会
3.10、重定向
可以用于设置默认呈现的组件,不然一般是需要先点击某个 RouterLink 才会显示出相应的组件,例如现在配置一组路由规则如下,那么在访问 / 时,会自动激活其下的 /home 对应的组件
{path:"/",redirect:'/home'
},
4、 进阶 —— Pinia
4.1、概述
Pinia 是 Vue 3 的一个符合直觉的 Vue.js 状态管理库,是 Vuex 的替代品。Pinia 提供了一种简洁、类型安全的方式来管理应用中的状态。它具有更好的 TypeScript 支持、简洁的 API 设计以及模块化的状态管理方式
安装:npm install pinia
特性 | 描述 |
---|---|
模块化设计 | 每个状态存储(store)都是一个模块,可以根据需要创建多个 store |
类型安全 | 对 TypeScript 友好,提供了良好的类型推导和类型检查 |
简洁的 API | 使用更简洁的 API 设计来管理状态、定义 getter 和 actions |
响应式 | 使用 Vue 3 的响应性系统,自动追踪依赖 |
支持插件 | 可以通过插件扩展功能,例如持久化插件等 |
4.2、环境搭建
修改 src/main.ts 如下
// 引入
import { createApp } from 'vue';
import router from './router'
import { createPinia } from 'pinia'// 组件
import App from './App.vue';// 创建实例
const app = createApp(App);// 使用
// 使用路由配置
app.use(router)
// 使用Pinia
const pinia = createPinia()
app.use(pinia)// 挂载
app.mount('#app')
完成之后观察 Vue Devtools 可以看到多出一个菠萝🍍的选项,说明环境搭建成功
4.3、示例
Store是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。它有三个概念:state、getter、action,相当于组件中 data、computed 和 methods 的概念
① 存储数据:编写 store 定义文件 src/store/count.ts 如下
// 引入defineStore用于创建store
import {defineStore} from 'pinia'// 定义并暴露一个store,推荐用 hook 的命名方式即 use...
export const useCountStore = defineStore(// 仓库名'count',// 配置{// 动作actions: {},// 状态state() {return {count1: 0,count2: 100}},// 计算getters: {}}
)
② 读取数据
<!-- src/pages/Home.vue -->
<template><h2>count1:{{ countStore.count1 }}</h2><h2>count2:{{ countStore.count2 }}</h2>
</template><script lang="ts" setup name="Home">import { useCountStore } from "@/store/count";// 获取 store 实体,Pinia 会确保不同组件返回的是同一个 store 实例const countStore = useCountStore();
</script>
4.4、修改数据(三种方法)
方法一:在组件中直接修改
<!-- src/pages/Home.vue -->
<template><h2>count1:{{ countStore.count1 }}</h2><h2>count2:{{ countStore.count2 }}</h2><button @click="change1">修改count1</button><button @click="change2">修改count2</button>
</template><script lang="ts" setup name="Home">import { useCountStore } from "@/store/count";const countStore = useCountStore();// 直接修改function change1() {countStore.count1 = 10;}// 批量修改function change2() {countStore.count1 = 50;countStore.count2 = 200