如何在 Vue 3 SSR 应用程序的路由器中使用 vuex 存储?
How do I use vuex store in the router in Vue 3 SSR app?
我有一个包含 SSR、Vue-Cli、Vuex 和 Typescript 的 Vue3 项目。
在路由器页面中,我需要将数据提交到 Vuex Store。在 .vue 文件中,我只使用 this.$store ,它在 vuex.d.ts 中输入,例如:
this.$store.commit("setFoo", "Bar")
但是我如何从没有 this 或 vue 实例的 ts 文件 (router/index.ts) 中做到这一点。
我已尝试导入商店索引文件并提交:
import store from "@/store/index"
store.commit("setFoo", "Bar")
但是我得到一个错误
Property 'commit' does not exist on type '() => Store<{ foo: string; }>'.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)
}
条目-client.js:
import createApp from "./main"
const { app, router } = createApp()
router.isReady().then(() => {
app.mount("#app", true)
})
条目-server.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,
}
}
Router/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
}
Package.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"
}
您的默认导出是一个函数
export default function () {
我认为你想这样做:
export default new Vuex.Store({...})
如果你想将它保留为一个函数,你也可以尝试 store().commit
但每次调用 store() 时都会创建一个新的 Vuex 实例
注意 Avoid Stateful Singletons rule applies not only to main app instance and store, but 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
- if you use standard createApp
, client side hydration 将不起作用
Vue provides a createSSRApp
method for use in client-side code to tell Vue to hydrate the existing static HTML instead of re-creating all the DOM elements
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,
}
}
我有一个包含 SSR、Vue-Cli、Vuex 和 Typescript 的 Vue3 项目。
在路由器页面中,我需要将数据提交到 Vuex Store。在 .vue 文件中,我只使用 this.$store ,它在 vuex.d.ts 中输入,例如:
this.$store.commit("setFoo", "Bar")
但是我如何从没有 this 或 vue 实例的 ts 文件 (router/index.ts) 中做到这一点。
我已尝试导入商店索引文件并提交:
import store from "@/store/index"
store.commit("setFoo", "Bar")
但是我得到一个错误
Property 'commit' does not exist on type '() => Store<{ foo: string; }>'.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)
}
条目-client.js:
import createApp from "./main"
const { app, router } = createApp()
router.isReady().then(() => {
app.mount("#app", true)
})
条目-server.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,
}
}
Router/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
}
Package.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"
}
您的默认导出是一个函数
export default function () {
我认为你想这样做:
export default new Vuex.Store({...})
如果你想将它保留为一个函数,你也可以尝试 store().commit
但每次调用 store() 时都会创建一个新的 Vuex 实例
注意 Avoid Stateful Singletons rule applies not only to main app instance and store, but 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
- if you use standard createApp
, client side hydration 将不起作用
Vue provides a
createSSRApp
method for use in client-side code to tell Vue to hydrate the existing static HTML instead of re-creating all the DOM elements
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,
}
}