前端框架擼起來——元件和路由

known發表於2021-11-24

框架只有一個html檔案,html中只有一個id是app的div,如何點選一個按鈕或者選單來顯示對應的頁面呢?最初大家都是通過拼接html字串,然後再繫結,這樣寫很不優雅,當系統功能模組龐大時,這樣下來難以維護。如何實現模組化以及寫出優雅的程式碼,接下來就是元件和路由的事情。

元件(Component)

元件是龐大系統的一個個小的零件,元件可以進行巢狀。系統有多個頁面構成,頁面有多個部件組成,頁面和部件都可以稱之為元件,他們都有共同的屬性和方法。本框架我們約定元件有render、mounted、destroy三個方法。

1)元件的定義

function TestPage() {
    //這裡寫元件的私有變數、共有屬性和方法、私有方法
    var component1 = new Component1();//私有變數component1
    var timer;//計時器
}

2)呈現方法(render)

//這裡是呈現TestPage元件的方法
//dom是根節點app,也可以是其他頁面中的節點
this.render = function(dom) {
    $('<div>').html('Component1').appendTo(dom);//呈現一個div
    component1.render(dom);//呈現巢狀元件component1
}

3)掛載方法(mounted)

//這裡是載入元件的後端介面資料
this.mounted = function() {
    component1.loadData();
    timer = setInterval(function() {...}, 1000);
}

4)銷燬方法(destroy)

//這裡是銷燬元件的資源,例如一個setInterval的物件
this.destroy = function() {
    clearInterval(timer);
}

5)元件完整程式碼

function TestPage() {
    var component1 = new Component1();
    var timer;
    
    this.render = function(dom) {
        $('<div>').html('Component1').appendTo(dom);
        component1.render(dom);
    }
    
    this.mounted = function() {
        component1.loadData();
        timer = setInterval(function() {...}, 1000);
    }
    
    this.destroy = function() {
        clearInterval(timer);
    }
}

路由(Router)

路由是不同元件之前的轉換器,起到元件自由切換的作用。路由可以進行巢狀,即頁面是最頂級的元件,渲染在根節點下面,頁面內部區塊也可以呈現不同的元件。本框架路由只提供兩個方法,即導航和回退,其實路由可以擴充套件更多的方法,如根據name或者模板來路由,這裡暫不實現。本框架暫不支援瀏覽器地址路由,有興趣的同學可以自己實現。

1)路由的定義

//elem是路由的節點物件
//option是路由的配置選項
function Router(elem, option) {
    //這裡寫路由的私有變數、共有屬性和方法、私有方法
    var _current = {};//儲存當前路由物件
}

2)導航方法(route)

//路由到指定的元件
//item為路由物件,必須包含component屬性
this.route = function(item) {
    //呈現前的驗證,例如登入驗證
    if (!_option.before(item))
        return;
    //銷燬當前元件
    _destroyComponent();
    //設定當前元件
    _setCurrent(item);
    //執行元件
    var component = item.component;
    if (component) {
        _renderComponent(component);
        _mountComponent(item, component);
    }
}

3)回退方法(back)

//回退到當前路由的上一個路由
this.back = function() {
    _this.route(_current.previous);
}

4)路由完整程式碼

function Router(elem, option) {
    //fields
    var _option = option || {},
        _elem = elem,
        _current = {},
        _this = this;

    //methods
    this.route = function (item) {
        if (!_option.before(item))
            return;

        _destroyComponent();
        _setCurrent(item);

        var component = item.component;
        if (component) {
            _renderComponent(component);
            _mountComponent(item, component);
        }
    }

    this.back = function () {
        _this.route(_current.previous);
    }
    
    //private
    function _destroyComponent() {
        var currComp = _current.component;
        currComp && currComp.destroy && currComp.destroy();
    }
    
    function _setCurrent(item) {
        if (!item.previous) {
            item.previous = _current; //儲存上一個路由
        }
        _current = item;
    }
    
    function _renderComponent(component) {
        if (typeof component === 'string') {
            _elem.html(component);//字串元件
        } else {
            _elem.html('');//清空節點
            component.render(_elem);//呈現元件
        }
    }
    
    function _mountComponent(item, component) {
        setTimeout(function () {
            _option.after && _option.after(item);//呈現後回撥公共邏輯
            component.mounted && component.mounted();//呼叫後臺資料
        }, 10);//延時執行,等dom呈現完成後
    }
}

下一章我們實現框架根元件App。

相關文章