Backbone.jsの基本的な使い方のまとめ

Backbone.jsとは

昔よく使用されていたJavaScriptライブラリ。現在はReactやVue.jsが主流なので使用されることはほとんどない。

Backbone.jsをVue.jsに書き換える機会があったので復習を兼ねて記事にしてみた。

Backbone.jsを使うには

Vue.jsを使うにはVue.jsのJSファイルを読み込むだけで良いが、Backbone.jsを使うにはBackbone.jsのJSファイルだけでなくjQueryとUnderscore.jsを読み込む必要がある。
※ jQueryとUnderscore.jsなしでも動作させることはできるが、Backbone.jsの仕様上、この2つを読み込んで使用するのが一般的

Backbone.jsはHTMLファイル内にJSファイルをCDNで読み込む記述を以下のように書けば簡単に使用できる。

<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://underscorejs.org/underscore.js"></script>
<script src="https://backbonejs.org/backbone.js"></script>

Backbone.jsを使用して読み込んだJSONを表示するサンプル

Backbone.jsの機能

Backbone.jsには主に以下の5つの機能がある。

これらの機能を使用しないのであれば使用しても意味がない。

種類 機能
Model 1つのデータを管理
View DOMと視覚表現の操作
Collection 複数のデータを管理
Router ハッシュによるURL振り分け
History 履歴管理

Backbone.jsでHello world

Backbone.jsでHello worldを実装したい場合はModelでメソッド実行時に表示する場合は以下のようにする。

// <div class="msg"></div>

const Model = Backbone.Model.extend({
    hello() {
      $('.msg').text('Hello')
    }
})
const model = new Model()
model.hello()

クリックイベントを利用する場合はViewを使う。

// <div id="app">
//   <button>click</button>
//   <div class="msg"></div>
// </div>

const View = Backbone.View.extend({
  el: '#app',
  events: {
    'click button': function() {
      this.$el.find('.msg').text('Hello')
    }
  }
})
new View()

Modelのデータと便利な関数

Modelは1つのデータを管理する。

Modelを作るにはBackbone.Model.extendを使う。defaultsで初期値を入れられる。

const Book = Backbone.Model.extend({
  defaults: {
    name: 'No Title',
    author: 'No One'
  }
})

Modelのインスタンスの作成はnewを使って以下のようになる。

const Jump = new Book({
  name: '銀魂',
  author: 'ゴリラ'
})

インスタンスのnameやauthorの値はgetで取得できる。

console.log(`${Jump.get('name')}の作者は${Jump.get('author')}`)
// => 銀魂の作者はゴリラ

getだけでなくsetもある。

Jump.set('birth', 1979)
console.log(`${Jump.get('birth')}年生まれ`)
// => 1979年生まれ

setしたものはunsetで削除できる。

Jump.unset('birth')
console.log(`${Jump.get('birth')}年生まれ`)
// => undefined年生まれ

現在Jumpでgetで取得できるものはattributesかtoJSON()で確認できる。この2つは同じなのでどちらを使っても良い。

console.log(Jump.attributes)
// {name: "銀魂", author: "ゴリラ"}

console.log(Jump.toJSON())
// {name: "銀魂", author: "ゴリラ"}

console.log(_.isEqual(Jump.attributes, Jump.toJSON()))
// => true

存在の有無はhasで確認。

console.log(Jump.has('author'))
// => true

console.log(Jump.has('servant'))
// => false

複製はclone()、オブジェクトのクリアはclear()を使う。

const Jump2 = Jump.clone()
console.log(Jump2.toJSON())
// {name: "銀魂", author: "ゴリラ"}

Jump2.clear()
console.log(Jump2.toJSON())
// => {}

Modelの関数は37種類

Modelの関数は全部で37種類ある。(new Backbone.Model).__proto__ですべて確認できるのですべて覚えておくと良いだろう。

console.log(B(new Backbone.Model).__proto__)
/*
on: ƒ (name, callback, context)
once: ƒ (name, callback, context)
off: ƒ (name, callback, context)
trigger: ƒ (name)
stopListening: ƒ (obj, name, callback)
listenTo: ƒ (obj, name, callback)
listenToOnce: ƒ (obj, name, callback)
bind: ƒ (name, callback, context)
unbind: ƒ (name, callback, context)
changed: null
validationError: null
idAttribute: "id"
initialize: ƒ ()
toJSON: ƒ (options)
sync: ƒ ()
get: ƒ (attr)
escape: ƒ (attr)
has: ƒ (attr)
set: ƒ (key, val, options)
unset: ƒ (attr, options)
clear: ƒ (options)
hasChanged: ƒ (attr)
changedAttributes: ƒ (diff)
previous: ƒ (attr)
previousAttributes: ƒ ()
fetch: ƒ (options)
save: ƒ (key, val, options)
destroy: ƒ (options)
url: ƒ ()
parse: ƒ (resp, options)
clone: ƒ ()
isNew: ƒ ()
isValid: ƒ (options)
_validate: ƒ (attrs, options)
keys: ƒ ()
values: ƒ ()
pairs: ƒ ()
invert: ƒ ()
pick: ƒ ()
omit: ƒ ()
*/

changeで変化した値を取得

setなどで値が変更されたことを検知するにはchangeを使用する。

const Book = Backbone.Model.extend({
  initialize() {
    this.on('change', () => {
      console.log('Model change!')
      if (this.hasChanged('author')) {
        console.log('author change!')
      }
    }),
    this.on('change:author', v => {
      console.log(`${v.previous('author')} => ${v.get('author')}`)
      // ゴリラ => ラッパ

      // 以下でも前後の値を取得できるが長いので非推奨
      console.log(v.previousAttributes().author)
      console.log(v.attributes.author)
    })
  }
})
const Jump = new Book({
  name: '銀魂',
  author: 'ゴリラ'
})
Jump.set('author', 'ラッパ')

Collectionを使用する

Collectionとは複数のデータ(Model)を管理するもので配列のようなものと考えるとわかりやすい。

const Book = Backbone.Model.extend({})
const Jump = new Book({
  name: '銀魂',
  author: 'ゴリラ'
})
const Jump2 = new Book({
  name: '鬼滅の刃',
  author: 'ワニ'
})

const Collection = Backbone.Collection.extend({
  model: Book
})
const Jumps = new Collection([Jump, Jump2])
console.log(Jumps.toJSON())
// (2) [{…}, {…}]
// 0: {name: "銀魂", author: "ゴリラ"}
// 1: {name: "鬼滅の刃", author: "ワニ"}

CollectionのインスタンスはforEachまたはeachが使えるが、値の取得はModel同様getである点に注意。

Jumps.each(v => {
  console.log(`${v.name}: ${v.author}`)
})
// undefined: undefined
// undefined: undefined

Jumps.each(v => {
  console.log(`${v.get('name')}: ${v.get('author')}`)
})
// 銀魂: ゴリラ
// 鬼滅の刃: ワニ

Collectionの関数は100種類以上

Modelの関数は全部で100種類以上ある。(new Backbone.Collection).__proto__ですべて確認できるのですべて覚えておくと良いだろう。

100種類以上と言ってもwhere, sort, pluckなどのUnderscore.jsのメソッドが多く含まれるのでUnderscore.js(Lodash)の基本的な使い方を知っていれば新たに覚えることは少ない。

console.log(Jumps.pluck('author'))
// => ["ゴリラ", "ワニ"]

Viewの使い方

Viewを使用してHTML内にDOM要素を挿入するにはModelとCollectionの場合は以下の通り。

const View = Backbone.View.extend({
  // <p id="item"></p>
  el: '#item',
  model: Jump,
  initialize() {
    this.render()
  },
  render() {
    const model = this.model
    const html = `${model.get('name')}: ${model.get('author')}`
    this.$el.html(html)
    // 銀魂: ゴリラ
  },
})
new View()
const View = Backbone.View.extend({
  // <p id="item"></p>
  el: '#item',
  collection: Jumps,
  initialize() {
    this.render()
  },
  render() {
    const c = this.collection
    const html = []
    c.each(v => html.push(`${v.get('name')}: ${v.get('author')}`))
    this.$el.html(html.join('<br>'))
    // 銀魂: ゴリラ<br>
    // 鬼滅の刃: ワニ
  },
})

Routerの使い方

RouterはハッシュによるURL振り分けを行う。

const Router = Backbone.Router.extend({
  routes: {
    ''          : 'top',
    'mypage'    : 'mypage',
    'page(/:id)': 'page'
  },
  top() {
    console.log('top')
    $('#title').text('top')
  },
  mypage() {
    console.log('mypage')
    $('#title').text('mypage')
  },
  page(id) {
    console.log('page/id: ' + id)
    $('#title').text(id + 'ページ目')
  },
})
new Router()
Backbone.history.start()

Backbone.js Routerサンプル