【问题标题】:How do I use vuex store in the router in Vue 3 SSR app?如何在 Vue 3 SSR 应用程序的路由器中使用 vuex 商店?
【发布时间】:2021-10-29 18:31:59
【问题描述】:

我有一个带有 SSR、Vue-Cli、Vuex 和 Typescript 的 Vue3 项目。

在路由器页面中,我需要将数据提交到 Vuex Store。在 .vue 文件中,我只需使用在 vuex.d.ts 中输入的 this.$store,例如:

this.$store.commit("setFoo", "Bar")

但是我如何从没有 this 或 vue 实例的 ts 文件(router/index.ts)中做到这一点。

我已尝试导入存储索引文件并提交:

import store from "@/store/index"

store.commit("setFoo", "Bar")

但我得到一个错误

类型'() => Store'.ts(2339)

存储文件(因为我正在运行 SSR,所以存储不能是单例):

import Vuex from "vuex"

export default function () {
  return new Vuex.Store({
    state: () => ({
      foo: "foo",
    }),
    mutations: {
      setFoo(state, payload) {
        state.foo = payload
      },
    },
  })
}

更新了 vuex 4 的存储文件:

import { createStore } from "vuex"

const store = {
  state: () => ({
    foo: "foo",
  })
}

export default function () {
  return createStore(store)
}

entry-client.js:

import createApp from "./main"

const { app, router } = createApp()

router.isReady().then(() => {
  app.mount("#app", true)
})

入口服务器.ts:

import createApp from "./main"

export default function () {
  const { app, router } = createApp()

  return {
    app,
    router,
  }
}

main.js:

import { createSSRApp, createApp, h } from "vue"
import { isSSR } from "@/helpers"
import createRouter from "@/router"
import createStore from "@/store"
import axios from "axios"
import VueAxios from "vue-axios"
import App from "@/App.vue"

export default function () {

  const rootComponent = {
    render: () => h(App),
    components: { App },
  }

  const app = (isSSR() ? createSSRApp : createApp)(rootComponent)
  const router = createRouter()
  const store = createStore()

  app.use(VueAxios, axios)
  app.use(router)
  app.use(store)

  app.provide("axios", app.config.globalProperties.axios)

  return {
    app,
    router,
    store,
  }
}

路由器/index.ts:

  import { createRouter, createWebHistory, createMemoryHistory } from "vue-router"
  import store from "@/store/index"
  import axios from "axios"
  import MockAdapter from "axios-mock-adapter"
  import { routes } from "./routes"
  import { isSSR } from "@/helpers"

  const history = isSSR()
    ? createMemoryHistory()
    : createWebHistory(process.env.BASE_URL)

  const router = createRouter({ routes, history })

  router.beforeEach(async (to, from, next) => {
   // do stuff with store
  })
  
  export default function () {
    return router
  }

包.json:

  "scripts": {
    "build:all": "npm run build:client && npm run build:server",
    "build:client": "vue-cli-service build --dest dist/client",
    "build:server": "export SSR=1 || set SSR=1&& vue-cli-service build --dest dist/server",
    "build:server:dev": "export SSR=1 || set SSR=1&& vue-cli-service build --mode development --dest dist/server",
    "serve:client": "vue-cli-service serve",
    "serve:server": "node ./dist/server/server.js",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@vue/server-renderer": "^3.2.4",
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "express": "^4.17.1",
    "vue": "^3.0.0",
    "vue-axios": "^3.2.5",
    "vue-router": "^4.0.0-0",
    "vuex": "^4.0.0-0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0",
    "@vue/cli-plugin-babel": "^5.0.0-beta.3",
    "@vue/cli-plugin-eslint": "^5.0.0-beta.3",
    "@vue/cli-plugin-router": "^5.0.0-beta.3",
    "@vue/cli-plugin-typescript": "^5.0.0-beta.3",
    "@vue/cli-plugin-vuex": "^5.0.0-beta.3",
    "@vue/cli-service": "^5.0.0-beta.3",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "axios-mock-adapter": "^1.20.0",
    "eslint": "^7.20.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^7.6.0",
    "node-sass": "^4.12.0",
    "prettier": "^2.2.1",
    "sass-loader": "^8.0.2",
    "typescript": "~4.1.5",
    "webpack-manifest-plugin": "^4.0.2",
    "webpack-node-externals": "^3.0.0"
  }

【问题讨论】:

  • 请分享 package.json 文件
  • 导入的不是store,而是store factory。调用它。
  • 在路由器页面我需要提交数据 ...你说的路由器页面是什么意思?请添加更多代码...
  • @EstusFlask 你能举个例子吗?
  • @MichalLevý 我现在添加了更多代码

标签: typescript vue.js vuex server-side-rendering vuejs3


【解决方案1】:

请注意,Avoid Stateful Singletons 规则不仅适用于主应用实例和商店,还适用于 also to a router

您当前的 Router/index.ts 创建有状态的单例。相反,您需要创建一个“路由器工厂”功能,以便每个服务器请求都获得新的路由器实例。额外的好处是现在您可以将商店实例传递给它

Router/index.ts

  import { createRouter, createWebHistory, createMemoryHistory } from "vue-router"
  import axios from "axios"
  import MockAdapter from "axios-mock-adapter"
  import { routes } from "./routes"
  import { isSSR } from "@/helpers"

  const createHistory = isSSR()
    ? createMemoryHistory
    : createWebHistory

  export default function (store) {
    const router = createRouter({ 
      routes, 
      history: createHistory(process.env.BASE_URL)
    })

    router.beforeEach(async (to, from, next) => {
      // do stuff with store (store comes from argument)
    })
  
    return router
  }

注意both server and client bundle should use createSSRApp - 如果你使用标准的createAppclient side hydration 将不起作用

Vue 提供了一个 createSSRApp 方法用于客户端代码,告诉 Vue 水合现有的静态 HTML,而不是重新创建所有 DOM 元素

main.js

import { createSSRApp, h } from "vue"
import { isSSR } from "@/helpers"
import createRouter from "@/router"
import createStore from "@/store"
import axios from "axios"
import VueAxios from "vue-axios"
import App from "@/App.vue"

export default function () {

  const rootComponent = {
    render: () => h(App),
    components: { App },
  }

  const app = createSSRApp(rootComponent)
  const store = createStore()
  const router = createRouter(store)

  app.use(VueAxios, axios)
  app.use(router)
  app.use(store)

  app.provide("axios", app.config.globalProperties.axios)

  return {
    app,
    router,
    store,
  }
}

【讨论】:

  • 除此之外,我同意 Boussadjra Brahim - 如果您使用 Vuex 4 new Vuex.Store 将不起作用,您需要使用 new api
  • 看起来很有希望,我会尝试并接受它,谢谢!
  • @Per 我刚刚编辑了我的答案以解决另一个问题(在客户端上使用 createApp 而不是 createSSRApp
【解决方案2】:

您的默认导出是一个函数

export default function () {

我想你想这样做:

export default new Vuex.Store({...})

如果您想将其保留为函数,您也可以尝试store().commit,但这只会在您每次调用 store() 时创建一个新的 Vuex 实例

【讨论】:

  • 因为我在运行 SSR,所以商店不能是单身
  • 我想我必须像这样创建它,以免 SSR 产生内存问题。但是,如果我运行 store().commit 它可以工作,但正如你所说,它可能会创建一个新商店。必须有一种方法可以使用在 entry-client 中创建的那个?
猜你喜欢
  • 2019-08-18
  • 2017-12-18
  • 2020-07-05
  • 2021-12-05
  • 2022-07-12
  • 2017-07-22
  • 2020-01-10
  • 2022-01-26
  • 2021-12-29
相关资源
最近更新 更多