Im creating a custom plugin that encapsulates a bunch of authentication functionality with vuex and vue-authenticate.
The problem im having is figuring out the correct way to load and install the module into VueJS, im not sure if its my webpack or vuejs knowledge that is lacking but so far I have the following
/node_modules/plugin/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import routes from './routes'
import store from './vuex/store'
import EventBus from './bus/eventBus'
import config from './config'
import ping from './vuex/modules/apiArchitect/ping/store'
import auth from './vuex/modules/apiArchitect/auth/store'
import user from './vuex/modules/apiArchitect/user/store'
Vue.use(Vuex)
Vue.use(EventBus)
const store = new Vuex.Store({
modules: {
ping,
user,
auth
},
strict: true
})
let apiArchitect = {}
apiArchitect.install = function (Vue, options) {
Vue.prototype.$apiArchitect = store,
Vue.prototype.$apiArchitect.$config = config
Vue.prototype.$apiArchitect.$routes = routes
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(apiArchitect)
}
}
export default apiArchitect
/src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import apiArchitect from 'vue-apiarchitect'
import addRouteGuards from 'vue-apiarchitect/src/addRouteGuards'
Vue.config.productionTip = false
Vue.config.env = process.env.NODE_ENV
Vue.use(router)
Vue.use(apiArchitect)
console.log(apiArchitect)
addRouteGuards(router)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
So far I am able to import the plugin and run the install hook with Vue.use(apiArchitect) I can access this.$apiArchitect in my App.vue.
The problem I have is that the plugin provides some auth routes stored in $apiArchitect.routes these routes need to be merged with the routes provided by router. If I try access $apiArchitect.routes in main.js I get an 'undefined' error I can only access them after vue has been instantiated. If I actually try console.log apiArchitect in main.js all I see is an object containing an install function none of the plugin i've provided which makes me belive its not installing correctly.
Does anyone know how i can access the apiArchitect.$routes property in main.js or a better way of achieving this?
Thanks
You can add routes dynamically with router.addRoutes() since 2.2.x.
The argument must be an Array using the same route config format with
the routes constructor option.
For example, you can use addRoutes in created hook of the root component:
// your plugin
const myPlugin = {
install: function(Vue, options) {
Vue.prototype.$myPlugin = {
routes: [{
path: '/myplugin', component: options.myComponent
}]
}
}
}
// components
const testComponent = { template: '<p>Component called by plugin route</p>' }
const Home = { template: '<p>Default component</p>' }
// router
const router = new VueRouter({
routes: [{
path: '/', component: Home
}]
})
Vue.use(VueRouter)
Vue.use(myPlugin, { myComponent: testComponent })
new Vue({
el: '#app',
router,
created() {
this.$router.addRoutes(this.$myPlugin.routes); // dinamically add routes
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<button #click="$router.push('/')">Home</button>
<button #click="$router.push('/myplugin')">Custom</button>
<router-view></router-view>
</div>
Related
I'm trying to create a webapp using VUE Vite with a router and store. The getter function in the vue file works fine. I have access to the chatMessages stored in the store.js file.
My problem is that I need to call the addMessage Action from the store.js file in the dev console using the browser.
Question: How could I archive this?
On older vue versions it would be done the following way using the main.js file:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './registerServiceWorker';
import { mapGetters, mapMutations, mapActions } from 'vuex';
Vue.config.productionTip = false;
const app = new Vue({
router,
store,
render: function (h) { return h(App) },
methods: {
...mapMutations([
'showLoading',
]),
...mapActions([
'addNotification',
]),
},
}).$mount('#app');
export default app;
Current vue3 chat.vue file:
<template>
<div></div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'Chat',
data: function() {
return {
}
},
methods: {
},
computed: {
...mapGetters({
chatMessages: 'chatMessageList',
}),
}
}
</script>
Current vue3 store.js file:
import { createStore } from 'vuex'
export default createStore({
state: {
chatMessages: {
list: [
{ type: "a", message: "test" }
]
}
},
mutations: {
addMessage(state, { type, message }) {
state.chatMessages.list.push({ type: type, message: message });
}
},
actions: {
addMessage({ commit }, { type, message }) {
commit('addMessage', { type, message });
}
},
getters: {
chatMessageList(state, getters) {
return state.chatMessages.list;
}
}
})
Current vue3 main.js file:
import App from "./App.vue";
import {createApp} from "vue";
import router from "./router/index.js";
import store from "./store/store";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.js";
window.app = createApp(App).use(router).use(store).mount('#app');
EDIT: I tested it the following way and I can call app.addMessage from the dev console but now the router wont work.
import App from "./App.vue";
import {createApp} from "vue";
import router from "./router/index.js";
import store from "./store/store";
import { mapGetters, mapMutations, mapActions } from 'vuex';
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.js";
window.app = createApp({
methods: {
...mapActions([
'addMessage',
]),
}
}).use(router).use(store).mount('#app');
Assigning the store to the window object seems like a great approach, where you can easily then call it like in domiatorov's answer.
Another approach is:
var store = Array.from(document.querySelectorAll('*')).find(e => e.__vue_app__).__vue_app__.config.globalProperties.$store;
var actions = store._actions;
actions.addMessage[0]('mytype', 'mymessage');
The first part queries the body for an element containing __vue_app__ and will return your instance. In there, you can access config.globalProperties.$store to return your store object.
store is already available in this scope and can be exposed the same way as app:
window.store = store;
It can be used in console the same way as in an app:
store.dispatch(...)
I believe you can simply assign the store to the window object.
But do it from a top level single file component, the very first that is using the store and not in the setup to make sure the store got everything loaded.
Providing you have something like App.vue:
In setup() you would assign:
window.vueStore = store;
and use it from the console calling window.vueStore.
first, i'm sorry that my English is not good.
...
I want to make vue.js components module.
I tried simple button component module and It works(it include vuex).
I saw this page.
and I want to make some module with 5 components that use both store and vue-router.
so I tried like this.
import LoadingSpinner from './components/common/LodingSpinner.vue';
import Modal from './components/common/Modal.vue';
import InputPage from './components/InputPage.vue';
import Page from './components/Page.vue';
import ProcessingPage from './components/ProcessingPage.vue';
import store from './store/index.js';
import route from './routes/index.js';
export default {
install(Vue, options) {
if (!options || !options.store) {
throw new Error('Please initialise plugin with a Vuex store.')
}
options.store.registerModule('dummylib', store);
options.route.registerModule('dummylib', route);
Vue.component('loading-spinner', LoadingSpinner);
Vue.component('modal', Modal);
Vue.component('input-page', InputPage);
Vue.component('page', Page);
Vue.component('processing-page', ProcessingPage);
}
when i finished it and install in new project, I can't see even App.vue and all components are missing.
Here is the main.js of the new project that will include the module.
import Vue from 'vue';
import App from './App.vue';
import store from './store';
import DummyButton from 'dummylib';
import router from './routes';
import InputPage from 'dummylib';
import Page from 'dummylib';
import ProcessingPage from 'dummylib';
import Modal from 'dummylib';
import LoadingSpinner from 'dummylib';
Vue.config.productionTip = false
Vue.use(DummyButton, {store});
Vue.use(InputPage, { store });
Vue.use(Page, { store });
Vue.use(ProcessingPage, { store });
Vue.use(Modal, { store });
Vue.use(LoadingSpinner);
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')
how can i solve this problems...
thank you for watching my question with not good english.
i hope you understand my intension.
How would I inject vuex into Vue 3, in Vue 2 it was possible like:
new Vue({
el: '#app',
store: store,
})
But in Vue 3 how would you do it since there is no new Vue().
The created store will be injected using .use method :
import { createApp } from 'vue'
import { createStore } from 'vuex'
// Create a new store instance.
const store = createStore({
state () {
return {
count: 1
}
}
})
const app = createApp({ /* your root component */ })
// Install the store instance as a plugin
app.use(store)
For more details check the Vuex 4 docs
To use it in child component in options api, try to provide it as follows :
app.use(store)
app.config.globalProperties.$store=store;
then use it like $store in child components
for composition api (setup hook), you could just import the useStore composable function which returns the store instance :
import {useStore} from 'vuex'
setup(){
const store=useStore()// store instead of `$store`
}
Together with Router:
import * as Vue from 'vue';
import App from './App.vue';
import router from './routes';
import {store} from "./store/store";
Vue.createApp(App).use(router, store).mount('#app');
I'm simply trying to use the store.subscribe() method in Vuex. I keep getting the following error, which seems to suggest that there is no store.subscribe method, contrary to what is in the docs:
Uncaught TypeError: WEBPACK_IMPORTED_MODULE_2__store.a.state.subscribe is not a function
this is my code:
app.js file, where I initialise everything, register my components etc:
import Vuex from 'vuex';
import router from './routes';
import store from './store';
window.Vue = require('vue');
const app = new Vue({
el: '#app',
router,
store: new Vuex.Store(store)
});
store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
my store.js file, which serves as my centralized Vuex store. It's been working perfectly until now:
import router from './routes';
export default {
state: {
sample: {
}
},
mutations: {
sample(state){
}
},
getters: {
sample(state){
return state.sample
}
}
}
how do i correct this issue
I think this happens because store is still just a regular object and not an instance of Vuex yet when you call it.
Can you try the following:
import Vuex from 'vuex';
import router from './routes';
import store from './store';
window.Vue = require('vue');
const myStore = new Vuex.Store(store)
const app = new Vue({
el: '#app',
router,
store: myStore
});
myStore.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
Add app object like app.store... because your store is not known as Vuex store instance at that level.
import Vuex from 'vuex';
import router from './routes';
import store from './store';
window.Vue = require('vue');
const app = new Vue({
el: '#app',
router,
store: new Vuex.Store(store)
});
app.$store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
I'm using Vue CLI which uses main.js to mount the app versus Server-Side Rendering (according to the tutorial I follow here) which uses app.js, entry-client.js and entry-server.js.
I tried to bypass main.js, but I'm having an error since its seems its required some how.
How can I work to keep main.js with app.js, entry-client.js and entry-server.js.
It might be a very basic question and I could maybe use the content of app.js in main.js, be I want to do things right.
main.js :
import Vue from 'vue'
import bootstrap from 'bootstrap'
import App from './App.vue'
import router from './router'
import store from './store'
import VueResource from 'vue-resource'
import BootstrapVue from 'bootstrap-vue'
import './registerServiceWorker'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.config.productionTip = false
Vue.use(VueResource)
Vue.use(BootstrapVue)
new Vue({
bootstrap,
router,
store,
render: h => h(App)
}).$mount('#app')
app.js :
import Vue from 'vue'
import App from './App.vue'
import bootstrap from 'bootstrap'
import { createRouter } from './router'
import store from './store'
import VueResource from 'vue-resource'
import BootstrapVue from 'bootstrap-vue'
import './registerServiceWorker'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.config.productionTip = false
Vue.use(VueResource)
Vue.use(BootstrapVue)
export function createApp () {
// create router instance
const router = createRouter()
const app = new Vue({
bootstrap,
router,
store,
render: h => h(App)
})
return { app, bootstrap, router, store }
}
server-client.js
import { createApp } from './app'
const { app, router } = createApp()
router.onReady(() => {
app.$mount('#app')
})
entry-server.js
import { createApp } from './app'
export default context => {
// since there could potentially be asynchronous route hooks or components,
// we will be returning a Promise so that the server can wait until
// everything is ready before rendering.
return new Promise((resolve, reject) => {
const { app, router } = createApp()
// set server-side router's location
router.push(context.url)
// wait until router has resolved possible async components and hooks
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// no matched routes, reject with 404
if (!matchedComponents.length) {
return reject({ code: 404 })
}
// the Promise should resolve to the app instance so it can be rendered
resolve(app)
}, reject)
})
}
Any help is greatly appreciated.
I just found how. By default, Vue CLI 3 comes with no vue.config.js. I had to create one at the root of the folder to override the entry which was set to /src/main.js.
I used the vue.config.js found on this github and onverwrittes the entry to ./src/entry-${target}
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const nodeExternals = require('webpack-node-externals')
const merge = require('lodash.merge')
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const createApiFile = TARGET_NODE
? './create-api-server.js'
: './create-api-client.js'
const target = TARGET_NODE
? 'server'
: 'client'
module.exports = {
configureWebpack: () => ({
entry: `./src/entry-${target}`,
target: TARGET_NODE ? 'node' : 'web',
node: TARGET_NODE ? undefined : false,
plugins: [
TARGET_NODE
? new VueSSRServerPlugin()
: new VueSSRClientPlugin()
],
externals: TARGET_NODE ? nodeExternals({
whitelist: /\.css$/
}) : undefined,
output: {
libraryTarget: TARGET_NODE
? 'commonjs2'
: undefined
},
optimization: {
splitChunks: undefined
},
resolve:{
alias: {
'create-api': createApiFile
}
}
}),
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options =>
merge(options, {
optimizeSSR: false
})
)
}
}