vuex数据流

# vuex数据流

对于一个数据,如果只是组件内部使用就是用 ref 管理;如果我们需要跨组件,跨页面共享的时候,我们就需要把数据从 Vue 的组件内部抽离出来,放在 Vuex 中去管理。

# 基本使用

使用 createStore 来创建一个数据存储,我们称之为 store

import { createStore } from "vuex";

const store = createStore({
  state() {
    return {
      count: 666,
    };
  },
  mutations: {
    add(state) {
      state.count++;
    },
  },
});

store 内部除了数据,还需要一个 mutation 配置去修改数据,你可以把这个 mutation 理解为数据更新的申请单,mutation 内部的函数会把 state 作为参数,我们直接操作 state.count 就可以完成数据的修改。

在入口文件中使用vuex的store

createApp(App).use(store).use(router).mount("#app");

在组件中使用

<template>
<div @click="add">
    {{count}}
</div>
</template>

<script setup>
import { computed } from 'vue'
import {useStore} from 'vuex'
let store = useStore()
let count = computed(()=>store.state.count)

function add(){
    store.commit('add')
}
</script>

# 简单实现vuex

首先,我们需要创建一个变量 store 用来存储数据。下一步就是把这个 store 的数据包转成响应式的数据,并且提供给 Vue 组件使用。

在 Vue 中有 provide/inject 这两个函数专门用来做数据共享,provide 注册了数据后,所有的子组件都可以通过 inject 获取数据。

import { inject, reactive } from 'vue'

const STORE_KEY = '__store__'

function useStore() {
  return inject(STORE_KEY)
}

function createStore(options) {
  return new Store(options)
}

class Store {
  constructor(options) {
    this._state = reactive({
      data: options.state()
    })
    this._mutations = options.mutations
  }
}

export { createStore, useStore }

# 路由vue-router

# 发展历史

在 jQuery 时代,对于大部分 Web 项目而言,前端都是不能控制路由的,而是需要依赖后端项目的路由系统。通常,前端项目也会部署在后端项目的模板里,对于每次的页面跳转,都由后端开发人员来负责重新渲染模板。

image.png

目前的前端开发中,用户访问路由后,无论是什么 URL 地址,都直接渲染一个前端的入口文件 index.html,然后就会在 index.html 文件中加载 JS 和 CSS。之后,JavaScript 获取当前的页面地址,以及当前路由匹配的组件,再去动态渲染当前页面即可。用户在页面上进行点击操作时,也不需要刷新页面,而是直接通过 JS 重新计算出匹配的路由渲染即可。

image.png

前端获得了路由的控制权,在 JavaScript 中控制路由系统。这种所有路由都渲染一个前端入口文件的方式,是单页面应用程序(SPA,single page application)应用的雏形。

Ajax 让数据的获取不需要刷新页面,SPA 应用让路由跳转也不需要刷新页面。浏览器路由的变化可以通过 pushState 来操作,这种纯前端开发应用的方式,以前称之为 Pjax (pushState+ Ajax)。

SPA 应用相比于模板的开发方式,对前端更加友好,比如:前端对项目的控制权更大了、交互体验也更加丝滑,更重要的是,前端项目终于可以独立出来单独部署了。

# 前端路由原理

通过 URL 区分路由的机制上,有两种实现方式,一种是 hash 模式,通过 URL 中 # 后面的内容做区分,我们称之为 hash-router;另外一个方式就是 history 模式,在这种方式下,路由看起来和正常的 URL 完全一致。

这两个不同的原理,在 vue-router 中对应两个函数,分别是 createWebHashHistory 和 createWebHistory。

# hash 模式

在 2014 年之前,大家是通过 hash 来实现前端路由,URL hash 中的 # 就是类似于下面代码中的这种 # : http://www.xxx.com/#/login

之后,在进行页面跳转的操作时,hash 值的变化并不会导致浏览器页面的刷新,只是会触发 hashchange 事件。在下面的代码中,通过对 hashchange 事件的监听,我们就可以在 fn 函数内部进行动态地页面切换。

window.addEventListener('hashchange',fn)

# history 模式

2014 年之后,因为 HTML5 标准发布,浏览器多了两个 API:pushState 和 replaceState。通过这两个 API ,我们可以改变 URL 地址,并且浏览器不会向后端发送请求,我们就能用另外一种方式实现前端路由。

在下面的代码中,我们监听了 popstate 事件,可以监听到通过 pushState 修改路由的变化。并且在 fn 函数中,我们实现了页面的更新操作。

window.addEventListener('popstate', fn)

# 实现vue-router


import {ref,inject} from 'vue'
const ROUTER_KEY = '__router__'

function createRouter(options){
    return new Router(options)
}

function useRouter(){
    return inject(ROUTER_KEY)
}
function createWebHashHistory(){
    function bindEvents(fn){
        window.addEventListener('hashchange',fn)
    }
    return {
        bindEvents,
        url:window.location.hash.slice(1) || '/'
    }
}
class Router{
    constructor(options){
        this.history = options.history
        this.routes = options.routes
        this.current = ref(this.history.url)

        this.history.bindEvents(()=>{
            this.current.value = window.location.hash.slice(1)
        })
    }
    install(app){
        app.provide(ROUTER_KEY,this)
    }
}

export {createRouter,createWebHashHistory,useRouter}

使用

import {
    createRouter,
    createWebHashHistory,
} from './grouter/index'
const router = createRouter({
  history: createWebHashHistory(),
  routes
})
<template>
    <component :is="comp"></component>
</template>
<script setup>

import {computed } from 'vue'
import { useRouter } from '../grouter/index'

let router = useRouter()

const comp = computed(()=>{
    const route = router.routes.find(
        (route) => route.path === router.current.value
    )
    return route?route.component : null
})
</script>
上次更新: 2022/4/26 21:16:49