【Vue.js】scrollBehavior 함수로 페이지 천이시의 스크롤을 유효하게 하는 비동기의 경우(transition 있음)도

소개



일에서 사용하게 되었기 때문에 1부터 Vue.js에 대해 배웠다.
제대로 기억하지 않으면 우선 그런 것을 비망록으로 1개 1개 남겨 둔다.

scrollBehavior 함수로 페이지 전환 시 스크롤 사용



원래 Vue.js에서는 event.preventDefault() 가 효과가 있어 디폴트의 움직임을 하지 않는다



아래와 같이 router-link 의 to 에 hash(#) 가 붙은 URL을 지정해, 그 링크를 클릭해 브라우저로 그 URL에 천이해도, id=next-user 의 요소에 자동적으로 스크롤 하지 않는다.
(URL을 브라우저에 직타했을 때는, 브라우저의 기능으로 id=next-user 의 요소에 스크롤 하지만.)


Users.vue
<template>
  <div>
    <!-- 省略 -->
    <router-link
      :to="'/users/' + (Number(id) + 1) + '/profile?lang=ja&page=2#next-user'"
    >
      次のユーザ
    </router-link>
    <div style="height: 700px"></div>
    <router-link
      id="next-user"
      :to="{
        name: 'users-id-profile',
        params: { id: Number(id) + 1 },
        query: { lang: 'ja', page: 2 },
        hash: '#next-user',
      }"
    >
      次のユーザ
    </router-link>
    <div style="height: 1400px"></div>
  </div>
</template>

<script>
export default {
  props: ["id"],
};
</script>

이것은 Vue.j에서 기본적으로 HTML의 각 요소의 기본 동작을 멈추는 방식 (JavaScript event.preventDefault()와 동일)이 있기 때문입니다.
이 #next-user로 id=next-user 의 요소로 스크롤 시키려면 스스로 그것을 구현할 필요가 있습니다. 그것은 scrollBehavior

scrollBehavior 함수로 페이지 전환 시 스크롤 사용



페이지 천이시의 스크롤을 유효하게 하기 위해서는, 이하와 같이 구현하면 된다.if (savePosition) {} , if (to.hash) {} , return { x: 0, y: 0 }; 라는 3단 구성은 일반적으로 자주 사용된다 scrollBehavior 함수의 형태이다.
  • if (savePosition) {} 스크롤로 위치가 바뀐 후에 브라우저 백 등으로 돌아왔을 때, 이전 화면에서의 위치 정보가 있을 때의 처리를 쓴다. return savePosition;로 설정하면 자동으로 화면 전환 전의 스크롤 위치 (사용자가 스스로 스크롤 한 경우에도 그 위치)로 돌아갈 수 있습니다. (무언가의 사이트인 항목을 클릭하여 페이지 전이하고, 브라우저백했을 때에 원래의 페이지가 화면 상단에서 열리는지, 전보고 있던 항목에서 열리는지,의 차이가 있었다고 하는 경험이 있다 생각하지만, Vue.js에서 구현하는 경우이 savePosition을 사용하면 원래 보았던 위치로 자동 스크롤 할 수 있습니다.)
  • if (to.hash) {} router계(페이지 천이등의 상태 천이가 있는 것)에는 to, from이라고 하는 오브젝트가 대체로 있지만, 그 페이지 천이의 때, 그 천이처의 URL에 '#'가 포함될 때의 처리를 쓰기. 이렇게 하면 항상 거리의 움직임(#next-user에서 id=next-user의 요소로 스크롤한다)이 된다.
  • return { x: 0, y: 0 }; 원래의 위치를 ​​기억하고 있지 않다·스크롤시킬 필요도 없는 경우의 처리로, 아무것도 하지 않기 때문에 x=0, y=0을 단순하게 return 하고 있다.

    동영상의 소스 코드는 이하.

  • router.js
    // 省略
    
    Vue.use(Router);
    
    export default new Router({
        // 省略
        scrollBehavior(to, from, savePosition) {
            console.log("to", to);
            console.log("from", from);
            console.log("savePosition", savePosition);
            if (savePosition) {
                return savePosition;
            }
    
            if (to.hash) {
                return {
                    selector: "#next-user",
                    offset: { x: 0, y: 100 }
                }
            }
    
            return { x: 0, y: 0 };
        }
    });
    

    소스 코드 전체는 이하.

    transiton이 설정되어 있을 때도 올바르게 scrollBehavior를 작동시키는 방법?



    사고방식으로는, transition의 JavaScript 훅을 사용해, 그 훅 함수가 불리는 것을 기동 스위치로서, 비동기적으로 scrollBehavior 함수를 호출한다고 하는 것을 한다.
    구체적으로는

  • transition 자바 스크립트 후크 을 사용하여 @before-enter 에서 요소가 사라져 나타났을 때…라는 처리를 구현하고, 거기에서 this.$root.$emit()
  • $ emit로 발화 한 커스텀 이벤트를 this.app.$root.$once('triggerScroll', () => {})로 캡처하고 비동기 Promise로 resolve

  • 라고 하는 일을 한다.

    동영상의 소스 코드는 이하.

    App.vue
    <template>
      <div>
        <router-view name="header"></router-view>
        <main class="container-sm">
          <transition name="fade" mode="out-in" @before-enter="beforeEnter">
            <router-view></router-view>
          </transition>
        </main>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        beforeEnter() {
          // this.$root:これはmain.jsの一番上の階層にあるVueインスタンスの事
          // ※単なるthisはここで言えば、App.vueというsingle file componentのVueインスタンス
          // this.$root.$emit:これにより、main.jsのVueインスタンスにtriggerScrollというイベントを発火させることができ、そのイベントをscrollBehavior関数が検知する事で非同期のscrollBehaviorの振る舞いが実装されている
          this.$root.$emit("triggerScroll");
        },
      },
    };
    </script>
    
    <style scoped>
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }
    .fade-enter-active,
    .fade-leave-active {
      transition: opacity 1s;
    }
    </style>
    

    router.js
    // 省略
    
    Vue.use(Router);
    
    export default new Router({
        // 省略
        scrollBehavior(to, from, savePosition) {
            // パスが同じ時はすぐにscrollBehaviorを実行する
            if (to.path.match("^/users/") && from.path.match("^/users/")) {
                this.app.$root.$nextTick(() => this.app.$root.$emit('triggerScroll'));
            }
    
            return new Promise(resolve => {
                // this:new Router()のインスタンス自体の事
                // this.app:new Router()インスタンスが挿入・適用されるVueインスタンス(今回で言えばmain.jsのVueインスタンス)
                // this.app === this.app.$root
                // $once($on):$emitで発火させたeventの処理を書ける構文(triggerScrollという$emitが発火した時にcallback関数が実行される)
                this.app.$root.$once('triggerScroll', () => {
                    let position = { x: 0, y: 0 };
    
                    if (savePosition) {
                        position = savePosition;
                    }
    
                    if (to.hash) {
                        position = {
                            selector: "#next-user",
                            offset: { x: 0, y: 100 }
                        }
                    }
    
                    resolve(position);
                });
            });
        }
    });
    

    소스 코드 전체는 이하.

    Vue.js 공부 메모 목록 기사 링크


  • Vue.js에 대해 공부할 때 작성한 공부 메모 기사 링크를 집계한 기사.
  • 좋은 웹페이지 즐겨찾기