1. 程式人生 > >【如何實現backbone元件化開發】 第二篇:優化方案的實現

【如何實現backbone元件化開發】 第二篇:優化方案的實現

系列文章
【如何實現backbone元件化開發】 第一篇:無元件所帶來的問題

在上一篇文章中,我們已經分析了Backbone在元件化開發上的不足,以及如何使用打包手段彌補這些不錯。接下來我們來逐步通過一個例子來講解優化的過程

1. 場景假設

假設我們需要使用Backbone編寫這樣一個輸入框元件,它有一個input和一個label組成。當用戶在input中輸入文字,並且觸發blur事件時,輸入的文字會在label中顯示。如下圖所示。
這裡寫圖片描述

我們還需要一個tab元件,tab元件上有多個tab切換按鈕,點選不同的按鈕能開啟對應的tab頁。每個tab頁中可以巢狀任意元件,例如巢狀上面的輸入框元件。如下圖所示。
這裡寫圖片描述

線上Demo
原始碼

2. 專案結構

這裡寫圖片描述

專案中包含myInput和tab兩個元件。myInput元件中包含5個檔案,我們挨個來看。

2.1 tpl.htm

tpl.htm是元件的模板檔案,例如underscore模板檔案。

2.2 model.js

model.js是Backbone中的Model例項。

2.3 view.js

view.js是Backbone中的view例項,view.js中會引用tpl.htm。

2.4 index.less

元件樣式檔案

2.5 index.js

元件的入口,元件入口會引入index.less、view.js、model.js。

3. 個檔案解析

3.1 tpl.htm

這裡的模板包含一個input元素和用於展示資料的value

<h3><%=title%></h3>
<div>
    <strong><%=label%></strong></strong><input class="my-input" type="text" name="firstname" placeholder=<%=placeholder%> />
</div>
<div>
    <strong
>
<%=word%></strong><%=value%> </div>

3.2 model.js

model檔案沒有什麼特殊。

var model = Backbone.Model.extend({
    defaults: function () {
        return {
            title: "title",
            label: "label",
            placeholder: "placeholder",
            word: "word",
            value: ""
        };
    },
});

3.3 view.js

這裡的view.js需要通過underscore-template-loader載入模板檔案。通過這個loader實現模板在程式碼打包階段的預編譯。loader的配置詳見第四章。

// 通過underscore-template-loader載入模板檔案
var tpl = require("./tpl.htm")

var view = Backbone.View.extend({
    className: 'input-module',
    template: tpl,
    events: {
        // 監聽input元素的chang事件,並修改model中的value值。
        'change .my-input': 'valuechange'
    },
    valuechange: function(event) {
        var newValue = event.currentTarget.value;
        this.model.set('value', newValue);
    },
    render: function() {
        this.$el.html(this.template(this.model.attributes));
        if (this.model.get('value') != '') {
            this.$el.find('input').attr('value', this.model.get('value'));
        }
        return this;
    }
});

3.4 index.less

index.less通過less-loader載入到js中,這也就解決了樣式檔案和元件分離的缺點。

.input-module {
    font-family: 'Microsoft YaHei';
}

strong {
    color: gray;
}

3.5 index.js

index.js通過引入model、view、模板檔案、樣式檔案,把一個元件所有需要的資源整合了起來。並對外提供元件初始化和配置的介面。
所以外部只需要引入元件的入口檔案,並進行配置,就可以載入元件的所有資源。這樣就做到了元件資源的集中管理。

var model = require("./model"),
    view = require("./view");

require("./index.less");

function myInput() {

}

// 對外初始化介面
myInput.prototype.init = function(title, label, placeholder, word) {
    this._model = new model({
        title: title,
        label: label,
        placeholder: placeholder,
        word: word,
        value: ""
    });

    this._view = new view({
        model: this._model
    });

    this._view.render();
}

// 設定元件標題介面
myInput.prototype.setTitle = function(title) {
    this._model.set('title', title);
}
// 獲取Dom
myInput.prototype.getView = function() {
    return this._view;
}

module.exports = myInput;

4 優化過程的解析

4.1 underscore-template-loader

在view.js中我們通過underscore-template-loader實現了在程式碼打包階段的模板編譯,這項優化可以節省瀏覽器的編譯模板所需要的時間,從而加快模組的載入。

underscore-template-loader原始碼

loader的配置

module: {
    loaders: [
      {
        test: /\.htm$/,
        loader: "underscore-template-loader",
        query: {
          // 這個配置可以在最終渲染的模板中加入註釋,此處使用模板的檔案路徑作為註釋
          prependFilenameComment: __dirname,
        }
      }
    ]
  }

更詳盡的配置可以在github中找到

4.2 less-loader

為了把模組的樣式和模組js打包到同一個元件模組中,我們可以使用less-loaderless-loader可以在程式碼打包階段把less檔案轉義為html內聯樣式,並通過js函式將內聯樣式插入到頁面中。less-loader的配置並不難,可以自行檢視文件。這裡只貼出基本的配置。

module: {
    loaders: [
      {
        test: /\.less$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          { loader: 'less-loader', options: { strictMath: true, noIeCompat: true } }
          ]
      }
    ]
  }

4.3 整合view與model

當我們引入一個元件時,我們通常希望元件提供一個入口檔案,元件的使用者只要引入入口檔案就可以呼叫元件的所有資源。然而一個Backbone元件通常由view和model構成,這使得元件的引用變成頭疼的事。

所以我們可以提供一個入口檔案,這個入口檔案會引入元件所需要的view、model、樣式檔案等。入口檔案對外提供init介面對view和model進行初始化,提供setXXX介面實現對model的修改,提供getView介面返回元件渲染結果。這樣我們就可以通過如果檔案操作Backbone元件。

Demo請看上一節中的index.js

4.4 元件的巢狀

在Backbone中實現元件巢狀的基本思路是先渲染父元件的Dom,然後將子元件的Dom插入到父元件的節點中。因為每個元件都提供getView介面用於返回元件的Dom,所以父元件可以呼叫這個介面嵌入任意元件。

我們可以通過父元件的init介面,把子元件的例項物件傳入父元件。父元件在構建完自身的Dom後掛在子元件。例如:

tab.prototype.add = function(title, content /* 子元件物件 */, isSelect) {
    var m = new model({
        title: title,
        content: content,
        isSelect: isSelect
    });

    this._collection.add(m);
}

元件巢狀中的另一個關鍵點是父子元件的通訊。先來說父元件如何向子元件傳遞訊息,方案父元件呼叫子元件的介面,例如子元件上可以提供元件銷燬介面,重新整理介面等。

而子元件向父元件傳送訊息則可以通過事件的機制,也就是說子元件的入口需要繼承Backbone的Event事件系統,父元件通過監聽子元件發出的訊息實現通訊。