VUE3照本宣科——路由与状态管理器

news/2024/7/10 1:46:09 标签: Vue, 前端, 开发语言

VUE3照本宣科——路由与状态管理器

  • 前言
  • 一、路由(router)
    • 1.createRouter
    • 2.router-link
    • 3.router-view
    • 4.useRoute
    • 5.useRouter
    • 6.路由守卫
    • 7.嵌套路由
  • 二、状态管理器(Pinia)
    • 1.定义Store
      • (1)Option Store
      • (2)Setup Store
    • 2.State
      • (1)访问state
      • (2)重置state
      • (3)变更state
      • (4)替换state
      • (5)订阅state
    • 3.Getter
      • (1)使用Getter
    • 4.Action
      • (1)使用Action
      • (2)订阅action


前言

👨‍💻👨‍🌾📝记录学习成果,以便温故而知新

“VUE3照本宣科”是指照着中文官网和菜鸟教程这两个“本”来学习一下VUE3。以前也学过VUE2,当时只再gitee留下一些代码,却没有记录学习的心得体会,有时也免不了会追忆一下。

以后出现“中文官网”不做特殊说明就是指:https://cn.vuejs.org/;菜鸟教程就是指:https://www.runoob.com/vue3/vue3-tutorial.html


一、路由(router)

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)

1.createRouter

创建一个可以被 Vue 应用使用的 Router 实例。

Vue.js 的官方路由文档中找到如下代码,感觉很短小经精悍:

// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }

// 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')

// 现在,应用已经启动了!

2.router-link

使用一个自定义组件 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 有时候想要 <router-link> 渲染成某种标签,例如<li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。
5.active-class 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。
6. exact-active-class 配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
7.event 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。

<!-- 字符串 -->
<router-link to="home">Home</router-link>
<!-- 渲染结果 -->
<a href="home">Home</a>

<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'home'">Home</router-link>

<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'home'">Home</router-link>

<!-- 同上 -->
<router-link :to="{ path: 'home' }">Home</router-link>

<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-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 并不属于这种情况。

3.router-view

将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

以上代码也来源于Vue.js 的官方路由文档,说明router-link与router-view的使用。

4.useRoute

返回当前的路由地址。相当于在模板中使用 $route。

地址相关信息:

  • $route.fullPath 路由完整路径
  • $route.matched 路由匹配路径
  • $route.meta 路由元数据
  • $route.name 路由名称
  • $route.params 路由参数,params传参
  • $route.path 路由路径
  • $route.query 路由参数,query传参
    其它相关信息就不一一罗列了。

5.useRouter

返回路由器实例。相当于在模板中使用 $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>

终端输出如图:
在这里插入图片描述

6.路由守卫

官方文档中称为“导航守卫 ”。
可以使用 router.beforeEach 注册一个全局前置守卫:

const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
})

接收两个参数:

  • to: 即将要进入的目标 用一种标准化的方式。
    from: 当前导航正要离开的路由 用一种标准化的方式

返回:

  • false: 取消当前的导航。
  • 一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像你调用 router.push() 一样,你可以设置诸如 replace: true 或 name: ‘home’ 之类的配置。

7.嵌套路由

示例代码:

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]

嵌套路由一般可以实现布局。

二、状态管理器(Pinia)

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())

1.定义Store

Store应该是保存状态的容器,但是至今没有看到过官方文档的明确说明,也可能是自己看官方文档不够仔细,搞得Store总是是是而非的。

Store是用 defineStore() 定义的,它的第一个参数被用作 id是必须传入的;第二个参数可接受两类值:Setup 函数或 Option 对象。

(1)Option Store

官方示例代码:

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管理状态时貌似也是这样定义的。

(2)Setup Store

官方示例代码:

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的示例代码,发现它们是殊途同归。

2.State

(1)访问state

示例代码:

const store = useStore()

store.count++

(2)重置state

示例代码:

const store = useStore()

store.$reset()

(3)变更state

调用 $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
})

示例代码二明显要比示例代码一更灵活。

(4)替换state

示例代码:

// 这实际上并没有替换`$state`
store.$state = { count: 24 }
// 在它内部调用 `$patch()`:
store.$patch({ count: 24 })

这个感觉就是变更,写法上不一样

(5)订阅state

示例代码:

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。

3.Getter

(1)使用Getter

示例代码:

<script setup>
import { useUserListStore } from './store'
const userList = useUserListStore()
const { getUserById } = storeToRefs(userList)
// 请注意,你需要使用 `getUserById.value` 来访问
// <script setup> 中的函数
</script>

<template>
  <p>User 2: {{ getUserById(2) }}</p>
</template>

重点看一下storeToRefs,pinia官网说:

创建一个引用对象,包含 store 的所有 state、 getter 和 plugin 添加的 state 属性。 类似于 toRefs(),但专门为 Pinia store 设计, 所以 method 和非响应式属性会被完全忽略。

4.Action

(1)使用Action

示例代码:

const store = useCounterStore()
// 将 action 作为 store 的方法进行调用
store.randomizeCounter()

其它的应用都是围绕着这段代码的核心思想展开的。

(2)订阅action

通过 store.$onAction() 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。

const unsubscribe = someStore.$onAction(
  ({
    name, // action 名称
    store, // store 实例,类似 `someStore`
    args, // 传递给 action 的参数数组
    after, // 在 action 返回或解决后的钩子
    onError, // action 抛出或拒绝的钩子
  }) => {
    // 为这个特定的 action 调用提供一个共享变量
    const startTime = Date.now()
    // 这将在执行 "store "的 action 之前触发。
    console.log(`Start "${name}" with params [${args.join(', ')}].`)

    // 这将在 action 成功并完全运行后触发。
    // 它等待着任何返回的 promise
    after((result) => {
      console.log(
        `Finished "${name}" after ${
          Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // 如果 action 抛出或返回一个拒绝的 promise,这将触发
    onError((error) => {
      console.warn(
        `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// 手动删除监听器
unsubscribe()

本系列专题组合式外的功能介绍得较少,如mapState与mapWritableState等功能请参看pinia官网https://pinia.vuejs.org/zh/。


http://www.niftyadmin.cn/n/5067875.html

相关文章

微信客户管理与营销

微信已经成为我们工作与生活中不可或缺的社交工具&#xff0c;销售人员也一般通过微信的方式来进行客户的开发、维护与成交等&#xff0c;这也是一种非常简单高效的方式。&#xff08;内容较长&#xff0c;都是自己的工作经验&#xff0c;建议提前点赞、收藏&#xff09; 那么…

OK3568 forlinx系统编译过程及问题汇总

1. 共享文件夹无法加载&#xff1b;通过网上把文件夹加载后&#xff0c;拷贝文件很慢&#xff0c;任务管理器查看发现硬盘读写速率很低。解决办法&#xff1a;重新安装vmware tools。 2. 拷贝Linux源码到虚拟机&#xff0c;解压。 3. 虚拟机基本库安装 forlinxubuntu:~$ sudo…

14链表-环形链表、龟兔赛跑算法

目录 LeetCode之路——141. 环形链表 分析&#xff1a; 解法一&#xff1a;哈希表 解法二&#xff1a;龟兔赛跑 LeetCode之路——141. 环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针…

【c语言】通讯录【动态版本:有排序和文件操作】

目录 一、通讯录定义 二、通讯录的实现 1、test.c中菜单的实现 2、通讯录的创建逻辑 3、初始化 4、检查容量和添加 5、查找 6、删除功能 7、修改功能 8、打印 9、查找并打印 10、qsort排序 11、摧毁 12、保存数据到文件 13、从文件中读数据 完整代码&#xff1a; 一、通讯录定…

如何使用 Media.io 生成不同年龄的照片

Media.io 是一个在线图片编辑器&#xff0c;提供多种功能&#xff0c;包括照片滤镜、图像裁剪和图像转换。其中&#xff0c;Media.io 的 AI 年龄转换功能可以根据上传的照片&#xff0c;生成不同年龄的照片。 使用 Media.io 生成不同年龄的照片 要使用 Media.io 生成不同年龄…

Mac 挂载 Alist网盘

挂载服务器的Alist 网盘到 Mac mac,使用的是 CloundMounter 这个软件进行挂载 http://ip:port/dav/ 需要在末尾加上 /dav/ 在一些服务器上&#xff0c;为了提供WebDAV服务&#xff0c;需要在URL地址的末尾添加"/dav/“。这是因为WebDAV协议规定了一些标准的URL路径&#x…

Python---类的定义和使用语法

定义&#xff1a; # 类的定义和使用语法 """ class 类名称: # 定义类类的属性(成员变量)类的行为(成员方法) 对象 类名称() # 创建对象 对象.类的属性 # 使用 对象.类的行为 # 使用 """# 成员方法的定义语法 #…

[论文必备]最强科研绘图分析工具Origin(2)——简单使用教程

本篇将介绍Origin的简单使用教程。 安装教程见上篇&#xff1a;[论文必备]最强科研绘图分析工具Origin&#xff08;1&#xff09;——安装教程 目录 &#x1f4e2;一、工具栏介绍 &#x1f4e3;1.1 行 1.1.1 标准栏 1.1.2 导入栏 1.1.3 工作表数据 1.1.4 图表数据 &a…