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

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


前言

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

"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是独立的,需要另外安装:

javascript 复制代码
npm install vue-router@4

main.js中安装路由代码:

javascript 复制代码
import router from './router'

app.use(router)

1.createRouter

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

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

javascript 复制代码
// 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')

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

使用一个自定义组件 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 类名。可以通过以下代码来替代。

  1. exact-active-class 配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。

7.event 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。

html 复制代码
<!-- 字符串 -->
<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>

在代码里使用编程式导航:

javascript 复制代码
// 字符串路径
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 对应的组件。你可以把它放在任何地方,以适应你的布局。

html 复制代码
<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。

演示代码:

javascript 复制代码
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 注册一个全局前置守卫:

javascript 复制代码
const router = createRouter({ ... })

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

接收两个参数:

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

返回:

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

7.嵌套路由

示例代码:

javascript 复制代码
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也是独立的,需要另外安装:

javascript 复制代码
npm install pinia

main.js中安装pinia代码:

javascript 复制代码
import { createPinia } from 'pinia'

app.use(createPinia())

1.定义Store

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

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

(1)Option Store

官方示例代码:

javascript 复制代码
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

官方示例代码:

javascript 复制代码
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

示例代码:

javascript 复制代码
const store = useStore()

store.count++

(2)重置state

示例代码:

javascript 复制代码
const store = useStore()

store.$reset()

(3)变更state

调用 $patch方法变更state。

示例代码一:

javascript 复制代码
store.$patch({
  count: store.count + 1,
  age: 120,
  name: 'DIO',
})

示例代码二:

javascript 复制代码
store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

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

(4)替换state

示例代码:

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

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

(5)订阅state

示例代码:

javascript 复制代码
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

示例代码:

javascript 复制代码
<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

示例代码:

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

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

(2)订阅action

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

javascript 复制代码
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/

相关推荐
cs_dn_Jie几秒前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic35 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿1 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
萧鼎1 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸1 小时前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农1 小时前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络