NoCodeで
CMSと連携する
デザインツールを
作っている話
2020-01-15
@miyaoka

本日のアジェンダ
1. STUDIOでやっていること
2. 動的なサイトを作れるようにする
3. CMSと連携させる
STUDIOで
やっていること
1
STUDIO
コードを書かずにWebサイト制作ができるプラットフォームサービス

こういう
要はホームページビルダーみたいな…
我々は
どういうものを
目指しているのか?
STUDIOの
WHY - HOW - WHAT
「なぜやるか?」
「どうやるか?」
「なにをやるか?」
WHY(ビジョン)
’Everyone is creative.’
「誰もが本来クリエイティブである」
→でもそうなってない
→なにかしらできない理由がある
HOW(ミッション)
’Unleash your creativity.’
「クリエイティビティを解放する」
→障壁になっていることを取り除く
WHAT
-
デザイナーとエンジニアのワークフローを一つにする
-
NoCodeでも動的な機能が実現できるようにする
-
CMS連携することでコンテンツドリブンの制作ができるようにする
「そのために何をするのか?」
我々フロントがやっていること



state定義
render関数
結果


コードによるワークフロー




state定義
render関数
結果




「がんばってコードを書く」

見た目・挙動を確認してコード調整

GUIエディタによるワークフロー


「いい感じに見た目をいじる」



state定義
render関数
結果


エディタにフィードバック


(STUDIOであらかじめ定義)
「がんばってコードを書く」
「いい感じに見た目をいじる」

✨Creativity✨
GUIエディタでできること

-
DOMの内容を編集(テキスト内容や画像パス、スタイルなど)
-
ドラッグしてリサイズ、ダブルクリックしてサイズautoに設定
-
要素の追加・削除・並び替え
-
などなど…
GUIで編集したデータをレンダリング
const textDom = {
content: {
type: 'text',
data: 'This is a Text.'
},
style: {
color: '#3239e6',
padding: '12px'
}
}
const imgDom = {
content: {
type: 'img',
src: 'https://example.com/img.jpg'
},
style: {
width: '126px',
height: 'auto',
margin: '12px 0px 0px 0px'
}
}

TextRenderer
ImgRenderer
Domの種類に応じたRender関数を用意して表示

const iconDom = {
content: {
type: 'icon',
data: 'face'
},
style: {
fontSize: '64px',
color: 'rgb(106, 176, 29)'
}
}
IconRenderer



ライブプレビュー
編集中の内容をリアルタイムに複数環境で確認したり共同編集したり
技術的な話:プレビューと公開
編集プレビュー中:
Firebaseでリアルタイム同期
公開サイト:
静的化したsnapshotデータを使って表示

そんなわけで
だいたい
いい感じにできる
ようになりました
静的サイトに限れば…
現状の課題点
- 動的なデータという概念が無いため、言ってみれば全てがレイアウト情報
-
つまりコンテンツとレイアウトが分離されていない
- コンテンツを更新していくようなワークフローに向かない(作りきりのLPの用途が多い)
-
動的ページという概念もない
- 一覧ページからの詳細ページをアイテム数分作らないといけない
- レイアウトを変えようとしてもたくさんページを編集する必要がある

動的なサイトを
作れるようにする
2
動的とは

人類は
まだ
発明してない
成功してるGUI環境は、単目的か、プログラミングの補助ツールであって、制御構造をテキストプログラミング以外で表現するのは、現状あまりコスパがよくない。
それはそう
NoCodeを目指しながら
コードの表現力も
利用するようにしました
STUDIO Code
たとえば日付表示

- テキスト要素にdateの変数をバインドしている
- しかし日付文字列はデザインに応じてフォーマットする必要がある
日付フォーマットUI
- 日付表示のパターンを選べるようにしたもの
- しかし、これ以外に曜日も表示したいなど要望はいくらでもありうる
- それらを完璧にGUIとして作るのはつらい

内部的には関数定義を保持

- 先程のフォーマットUIはDate.toLocaleString()の引数の組み合わせをいくつか選択肢化して、関数そのもの(の文字列)を保存するようにしたもの
- 詳細にフォーマットしたければこのコード自体を編集すればどうとでもなる
ワークフローのイメージ


定義をいじる
コードを書く
ビルドする
実行する

再計算する

実行されてる


ふつう
GUI化
再ビルドしなくても
処理を変えられるようにする
つまり動的に
stateを持った
componentを作りたい
export default Vue.component('DomGenerator', {
functional: true,
props: {
studioDom: {
type: Object as PropType<StudioDom>,
required: true
},
vm: {
type: Object as PropType<VM>,
required: false,
default: null
}
},
render(h, { props }): VNode {
const defs = props.studioDom.defs
const childProps =
defs || !props.vm
? {
...props,
vm: createVm(defs || [])
}
: props
return h(getRenderer(props.studioDom), {
props: childProps
})
}
})
export const createVm = (defs: StudioDef[]): VM => {
const data = defs...
const computed = defs...
const methods = defs...
// 略
return new Vue({
data: () => data,
computed,
methods
})
}
各DOMのpropにVMを追加しちゃう
const dom = {
type: 'component',
defs: [
{
type: 'state',
key: 'count',
value: 5
},
{
type: 'getter',
key: 'square',
value: 'return count * count'
},
]
}

GUIで定義をいじる
新しい定義からDOMを再生成
必要であればVMを作る
(stateやmethodが定義されているDOMでscopeを作る)
こういうコードと同等のことをする
export const getVNodeData = (
studioDom: StudioDom,
vm: VM,
): VNodeData => {
const data = studioDom.data
return {
on: getEventHandlers(vm, data?.on),
style: getConditionalStyle(data?.style)
}
}

button要素のイベントにメソッドをアサインする(GUI)
DOM生成部分でvNodeDataのonにvmのメソッドを紐付ける
const dom = {
tag: 'button',
data: {
on: {
click: 'increment'
}
},
render(h, { props }): VNode {
const { studioDom, vm } = props
const text = studioDom.content?.value
return h(
studioDom.tag || 'div',
{
...getVNodeData(studioDom, vm, scopeRootId),
domProps: {
innerHTML: text ? compileTemplate(text, vm) : ''
}
}
)
}
<template>
<div>
<div>count: {{count}}</div>
<button @click="increment">increment</button>
</div>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue'
export default Vue.extend({
data(){
return {
count: 5
}
},
methods:{
increment(){
this.count++;
},
こういう
よく
わかんない
ですよね
デモ

右パネルでの変更:
要素定義自体(初期値)を変える
ボタンをクリック:
methodを呼んでvmのstateをミューテーション
元々のSTUDIOはエディタで初期値を書き換えることしかできなかった
つまりなにができるようになるのか
たとえばアコーディオンメニュー
- アコーディオンcomponentがisOpenedというstateを持つ
- stateをミューテーションするtoggleメソッドを持つ
- ヘッダのclickイベントにtoggleメソッドを紐付ける
- on/off時のstyleを変数を使って変更

こういった制御を独自にやるのは大変なので、実質コードを書くのと同じアプローチにして、GUIはその入力装置にする
あとは
UIへの落とし込みをがんばる

何も考えなくても使えるのが
正しいんですよ!
CPO: keima
変数バインディング

Design with Real Data
- モックではなく実データを注入してデザインする
- JSONを要素にドラッグしてbindする
- デザイナーとエンジニアに分かれていたワークフローを統一させることができる

CMSと連携する
3
非エンジニアユーザーがAPI接続設定するのは難しいので、まずはNoConfigでいけるようにSTUDIO用のCMSを作りました
CMS管理画面

一覧画面
コンテント
モデル

コンテント編集画面

モデル編集画面
記事を作って
そこから
デザインエディタ

CMSコンテントをドラッグで配置

記事一覧ページ
詳細ページ

データバインディング

というわけで
CMSでコンテンツを編集し、
デザインエディタには
stateとして注入する
stateに応じた表示制御するのが
STUDIO Code機能
そんな感じで作ってます
いつできるのか?
4月くらいには…

NoCodeでCMSと連携するデザインツールを作っている話
By Masaya Kazama
NoCodeでCMSと連携するデザインツールを作っている話
- 4,377