New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug Report] "$attrs is readonly" and "$listeners is readonly" console messages. #4068

Open
appurist opened this Issue May 15, 2018 · 77 comments

Comments

Projects
None yet
@appurist
Copy link
Contributor

appurist commented May 15, 2018

Versions and Environment

Vuetify: 1.1.0-alpha.4
Vue: 2.5.16
Browsers: Chrome 66.0.3359.139
OS: Linux x86_64

Steps to reproduce

Expected Behavior

Normal console messages (quieter). This was the case in 1.0.

Actual Behavior

This pair of messages appears for every button in the toolbar. There are two buttons in the repo example, so there are four messages total (first 2 shown below, repeats not shown).

./node_modules/vue/dist/vue.runtime.common.js:589 [Vue warn]: $attrs is readonly.

found in

---> <VBtn>
       <VToolbar>
         <VApp>
           <Electify> at src/renderer/App.vue
             <Root>
warn @ ./node_modules/vue/dist/vue.runtime.common.js:589
./node_modules/vue/dist/vue.runtime.common.js:589 [Vue warn]: $listeners is readonly.

found in

---> <VBtn>
       <VToolbar>
         <VApp>
           <Electify> at src/renderer/App.vue
             <Root>
warn @ ./node_modules/vue/dist/vue.runtime.common.js:589

Reproduction Link

https://github.com/appurist/electrify

Other comments

  1. Replace 1.1.alpha.4 with 1.0 and the messages disappear.
  2. Removing the buttons resolves the messages.
  3. Removing the footer entirely resolves the messages.
  4. Removing app from the footer resolves the messages.
  5. Removing app from the header does NOT resolve the messages.
  6. Replacing the v-button/v-icon combinations with simple v-icon resolves the messages.

Note: If the dev tools are not visible for the console when running the app, Ctrl-Shift-I shows them (also menu). I was unable to reproduce this with a web build of the app, thus unable to reproduce with a simpler codepen either; there may be some kind of conflict between Vuetify's use of an app mix-in and Electron.

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 15, 2018

I believe the problem may lie in src/mixins/applicationable.js due to the watch triggering an unbind/bind sequence after Vue has completed initialization. (Is this used anywhere other than Jest testing?)

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 15, 2018

Note that the warnings only come out (from Vue) in src/core/instance/lifecycle.js if isUpdatingChildComponent (which starts as true) has already been set to false. This is expected to be true once and then report this error if modifications are attempted after it is cleared.

There are several references to this error being triggered if Vue is included more than once (e.g. more than one version) but I don't think that's the case here. More detail here.

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 15, 2018

Oh I just found this: vuejs/vue-test-utils#532
The "two instances of Vue" problem might be esm vs commonjs.

@KaelWD

This comment has been minimized.

Copy link
Member

KaelWD commented May 29, 2018

two instances of Vue

That'll probably be because we have to import vue ourselves now for TS to work. Particularly if it only happens with VBtn. I tried fixing this with 4890c83 but I think it'll need to be factory functions instead unfortunately.

@KaelWD

This comment has been minimized.

Copy link
Member

KaelWD commented May 29, 2018

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 29, 2018

I started by making the same change in my webpack file and it did not help, it seems like there's still two different paths to Vue, even if they are all esm, which may mean two instances. With webpack, if one path is absolute and one is relative, perhaps it treats those as different? Or one is being webpacked and one not...

I had used yarn link earlier, but once I completed my commits/PRs, while trying to track this down, I had reinstalled vuetify@next and nuked the whole node_modules tree and reinstalled. Also, my coworker has the same trouble and doesn't use link. It doesn't seem to affect our web build, maybe it's defaulting to production there, but it does affect both our 'dev' and 'build' Electron builds.

But it's not Electron-specific, the vue-test-utils issue mentioned above includes a codesandbox repro which isn't electron. They made a change in a beta update and when they attempt to use it with Vuetify, they have the same problem. (That looks like maybe 1.0 of our code here, but their explanation for the trigger may provide a clue, changing their waters to sync triggering an extra rerender.

To be honest, this feels like a bug in the check in Vue to me, but I don't really understand the problem yet so the jury is out on that.

Hmmm. Is the Vuetify load of Vue available from the object, e.g. as Vuetify.$vue or something like that? If so, maybe instead of importing it we just assign it like Vue = Vuetify.$vue and use that one.

@KaelWD

This comment has been minimized.

Copy link
Member

KaelWD commented May 29, 2018

Yeah I'm not sure either, but as it's happening outside of tests and works fine with vuetify 1.0, I don't really know what else it could be. Vuetify's vue import is not made available anywhere, but WebpackBundleAnalyzer will show if there's different versions loaded.

I had to do this to the docs repo to prevent it from loading vue from the symlinked node_modules: https://github.com/vuetifyjs/vuetifyjs.com/commit/5fa3c6900335984d384382832b3506454292fe4f

@johnleider

This comment has been minimized.

Copy link
Member

johnleider commented May 29, 2018

This will occur when using a locally linked Vuetify package and not aliasing vue to the esm file.

@bdeo

This comment has been minimized.

Copy link
Member

bdeo commented May 30, 2018

This still occurs even even with aliasing 'vue$': 'vue/dist/vue.esm.js', in webpack config for jest/vue-test-utils tests for me.

@johnleider

This comment has been minimized.

Copy link
Member

johnleider commented May 30, 2018

In regards to vue-test-utils, this was a bug introduced in beta updates, vuejs/vue-test-utils#532

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 30, 2018

In my repo above, I'm not doing any tests, or using vue-test-utils at all. I've also tried forcing the vue$ alias with both relative and path.resolve naming to no effect. I've also just confirmed it reproduces with the beta.0 release, however, I see now that the two versions of Vue are loaded, mostly vue.esm.js but also vue.common.js for the top three stack frames:
stack trace on breakpoint
As far as I can tell, that's all Vue code, just two copies and in the instance that complains, the isUpdatingChildComponent was initialized to false and updateChildComponent is never called (in that instance of Vue) so it has never been set to true.

The Vue check is to confirm that the code is in updateChildComponent (which it is, 4th stack frame in the traceback) but it is in the other instance so the boolean is still false in the runtime-only instance.

@KaelWD KaelWD added the wontfix label May 30, 2018

@KaelWD

This comment has been minimized.

Copy link
Member

KaelWD commented May 30, 2018

tl;dr: Externalising vue but not vuetify causes duplicate modules. Add vuetify in here too to fix it: https://github.com/appurist/electrify/blob/master/.electron-vue/webpack.renderer.config.js#L21

What's happening is you have a copy of vue that's been run through webpack (the one in renderer.js, image 1) and the original straight from node_modules (image 2). Your application uses the webpacked one, and vuetify imports the original directly because webpack doesn't touch it. This wasn't a problem in 1.0 because our components were just bare objects, but now with typescript we have to export vue instances instead.

image

image

image

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented May 30, 2018

Aha, I finally understand how the second copy is being loaded, which is which, etc. Thanks so much @KaelWD !

And I can confirm that if I include both vue and vuetify in that whiteListedModules list (i.e. treat them both the same way in the externals section of the config), that the problem goes away for both the 'electrify' sample repo above, as well as our internal project.

Case closed, we don't even need a change to Vuetify for this, we can take care of it in our webpack configs.

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented Jun 5, 2018

I think this issue can be considered resolved, especially with Vue issue #8278 tracking the better longer-term solution.

The actual fix for this is to ensure both Vue and Vuetify are treated the same way in the webpack externals, e.g. putting both (or neither?) in the let whiteListedModules = ['vue', 'vuetify'] line in these vuetify webpack builds.

@darylteo

This comment has been minimized.

Copy link

darylteo commented Jul 4, 2018

@KaelWD @johnleider

Posting here as I can't find the time to create a reproduction at work for now.

Upgrading to v1.1.1 - this issue seems to occurring for my project because I was vendoring vuetify using Webpack 4 DllPlugin. The problem seems to go away if I've taken vuetify out of the vendoring. Whether "vue" is in the vendoring or not don't seem to make a difference.

If you think this is a legit issue you'd like to look at, please let me know if I should open a new ticket or if you'd just like to reopen. Thank you.

Dary

@johnleider

This comment has been minimized.

Copy link
Member

johnleider commented Jul 4, 2018

If you feel that your issue deviates enough from the resolution provided here @darylteo , create a new issue with reproduction please, thank you!

@lukebyrne

This comment has been minimized.

Copy link

lukebyrne commented Jul 6, 2018

If you run into this problem via a Rails web app, here is a skeleton app on how to fix it

https://github.com/lukebyrne/rails-vuetify-test

Just follow the git commits to see the changes made to get it resolved.

Many thanks to @KaelWD

@ericbyrd

This comment has been minimized.

Copy link

ericbyrd commented Jul 6, 2018

We encountered this issue as well when upgrading to 1.1.1 (running dotnetcore2.1, webpack 4). @darylteo thank you for your comment - removing vuetify (and only vuetitfy) from the webpack.config.vendor.js vendor entries fixed this for us (for now . . .). That was the only change we made - we don't have any whitelisting going on in our webpack config.

@LeoMartinDev

This comment has been minimized.

Copy link

LeoMartinDev commented Sep 25, 2018

Actually, I am not able to use Vuetify at all.
Just started a new project using electron-vue and installed Vuetify.
See: https://github.com/LeoMartinDev/vuetify-issue-4068

@blackfox00

This comment has been minimized.

Copy link

blackfox00 commented Sep 27, 2018

@KaelWD Even if I install Vuetify on global Vue first and then on localVue, I still get the warning message when the unit tests are run. Is there another way (except using global Vue for tests of course)?

@LeoMartinDev

This comment has been minimized.

Copy link

LeoMartinDev commented Sep 27, 2018

Just found a solution using Electron vue, just add 'vuetify' in webpack.renderer.js whitelistedModules

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented Sep 30, 2018

@LeoMartinDev I think that is effectively what @KaelWD suggested in his May 30 comment and what I confirmed addressed the problem nicely for me.

I think this particular problem has been explained, and workarounds posted. There seem to be two remaining related problems, but I think they are both only somewhat related and not the problem I reported above:

  • There is some question as to whether Vue should deal with its self-compatibility issue better and perhaps auto-detect an existing Vue instance better, but I think that is really for the Vue team to determine and work on, and I think there's already an issue for that.

  • Also whether or not vue-test-utils requires localVue or not and has a compatibility issue is also separate from this Vuetify report.

I'd like to see this issue closed as the original problem reported has been identified and resolved, and if someone still has a more specific use case in combination with something else, it should probably be more directly reported and separately reported to the relevant teams rather than tacked on here. I think that in both of these cases, there is a separate team that should be investigating it, if they aren't already.

@kakenbok

This comment has been minimized.

Copy link

kakenbok commented Oct 12, 2018

what has been the final fix?

@SebastianNiemann

This comment has been minimized.

Copy link

SebastianNiemann commented Oct 15, 2018

To add on this issue, in VuePress, .vuepress/enhanceApp.js is used for app level enhancements (like adding Vuetify support), exposing a local Vue instance. See https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements

Adding Vuetify would look like this:

import Vuetify from 'vuetify';

export default ({ Vue, options, router, siteData }) => {
  Vue.use(Vuetify);

  /* ... */
}

Because of this, we also got a lot of $listeners is readonly. as well as $attrs is readonly. warnings and our whole navigation drawer was rendered unusable (however, at least easy to discover during development).


And just in case. Using something like

import Vue from ' vue';
import Vuetify from 'vuetify';
Vue.use(Vuetify);

export default ({ _, options, router, siteData }) => {
  /* ... */
}

as a workaround will only result in Unknown custom element: <v-app> - did you register the component correctly? errors.

@KaelWD

This comment has been minimized.

Copy link
Member

KaelWD commented Oct 15, 2018

Looks like vuepress uses a bunch of symlinks to the different packages. You'll have to set an alias to vue in the webpack config, using an absolute path.

@kakenbok

This comment has been minimized.

Copy link

kakenbok commented Oct 15, 2018

I discovered, that this behaviour may affect any other stateful 3rd party module, so I started to add those libs aswell to the vue.conf.js. Seems to be working ... but perhaps nothing for the broad public just wanting to use a library. ... I couldn't find anything related to this issue in your readme file, but I saw your "export typ vue" commit being reverted, so I assumed, that there could be an official workaround now :-)

@builder7777

This comment was marked as off-topic.

Copy link

builder7777 commented Dec 12, 2018

I just ran into the same problem (along with [Vuetify] Multiple instances of Vue detected error) while testing in Jest.

@posva

This comment has been minimized.

Copy link
Contributor

posva commented Dec 13, 2018

The solution I've used in some projects is to silence this specific log from Vuetify. Still a hack, but no red in console.

const logError = console.error;
console.error = (...args) => {
  if (
    args[0].includes("[Vuetify]") &&
    args[0].includes("https://github.com/vuetifyjs/vuetify/issues/4068")
  )
    return;
  logError(...args);
};

// do it before calling use(Vuetify)
const localVue = createLocalVue();
localVue.use(Vuetify);

Depending on what you use for your tests (jest, mocha), you may want to include this in a setup file or at the beginning of for your test. If you do the last one, you may want to restore the original value of console.error in an after all hook

@builder7777

This comment was marked as off-topic.

Copy link

builder7777 commented Dec 20, 2018

just curious, why was my comment marked as off-topic? this ticket is literally referenced in the error i mentioned

@johnleider

This comment was marked as off-topic.

Copy link
Member

johnleider commented Dec 20, 2018

@builder7777 Marked as off-topic because there wasn't an option for irrelevant. There is no point to post me too.

@imtiyazs

This comment has been minimized.

Copy link

imtiyazs commented Jan 1, 2019

I think this issue can be considered resolved, especially with Vue issue #8278 tracking the better longer-term solution.

The actual fix for this is to ensure both Vue and Vuetify are treated the same way in the webpack externals, e.g. putting both (or neither?) in the let whiteListedModules = ['vue', 'vuetify'] line in these vuetify webpack builds.

Worked! Thanks!.

@psychosis448

This comment has been minimized.

Copy link

psychosis448 commented Jan 17, 2019

The actual fix for this is to ensure both Vue and Vuetify are treated the same way in the webpack externals, e.g. putting both (or neither?) in the let whiteListedModules = ['vue', 'vuetify'] line in these vuetify webpack builds.

This seems to be a solution for vue-electron?

vue.config.js (vue v.2.5.17, vuetify v1.3.4)

module.exports = {
  configureWebpack: {
    externals: {
      vue: 'Vue',
      vuetify: 'Vuetify'
    }
  }
}

does not prevent the log from showing up.

I dislike both solutions to surpress the log as @posva suggested or polluting the global Vue instance as suggested in #4964

@robwold

This comment has been minimized.

Copy link

robwold commented Jan 28, 2019

If you run into this problem via a Rails web app, here is a skeleton app on how to fix it

https://github.com/lukebyrne/rails-vuetify-test

Just follow the git commits to see the changes made to get it resolved.

Many thanks to @KaelWD

@lukebyrne A colleague is really struggling with this issue in a Rails app, but your link 404s - did you delete this repo? Any pointers in the right direction would be hugely appreciated...

@Vicnovais

This comment has been minimized.

Copy link

Vicnovais commented Jan 31, 2019

@KaelWD @johnleider

Posting here as I can't find the time to create a reproduction at work for now.

Upgrading to v1.1.1 - this issue seems to occurring for my project because I was vendoring vuetify using Webpack 4 DllPlugin. The problem seems to go away if I've taken vuetify out of the vendoring. Whether "vue" is in the vendoring or not don't seem to make a difference.

If you think this is a legit issue you'd like to look at, please let me know if I should open a new ticket or if you'd just like to reopen. Thank you.

Dary

I have the same problem. The problem starts when I include vuetify on vendor bundle. Is there any update on this?

@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented Feb 1, 2019

@Vicnovais I'm not an expert on this, I had the same problem, but the root cause is including Vue and Vuetify different ways, e.g. one in webpack one not, or similar. If you bundle them the same, all should be good. Did you see the comment above by @psychosis448 putting them both in externals?

module.exports = {
  configureWebpack: {
    externals: {
      vue: 'Vue',
      vuetify: 'Vuetify'
    }
  }
}

That is an example of one of many solutions to ensuring that either both are bundled or excluded (in this case both excluded from the pack).

@Vicnovais

This comment has been minimized.

Copy link

Vicnovais commented Feb 1, 2019

@appurist If I put both of them in external it works, but I'd like to bundle them together in vendor bundle. Like @darylteo said, it does not matter if they (vue and vuetify) are or are not together at the same time in the bundle, it just simply fails saying that there are multiple instances. This is part of my bundle configuration:

    entry: {
      vendor: ['event-source-polyfill', 'vue', 'vuex', 'axios', 'vue-router', 'jquery', 'vuetify/dist/vuetify.css']
    },
    output: {
      path: path.join(__dirname, 'wwwroot', 'dist'),
      publicPath: '/dist/',
      filename: '[name].js',
      library: '[name]_[hash]'
    }
@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented Feb 1, 2019

I think the problem with them bundled is that they are slightly different (Vue vs Vue+compiler? Same folder paths? Module export conventions?) There are a lot of things that can go wrong with it bundled. Tough to get just right.

@psychosis448

This comment has been minimized.

Copy link

psychosis448 commented Feb 6, 2019

@appurist unfortunately putting both into externals does not solve the problem when using Vue for me.

ATM I went with disabling the message for the test environment within node_modules, which is not an option if you are working with multiple people:

if (vue__WEBPACK_IMPORTED_MODULE_0___default.a !== Vue && process.env.NODE_ENV !== 'test')
@appurist

This comment has been minimized.

Copy link
Contributor Author

appurist commented Feb 6, 2019

@psychosis448 It might also be due to your app importing a slightly different Vue implementation than Vuetify does. My report near the top showed a debugger window where both vue.esm.js and vue.common.js were loaded. As we found above, mixing one packed and one external copy can create this issue, or any other combination of two copies being loaded, such as including both the runtime-only and the version that includes the Vue compiler, or in the case above, both the ESM + Common.js variants.

This is really a problem with Vue and is tracked in Vue issue #8278 but... it might be possible for Vuetify to avoid it if Vuetify could just avoid importing Vue at all. For example, put the onus on the end-developer to import it and apply it such that it is available via $Vue or some other method that does not require Vuetify to import it itself. I think this is how Vuetify 1.0 worked.

@broql

This comment has been minimized.

Copy link

broql commented Feb 6, 2019

@kakenbok

Solution for web apps is in this comment #4068 (comment) by @AMontagu

Vue.use(Vuetify)
instead of
localVue.use(Vuetify)

@ericrtung

This comment has been minimized.

Copy link

ericrtung commented Feb 15, 2019

I ran into this issue while developing a Vue SPA with HMR in .Net Core 2.2 using the webpack.config.json + webpack.vendor.config.json strategy.

The only way I was able to resolve it and still be able to do a vanilla import in my main.js, eg:

import Vue from 'vue';
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css

Vue.use(Vuetify);

Was to alias Vue to the target version I wanted in both the base and vendor.config's:

webpack.config.json:

const path                  = require('path');
const webpack               = require('webpack');
const VueLoaderPlugin       = require('vue-loader/lib/plugin');
const MiniCssExtractPlugin  = require("mini-css-extract-plugin");

const bundleOutputDir       = './wwwroot/dist';

module.exports = (env) => {
    const isDevBuild = !(env && env.prod);

    return [{
        mode: isDevBuild ? "development" : "production",
        context: __dirname,
        resolve: {
            extensions: ['.js'],
            alias: {
                'vue$': 'vue/dist/vue.js',
                'stringify': 'json-stringify-pretty-compact'
            }
        },
        entry: {
            main: './ClientApp/main.js'
        },
        devtool: 'source-map',
        output: {
            path: path.join(__dirname, bundleOutputDir),
            filename: '[name].js',
            publicPath: '/dist/'
        },
        module: {
            rules: [
                // REF: https://vue-loader.vuejs.org/guide/extract-css.html#webpack-4
                { test: /\.vue$/, loader: 'vue-loader' },
                { test: /\.js$/, loader: 'babel-loader' },
                {
                    test: /\.css$/,
                    use: [
                        isDevBuild
                            ? 'vue-style-loader'
                            : MiniCssExtractPlugin.loader,
                        'css-loader'
                    ]
                },
                { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=10000' }
            ]
        },
        plugins: [
            new webpack.DefinePlugin({
                'process.env': {
                    NODE_ENV: JSON.stringify(isDevBuild ? 'development' : 'production')
                }
            }),
            new webpack.ProvidePlugin({
                _: 'lodash',
                $: 'jquery',
                jQuery: 'jquery',
                moment: 'moment',
                vue: ['vue/dist/vue.js', 'default']
            }),
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./wwwroot/dist/vendor-manifest.json')
            }),
            new MiniCssExtractPlugin({
                filename: 'site.css'
            }),
            new VueLoaderPlugin()
        ]
    }];
};

webpack.vendor.config.json:

const path                  = require('path');
const webpack               = require('webpack');
const MiniCssExtractPlugin  = require("mini-css-extract-plugin");

module.exports = (env) => {
    const isDevBuild = !(env && env.prod);
    
    return [{
        mode: isDevBuild ? "development" : "production",
        stats: { modules: false },
        resolve: {
            extensions: ['.js'],
            alias: {
                'vue$': 'vue/dist/vue.js',
            }
        },
        entry: {
            vendor: [
                'path',
                'jquery',
                '@babel/polyfill',
                'vuetify/dist/vuetify.min.css',
                'vuetify',
                'vue/dist/vue.js',
                'vue-router',
                'vee-validate',
                'chart.js',
                'lodash',
                'moment'
            ]
        },
        module: {
            rules: [
                { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader" ] },
                { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=10000' }
            ]
        },
        output: {
            path: path.join(__dirname, 'wwwroot', 'dist'),
            publicPath: '/dist/',
            filename: '[name].js',
            library: '[name]_[hash]'
        },
        plugins: [
            new MiniCssExtractPlugin({
                filename: "[name].css",
                chunkFilename: "[id].css"
            }),
            new webpack.DllPlugin({
                path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
                name: '[name]_[hash]'
            }),
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': isDevBuild ? '"development"' : '"production"'
            })
        ]
    }];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment