Поддерживает несколько компонентов и доступ к свойствам Vuex

#javascript #vue.js #vuex

#javascript #vue.js #vuex

Вопрос:

Я изучаю Vue, использую его с Vuex (без Webpack), но у меня есть несколько вопросов при реализации этого простого примера, это непонятно для меня в документах.

  1. Не знаю почему, но я не могу получить доступ к хранилищу Vuex, используя this указатель внутри свойства компонента computed , например: this.$store.state.nav.title , что приводит меня к использованию глобальной app переменной вместо этого. Кроме того, this.$parent и $root не работают.

  2. Правильно ли инициализировать несколько компонентов Vue одновременно, например, таким образом, и не должны ли они монтироваться автоматически, когда я передаю components свойство объекту Vue construct? Как правильно инициализировать, например, компоненты верхнего, нижнего колонтитулов и тела одновременно?

 var app = new Vue({
    el: document.getElementById('app'),
    data: {
        title:store.state.nav.title
    },
    computed: {},
    methods:{},
    mounted:function(){},
    updated:function(){},
    store:store,
    components:{
        componentheader,
        componentnavbar,
        componentbody,
        componentfooter
    }
});

for (var companent_name in app.$root.$options.components) {
    if(typeof app.$root.$options.components[companent_name] === 'function') {
        var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
        var component = new MyComponent().$mount();
        document.getElementById('app').appendChild(component.$el);
    }
}
  

Вот полный пример:

 var store = new Vuex.Store({
  state: {
    nav: {
      title: 'my site'
    }
  },
  mutations: {
    changeTitle: function(t, a) {
      this.state.nav.title = a;
    }
  }
});

var componentheader = Vue.component('componentheader', {
  computed: {
    title() {
      return app.$store.state.nav.title
    }
  },
  template: '#header_tpl',
  mounted: function() {},
  updated: function() {}
});

var componentnavbar = Vue.component('componentnavbar', {
  computed: {
    title() {
      return app.$store.state.nav.title
    }
  },
  template: '#navbar_tpl',
  mounted: function() {},
  updated: function() {}
});

var componentbody = Vue.component('componentbody', {
  computed: {
    title() {
      return app.$store.state.nav.title
    }
  },
  template: '#body_tpl',
  mounted: function() {},
  updated: function() {}
});

var componentfooter = Vue.component('componentfooter', {
  computed: {
    title() {
      return app.$store.state.nav.title
    }
  },
  template: '#footer_tpl',
  mounted: function() {},
  updated: function() {}
});

var app = new Vue({
  el: document.getElementById('app'),
  data: {
    title: store.state.nav.title
  },
  computed: {},
  methods: {},
  mounted: function() {},
  updated: function() {},
  store: store,
  components: {
    componentheader,
    componentnavbar,
    componentbody,
    componentfooter
  }
});

Vue.use(Vuex);


for (var companent_name in app.$root.$options.components) {
  if (typeof app.$root.$options.components[companent_name] === 'function') {
    var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
    var component = new MyComponent().$mount();
    document.getElementById('app').appendChild(component.$el);
  }
}

Vue.config.devtools = false;
Vue.config.productionTip = false;  
 * {
  margin: 0;
  padding: 0;
  color: #fff;
  text-align: center;
  font-size: 19px;
}

html,
body,
.container {
  height: 100%;
}

#app {
  position: relative;
  height: 100%;
  min-height: 100%;
}

header {
  width: 100%;
  height: 80px;
}

nav.navbar {
  box-sizing: border-box;
  min-height: 100%;
  padding-bottom: 90px;
  width: 80px;
  height: 100%;
  position: absolute;
}

.container {
  box-sizing: border-box;
  min-height: 100%;
  padding-bottom: 90px;
  color: #000;
}

footer {
  height: 80px;
  margin-top: -80px;
}

footer,
nav,
header {
  background: #000;
}

header div,
footer div {
  padding: 15px;
}

nav ul {
  list-style-type: none;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.5.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <div id="app">

  </div>
  <script type="text/x-template" id="header_tpl">
    <header class="header">
      <div>
        header {{ title }}
      </div>
    </header>
  </script>

  <script type="text/x-template" id="navbar_tpl">
    <nav class="navbar">
      <ul>
        <li>navbar {{ title }}</li>
      </ul>
    </nav>
  </script>


  <script type="text/x-template" id="body_tpl">
    <div class="container">
      <div>
        body {{ title }}
      </div>
    </div>
  </script>
  <script type="text/x-template" id="footer_tpl">
    <footer class="footer">
      <div>
        footer {{ title }}
      </div>
    </footer>
  </script>

</body>

</html>  

Ответ №1:

Вы, кажется, запутались в экземпляре Vue и компоненте Vue. В принципе, вам нужен только один экземпляр Vue с несколькими компонентами для создания вашего приложения.

Чтобы ответить на ваш первый вопрос, это не работает, потому что вы не установили хранилище для каждого созданного вами экземпляра Vue (вы устанавливаете только 1 экземпляр с именем app).

 for (var companent_name in app.$root.$options.components) {
  if (typeof app.$root.$options.components[companent_name] === 'function') {
    var MyComponent = Vue.extend(app.$root.$options.components[companent_name]);
    var component = new MyComponent({
      store // <-- install here
    }).$mount();
    document.getElementById('app').appendChild(component.$el);
  }
}
  

Рабочий пример здесь

На самом деле вы можете просто использовать, store поскольку все store и app.$store и this.$store — это один и тот же объект. Преимущество this.$store в том, что вам не нужно импортировать store в каждый файл компонента для отдельных файловых компонентов.

Чтобы ответить на ваш второй вопрос,

 <div id="app">
  <componentheader></componentheader>
  <componentnavbar></componentnavbar>
  <componentbody></componentbody>
  <componentfooter></componentfooter>
</div>
  

Рабочий пример здесь

Комментарии:

1. Спасибо за ответ на первый вопрос, который я теперь понимаю. Но что касается второго вопроса, причина, по которой я не использую привязку тегов шаблонов непосредственно внутри #app, заключается в том, что я хочу использовать vue router в будущем и монтировать компоненты по требованию.