记录学习成果,以便温故而知新
“VUE3照本宣科”是指照着中文官网和菜鸟教程这两个“本”来学习一下VUE3。以前也学过VUE2,当时只再gitee留下一些代码,却没有记录学习的心得体会,有时也免不了会追忆一下。
以后出现“中文官网”不做特殊说明就是指:https://cn.vuejs.org/;菜鸟教程就是指:https://www.runoob.com/vue3/vue3-tutorial.html
Vue.js 的官方路由的内容洋洋洒洒,蔚为大观,内容太多,现在介绍zbxk项目中路由的默认内容及其它常用项。
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
所以说路由与Vue是独立的,需要另外安装:
npm install vue-router@4
main.js中安装路由代码:
import router from './router'
app.use(router)
创建一个可以被 Vue 应用使用的 Router 实例。
在Vue.js 的官方路由文档中找到如下代码,感觉很短小经精悍:
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: 'Home' }
const About = { template: 'About' }
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
// 现在,应用已经启动了!
使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。
在Vue.js 的官方路由文档实在是没有找组件 router-link更详细的介绍,而菜鸟教程却更全面一点。
1.to 表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。
2.replace 设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。
3.append 设置 append 属性后,则在当前 (相对) 路径前添加其路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b
4.tag 有时候想要渲染成某种标签,例如 。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。
5.active-class 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。
6. exact-active-class 配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
7.event 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
<router-link to="home">Homerouter-link>
<a href="home">Homea>
<router-link v-bind:to="'home'">Homerouter-link>
<router-link :to="'home'">Homerouter-link>
<router-link :to="{ path: 'home' }">Homerouter-link>
<router-link :to="{ name: 'user', params: { userId: 123 }}">Userrouter-link>
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Registerrouter-link>
在代码里使用编程式导航:
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。
将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。
<div id="app">
<h1>Hello App!h1>
<p>
<router-link to="/">Go to Homerouter-link>
<router-link to="/about">Go to Aboutrouter-link>
p>
<router-view>router-view>
div>
以上代码也来源于Vue.js 的官方路由文档,说明router-link与router-view的使用。
返回当前的路由地址。相当于在模板中使用 $route。
地址相关信息:
返回路由器实例。相当于在模板中使用 $router。
演示代码:
script setup>
import { onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
onMounted(() => {
console.log(route)
console.log(router)
})
</script>
官方文档中称为“导航守卫 ”。
可以使用 router.beforeEach 注册一个全局前置守卫:
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
接收两个参数:
- to: 即将要进入的目标 用一种标准化的方式。
from: 当前导航正要离开的路由 用一种标准化的方式
返回:
- false: 取消当前的导航。
- 一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像你调用 router.push() 一样,你可以设置诸如 replace: true 或 name: ‘home’ 之类的配置。
示例代码:
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 内部
path: 'profile',
component: UserProfile,
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 将被渲染到 User 的 内部
path: 'posts',
component: UserPosts,
},
],
},
]
嵌套路由一般可以实现布局。
zbxk项目是用的pinia作为状态管理器,pinia官网中说:
Pinia是Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
使用 Pinia可以获得如下功能:
- Devtools 支持
- 追踪 actions、mutations 的时间线
- 在组件中展示它们所用到的 Store
- 让调试更容易的 Time travel
- 热更新
- 不必重载页面即可修改 Store
- 开发时可保持当前的 State
- 插件:可通过插件扩展 Pinia 功能
- 为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
- 支持服务端渲染
Pinia与Vue也是独立的,需要另外安装:
npm install pinia
main.js中安装pinia代码:
import { createPinia } from 'pinia'
app.use(createPinia())
Store应该是保存状态的容器,但是至今没有看到过官方文档的明确说明,也可能是自己看官方文档不够仔细,搞得Store总是是是而非的。
Store是用 defineStore() 定义的,它的第一个参数被用作 id是必须传入的;第二个参数可接受两类值:Setup 函数或 Option 对象。
官方示例代码:
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2,//稍有改动
},
actions: {
increment() {
this.count++
},
},
})
state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
以上介绍是不是熟悉的配方、熟悉的味道?Vue2使用Vuex管理状态时貌似也是这样定义的。
官方示例代码:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)//这行代码官方示例中没有,zbxk项目默认代码里是有的
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
在 Setup Store 中:
- ref() 就是 state 属性
- computed() 就是 getters
- function() 就是 actions
对比Option Store与Setup Store的示例代码,发现它们是殊途同归。
示例代码:
const store = useStore()
store.count++
示例代码:
const store = useStore()
store.$reset()
调用 $patch方法变更state。
示例代码一:
store.$patch({
count: store.count + 1,
age: 120,
name: 'DIO',
})
示例代码二:
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
示例代码二明显要比示例代码一更灵活。
示例代码:
// 这实际上并没有替换`$state`
store.$state = { count: 24 }
// 在它内部调用 `$patch()`:
store.$patch({ count: 24 })
这个感觉就是变更,写法上不一样
示例代码:
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
本来以为这就是侦听的,看到pinia官网上所说:
比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次。
才发觉这是订阅的patch。
示例代码:
<script setup>
import { useUserListStore } from './store'
const userList = useUserListStore()
const { getUserById } = storeToRefs(userList)
// 请注意,你需要使用 `getUserById.value` 来访问
//