How to integrate vuex store into router in Vue 3 SSR application?
P粉883223328
2023-08-25 10:09:45
<p>I have a Vue3 project using SSR, Vue-Cli, Vuex and Typescript. </p>
<p>In the routing page, I need to submit data to the Vuex Store. In the .vue file, I can simply use this.$store, which is type-defined in vuex.d.ts like this: </p>
<pre class="brush:php;toolbar:false;">this.$store.commit("setFoo", "Bar")</pre>
<p>But how can I do this in a ts file (router/index.ts) without this or vue instance? </p>
<p>I tried to import the store index file and submit it: </p>
<pre class="brush:php;toolbar:false;">import store from "@/store/index"
store.commit("setFoo", "Bar")</pre>
<p>But I got an error: </p>
<blockquote>
<p>Property 'commit' does not exist on type '() => Store<{ foo: string; }>'.ts(2339)</p>
</blockquote>
<p>store file (since I am running SSR, the store cannot be a singleton): </p>
<pre class="brush:php;toolbar:false;">import Vuex from "vuex"
export default function () {
return new Vuex.Store({
state: () => ({
foo: "foo",
}),
mutations: {
setFoo(state, payload) {
state.foo = payload
},
},
})
}</pre>
<p>Updated vuex 4 store file: </p>
<pre class="brush:php;toolbar:false;">import { createStore } from "vuex"
const store = {
state: () => ({
foo: "foo",
})
}
export default function () {
return createStore(store)
}</pre>
<p>entry-client.js:</p>
<pre class="brush:php;toolbar:false;">import createApp from "./main"
const { app, router } = createApp()
router.isReady().then(() => {
app.mount("#app", true)
})</pre>
<p>Portal server.ts:</p>
<pre class="brush:php;toolbar:false;">import createApp from "./main"
export default function () {
const { app, router } = createApp()
return {
app,
router,
}
}</pre>
<p>main.js:</p>
<pre class="brush:php;toolbar:false;">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,
}
}</pre>
<p>router/index.ts:</p>
<pre class="brush:php;toolbar:false;">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
}</pre>
<p>包.json:</p>
<pre class="brush:php;toolbar:false;">"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"
}</pre>
Your default export is a function
export default function () {
I think what you want to do is this:
export default new Vuex.Store({...})
If you want to keep it as a function, you can also try
store().commit
but this will create a new Vuex instance every time store() is calledPlease note that Avoid using stateful singletons The rules apply not only to the main application instance and storage, but also to the router
Your current
Router/index.ts
creates a stateful singleton. You need to create a "router factory" function so that every server request gets a new router instance. Another benefit is that now you can pass a storage instance to itRouter/index.ts
Please note that , both server and client bundles should use
createSSRApp
- if using the standardcreateApp
, Client Hydrationwill not work properlymain.js