みなさんこんにちわ。HeadlessというスペルをHeadressとたまに間違えるいとうです。最近HeadlessCMSはやってますね。ContentfulやCraftCMS、WP REST API、国産のmicroCMSなど様々なHeadlessCMSが生まれています。とても便利で案件でも増えてきました。
その背景にはやはり低コスト、アジャイル、自由度の増したデザインetc.....メリットしかありません。世界の3割のサイトがWordpressでできていると言われていたのがすごく前のように感じます。
いえーい、Wordpressくんみてるー?
本題、リンクカードとは
本題です。案件でNuxtでつくったブログにリンクカードを表示したいと言われたのでどうしたら実装できるのかを備忘録程度に書いておきます。
リンクカードというのはその名前の通り、リンクのカードです。
Notionでいう↑こういうやつです。
要件としてはURLの文字列から、タイトル、ディスクリプション、favicon、og:imageを取得するというものです。
今回の記事では前提として、フレームワークにNuxt、CMSはmicroCMSを使用します。
とりあえずURLを表示する。
とりあえず好きなサイトのURLを取得しましょう。
URLの文字列からfetchしてHTMLを取得し、ヘッドの情報を抜き取りましょう。簡単なスクレイピングです。
<script>
export default {
data() {
return {
link: 'https://www.awwwards.com/',
}
},
mounted() {
console.log(this.link)
const url = this.link
fetch(url)
.then((res) => res.text())
.then((text) => {
const el = new DOMParser().parseFromString(text, 'text/html')
const headEls = el.head.children
Array.from(headEls).map((v) => {
console.log(v)
const prop = v.getAttribute('property')
if (!prop) return
console.log(prop, v.getAttribute('content'))
})
})
},
}
</script>
こんな感じのコードになります。
簡単に説明すると、URLの文字列をもとにfetchしheadの情報を抜き取ってproperty とcontentをタグに関係なく表示します。
しかしこのままだとCORSエラーになります
URLをfetchする場合、セキュリティの都合上からCORSに怒られてしまうのです。
なのでCORSエラーを回避するためにURLの頭に https://cors-anywhere.herokuapp.com/
をつけてあげます。
mounted() {
console.log(this.link)
const CORS_PROXY = 'https://cors-anywhere.herokuapp.com/'
const url = CORS_PROXY + this.link
fetch(url)
.then((res) => res.text())
.then((text) => {
const el = new DOMParser().parseFromString(text, 'text/html')
const headEls = el.head.children
Array.from(headEls).map((v) => {
console.log(v)
const prop = v.getAttribute('property')
if (!prop) return
console.log(prop, v.getAttribute('content'))
})
})
},
これでエラーは出ません。やったね。
HeadlessCMSと組み合わせる。
HeadlessCMSと組み合わせます。
<template lang="pug">
.container
div(v-html="singleData")
</template>
<script>
export default {
async fetch() {
const [single] = await Promise.all([
this.$axios.get('CMSのAPI', this.headers),
])
this.singleData = single
},
data() {
return {
link: 'https://www.awwwards.com/',
singleData: '',
}
},
mounted() {
console.log(this.link)
const CORS_PROXY = 'https://cors-anywhere.herokuapp.com/'
const url = CORS_PROXY + this.link
fetch(url)
.then((res) => res.text())
.then((text) => {
const el = new DOMParser().parseFromString(text, 'text/html')
const headEls = el.head.children
Array.from(headEls).map((v) => {
console.log(v)
const prop = v.getAttribute('property')
if (!prop) return
console.log(prop, v.getAttribute('content'))
})
})
},
}
</script>
ざっくりですが、こんな感じになるかと思います。this.header
はヘッダーの情報とかそんな感じです。
div(v-html="singleData")
の中にCMSから取得したhtmlが吐き出されます。NuxtでSSRを使用している場合、レンダリングされたhtmlはmounted
などで取得することはできません。
これはライフサイクルの仕様です。なので、moutend
関数ではなく、updated
関数を使います。updated
関数は内容がDOMにレンダリングされたあとに実行されます。
<template lang="pug">
.container
div.container__body(v-html="singleData")
</template>
<script>
export default {
async fetch() {
const [single] = await Promise.all([
this.$axios.get('CMSのAPI', this.headers),
])
this.singleData = single
},
data() {
return {
link: 'https://www.awwwards.com/',
singleData: '',
}
},
updated() {
const bodyArray = document.querySelector('.container__body')
bodyArray.forEach((bodyEl) => {
const linkElArray = bodyEl.querySelectorAll('a')
linkElArray.forEach((linkEl) => {
this.getHead(linkEl.href, linkEl)
})
})
},
methods: {
getHead(linkString, linkEl) {
console.log(this.link)
const CORS_PROXY = 'https://cors-anywhere.herokuapp.com/'
const url = CORS_PROXY + linkString
fetch(url)
.then((res) => res.text())
.then((text) => {
const el = new DOMParser().parseFromString(text, 'text/html')
const headEls = el.head.children
Array.from(headEls).map((v) => {
const title = el.head.querySelector.textContent
console.log(title)
linkEl.innerHTML = `<p>${title}</p>`
})
})
},
},
}
</script>
update
関数をつかってレンダリング後にレンダリングしたエレメントオブジェクトを配列で取得します。その中からaタグだけを抜き出しgetHead
メソッドにURLと要素を渡しましょう。
あとは上に書かれたコードの通りです。最終的には要素の内容を書き換えることで pタグの中にURLから取得されたサイトタイトルを表示しています。
やっていることは簡単...だけど
https://cors-anywhere.herokuapp.com/
は魔法の文字列ですが、もちろんデメリットもあります。もともとCORSというのはサイトのセキュリティを保守するためにあるのでこれを無条件で回避しようとすると悪意あるサイトからの攻撃も許してしまう可能性があります。
ですので、絶対信頼できるURLしか貼らないようにしましょう。
みなさまも良いHeadlessCMSライフを!