亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

基于zepto的組件系統(tǒng)搭建

EasonTyler / 1126人閱讀

摘要:前言隨著前端開發(fā)復(fù)雜度的日益提升,組件化開發(fā)應(yīng)運(yùn)而生,對(duì)于一個(gè)相對(duì)簡(jiǎn)單的活動(dòng)頁面開發(fā)如何進(jìn)行組件化是本文的主要內(nèi)容。

前言

隨著前端開發(fā)復(fù)雜度的日益提升,組件化開發(fā)應(yīng)運(yùn)而生,對(duì)于一個(gè)相對(duì)簡(jiǎn)單的活動(dòng)頁面開發(fā)如何進(jìn)行組件化是本文的主要內(nèi)容。

概述

下面我們看一下在zepto的基礎(chǔ)上如何構(gòu)建組件系統(tǒng),首先,我們要解決第一個(gè)問題,如何引用一個(gè)組件,我們可以通過設(shè)置一個(gè)屬性data-component來引用自定義的組件:

那么如何向組件中傳入數(shù)據(jù)呢,我們同樣也可以通過設(shè)置屬性來向組件傳遞數(shù)據(jù),比如傳入一個(gè)id值:

那么組件之間如何進(jìn)行通信呢,我們可以采用觀察者模式來實(shí)現(xiàn)。

寫一個(gè)組件

我們先來看看我們?nèi)绾蝸韺懸粋€(gè)組件

//a.js
defineComponent("a", function (component) {
    var el = "

input-editor

"; var id = component.getProp("id");//獲取參數(shù)id $(this).append(el);//視圖渲染 component.setStyle(".a{color:green}");//定義樣式 $(this).find("p").on("click", function () { component.emit("test", id, "2");//觸發(fā)test }); });

我們先看看這個(gè)組件是怎么定義的,首先調(diào)用defineComponent(先不管這個(gè)函數(shù)在哪定義的)定義一個(gè)組件a,后面那個(gè)函數(shù)是組件a的組要邏輯,這個(gè)函數(shù)傳入了一個(gè)component(先不管這個(gè)是哪來的,先看它能干啥),在前面我們說過如何向組件傳遞數(shù)據(jù),在組件里我們通過component.getProp("id")來獲取,樣式我們通過component.setStyle(".a{color:green}")來定義,組件之前的通信我們通過component.emit()來觸發(fā)(在別的組件里通過component.on()來注冊(cè)),看上去我們基本解決了前面關(guān)于組件的一些問題,那么這個(gè)是怎么實(shí)現(xiàn)的呢?

組件實(shí)現(xiàn)原理

我們先來看看上面那個(gè)組件我們應(yīng)該如何來實(shí)現(xiàn),從上面定義一個(gè)組件來看有兩個(gè)地方是比較關(guān)鍵的,一個(gè)是defineComponent是怎么實(shí)現(xiàn)的,一個(gè)就是component是什么。

我們先來看看defineComponent是怎么實(shí)現(xiàn)的,很顯然defineComponent必須定義為全局的(要不然a.js就無法使用了,而且必須在加載a.js之前定義defineComponent),我們來看看defineComponent的代碼

//component.js
  var component = new Component();
  window.defineComponent = function (name, fn) {
        component.components[name] = {
            init: function () {
                //設(shè)置currentComponent為當(dāng)前組件
                currentComponent = this;
                fn.call(this, component);
                component.init(this);
            }
        };
    }

這里我們可以看到定義了一個(gè)類Component,component是它的一個(gè)實(shí)例,defineComponent就是在component.components注冊(cè)一個(gè)組件,這里的關(guān)鍵是Component類,我們來看看Component是怎么定義的

//component.js
  /**
     * Component類
     * @constructor
     */
    function Component() {
        this.components = {};//所有的組件
        this.events = {};//注冊(cè)的事件
        this.loadStyle = {};
        this.init("body");//初始化
    }

    var currentComponent = null;//當(dāng)前的組件
    /**
     * 類的初始化函數(shù)
     * @param container 初始化的范圍,默認(rèn)情況下是body
     */
    Component.prototype.init = function (container) {
        var self = this;
        container = container || "body";
        $(container).find("[data-component]").each(function () {
            self.initComponent(this);
        });

    };
    /**
     *  初始化單個(gè)組件
     * @param context 當(dāng)前組件
     */
    Component.prototype.initComponent = function (context) {

        var self = this;
        var componentName = $(context).attr("data-component");
        if (this.components[componentName]) {
            this.components[componentName].init.call(context);
        } else {
            _loadScript("http://" + document.domain + ":5000/dist/components/" + componentName + ".js", function () {
                self.components[componentName].init.call(context);
                //設(shè)置樣式,同一個(gè)組件只設(shè)置一次
                if (!self.loadStyle[componentName] && self.components[componentName].style) {
                    $("head").append("");
                    self.loadStyle[componentName] = true;
                }
            });
        }

    };
    /**
     * 設(shè)置樣式
     * @param style 樣式
     */
    Component.prototype.setStyle = function (style) {
        //獲取當(dāng)前組件的名稱,currentComponent就是當(dāng)前組件
        var currentComponentName = $(currentComponent).attr("data-component");
        var component = this.components[currentComponentName];
        if (component && !component.style) {
            component.style = style;
        }
    };
    /**
     * 獲取組件參數(shù)
     * @param prop 參數(shù)名
     * @returns {*|jQuery}
     */
    Component.prototype.getProp = function (prop) {
        var currentComponentNme = $(currentComponent).attr("data-component");
        if ($(currentComponent).attr("data-" + prop)) {
            return $(currentComponent).attr("data-" + prop)
        } else {
            //屬性不存在時(shí)報(bào)錯(cuò)
            throw Error("the attribute data-" + prop + " of " + currentComponentNme + " is undefined or empty")
        }

    };
    /**
     * 注冊(cè)事件
     * @param name 事件名
     * @param fn 事件函數(shù)
     */
    Component.prototype.on = function (name, fn) {
        this.events[name] = this.events[name] ? this.events[name] : [];
        this.events[name].push(fn);
    };
    /**
     * 觸發(fā)事件
     */
    Component.prototype.emit = function () {
        var args = [].slice.apply(arguments);
        var eventName = args[0];
        var params = args.slice(1);
        if(this.events[eventName]){
            this.events[eventName].map(function (fn) {
                fn.apply(null, params);
            });
        }else{
            //事件不存在時(shí)報(bào)錯(cuò)
            throw Error("the event " + eventName + " is undefined")
        }

    };
    /**
     * 動(dòng)態(tài)加載組價(jià)
     * @param url 組件路徑
     * @param callback 回調(diào)函數(shù)
     * @private
     */
    function _loadScript(url, callback) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        if (typeof(callback) != "undefined") {
            if (script.readyState) {
                script.onreadystatechange = function () {
                    if (script.readyState == "loaded" || script.readyState == "complete") {
                        script.onreadystatechange = null;
                        callback();
                        $(script).remove();
                    }
                };
            } else {
                script.onload = function () {
                    callback();
                    $(script).remove();
                };
            }
        }
        script.src = url;
        $("body").append(script);
    }

我們先了解一下大概的流程


大致的流程就是上面這張流程圖了,我們所有的組件都是注冊(cè)在component.components里,事件都是在component.events里面。

我們回頭看一下組件components里頭的init方法

//component.js
  var component = new Component();
  window.defineComponent = function (name, fn) {
        component.components[name] = {
            init: function () {
                //設(shè)置currentComponent為當(dāng)前組件
                currentComponent = this;
                fn.call(this, component);
                component.init(this);
            }
        };
    }

首先,將this賦給currentComponent,這個(gè)在哪里會(huì)用到呢?在個(gè)getProp和setStyle這兩個(gè)方法里都用到了

//component.js
        /**
     * 設(shè)置樣式
     * @param style 樣式
     */
    Component.prototype.setStyle = function (style) {
        console.log(currentComponent);
        //獲取當(dāng)前組件的名稱,currentComponent就是當(dāng)前組件
        var currentComponentName = $(currentComponent).attr("data-component");
        var component = this.components[currentComponentName];
        if (component && !component.style) {
            component.style = style;
        }
    };
    /**
     * 獲取組件參數(shù)
     * @param prop 參數(shù)名
     * @returns {*|jQuery}
     */
    Component.prototype.getProp = function (prop) {
        return $(currentComponent).attr("data-" + prop)
    };

到這里大家可能會(huì)對(duì)this比較疑惑,這個(gè)this到底是什么,我們可以先看在那個(gè)地方調(diào)用了組件的init方法

//component.js
        /**
     *  初始化單個(gè)組件
     * @param componentName 組件名
     * @param context 當(dāng)前組件
     */
    Component.prototype.initComponent = function (componentName, context) {

        var self = this;
        if (this.components[componentName]) {
            this.components[componentName].init.call(context);
        } else {
            _loadScript("http://" + document.domain + ":5000/components/" + componentName + ".js", function () {
                self.components[componentName].init.call(context);
                //設(shè)置樣式,同一個(gè)組件只設(shè)置一次
                if (!self.loadStyle[componentName] && self.components[componentName].style) {
                    $("head").append("");
                    self.loadStyle[componentName] = true;
                }
            });
        }

    };

就是在單個(gè)組件初始化的調(diào)用了init方法,這里有call改變了init的this,使得this=context,那么這個(gè)context又是啥呢

//component.js
       /**
     * 類的初始化函數(shù)
     * @param container 初始化的范圍,默認(rèn)情況下是body
     */
    Component.prototype.init = function (container) {
        var self = this;
        container = container || "body";
        $(container).find("[data-component]").each(function () {
            var componentName = $(this).attr("data-component");
            console.log(this);
            self.initComponent(componentName, this);
        });

    };

context其實(shí)就是遍歷的每一個(gè)組件,到這里我們回過頭來看看我們是怎么定義一個(gè)組件

//b.js
defineComponent("b", function (component) {
    var el = "

text-editor

"; $(this).append(el); component.on("test", function (a, b) { console.log(a + b); }); var style = ".text-editor{color:red}"; component.setStyle(style) });

我們知道this就是組件本身也就是下面這個(gè)

這個(gè)組件通過component.on注冊(cè)了一個(gè)test事件,在前面我們知道test事件是在a組件觸發(fā)的,到這里我們就把整個(gè)組件系統(tǒng)框架開發(fā)完成了,下面就是一個(gè)個(gè)去增加組件就好了,整個(gè)的代碼如下:

//component.js
(function () {
    /**
     * Component類
     * @constructor
     */
    function Component() {
        this.components = {};//所有的組件
        this.events = {};//注冊(cè)的事件
        this.loadStyle = {};
        this.init("body");//初始化
    }

    var currentComponent = null;//當(dāng)前的組件
    /**
     * 類的初始化函數(shù)
     * @param container 初始化的范圍,默認(rèn)情況下是body
     */
    Component.prototype.init = function (container) {
        var self = this;
        container = container || "body";
        $(container).find("[data-component]").each(function () {
            self.initComponent(this);
        });

    };
    /**
     *  初始化單個(gè)組件
     * @param context 當(dāng)前組件
     */
    Component.prototype.initComponent = function (context) {

        var self = this;
        var componentName = $(context).attr("data-component");
        if (this.components[componentName]) {
            this.components[componentName].init.call(context);
        } else {
            _loadScript("http://" + document.domain + ":5000/dist/components/" + componentName + ".js", function () {
                self.components[componentName].init.call(context);
                //設(shè)置樣式,同一個(gè)組件只設(shè)置一次
                if (!self.loadStyle[componentName] && self.components[componentName].style) {
                    $("head").append("");
                    self.loadStyle[componentName] = true;
                }
            });
        }

    };
    /**
     * 設(shè)置樣式
     * @param style 樣式
     */
    Component.prototype.setStyle = function (style) {
        //獲取當(dāng)前組件的名稱,currentComponent就是當(dāng)前組件
        var currentComponentName = $(currentComponent).attr("data-component");
        var component = this.components[currentComponentName];
        if (component && !component.style) {
            component.style = style;
        }
    };
    /**
     * 獲取組件參數(shù)
     * @param prop 參數(shù)名
     * @returns {*|jQuery}
     */
    Component.prototype.getProp = function (prop) {
        var currentComponentNme = $(currentComponent).attr("data-component");
        if ($(currentComponent).attr("data-" + prop)) {
            return $(currentComponent).attr("data-" + prop)
        } else {
            //屬性不存在時(shí)報(bào)錯(cuò)
            throw Error("the attribute data-" + prop + " of " + currentComponentNme + " is undefined or empty")
        }

    };
    /**
     * 注冊(cè)事件
     * @param name 事件名
     * @param fn 事件函數(shù)
     */
    Component.prototype.on = function (name, fn) {
        this.events[name] = this.events[name] ? this.events[name] : [];
        this.events[name].push(fn);
    };
    /**
     * 觸發(fā)事件
     */
    Component.prototype.emit = function () {
        var args = [].slice.apply(arguments);
        var eventName = args[0];
        var params = args.slice(1);
        if(this.events[eventName]){
            this.events[eventName].map(function (fn) {
                fn.apply(null, params);
            });
        }else{
            //事件不存在時(shí)報(bào)錯(cuò)
            throw Error("the event " + eventName + " is undefined")
        }

    };
    /**
     * 動(dòng)態(tài)加載組價(jià)
     * @param url 組件路徑
     * @param callback 回調(diào)函數(shù)
     * @private
     */
    function _loadScript(url, callback) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        if (typeof(callback) != "undefined") {
            if (script.readyState) {
                script.onreadystatechange = function () {
                    if (script.readyState == "loaded" || script.readyState == "complete") {
                        script.onreadystatechange = null;
                        callback();
                        $(script).remove();
                    }
                };
            } else {
                script.onload = function () {
                    callback();
                    $(script).remove();
                };
            }
        }
        script.src = url;
        $("body").append(script);
    }

    var component = new Component();

    window.defineComponent = function (name, fn) {
        component.components[name] = {
            init: function () {
                //設(shè)置currentComponent為當(dāng)前組件
                currentComponent = this;
                fn.call(this, component);
                component.init(this);
            }
        };
    }

})();
工程化

上面搭建的組件系統(tǒng)有個(gè)不好的地方,就是我們定義的htmlstyle都是字符串,對(duì)于一些大的組件來說,htmlstyle都是非常長的,這樣的話調(diào)試就會(huì)很困難,因此,我們需要對(duì)組件系統(tǒng)進(jìn)行工程化,最終目標(biāo)是html,jscss可以分開開發(fā),現(xiàn)有的工程化工具比較多,你可以用gulp或者node自己寫一個(gè)工具,這里介紹一下如何使用node來實(shí)現(xiàn)組件系統(tǒng)的工程化。

我們先來看看目錄結(jié)構(gòu)


我們首先要獲取到編譯前組件的路徑

//get-path.js
var glob = require("glob");
exports.getEntries = function (globPath) {
    var entries = {};
    /**
     * 讀取src目錄,并進(jìn)行路徑裁剪
     */
    glob.sync(globPath).forEach(function (entry) {
        var tmp = entry.split("/");
        tmp.shift();
        tmp.pop();
        var pathname = tmp.join("/"); // 獲取前兩個(gè)元素

        entries[pathname] = entry;

    });

    return entries;
};

然后根據(jù)路徑分別讀取index.js,index.html,index.css

//read-file.js
var readline = require("readline");
var fs = require("fs");

exports.readFile = function (file, fn) {
    console.log(file);
    var fRead = fs.createReadStream(file);
    var objReadline = readline.createInterface({
        input: fRead
    });
    function trim(str) {
        return str.replace(/(^s*)|(s*$)|(//(.*))|(/*(.*)*/)/g, "");
    }
    var fileStr = "";
    objReadline.on("line", function (line) {
        fileStr += trim(line);
    });
    objReadline.on("close", function () {
        fn(fileStr)
    });
};


//get-component.js
var fs = require("fs");
var os = require("os");

var getPaths = require("./get-path.js");
var routesPath = getPaths.getEntries("./src/components/**/index.js");

var readFile = require("./read-file");

for (var i in routesPath) {
    (function (i) {
        var outFile = i.replace("src", "dist");
        readFile.readFile(i + "/index.js", function (fileStr) {
            var js = fileStr;
            readFile.readFile(i + "/index.html", function (fileStr) {
                js = js.replace("", fileStr);
                readFile.readFile(i + "/index.css", function (fileStr) {
                    js = js.replace("