I have a Vue3 project using SSR, Vue-Cli, Vuex and Typescript.
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:
this.$store.commit("setFoo", "Bar")
But how can I do this in a ts file (router/index.ts) without this or vue instance?
I tried to import the store index file and submit it:
import store from "@/store/index" store.commit("setFoo", "Bar")
But I got an error:
Property 'commit' does not exist on type '() => Store<{ foo: string; }>'.ts(2339)
store file (since I am running SSR, the store cannot be a singleton):
import Vuex from "vuex" export default function () { return new Vuex.Store({ state: () => ({ foo: "foo", }), mutations: { setFoo(state, payload) { state.foo = payload }, }, }) }
Updated vuex 4 store file:
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) })
Portal 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 }
包.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" }
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 thatAvoid using stateful singletonsThe rules apply not only to the main application instance and storage, but also to therouter
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