ホームページ  >  記事  >  ウェブフロントエンド  >  backbone.js の簡単な開始例

backbone.js の簡単な開始例

小云云
小云云オリジナル
2018-01-15 10:28:532342ブラウズ

Backbone はシンプルで柔軟で、リッチな JS アプリケーションと企業 Web サイトの両方で使用できます。View および一方向のデータ フロー向けの React の設計と比較して、Backbone は MVC のアイデアをより具体化できるため、その入門的なサンプルを作成します。友人が参照できるようにする必要があります。皆さんのお役に立てれば幸いです。

2011 年に初めてフロントエンド MVC フレームワークを使い始めたときに記事を書きました。当時は Knockout と Backbone の両方が使用されましたが、その後のすべてのプロジェクトでは Backbone が使用されました。主な理由は、それがシンプルで柔軟性があり、リッチであるかどうかに関係ありません。 JS アプリケーションまたは企業 Web サイトはすべて役に立ちます。 React の View と一方向のデータ フローの設計と比較して、Backbone は MVC のアイデアをよりよく具体化できるため、次のようにその導入例を書きました: 構造は 4 つのセクションに分かれており、モデルを紹介します。 /View/Collection. データのリモート取得を実行し、テーブルに表示し、変更および削除します。

2. これは「サンプル」と呼ばれるコードであり、各セクションの最初のコードが完全なコードです。をコピーして貼り付けることで使用できます。コードの前のセクションに記述されているため、コードの各セクションの新しい内容は 20 行を超えません (中括弧を含む)。

4. 開発環境は Chrome で、github の API を使用しているため、Chrome はローカル パス (file:/ の形式のパス) でもデータを取得できます。 /)。

0. はじめに

ほとんどすべてのフレームワークは 2 つのことを行います。1 つは、適切な場所にコードを記述できるようにすること、もう 1 つは、汚い作業を実行できるようにすることです。 Backbone は明確な MVC コード構造を実装し、データ モデルとビュー マッピングの問題を解決します。 JS 関連のプロジェクトはすべて使用できますが、Backbone は、JS を使用して大量のページ コンテンツ (主に HTML) を生成する必要があり、ユーザーがページ要素と頻繁に対話するシナリオに最適です。

Backbone オブジェクトには、Model/Collection/View/Router/History という 5 つの重要な機能があります。 Router と History は Web アプリケーション用に最適化されています。まず、pushState 関連の知識を理解しておくことをお勧めします。エントリー段階ではモデル/コレクション/ビューしか理解できません。モデルはコア、コレクションはモデルのコレクション、ビューはフロントエンドでのモデルの変更を反映すると考えてください。

1. モデル

モデルはすべての JS アプリケーションの中核であり、多くのバックボーン チュートリアルでは View から始めることが多く、View を理解することの方が重要です。以下のコードはgithub APIから要点情報を取得してページに表示する実装です:

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>

<link href="http://cdn.bootcss.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">
</head>
<body>
 <table id="js-id-gists" class="table">
  <thead><th>description</th><th>URL</th><th>created_at</th></thead>
  <tbody></tbody>
 </table>
 <script type="text/javascript">
 var Gist = Backbone.Model.extend({
  url: 'https://api.github.com/gists/public',
  parse: function (response) {
   return (response[0]);
  }
 }),
  gist = new Gist();

 gist.on('change', function (model) {
  var tbody = document.getElementById('js-id-gists').children[1],
   tr = document.getElementById(model.get('id'));
  if (!tr) {
   tr = document.createElement('tr');
   tr.setAttribute('id', model.get('id'));
  }
  tr.innerHTML = '<td>' + model.get('description') + '</td><td>' + model.get('url') + '</td><td>' + model.get('created_at') + '</td>';
  tbody.appendChild(tr);
 });
 gist.fetch();
 </script>
</body>
</html>
LINE4~8: 使用するJSライブラリを読み込みます。 Ajax リクエストと一部の View 関数には jQuery のサポート (または ajax/View 関数の書き換え) が必要です。Backbone のコードは Underscore に基づいています (または Lo-Dash に置き換えられます)。デフォルトのスタイルがあまりにも醜いためです。

LINE16~22: モデルを作成し、インスタンス化します。 url はデータ ソース (API インターフェイス) のアドレスであり、返されたデータを処理するために parse が使用されます。実際に返されるのは配列です。ここでは最初のオブジェクトを取得します。

LINE24~33:バインド変更イベント。 View はまだ使ったことがないので、HTML を自分で処理する必要があります。この 10 行のコードは主に get (model.get) の使用法であり、他の機能は後で View を使用して実装します。

LINE34: フェッチを実行します。リモートからデータを取得すると、データの取得後に変更イベントがトリガーされます。同期メソッドをオーバーライドできます

Chrome のコンソールを開いて gist を入力すると、モデルによって取得された属性が表示されます:

モデルはデータとデータ関連のロジックを提供します。上の図で出力される属性は、コード内の fetch/parse/get/set はすべてデータを操作します。その他には、関数名からその目的がおおよそわかります。また、非常に一般的に使用される検証関数もあり、設定/保存操作中のデータ検証に使用されます。検証に失敗すると、無効なイベントがトリガーされます:

/* 替换之前代码的JS部分(LINE16~34) */
 var Gist = Backbone.Model.extend({
  url: 'https://api.github.com/gists/public',
  parse: function (response) {
   return (response[0]);
  },
  defaults: {
   website: 'dmyz'
  },
  validate: function (attrs) {
   if (attrs.website == 'dmyz') {
    return 'Website Error';
   }
  }
 }),
  gist = new Gist();

 gist.on('invalid', function (model, error) {
  alert(error);
 });
 gist.on('change', function (model) {
  var tbody = document.getElementById('js-id-gists').children[1],
   tr = document.getElementById(model.get('id'));
  if (!tr) {
   tr = document.createElement('tr');
   tr.setAttribute('id', model.get('id'));
  }
  tr.innerHTML = '<td>'+ model.get('description') +'</td><td>'+ model.get('url') +'</td><td>'+ model.get('created_at') +'</td>';
  tbody.appendChild(tr);
 });
 gist.save();
前のコードと比較すると、4 つの変更があります:

LINE7~9。 : デフォルトが追加されました。属性に Web サイトがない場合 (Web サイトの値が空ではないことに注意してください)、Web サイトの値は dmyz に設定されます。

LINE10~14: 検証機能を追加します。 Web サイトの値が dmyz の場合、無効なイベントがトリガーされます。

LINE18~20: バインド無効なイベント、アラートによってエラーが返されました。

LINE31: フェッチなし、操作を直接保存します。


フェッチがないため、データはページに表示されません。保存操作が実行されると、検証関数が呼び出され、検証が失敗すると、無効なイベントがトリガーされ、アラートがエラー メッセージを表示します。同時に、保存操作によりモデルの URL への PUT リクエストも開始されます。github API は PUT を処理しないため、404 エラーが返されます。

コンソールに gist.set(‘description’, ‘demo’) と入力すると、ページ要素もそれに応じて変更されることがわかります。 gist.set(‘description’, gist.previous(‘description’)) を実行して、以前の値を復元します。これは Model と View の間のマッピングです。次のセクションでは、Backbone の View を使用して実装します。

2. View

Backbone の View を使用して、前のコードの LINE24 ~ 33 の部分を書き換えます。

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>

<link href="http://cdn.bootcss.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">
</head>
<body>
 <table id="js-id-gists" class="table">
  <thead><th>description</th><th>URL</th><th>created_at</th><th></th></thead>
  <tbody></tbody>
 </table>
 <script type="text/javascript">
 var Gist = Backbone.Model.extend({
  url: 'https://api.github.com/gists/public',
  parse: function (response) {
   return response[0];
  }
 }),
  gist = new Gist();

 var GistRow = Backbone.View.extend({
  el: 'tbody',
  MODEL: gist,
  events: {
   'click a': 'replaceURL'
  },
  replaceURL: function () {
   this.MODEL.set('url', 'http://dmyz.org');
  },
  initialize: function () {
   this.listenTo(this.MODEL, 'change', this.render);
  },
  render: function () {
   var model = this.MODEL,
    tr = document.createElement('tr');
   tr.innerHTML = '<td>' + model.get('description') + '</td><td>' + model.get('url') + '</td><td>' + model.get('created_at') + '</td><td><a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" >®</a></td>';
   this.el.innerHTML = tr.outerHTML;
   return this;
  }
 });
 var tr = new GistRow();
 gist.fetch();
 </script>
</body>
</html>

LINE25: 所有的View都是基于DOM的,指定el会选择页面的元素,指定tagName会创建相应的DOM,如果都没有指定会是一个空的p。
LINE27~32: 绑定click事件到a标签,replaceURL函数会修改(set)url属性的值。
LINE33~35: View的初始化函数(initialize),监听change事件,当Model数据更新时触发render函数。
LINE36~42: render函数。主要是LINE41~42这两行,把生成的HTML代码写到this.el,返回this。
LINE44: 实例化GistRow,初始化函数(initialize)会被执行。

点击行末的a标签,页面显示的这条记录的URL会被修改成http://dmyz.org。

这个View名为GistRow,选择的却是tbody标签,这显然是不合理的。接下来更改JS代码,显示API返回的30条数据:

/* 替换之前代码的JS部分(LINE16~45) */
 var Gist = Backbone.Model.extend(),
  Gists = Backbone.Model.extend({
   url: 'https://api.github.com/gists/public',
   parse: function (response) {
    return response;
   }
  }),
  gists = new Gists();

 var GistRow = Backbone.View.extend({
  tagName: 'tr',
  render: function (object) {
   var model = new Gist(object);
   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td></td>'
   return this;
  }
 });

 var GistsView = Backbone.View.extend({
  el: 'tbody',
  model: gists,
  initialize: function () {
   this.listenTo(this.model, 'change', this.render);
  },
  render: function () {
   var html = '';
   _.forEach(this.model.attributes, function (object) {
    var tr = new GistRow();
    html += tr.render(object).el.outerHTML;
   });
   this.el.innerHTML = html;
   return this;
  }
 });
 var gistsView = new GistsView();
 gists.fetch();

LINE2~9: 创建了两个Model(Gist和Gists),parse现在返回完整Array而不只是第一条。
LINE11~18: 创建一个tr。render方法会传一个Object来实例化一个Gist的Model,再从这个Model里get需要的值。
LINE26~34: 遍历Model中的所有属性。现在使用的是Model而不是Collection,所以遍历出的是Object。forEach是Underscore的函数。

Backbone的View更多的是组织代码的作用,它实际干的活很少。View的model属性在本节第一段代码用的是大写,表明只是一个名字,并不是说给View传一个Model它会替你完成什么,控制逻辑还是要自己写。还有View中经常会用到的template函数,也是要自己定义的,具体结合哪种模板引擎来用就看自己的需求了。

这段代码中的Gists比较难操作其中的每一个值,它其实应该是Gist的集合,这就是Backbone的Collection做的事了。

3. Collection

Collection是Model的集合,在这个Collection中的Model如果触发了某个事件,可以在Collection中接收到并做处理。第2节的代码用Collection实现:

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>

<link href="http://cdn.bootcss.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet">
</head>
<body>
 <table id="js-id-gists" class="table">
  <thead><th>description</th><th>URL</th><th>created_at</th><th></th></thead>
  <tbody></tbody>
 </table>
 <script type="text/javascript">
 var Gist = Backbone.Model.extend(),
  Gists = Backbone.Collection.extend({
   model: Gist,
   url: 'https://api.github.com/gists/public',
   parse: function (response) {
    return response;
   }
  }),
  gists = new Gists();

 var GistRow = Backbone.View.extend({
  tagName: 'tr',
  render: function (model) {
   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td></td>'
   return this;
  }
 });

 var GistsView = Backbone.View.extend({
  el: 'tbody',
  collection: gists,
  initialize: function () {
   this.listenTo(this.collection, 'reset', this.render);
  },
  render: function () {
   var html = '';
   _.forEach(this.collection.models, function (model) {
    var tr = new GistRow();
    html += tr.render(model).el.outerHTML;
   });
   this.el.innerHTML = html;
   return this;
  }
 });
 var gistsView = new GistsView();
 gists.fetch({reset: true});
 </script>
</body>
</html>

LINE17~23: 基本跟第2节的第2段代码一样。把Model改成Collection,指定Collection的Model,这样Collectio获得返回值会自动封装成Model的Array。
LINE38: Collection和Model不同,获取到数据也不会触发事件,所以绑定一个reset事件,在之后的fetch操作中传递{reset: true}。
LINE42~45: 从Collection从遍历Model,传给GistRow这个View,生成HTML。

Collection是Backbone里功能最多的函数(虽然其中很多是Underscore的),而且只要理解了Model和View的关系,使用Collection不会有任何障碍。给Collection绑定各种事件来实现丰富的交互功能了,以下这段JS代码会加入删除/编辑的操作,可以在JSBIN上查看源代码和执行结果。只是增加了事件,没有什么新内容,所以就不做说明了,附上JSBIN的演示地址:http://jsbin.com/jevisopo/1

/* 替换之前代码的JS部分(LINE16~51) */
 var Gist = Backbone.Model.extend(),
  Gists = Backbone.Collection.extend({
   model: Gist,
   url: 'https://api.github.com/gists/public',
   parse: function (response) {
    return response;
   }
  }),
  gists = new Gists();

 var GistRow = Backbone.View.extend({
  tagName: 'tr',
  render: function (model) {
   this.el.id = model.cid;
   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td><a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="js-remove">X</a> <a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="js-edit">E</a> </td>'
   return this;
  }
 });

 var GistsView = Backbone.View.extend({
  el: 'tbody',
  collection: gists,
  events: {
   'click a.js-remove': function (e) {
    var cid = e.currentTarget.parentElement.parentElement.id;
    gists.get(cid).destroy();
    gists.remove(cid);
   },
   'click a.js-edit': 'editRow',
   'blur td[contenteditable]': 'saveRow'
  },
  editRow: function (e) {
   var tr = e.currentTarget.parentElement.parentElement,
    i = 0;

   while (i < 3) {
    tr.children[i].setAttribute('contenteditable', true);
    i++;
   }
  },
  saveRow: function (e) {
   var tr = e.currentTarget.parentElement,
    model = gists.get(tr.id);

   model.set({
    'description' : tr.children[0].innerText,
    'url': tr.children[1].innerText,
    'created_at': tr.children[2].innerText
   });
   model.save();
  },
  initialize: function () {
   var self = this;
   _.forEach(['reset', 'remove', 'range'], function (e) {
    self.listenTo(self.collection, e, self.render);
   });
  },
  render: function () {
   var html = '';
   _.forEach(this.collection.models, function (model) {
    var tr = new GistRow();
    html += tr.render(model).el.outerHTML;
   });
   this.el.innerHTML = html;
   return this;
  }
 });
 var gistsView = new GistsView();
 gists.fetch({reset: true});

Afterword

虽然是入门范例,但因为篇幅有限,有些基本语言特征和Backbone的功能不可能面面俱到,如果还看不懂肯定是我漏掉了需要解释的点,请(在Google之后)评论或是邮件告知。

Backbone不是jQuery插件,引入以后整个DOM立即实现增删改查了,也做不到KnockoutJS/AnglarJS那样,在DOM上做数据绑定就自动完成逻辑。它是将一些前端工作处理得更好更规范,如果学习前端MVC的目的是想轻松完成工作,Backbone可能不是最佳选择。如果有一个项目,100多行HTML和1000多行JS,JS主要都在操作页面DOM(如果讨厌+号连接HTML还可以搭配React/JSX来写),那就可以考虑用Backbone来重写了,它比其他庞大的MVC框架要容易掌握得多,作为入门学习也是非常不错的。

相关推荐:

使用Backbone.js创建一个增删改查表的实例代码

javascript - 关于backbone.js里的model.set和model.get

Backbone.js中的集合详解_其它

以上がbackbone.js の簡単な開始例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。