Vue components not rendering when #nuxtjs/storybook is used in a Vue Storefront Next project - possibly a Typescript issue? - vue.js

I am trying to use #nuxtjs/storybook inside a Vue Storefront Next (https://docs.vuestorefront.io/v2/general/key-concepts.html) project.
I can get Storybook to open and to show stories, but the component within the stories are not rendered. For example, if I try and use the example from https://storybook.nuxtjs.org/usage then I see a <link> element in devtools (the name of the Vue component), not a rendered <a> element (the content of the Vue component):
I then switched to trying to use another simple component:
https://codesandbox.io/s/admiring-pine-2byq7?file=/components/Logo.vue
https://codesandbox.io/s/admiring-pine-2byq7?file=/components/Logo.stories.js
But that doesn't work either, you can see an example of that here: https://pedantic-chandrasekhar-a83cfc.netlify.app/?path=/story/logo--logo (I had trouble getting Storybook to work on Codesandbox).
Vue Storefront Next is based on Nuxt but I had to make a few changes to get Storybook to open:
Update the build section within nuxt.config.js:
babel: {
presets({ envName }) {
return [
[
'#nuxt/babel-preset-app',
{
corejs: { version: 3 }
}
]
]
},
ignore: [/[\/\\]core-js/, /#babel[\/\\]runtime/],
},
install #babel/runtime-corejs3, core-js 3, and ts-node
ts-node was necessary because Vue Storefront Next provides a tsconfig.json file for development of part of the project, and that makes #nuxtjs/storybook module think the project is a Typescript project (https://github.com/nuxt-community/storybook/blob/e5b3698482873d7129cd763a0422b8c3151cee0b/src/index.ts#L67-L76), but the Vue Storefront project does not use #nuxt/typescript-runtime - I am wondering if this is part of the problem?
You can see the package.json content on Codesandbox: https://codesandbox.io/s/admiring-pine-2byq7?file=/package.json
Any clues as to how to fix this issue would be SUPER appreciated, thank you! I've spent the best part of a day on this but I don't know enough about Storybook or Nuxt to be able to debug it myself, unfortunately :(

It turns out that the #nuxtjs/storybook module seems to be dependent on components: true being set in the project's nuxt.config.js file.
This isn't mentioned anywhere in the #nuxtjs/storybook documentation, but I've raised a Github issue to point this out and will raise a PR against the docs if the maintainer agrees.
You can see my thought process behind how I discovered this issue in this Github thread: https://github.com/nuxt-community/storybook/issues/233#issuecomment-785027558

Related

[Vue warn]: $attrs is readonly. $listeners is readonly [duplicate]

I am new to Vue. I am trying to develop a chatting application where friend list will be shown on the left menu and the chat box will be shown at the body. I am loading friend list using an ajax call and routing to chat box based on friend id. Here is the code sample.
<div class="chat-container clearfix" id="chat">
<div class="people-list" id="people-list">
<chatlist-component></chatlist-component>
</div>
<div class="chat">
<router-view></router-view>
</div>
</div>
chat list component will load friend list from the server. Here is my app.js file;
Vue.use(VueRouter)
const router = new VueRouter({
routes,
linkActiveClass: "active"
});
import ChatComponent from './components/ChatComponent';
const routes = [
{ path: '/chat/:id/:name', component: ChatComponent , name: 'chat'}
];
Vue.component('chatlist-component', require('./components/ChatlistComponent.vue'));
const app = new Vue({
el: '#chat',
router
});
And Chat list component template code
<li class="clearfix" v-for="user in users">
<img :src="baseUrl+'/img/default_image.jpeg'" alt="avatar" class="chat-avatar rounded-circle" />
<router-link class="about" :to="{ name: 'chat', params: { id: user.id, name:user.name }}">
<div class="name">{{user.name}}</div>
<div class="status">
<i class="fa fa-circle online"></i> online
</div>
</router-link>
</li>
It works fine until I switch to another user. When I click on any router list from chatlist component it works fine but throws following error to console.
app.js:19302 [Vue warn]: $attrs is readonly.
found in
---> <RouterLink>
<ChatlistComponent> at resources/assets/js/components/ChatlistComponent.vue
<Root>
app.js:19302 [Vue warn]: $listeners is readonly.
found in
---> <RouterLink>
<ChatlistComponent> at resources/assets/js/components/ChatlistComponent.vue
<Root>
app.js:19302 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "to"
Thanks in advance
First, these errors only come out in non-production builds, however they indicate a problem that should be resolved before production release.
The $attrs, $listeners and other warnings are displayed if there's more than one instance of Vue loaded. As I understand it, this can happen usually for one these reasons:
it is being loaded/packed into the bundle by webpack and also loaded externally (not via webpack)
it is being loaded by something you include (e.g. vuetify, vue-test-utils, vue-cli-electron-builder) one way and by your webpack config another way (e.g. absolute vs relative paths, common.js vs esm.js vue files, runtime-only vue vs compiler+runtime vue)
If you click on that line (it was app.js:19302 in your output above) and put a breakpoint where the message is coming out, you can see the list of modules in the stack traceback to see if there's more than one path to Vue listed. For example, see that the top three modules have a different path below (but are all part of Vue):
If you see Vue showing up in two or more different ways, it demonstrates that more than one instance of Vue is loaded. Vue only supports a single instance, and can produce these error messages if more than one is loaded.
There are several links to issues included above, the Vuetify issue was the one I filed. In that case, Vuetify requires Vue and was loading it differently than I was. Usually the fix is to check your webpack config where Vue is specified (or isn't) and try to make it match the way the other copy is being included (e.g. absolute vs relative path, or packed vs external).
This error was happening to me because I was using Git submodules in my project and I had the following folders:
./node_modules - this is for the main project. There was vue installed in these node_modules.
./my_git_submodules/vue_design_system/node_modules - design system that provides basic components (also uses vue). Vue was also installed here!!
So because both:
./node_modules/vue and
./my_git_submodules/vue_design_system/node_modules/vue
existed, running npm run serve (vue-cli-service build underneath) built two instances of Vue. This was
a really nasty issue because it broke reactivity in certain cases
(ie. you click a button and nothing happens - you just get the
$listeners readonly error)
Quick fix:
removing node_modules in the child folder (vue_design_system) and running npm run serve worked for me.
Long term fix:
you'll probably need to make Webpack ignore nested node_modules folders by adding a rule in vue.config.js
Adding this to vue.config.js, worked for me.
const path = require('path')
module.exports = {
configureWebpack: {
externals: {
vue: 'Vue'
}
}
}
I faced the same problem. For me, as I installed a local module using
yarn add ../somemodule --force
Seems this command will copy the whole module directory include the node_modules sub-directory. That causes the duplication of the same version of "vue" module. And in the browser devtool I can see multiple sources of the module.
For me, the solution is manually to delete the node_modules every time after install the local module.
In my case, I was migrating from a project that didn't use VueCLI. In that project, I imported vue from a CDN <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>. In my Vue CLI project, I had copied and pasted my whole index.html file to the Public folder. Vue CLI has it's own way of importing vue so they were clashing. I simply removed the script and the problem was solved.
I had the same problem as above. I am using git submodules and I used
yarn add ./submodule
Somehow there ended up being a node_modules directory in the ./submodule directory and that confused everything.
And it was loading two Vue instances.
This can also happen because you are using npm link to use your unpublished vue packages. Basically identical to #walnut_salami explanation.
The fix for this problem is by moving to this way of including unpublished modules.
Looks like as if there are many possible answers.
I added a npm package manually to the node folder and the problem was the missing npm i command. After using the install command, everything worked fine.
In my case, this error arose after module federating my components.
I solved it by sharing vue in the component receiver to the component supplier.
e.g
webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "dashboard",
remotes: {
web_common: "web_common#http://localhost:8081/remoteEntry.js"
},
shared: {
vue: { // this solved my issue.
eager: true,
singleton: true,
requiredVersion: deps.vue
},
}
}),
]
}

Nuxt Generate & Start command ruins VueSlickCarousel components

When i run the command to deploy my nuxt app. The VueSlickCarousel Component plugin i use to for my slider is not rendering the default vue-slick-carousel.css even though i include it in the global css array in the nuxt.config.js, does anyone know why this is happening and how to resolve it?
css: [
'~/assets/scss/main.scss',
'vue-slick-carousel/dist/vue-slick-carousel.css',
'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
],

Bundling a plugin with Rollup but having duplicate Vue.js package imported in the client app's bundle (Nuxt)

Dear Stack Overflow / Vue.js / Rollup community
This could be a noob question for the master plugin developers working with Vue and Rollup. I will write the question very explicitly hoping that it could help other noobs like me in the future.
I have simple plugin that helps with form validation. One of the components in this plugin imports Vue in order to programatically create a component and append to DOM on mount like below:
import Vue from 'vue'
import Notification from './Notification.vue' /* a very simple Vue component */
...
mounted() {
const NotificationClass = Vue.extend(Notification)
const notificationInstance = new NotificationClass({ propsData: { name: 'ABC' } })
notificationInstance.$mount('#something')
}
This works as expected, and this plugin is bundled using Rollup with a config like this:
import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel'
import { terser } from 'rollup-plugin-terser'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
export default {
input: 'src/index.js',
output: {
name: 'forms',
globals: {
vue: 'Vue'
}
},
plugins: [
vue(),
babel(),
resolve(),
commonjs(),
terser()
],
external: ['vue']
}
As you can see, Vue.js is getting externalised in this bundle. The aim (and the assumption) is that the client app that imports this plugin will be running on Vue, therefore there's no need to bundle it here (assumption).
The very simple src/index.js that the bundler uses is below:
import Form from './Form.vue'
export default {
install(Vue, _) {
Vue.component('bs-form', Form)
}
}
Rollup creates 2 files (one esm and one umd) and references them in in the plugins package.json file like below:
"name": "bs-forms",
"main": "./dist/umd.js",
"module": "./dist/esm.js",
"files": [
"dist/*"
],
"scripts": {
"build": "npm run build:umd & npm run build:es",
"build:es": "rollup --config rollup.config.js --format es --file dist/esm.js",
"build:umd": "rollup --config rollup.config.js --format umd --file dist/umd.js"
}
Everything works as expected up to this point and the bundles are generated nicely.
The client app (Nuxt SSR) imports this plugin (using npm-link since it's in development) with a very simple import in a plugin file:
/* main.js*/
import Vue from 'vue'
import bsForms from 'bs-forms'
Vue.use(bsForms)
This plugin file (main.js) is added to nuxt.config.js as a plugin:
// Nuxt Plugins
...
plugins: [{src: '~/plugins/main'}]
...
Everything still works as expected but here comes the problem:
Since the clients is a Nuxt app, the Vue is imported by default of course but the externalised Vue module (by the forms plugin) is also imported in the client. Therefore there is a duplication of this package in the client bundle.
I guess the client app can configure its webpack config in order to remove this duplicated module. Perhaps by using something like a Dedupe plugin or something? Can someone suggests how to best handle situation like these?
But what I really want to learn, is the best practice of bundling the plugin at the first place, so that the client doesn't have to change anything in its config and simply imports this plugin and move on.
I know that importing the Vue.js in the plugin may not be a great thing to do at the first place. But there could be other reasons for an import like this as well, for example imagine that the plugin could be written in Typescript and Vue.js / Typescript is written by using Vue.extend statements (see below) which also imports Vue (in order to enable type interface):
import Vue from 'vue'
const Component = Vue.extend({
// type inference enabled
})
So here's the long question. Please masters of Rollup, help me and the community out by suggesting best practice approaches (or your approaches) to handle situations like these.
Thank you!!!!
I had the same problem and I found this answer of #vatson very helpful
Your problem is the combination of "npm link", the nature of nodejs module loading and the vue intolerance to multiple instances from different places.
Short introduction how import in nodejs works. If your script has some kind of library import, then nodejs initially looks in the local node_modules folder, if local node_modules doesn't contain required dependency then nodejs goes to the folder above to find node_modules and your imported dependency there.
You do not need to publish your package on NPM. It is enough if you generate your package locally using npm pack and then install it in your other project npm install /absolute_path_to_your_local_package/your_package_name.tgz. If you update something in your package, you can reinstall it in your other project and everything should work.
Here is the source about the difference between npm pack and npm link https://stackoverflow.com/a/50689049/6072503.
I have sorted this problem with an interesting caveat:
The duplicate Vue package doesn't get imported when the plugin is used via an NPM package (installed by npm install -save <plugin-name> )
However, during development, if you use the package vie npm link (like npm link <plugin-name>) then Vue gets imported twice, like shown in that image in the original question.
People who encounter similar problems in the future, please try to publish and import your package and see if it makes any difference.
Thank you!

Can't add directive as plugin in Nuxt application

I'm trying to incorporate the Ripple package into my Nuxt application.
Following Nuxt docs and the package docs example I have a ripple.js file in plugins/ directory containing this:
import Vue from 'vue'
import Ripple from 'vue-ripple-directive'
Vue.directive('ripple', Ripple)
Then in nuxt.config.js I have:
plugins: [
'~/plugins/ripple.js'
],
But now the app doesn't work at all, with some Unexpected token export error message on the screen, and a "Missing stack frames" error message in vm.js.
I have no idea what that means nor what I'm doing wrong, any suggestion?
This is due to an SSR error, where vue-ripple-directive cannot be used on the server. In order to get around this, you need to instruct Nuxt to only load the plugin on the client side.
To fix this, do the following 2 things:
First, rename ripple.js to ripple.client.js.
Second, update the plugins array to the following:
plugins: [
'~/plugins/ripple.client.js'
]
The .client postfix signals to nuxt to only run the plugin on the client.
More information can be found here
Always keep this method in mind when adding Vue plugins, especially when they interact with the DOM in some way. Most that I've come across require this method to function without errors, as the DOM is unavailable on the server.

Nuxt error : Unknown custom element

I am using nuxt js. I am trying to install a vue package vue-zoom. Actually few more plugins.
{ src: '~/plugins/zoom', ssr: false },
Here I kept ssr false because it gives errors like document is not defined...
In my plugins/zoom.js file I have this
import Vue from 'vue';
import vZoom from 'vue-zoom'
Vue.use(vZoom);
Now when I am trying to use this plugin like this
<v-zoom :img="`/uploads/${displayImg}`" ></v-zoom>
It gives me the above error.
Any reason or thought how can I use this plugin or similar in nuxt js?
I tried few more all gives similar errors.
Thank you
I got the same error when having the buildDir set to my functions/.nuxt folder, which I was using for SSR via Firebase Functions. I was able to solve the issue by making sure neither nuxt nor vue was installed in the node_modules inside the functions folder.
Are you using a similar setup by chance?