Как передать ClientWidth в свойство компонента?
Я пытаюсь передать clientWidth родителя в компонент, но получаю ошибку: TypeError: Cannot read property 'clientWidth' of undefined. Подскажите, пожалуйста, как её исправить?
Vue.component('my-component', {
template: '<p>{{ clientWidth }}</p>',
props: ['clientWidth'],
});
new Vue({ el: '#app' });
<div id="app">
<div class="parent" ref="parent">
<my-component :client-width="$refs.parent.clientWidth"></my-component>
</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
Ответы (2 шт):
Похоже $refs инициализируется после рендера компонента, но до выполнения mounted. Поэтому в шаблоне $refs.parent возвращает undefined.
Vue.component('my-component', {
template: '<p>{{ clientWidth }}</p>',
props: ['clientWidth']
});
new Vue({ el: '#app',
data(){
return {parentWidth: 0};
},
mounted(){
this.parentWidth = this.$refs.parent.clientWidth;
}
});
<div id="app">
<div class="parent" ref="parent">
<my-component :client-width="parentWidth"></my-component>
</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
Простой глобальный миксин решает задачу отслеживания изменения состояния жизненного цикла компонента на "смонтирован":
Vue.mixin({
computed: {
$isMounted() {
return this._lc.isMounted;
},
},
created() {
this._lc = Vue.observable({ isMounted: false });
},
mounted() {
this.$nextTick(() => void(this._lc.isMounted = true));
},
});
Vue.component('my-component', {
template: '<p>clientWidth: {{ clientWidth }}px</p>',
props: ['clientWidth'],
});
new Vue({ el: '#app' });
<div id="app">
<div class="parent" ref="parent">
<my-component v-if="$isMounted" :client-width="$refs.parent.clientWidth"></my-component>
</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
То есть, поместив этот вызов Vue.mixin в main.js (до создания корневого экземпляра Vue), можно в шаблоне любого компонента использовать реактивное свойство $isMounted без каких-либо локальных бойлерплейтов.
// При желании, можно расширить миксин дополнительными свойствами отражающими состояние компонента. Но именно из жизненного цикла, обычно требуется отслеживать только монтирование (т.к. остальные задачи уже имеют специфичные для каждого компонента локальные побочные эффекты - и поэтому, они решаются локальными хуками).
То же самое, но уже не миксином, а через Composition API:
/* src/composables/useIsMounted.js */
import { reactive, computed, onMounted } from 'vue';
export default function() { // Вообще, тут можно и `ref` использовать, т.к. значение одиночное:
const _lc = reactive({ isMounted: false }), // | const _isMounted = ref(false),
isMounted = computed(() => _lc.isMounted); // | isMounted = computed(() => _isMounted.value);
onMounted(() => void(_lc.isMounted = true)); // | onMounted(() => void(_isMounted.value = true));
return isMounted;
};
И в компоненте:
import useIsMounted from '@/composables/useIsMounted';
export default {
// ...
setup() {
// ...
return {
isMounted: useIsMounted(),
// ...
};
},
};
В шаблоне свойство используется уже с именем isMounted, то есть без $!
// Потому что такой префикс имеет смысл только с глобальным миксином.
Почитать:
- Vue.observable (Vue 2.6+)
- Диаграмма жизненного цикла компонента (доки Vue 2.*, но актуально и с Vue 3.*)
reactiveкак новыйobservable, и основы реактивности в актуальной версии (Vue 3.*)