admin管理员组

文章数量:1406060

I am using vue 3 template refs, in a nuxt project, with position API as I have done with other ponents which seem to work fine. However, in this instance, they are returning null.

Here is my template:

<template>
    <div class="horizontal-scroll-fix" ref="container">
        <div class="horizontal-scroll-fix__scroll-fix">
            <container class="horizontal-scroll-fix__container">
                <div class="horizontal-scroll-fix__viewport" ref="viewport">
                    <div class="horizontal-scroll-fix__wrapper" ref="wrapper">
                        <slot></slot>
                    </div>
                </div>
            </container>
        </div>
    </div>
</template>


<script>

    import { ref, puted, onMounted, onBeforeUnmount, useSlots } from 'vue';

    export default { // tried also to use shorthand <script setup> but no luck either
        setup() {
            const container = ref(null);
            const wrapper = ref(null);
            const viewport = ref(null);


            onMounted(() => {
                if (process.client) {
                    console.log(container?.value) // returns undefined
                }
            });


        }
    }

</script>

console.logging the ref object returns the following:

RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: undefined, _value: undefined}

UDPDATE

I have since been informed that I need to return the refs at the end of the set up script. so return { container, wrapper, viewport } However, what is confusing is that all other ponents I have in my project, don't do this, but work fine. so what is different about this one that I am not seeing? Here is an example of another ponent that has template refs that works perfectly fine, and doesn't return the values:

<template>
    <container>
        <div :class="'sequence sequence--'+section.displayAs">
            <div class="sequence__content" ref="content">
                // removed inner content for the purpose of demonstrating
            </div>
        </div>
    </container>
</template>

<script setup> 
    import { ref, puted, onMounted, onUnmounted } from 'vue';
    const props = defineProps({
        section: {
            required:true,
            type:Object
        }
    });

    const isDesktop = ref(false);
    const currentSectionIndex = ref(0);
    const fixedVisual = ref(null);
    const content = ref(null);

    function initMediaQuery() {
        const mediaQuery = window.matchMedia('(min-width:1024px)');
        checkDeviceSize(mediaQuery);
        mediaQuery.addListener(checkDeviceSize);
        
    };
    function checkDeviceSize(query) {
        if (query && query.matches) {
          isDesktop.value = true
        } else {
          isDesktop.value = false
        }
    };
    function initObserver() {
        if (props.section?.displayAs === 'timeline' && isDesktop) {
            console.log(isDesktop);
            const target = fixedVisual;
            const sections = content?.value.querySelectorAll('.sequence__section');
            if (target && sections?.length) {
                let callback = (entries, observer) => {
                    entries.forEach((entry,index) => {
                        if (entry.isIntersecting) {
                            currentSectionIndex.value = parseInt(entry.target.getAttribute('data-index'));
                        }   
                    })
                }
                let options = {
                    rootMargin: '0px',
                    threshold:1.0
                }
                let observer = new IntersectionObserver(callback,options);
                sections.forEach(section => {
                    observer.observe(section);
                });
            }
        }
    }


    onMounted(() => {
        if (process.client) {
            initMediaQuery();
            initObserver();
            window.addEventListener("resize", initObserver);
        }
    });

    onUnmounted(()=> {
        if (process.client) {
            window.removeEventListener("resize", initObserver);
        }
    });
    



</script>

I am using vue 3 template refs, in a nuxt project, with position API as I have done with other ponents which seem to work fine. However, in this instance, they are returning null.

Here is my template:

<template>
    <div class="horizontal-scroll-fix" ref="container">
        <div class="horizontal-scroll-fix__scroll-fix">
            <container class="horizontal-scroll-fix__container">
                <div class="horizontal-scroll-fix__viewport" ref="viewport">
                    <div class="horizontal-scroll-fix__wrapper" ref="wrapper">
                        <slot></slot>
                    </div>
                </div>
            </container>
        </div>
    </div>
</template>


<script>

    import { ref, puted, onMounted, onBeforeUnmount, useSlots } from 'vue';

    export default { // tried also to use shorthand <script setup> but no luck either
        setup() {
            const container = ref(null);
            const wrapper = ref(null);
            const viewport = ref(null);


            onMounted(() => {
                if (process.client) {
                    console.log(container?.value) // returns undefined
                }
            });


        }
    }

</script>

console.logging the ref object returns the following:

RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: undefined, _value: undefined}

UDPDATE

I have since been informed that I need to return the refs at the end of the set up script. so return { container, wrapper, viewport } However, what is confusing is that all other ponents I have in my project, don't do this, but work fine. so what is different about this one that I am not seeing? Here is an example of another ponent that has template refs that works perfectly fine, and doesn't return the values:

<template>
    <container>
        <div :class="'sequence sequence--'+section.displayAs">
            <div class="sequence__content" ref="content">
                // removed inner content for the purpose of demonstrating
            </div>
        </div>
    </container>
</template>

<script setup> 
    import { ref, puted, onMounted, onUnmounted } from 'vue';
    const props = defineProps({
        section: {
            required:true,
            type:Object
        }
    });

    const isDesktop = ref(false);
    const currentSectionIndex = ref(0);
    const fixedVisual = ref(null);
    const content = ref(null);

    function initMediaQuery() {
        const mediaQuery = window.matchMedia('(min-width:1024px)');
        checkDeviceSize(mediaQuery);
        mediaQuery.addListener(checkDeviceSize);
        
    };
    function checkDeviceSize(query) {
        if (query && query.matches) {
          isDesktop.value = true
        } else {
          isDesktop.value = false
        }
    };
    function initObserver() {
        if (props.section?.displayAs === 'timeline' && isDesktop) {
            console.log(isDesktop);
            const target = fixedVisual;
            const sections = content?.value.querySelectorAll('.sequence__section');
            if (target && sections?.length) {
                let callback = (entries, observer) => {
                    entries.forEach((entry,index) => {
                        if (entry.isIntersecting) {
                            currentSectionIndex.value = parseInt(entry.target.getAttribute('data-index'));
                        }   
                    })
                }
                let options = {
                    rootMargin: '0px',
                    threshold:1.0
                }
                let observer = new IntersectionObserver(callback,options);
                sections.forEach(section => {
                    observer.observe(section);
                });
            }
        }
    }


    onMounted(() => {
        if (process.client) {
            initMediaQuery();
            initObserver();
            window.addEventListener("resize", initObserver);
        }
    });

    onUnmounted(()=> {
        if (process.client) {
            window.removeEventListener("resize", initObserver);
        }
    });
    



</script>

Share Improve this question edited Oct 27, 2022 at 10:27 ToddPadwick asked Oct 26, 2022 at 17:02 ToddPadwickToddPadwick 5051 gold badge7 silver badges14 bronze badges 1
  • I think it works in other ponents because with <script setup> variables don't need to be returned at the end. – Gabriel Commented Oct 27, 2022 at 15:03
Add a ment  | 

4 Answers 4

Reset to default 3

You need to return the 3 variables at the end of the setup() method :

setup() {
    ...
    return {
        container,
        wrapper,
        viewport
    }
}

The method setup() is called at the beginning of the lifespan of the ponent, just as renderer encounters it. That means that all elements used in the body of the <template> must be known at the moment that setup() finishes.

Body of your setup is missing the return of ref values you wish to use in <template>. In order to get the expected behavior, you should change your setup():

setup() {
    const container = ref(null);
    const wrapper = ref(null);
    const viewport = ref(null);

    // callbacks such as onMounted, onBeforeCreate, etc.
    
    return { container, wrapper, viewport };
}

In my case, I was using client rendering, and needed this:

        onMounted( async () => {
            await nextTick(); // we need this for client rendering,
            // get ref.value
        });

see here: https://nuxt./docs/guide/directory-structure/ponents#client-ponents

It took me hours to find this, googling the error message, so I hope this helps someone else.

To answer @ToddPadwick about why other ponents don't return the refs and still work fine :

Nuxt 3 docs explain it by auto-import : https://nuxt./docs/guide/concepts/auto-imports#vue-auto-imports

Vue 3 exposes Reactivity APIs like ref or puted, as well as lifecycle hooks and helpers that are auto-imported by Nuxt.

So, you don't have to write return { container, wrapper, viewport } at the end of your script. Nuxt 3 will do it for you except if you've turn it off like this :

https://nuxt./docs/guide/concepts/auto-imports#disabling-auto-imports

本文标签: javascriptVue 3 Template refs not returning a valueStack Overflow