i'm currently trying to create a library out of an existing project, that should export a component with a "flow" inside it (eg. Imagine the library as an iframe replace, so importing the library should give the possibility to import a component that handle it's own flow, data, etc..).
So, the pinia instance is shared across the Host
and the Library
.
The Host
has also a Vuex instance installed on it, we will see it later on.
Library
Node: v16.17.0
npm: v8.15.0
Library
exports a default method install
that manage to apply all the plugins that needs in order to execute correctly.
export default {
install(Vue) {
// Vue.use(...) Vue custom plugins that install only prototypes
Vue.use(CustomRouterPlugin); // install a plugin that uses store inside it, it replace the VueRouter functionality with our state based management
Vue.component("LibraryCore", LibraryCore);
},
};
It also exports all the stores used by the Library
and LibraryCore
component:
import * as Stores from '@/stores'
// install method exports
export { Stores, LibraryCore };
index.js
import { useAStore } from "./astore";
import { useBStore } from "./bstore";
import { useCStore } from "./cstore";
// continue ...
export {
useAStore,
useBStore,
useCStore
};
stores/index.js
All the stores in Library
are pretty the same:
import { defineStore } from "pinia";
import { routes } from "@/router";
export const useRouterStore = defineStore("router", {
state: () => ({
routeName: "home",
routes: routes, // {'home': { name: 'home',component: Object | VueComponent, etc..} }
}),
getters: {
currentRoute: function () {
return this.routes[this.routeName];
},
},
actions: {
setCurrentRoute: function (routeName) {
if (!this.routes[routeName]) {
throw new Error(`Route ${routeName} does not exist`);
}
this.routeName = routeName;
},
},
});
stores/namestore.js
This is the Library
vite config file:
import { fileURLToPath } from "url";
import { defineConfig, loadEnv } from "vite";
import { createVuePlugin as vue2 } from "vite-plugin-vue2";
import path from "path";
/** @type {import('vite').UserConfig} */
export default ({ mode }) => {
let buildConfigs = {
brotliSize: true,
};
if (mode.includes("library")) {
buildConfigs.lib = {
entry: path.resolve(__dirname, "src/index.js"),
name: "cf-components",
formats: ["es"],
fileName: (format) => `cf-components.${format}.js`,
};
buildConfigs.rollupOptions = {
external: [
'vue',
'pinia',
'vue-router'
],
output: {
dir: "dist",
globals: {
vue: "Vue",
},
},
};
}
return defineConfig({
plugins: [
vue2({
jsx: true,
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
vue: "vue/dist/vue.esm.js",
},
dedupe: ["vue"],
},
build: buildConfigs,
});
};
vite.config.js
And the Library
package.json:
{
"name": "@company/cf-components",
"version": "0.2.10",
"private": false,
"repository": {
"type": "git",
"url": "repo url"
},
"files": [
"dist"
],
"main": "dist/cf-components.cjs.js",
"module": "dist/cf-components.es.js",
"exports": {
".": {
"require": "./dist/cf-components.cjs.js",
"import": "./dist/cf-components.es.js"
},
"./css": "./dist/style.css"
},
"sideEffects": false,
"scripts": {
"dev": "vite",
"build-staging": "vite build --mode staging",
"build-library": "vite build --mode library",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@formkit/auto-animate": "^1.0.0-beta.3",
"@lottiefiles/vue-lottie-player": "^1.0.9",
"@tailwindcss/line-clamp": "^0.4.2",
"chart.js": "^3.9.1",
"dompurify": "^2.4.0",
"lodash": "^4.17.21",
"marked": "^4.1.0",
"tippy.js": "^6.3.7",
"vue-i18n": "^8.28.2",
"axios": "^0.27.2",
"js-cookie": "^3.0.1"
},
"peerDependencies": {
"vue": "^2.7.10",
"vue-router": "^3.5.1",
"pinia": "^2.0.22",
},
"devDependencies": {
"@types/markdown-it": "^12.2.3",
"@vitejs/plugin-basic-ssl": "^0.1.2",
"autoprefixer": "^10.4.8",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.1.8",
"vite": "^3.2.0",
"vite-plugin-vue2": "^2.0.2",
"vue-template-compiler": "^2.7.10"
}
}
package.json
All components inside Library
use mapState,mapAction and mapStores from pinia
.
Running the Library
as a normal Vue app using npm run dev
works fine.
Host
Node: v16.17.0
npm: v8.15.0
Host
is a laravel application using laravel mix: 8.15.0
The Library
package is imported using npm link
for local development.
The Host
imports Library
and install it's plugin:
import style from '@company/cf-components/css';
import { createPinia, PiniaVuePlugin } from 'pinia';
import LibraryPlugin, { AxiosPiniaPlugin, CfStores } from '@company/cf-components';
const pinia = createPinia();
Vue.use(PiniaVuePlugin); // imported from pinia
pinia.use(AxiosPiniaPlugin); // custom pinia plugin that inject a custom axios instance
Vue.use(LibraryPlugin);
const app = new Vue({
el: '#app',
router: router,
store: store, // the host's vuex store
i18n: i18n,
pinia: pinia,
render: (h) => h(App),
});
app.js
And use the Library
component inside a Test component:
<template lang="">
<LibraryCore class="mt-32" :isPluginValue="true"></LibraryCore>
<!-- :isPluginValue tell the library component to act as a Plugin, so it doesn't use the VueRouter but our custom made Router based on state -->
</template>
<script>
import style from '@company/cf-components/css';
import { mapStores } from 'pinia';
export default {
name: "Test"
};
</script>
In Host
the LibraryCore
is rendered correctly.
Also the Vue DevTools
shows pinia
's stores correctly with also the Vuex
's store.
When an action that should mutate the Library
's store is called, the values inside the devtools pinia's store change correctly, but the changes are not reflected inside the components.
Example:
mapping the "setCurrentRoute" action inside the component, and calling it, actually change the state value from the default 'home' to the one given as parameter.
Also the getter "currentRoute", inside dev tools, change accordingly the state "routeName" and returns the expected element.
But it looks like that this changes are not reactive, and doesn't reflect in the component computed proprieties.
...mapState(useRouterStore, ['currentRoute'])
What did i tried
- I've tried to create and instantiate a different
pinia
store insideHost
and use it from anHost
's component. And it looks like that when using it inside anHost
component it's reactivity works. - I've instantiate a
Library
's exported store inside anHost
's component and tried to show a state value inside the component, and modifying that state value inside the dev tools seems to not be reactive also.
What i expect
The desired behavior is that when a Library
's store value is mutated, the mutation should be reflected to the Library
's component making it reactive and changing accordingly to the state values.