简体   繁体   中英

Using vue-router gives "Cannot find element #app" and "TypeError: Cannot read property 'matched' of undefined" errors

I am attempting to use Vue.js to recreate a frontend app I previously made using Scala Play (I'd like to learn more about component-based web design).

I had everything loading fine for the first page I made, but I wanted to implement routing so I could have multiple pages (rather than just having one big single-page application). I installed vue-router using npm install vue-router --save , and am trying to follow the following tutorial (using the components I've already made though): https://scotch.io/tutorials/how-to-build-a-simple-single-page-application-using-vue-2-part-1 .

As far as I can tell, everything should be loading smoothly, but nothing loads and I get the following console errors:

[Vue warn]: Cannot find element: #app
[Vue warn]: Cannot find element: #app
[Vue warn]: Error in render: "TypeError: Cannot read property 'matched' of undefined"

found in

---> <App> at src/App.vue
       <Root>

TypeError: Cannot read property 'matched' of undefined
    at render (vue-router.esm.js?8c4f:76)
    at createFunctionalComponent (vue.runtime.esm.js?2b0e:3033)
    .... (etc)

I've not pasted the full stack traces but can do if needed.

My code is below:

main.js

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'

import Home from './components/Home.vue'

Vue.use(VueRouter)

const routes = [
  { path: '/', component: Home }
]

const router = new VueRouter({
  routes
})

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
  router
}).$mount('#app')

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

App.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'app'
}
</script>

<style>
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
  body {
    font-family: Arial, Helvetica, sans-serif;
    line-height: 1.4;
    background-color: aliceblue;
  }
</style>

components/Home.vue

<template>
  <div id="app">
    <Header />
    <Posts v-bind:posts="posts" />
  </div>
</template>

<script>

import Posts from './Posts.vue'
import Header from './Header.vue'

export default {
  name: 'home',
  components: {
    Header, Posts
  },
  data() {
    return {
      posts: []
    }
  }
}
</script>

<style>
</style>

Let me know if I need to include Header.vue and Posts.vue too - I've omitted them because they were rendering fine before I tried to use vue-router.

How do I get rid of these console errors so my home page renders correctly when using vue-router? I have searched around and so far, all of my search results have led me to some variation on "make sure router is lowercase" (it is), "make sure routes is lowercase" (it is), and "use import Vue from 'vue/dist/vue.js' " (didn't work).

With regard to the original issue:

As I am sure you know from the comment discussion, the errors [Vue warn]: Cannot find element: #app were caused because two Vue instances were configured to mount—one after another—at the same root DOM node #app .

The first Vue instance mounts, and in doing so, replaces the root DOM node with the HTML defined in its own template. Therefore, the #app root DOM node no longer exists, and the second Vue instance fails to mount because it cannot find any DOM node with id #app .

This implies that if the first Vue template included a DOM node with id #app , the second Vue instance would probably mount there, replacing any existing HTML from the first instance with its own.

This error is resolved by removing the duplicate Vue instance.


With regard to your follow-up question:

I don't have the faintest idea why this works without all the el: '#app', template: '<App/>', components: { App }

The first thing to note is that Vue instance property vm.$el and Vue instance method vm.$mount() are functionally equivalent for the most part—each allows you to specify the root DOM element where the Vue instance will be mounted.

The main difference is in their application:

  • You use vm.$el to declare the root DOM node at instance creation. Supplying a value is not obligatory however, and if you don't the instance is created in an unmounted state .

  • vm.$mount() allows you to specify a root DOM node for an unmounted instance after instantiation.

As stated in the docs:

If a Vue instance didn't receive the el option at instantiation, it will be in “unmounted” state, without an associated DOM element. vm.$mount() can be used to manually start the mounting of an unmounted Vue instance.

Of course, if you use vm.$mount() instead of vm.$el at instance creation, this is largely a matter of developer taste (although $mount() does also accept a variety of inputs so there are other subtle differences there). But from the perspective of your question, there is no practical difference.

Finally, Vue's template property and render() method are simply two strategies for defining the template structure of a Vue instance:

  • template is the higher-level template abstraction that allows you to define a string template ; eg: <div><App/></div> which Vue parses into a structure, instantiating any child components as required.

  • render() is an alternative, lower-level template abstraction that is a closer-to-the-compiler alternative to templates . It is more flexible for finer-grained/programmatic use-cases or for use with other template languages such as JSX or raw JS.

The main obvious difference in your examples is that template defines a string template using the <App/> component. The Vue instance needs to know what this component is before this template string is parsed, so it must be declared as a component.

Whereas, render() allows the App component to be passed for render directly, so it is not required to define a template property or declare any components.

My outlook is that the former is more explicit—which is not a bad thing—and is more declarative and readable when declaring a structure with several components. To that end, Vue seems to recommend template strings for most usage. That said, the latter approach can also be neat, and you'll often see it where a single, top-level, entry-point component (eg: App ) is being mounted.

Another (implicit) strategy is the DOM template . In the absence of a template property or render() method, the HTML structure defined within the root DOM node is used—although this is generally discouraged.

Hope this helps :)

I fixed the issue by cropping down my main.js to only contain one new Vue section, which looks like this now:

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

I don't have the faintest idea why this works without all the el: '#app', template: '<App/>', components: { App } stuff though, so if that could be explained I'd appreciate it - I was hesitant to add an answer as I don't know why this worked; if someone adds an answer explaining it I'd be happy to accept that (or feel free to edit my answer with an explanation and cut this paragraph out).

I found that the issue could be missing

<div id="app"></div>

at the body of your index.html.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM