웹사이트 검색

Vue.js 및 vue-router로 페이지 제목 및 메타데이터를 업데이트하는 방법


소개

vue-router는 Vue.js를 위한 탁월한 라우팅 솔루션이지만 경로 변경 시 페이지 제목과 메타데이터를 업데이트하려면 추가 구성이 필요합니다. 페이지가 변경될 때 브라우저의 제목을 변경하고 싶을 때가 있을 것입니다. 그리고 SEO(검색 엔진 최적화)의 경우 모든 검색 결과 또는 웹사이트 링크가 모든 경로에 대해 "홈 페이지\라고 표시되는 것을 원하지 않을 것입니다.

이 문서에서는 이 기능을 직접 추가하는 방법을 알아봅니다. 사용자 지정 가능한 페이지 제목과 경로 변경에 대한 메타데이터를 사용하여 예제 Vue 애플리케이션을 빌드합니다.

전제 조건

이 자습서를 완료하려면 다음이 필요합니다.

  • Node.js를 로컬에 설치했습니다. Node.js를 설치하고 로컬 개발 환경을 만드는 방법에 따라 수행할 수 있습니다.

이 튜토리얼은 Node v14.6.0, npm v6.14.7, Vue.js v2.6.11, vue-router v3.2.0 및 @vue/cli v4에서 검증되었습니다. 4.6.

1단계 — Vue 프로젝트 생성 및 종속성 설치

새로운 Vue 프로젝트를 만들어 봅시다.

먼저 터미널을 열고 vue-cli를 사용하여 Vue 프로젝트를 만듭니다.

  1. npx @vue/cli@4.4.6 create --inlinePreset='{ "useConfigFiles": false, "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "base", "lintOn": ["save"] } }, "router": true, "routerHistoryMode": true }' vue-router-meta-example

이 긴 명령은 @vue/cli/packages/@vue/cli/lib/options.js에 의해 설정된 기본값을 기반으로 하는 사전 설정 집합입니다. 가독성을 위해 형식을 다시 지정하면 다음과 같이 표시됩니다.

{
  "useConfigFiles": false,
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "base",
      "lintOn": ["save"]
    }
  },
  "router": true,
  "routerHistoryMode": true
}

이러한 사전 설정은 vue-router를 플러그인(cli-plugin-router)으로 추가하고, 히스토리 모드를 활성화하고, Babel을 추가하고, ESLint를 추가합니다.

이 자습서에서는 TypesScript, PWA(Progressive Web App) 지원, Vuex, CSS 전처리기, 단위 테스트 또는 종단 간(E2E) 테스트가 필요하지 않습니다.

다음으로 새 프로젝트 디렉터리로 이동합니다.

  1. cd vue-router-meta-example

이 시점에서 빌드할 새로운 Vue 프로젝트가 있습니다. 다음 단계는 애플리케이션에서 샘플 경로를 정의하는 것입니다. 응용 프로그램의 구조를 설정하면 제목메타 변경 사항을 볼 수 있습니다.

2단계 - 샘플 경로 및 템플릿 정의

이 예에서 목표는 다음으로 구성된 애플리케이션을 구성하는 것입니다.

  • 홈 경로(/)
  • 인접한 About 경로(/about)
  • 및 중첩된 자주 묻는 질문 경로(/about/frequently-asked-questions)

이제 main.js를 엽니다.

  1. nano src/main.js

잠시 시간을 내어 cli-plugin-router에 의해 VueRouter가 어떻게 추가되었는지 숙지하십시오.

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

Vue.config.productionTip = false

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

이제 router/index.js를 엽니다.

  1. nano src/router/index.js

잠시 시간을 내어 cli-plugin-router에 의해 생성된 \Home\\About\에 대한 경로를 숙지하십시오. 중첩된 \자주 묻는 질문\에 대한 경로를 추가합니다.

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
    children: [
      {
        path: 'frequently-asked-questions',
        component: FrequentlyAskedQuestions,
      }
    ]
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

이렇게 하면 이 자습서에서 원하는 라우팅이 설정됩니다. 아직 존재하지 않는 보기를 참조하고 있음에 유의하십시오. 다음에 다룰 것입니다.

views 디렉터리에 FrequentlyAskedQuestions.vue라는 새 파일을 만듭니다.

  1. nano src/views/FrequentlyAskedQuestions.vue

그런 다음 템플릿을 추가합니다.

<template>
  <div>
    <h2>Frequently Asked Questions</h2>
    <dl>
      <dt>What is your favorite aquatic animal?</dt>
      <dd>Sharks.</dd>
      <dt>What is your second favorite aquatic animal?</dt>
      <dd>Dolphins.</dd>
      <dt>What is your third favorite aquatic animal?</dt>
      <dd>Cuttlefish.</dd>
    </dl> 
 </div>
</template>

<style>
dt {
  font-weight: bold;
}

dd {
  margin: 0;
}
</style>

새로운 뷰가 있지만 여전히 애플리케이션에서 참조해야 합니다.

이제 About.vue를 엽니다.

  1. nano src/views/About.vue

다음으로 를 추가하여 중첩된 경로가 자식을 표시하도록 합니다.

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <router-view/>
  </div>
</template>

이제 App.vue를 엽니다.

  1. nano src/App.vue

잠시 시간을 내어 파일이 cli-plugin-router에 의해 수정되는 방식을 숙지하십시오. 그리고 \자주 묻는 질문\를 추가합니다.

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/about/frequently-asked-questions">FAQ</router-link>
    </div>
    <router-view/>
  </div>
</template>

이 시점에서 \Home\, \About\\Frequently Asked Questions\에 대한 경로가 있는 Vue 애플리케이션이 있습니다. 다음 명령을 실행할 수 있습니다.

  1. npm run serve

그리고 웹 브라우저에서 localhost:8080을 방문하십시오. 탐색 링크를 클릭하면 예상 구성 요소가 표시됩니다. 그러나 <title><meta> 태그는 아직 변경되지 않습니다.

3단계 - 경로 메타 필드 및 내비게이션 가드 추가

vue-routertitlemeta 값에 대한 경로 메타 필드를 지원합니다. 경로를 다시 방문하고 메타 필드를 추가해 봅시다.

router/index.js 열기:

  1. nano src/router/index.js

그리고 \Home\, \About\\Frequently Asked Questions\에 대한 meta 필드를 추가합니다.

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import FrequentlyAskedQuestions from '../views/FrequentlyAskedQuestions.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: {
      title: 'Home Page - Example App',
      metaTags: [
        {
          name: 'description',
          content: 'The home page of our example app.'
        },
        {
          property: 'og:description',
          content: 'The home page of our example app.'
        }
      ]
    }
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
    meta: {
      title: 'About Page - Example App',
      metaTags: [
        {
          name: 'description',
          content: 'The about page of our example app.'
        },
        {
          property: 'og:description',
          content: 'The about page of our example app.'
        }
      ]
    },
    children: [
      {
        path: 'frequently-asked-questions',
        component: FrequentlyAskedQuestions,
        meta: {
          title: 'Nested - About Page - Example App'
        }
      }
    ]
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

그러나 이렇게 하면 경로 변경 시 페이지 제목과 메타데이터가 업데이트되지 않습니다.

이를 위해서는 맞춤형 내비게이션 가드가 필요합니다.

route/index.js 파일에서 경로 뒤 router를 내보내기 전에 전역 탐색 가드를 추가합니다.

// ... 

// This callback runs before every route change, including on page load.
router.beforeEach((to, from, next) => {
  // This goes through the matched routes from last to first, finding the closest route with a title.
  // e.g., if we have `/some/deep/nested/route` and `/some`, `/deep`, and `/nested` have titles,
  // `/nested`'s will be chosen.
  const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);

  // Find the nearest route element with meta tags.
  const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);

  const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);

  // If a route with a title was found, set the document (page) title to that value.
  if(nearestWithTitle) {
    document.title = nearestWithTitle.meta.title;
  } else if(previousNearestWithMeta) {
    document.title = previousNearestWithMeta.meta.title;
  }

  // Remove any stale meta tags from the document using the key attribute we set below.
  Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));

  // Skip rendering meta tags if there are none.
  if(!nearestWithMeta) return next();

  // Turn the meta tag definitions into actual elements in the head.
  nearestWithMeta.meta.metaTags.map(tagDef => {
    const tag = document.createElement('meta');

    Object.keys(tagDef).forEach(key => {
      tag.setAttribute(key, tagDef[key]);
    });

    // We use this to track which meta tags we create so we don't interfere with other ones.
    tag.setAttribute('data-vue-router-controlled', '');

    return tag;
  })
  // Add the meta tags to the document head.
  .forEach(tag => document.head.appendChild(tag));

  next();
});

// ...

이 시점에서 경로, 메타 필드 및 내비게이션 가드가 포함된 Vue 애플리케이션이 있습니다. 다음 명령을 실행할 수 있습니다.

  1. npm run serve

그리고 웹 브라우저에서 localhost:8080을 방문하십시오. 이제 경로가 변경되면 <title> 페이지가 가장 일치하는 경로의 title로 업데이트됩니다. 마찬가지로 <meta> 태그도 업데이트됩니다.

결론

이 자습서에서는 메타 필드 및 탐색 가드를 사용하여 경로 변경 시 페이지 제목 및 메타데이터를 업데이트하는 방법을 배웠습니다.

사전 렌더링을 사용하는 경우 이러한 변경 사항이 사전 렌더링된 HTML 파일에 적용되어 SEO에 매우 효과적입니다. SSR(서버측 렌더링)의 경우 조금 더 복잡할 수 있습니다.

또한 동적이고 자주 업데이트되는 타이틀은 이 방법으로 문제가 되지 않는다는 점도 주목할 가치가 있습니다. 이러한 사용 사례의 경우 수동으로 document.title을 업데이트해야 할 것입니다.

Vue.js에 대해 자세히 알아보려면 연습 및 프로그래밍 프로젝트에 대한 Vue.js 주제 페이지를 확인하세요.