請負の仕事でHPを作ることになった。
お客様自身でサイトの修正を行えるようにしたいという要望があり、Headless CMS等を調査したが日本語化しているのがなさそうだったので自前で作成してみることに。
タイトルにある通りバックエンドはFirebase Cloud FunctionsとFirebase Realtime Databaseを使いました。
フロントエンドはNuxtでQuillというWYSIWYGエディタを組み込むことにしました。
その中でつまずいた点を今後のためにメモします。
RSSフィードを読み込みはSSR必須だった
CORSのためにSPAでRSSからデータ抜き出して表示することが出来なかった。
対応としてはiframeを使ってその部分だけSSRで出力。
ちなみにRSSのパースにはxml2jsを使用しました。
参考までにパース部分のscriptを書くと
<script>
function asyncParseString(xml) {
return new Promise(function(resolve, reject) {
const options = {
trim: true,
explicitArray: false
}
const parseString = require('xml2js').parseString
parseString(xml, options, function(err, obj) {
if (err) {
return reject(err)
}
return resolve(obj)
})
})
}
export default {
async asyncData({ store, route, app }) {
// RSS feedを取得するために料金プランを変更する必要があった
const rss = await app.$axios.get(
'http://blog.goo.ne.jp/hoge/rss2.xml'
)
const item = await asyncParseString(rss.data)
const blog = {
title: item.rss.channel.title,
item: item.rss.channel.item
.map(i => {
return { title: i.title, link: i.link }
})
}
return { blog }
}
}
</script>
また、SSR時に外部サイト(上の例ではblog.goo.ne.jp)にアクセスするにはfirebaseの料金プランをSparkからBlaze or Flameに変える必要がありました。ちなみに固定費がかかるのが嫌だったのでBlazeにしました。
Quillの設定が難しかった
Quillで日本語フォントを使えるようにするには
まず、plugins/quill.jsを作ります。
import Vue from 'vue'
import Quill from 'quill'
import VueQuill from 'vue-quill-editor'
// 入力フォント設定
const Font = Quill.import('formats/font')
Font.whitelist = ['yu-gothic', 'yu-mincho', 'meiryo']
Quill.register(Font, true)
const options = {
modules: {
toolbar: {
container: [
[{ font: ['Sans Serif', 'yu-gothic', 'yu-mincho', 'meiryo'] }]
]
}
},
placeholder: '記事を入力してください'
}
export default () => {
Vue.use(VueQuill, options)
}
やってることはQuillからFontを取り出してwhitelistに游ゴシック、游明朝、メイリオを設定してます。
その後toolbarにfontを設定してfont選択ドロップダウンを表示させます。
その後cssまたはvueファイルのscopedではないstyleに以下を記述
<style>
ql-font-yu-gothic {
font-family: '游ゴシック Medium', 'Yu Gothic Medium', '游ゴシック体', YuGothic,
sans-serif;
}
.ql-snow .ql-picker.ql-font [data-value='yu-gothic'].ql-picker-item::before,
.ql-snow .ql-picker.ql-font [data-value='yu-gothic'].ql-picker-label::before {
content: '游ゴシック';
font-family: '游ゴシック Medium', 'Yu Gothic Medium', '游ゴシック体', YuGothic,
sans-serif;
}
.ql-font-yu-mincho {
font-family: 'Yu Mincho', YuMincho, HG明朝B, 'MS Mincho', serif;
}
.ql-snow .ql-picker.ql-font [data-value='yu-mincho'].ql-picker-item::before,
.ql-snow .ql-picker.ql-font [data-value='yu-mincho'].ql-picker-label::before {
content: '游明朝';
font-family: 'Yu Mincho', YuMincho, HG明朝B, 'MS Mincho', serif;
}
.ql-font-meiryo {
font-family: 'Meiryo';
}
.ql-snow .ql-picker.ql-font [data-value='meiryo'].ql-picker-item::before,
.ql-snow .ql-picker.ql-font [data-value='meiryo'].ql-picker-label::before {
content: 'メイリオ';
font-family: 'Meiryo';
}
</style>
styleを設定することでQuillの編集結果を参照するときにフォントの設定がされます。
また、エディター表示時にツールバーに游ゴシック、游明朝、メイリオがそれぞれ表示されます。
Quillではテーブルがデフォルトで扱えない
お客様の要望でテーブルも出せるようにしたかったので方法を調べました。quill-table-moduleを使えばよいことが分かったので取り込むことに。
例によってplugins/quill.jsを編集します。
import Vue from 'vue'
import Quill from 'quill'
import VueQuill from 'vue-quill-editor'
import quillTable from 'quill-table-module'
Quill.register(quillTable.TableCell)
Quill.register(quillTable.TableRow)
Quill.register(quillTable.Table)
Quill.register(quillTable.Contain)
Quill.register('modules/table', quillTable.TableModule)
const maxRows = 10
const maxCols = 5
const tableOptions = []
for (let r = 1; r <= maxRows; r++) {
for (let c = 1; c <= maxCols; c++) {
tableOptions.push('newtable_' + r + '_' + c)
}
}
const options = {
modules: {
table: true,
toolbar: {
container: [
[
{ table: tableOptions },
{ table: 'append-row' },
{ table: 'append-col' }
]
]
}
}
}
export default () => {
Vue.use(VueQuill, options)
}
やってることはquillTableからTable,Row,TableCell等の要素を取り出してQuillに登録。
Quillでは画像がBase64で保存されるのでデフォルトではDBに登録されてしまう
画像がBase64で保存されるとRealtime Databaseの料金が上がってしまうのでFirebase Storageに登録できるようにします。
やっぱりplugins/quill.jsを編集します。
import Vue from 'vue'
import Quill from 'quill'
import VueQuill from 'vue-quill-editor'
const InlineBlot = Quill.import('blots/embed')
class ImageBlot extends InlineBlot {
static create(data) {
const node = super.create(data)
node.setAttribute('src', data.src)
node.setAttribute('width', data.width)
node.setAttribute('height', data.height)
return node
}
static value(domNode) {
const { src, width, height } = domNode
return { src, width, height }
}
}
ImageBlot.blotName = 'imageBlot'
ImageBlot.className = 'image-blot'
ImageBlot.tagName = 'img'
Quill.register({ 'formats/imageBlot': ImageBlot })
const options = {
modules: {
toolbar: {
container: [
['image']
],
handlers: {
image: function() {
document.getElementById('getFile').click()
}
}
}
}
}
export default () => {
Vue.use(VueQuill, options)
}
ソースを見てお気づきでしょうが**document.getElementById('getFile')**と、idがgetFileの要素(input[type=file])に依存した形になっています。ここは後で何とかしたいところです。。。
次に登録・編集画面も変更します。
<template>
<section>
<div style="margin: 1em;">
<quill-editor ref="quillEditor" v-model="formData.content" />
<input
id="getFile"
accept="image/png, image/jpeg, image/gif"
type="file"
@change="uploadFunction($event)"
/>
</div>
</section>
</template>
<script>
import Vue from 'vue'
import firebase from '~/plugins/firebase'
import moment from '~/plugins/moment'
export default Vue.extend({
data() {
return {
formData: {
title: '',
content: '',
publishAt: new Date(),
public: null,
userId: ''
}
}
},
methods: {
async uploadFunction(e) {
// file upload
const now = moment().format('YYYYMMDD-hhmmss')
const storageRef = firebase.storage().ref()
const file = e.target.files[0]
const fileRef = storageRef.child(`images/${now}/${file.name}`)
await fileRef.put(file)
fileRef.updateMetadata({ cacheControl: 'public,max-age=14400000' })
const downloadRef = firebase.storage().ref(`images/${now}/${file.name}`)
const url = await downloadRef.getDownloadURL()
// insert image into quill
const quill = this.$refs.quillEditor.quill
const index = quill.getSelection(true).index
const img = new Image()
const embed = {}
img.onload = function() {
embed.width = this.width
embed.height = this.height
embed.src = url
quill.insertEmbed(index, 'imageBlot', embed)
}
img.src = url
}
}
})
</script>
やっていることはtoolbarの画像ボタンクリック時に非表示にしたgetFileをクリックする。その後ファイル選択時にuploadFunctionメソッドを呼び、Firebase Storageにファイルを登録し、download用URLを取得、Quillに画像タグを登録します。
なんだか書いててNuxt上でのQuillの使い方になってきた気がするのでいったん終了。ほかにも躓いたところあるので別の記事を書くかも。