# 一文了解 Layui

Layui 是一套开源免费的 Web UI 组件库,采用自身轻量级模块化规范,遵循原生态的 HTML/CSS/JavaScript 开发模式,极易上手,拿来即用。其风格简约轻盈,而内在雅致丰盈,甚至包括文档在内的每一处细节都经过精心雕琢,非常适合网页界面的快速构建。Layui 区别于一众主流的前端框架,却并非逆道而行,而是信奉返璞归真之道。确切地说,它更多是面向于追求简单的务实主义者,即无需涉足各类构建工具,只需面向浏览器本身,便可将页面所需呈现的元素与交互信手拈来。

# 基本结构

Layui 框架最基本的结构是一个立即执行函数

;!function(win){
  "use strict";
  // ...
})(window)

# 基本声明

!(function (win) {
  "use strict";
  const Layui = function(){
    this.v = '2.7.5'; // layui 版本号
  }
  GLOBAL = win.LAYUI_GLOBAL || {} //识别预先可能定义的指定全局对象
  //exports layui
  win.layui = new Layui();
})(window);

# 基本配置

!(function (win) {
  "use strict";
  const Layui = function () {
    this.v = "2.7.5"; // layui 版本号
  };
  GLOBAL = win.LAYUI_GLOBAL || {}; //识别预先可能定义的指定全局对象
  const doc = win.document;
  const config = {
    modules: {}, // 模块物理路径
    status: {}, // 模块加载状态
    timeout: 10, // 符合规范的模块请求最长等待秒数
    event: {} // 模块自定义事件
  };
  //记录基础数据
  Layui.prototype.cache = config;
  //exports layui
  win.layui = new Layui();
})(window);

# 模块系统

# 定义模块

//定义模块
Layui.prototype.define = function (deps, factory) {
  var that = this,
    type = typeof deps === "function",
    callback = function () {
      var setApp = function (app, exports) {
        layui[app] = exports;
        config.status[app] = true;
      };
      typeof factory === "function" &&
        factory(function (app, exports) {
          setApp(app, exports);
          config.callback[app] = function () {
            factory(setApp);
          };
        });
      return this;
    };

  type && ((factory = deps), (deps = []));
  that.use(deps, callback, null, "define");
  return that;
};

# 使用模块

  //使用特定模块
  Layui.prototype.use = function (apps, callback, exports, from) {
    var that = this,
      dir = (config.dir = config.dir ? config.dir : getPath),
      head = doc.getElementsByTagName("head")[0];

    apps = (function () {
      if (typeof apps === "string") {
        return [apps];
      }
      //当第一个参数为 function 时,则自动加载所有内置模块,且执行的回调即为该 function 参数;
      else if (typeof apps === "function") {
        callback = apps;
        return ["all"];
      }
      return apps;
    })();

    //如果页面已经存在 jQuery 1.7+ 库且所定义的模块依赖 jQuery,则不加载内部 jquery 模块
    if (win.jQuery && jQuery.fn.on) {
      that.each(apps, function (index, item) {
        if (item === "jquery") {
          apps.splice(index, 1);
        }
      });
      layui.jquery = layui.$ = jQuery;
    }

    var item = apps[0],
      timeout = 0;
    exports = exports || [];

    //静态资源host
    config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//) || ["//" + location.host + "/"])[0];

    //加载完毕
    function onScriptLoad(e, url) {
      var readyRegExp = navigator.platform === "PLaySTATION 3" ? /^complete$/ : /^(complete|loaded)$/;
      if (e.type === "load" || readyRegExp.test((e.currentTarget || e.srcElement).readyState)) {
        config.modules[item] = url;
        head.removeChild(node);
        (function poll() {
          if (++timeout > (config.timeout * 1000) / 4) {
            return error(item + " is not a valid module", "error");
          }
          config.status[item] ? onCallback() : setTimeout(poll, 4);
        })();
      }
    }

    //回调
    function onCallback() {
      exports.push(layui[item]);
      apps.length > 1
        ? that.use(apps.slice(1), callback, exports, from)
        : typeof callback === "function" &&
          (function () {
            //保证文档加载完毕再执行回调
            if (layui.jquery && typeof layui.jquery === "function" && from !== "define") {
              return layui.jquery(function () {
                callback.apply(layui, exports);
              });
            }
            callback.apply(layui, exports);
          })();
    }

    //如果引入了聚合板,内置的模块则不必重复加载
    if (apps.length === 0 || (layui["layui.all"] && modules[item])) {
      return onCallback(), that;
    }

    //获取加载的模块 URL
    //如果是内置模块,则按照 dir 参数拼接模块路径
    //如果是扩展模块,则判断模块路径值是否为 {/} 开头,
    //如果路径值是 {/} 开头,则模块路径即为后面紧跟的字符。
    //否则,则按照 base 参数拼接模块路径

    var url =
      (modules[item] ? dir + "modules/" : /^\{\/\}/.test(that.modules[item]) ? "" : config.base || "") +
      (that.modules[item] || item) +
      ".js";
    url = url.replace(/^\{\/\}/, "");

    //如果扩展模块(即:非内置模块)对象已经存在,则不必再加载
    if (!config.modules[item] && layui[item]) {
      config.modules[item] = url; //并记录起该扩展模块的 url
    }

    //首次加载模块
    if (!config.modules[item]) {
      var node = doc.createElement("script");

      node.async = true;
      node.charset = "utf-8";
      node.src =
        url +
        (function () {
          var version = config.version === true ? config.v || new Date().getTime() : config.version || "";
          return version ? "?v=" + version : "";
        })();

      head.appendChild(node);

      if (
        node.attachEvent &&
        !(node.attachEvent.toString && node.attachEvent.toString().indexOf("[native code") < 0) &&
        !isOpera
      ) {
        node.attachEvent("onreadystatechange", function (e) {
          onScriptLoad(e, url);
        });
      } else {
        node.addEventListener(
          "load",
          function (e) {
            onScriptLoad(e, url);
          },
          false
        );
      }

      config.modules[item] = url;
    } else {
      //缓存
      (function poll() {
        if (++timeout > (config.timeout * 1000) / 4) {
          return error(item + " is not a valid module", "error");
        }
        typeof config.modules[item] === "string" && config.status[item] ? onCallback() : setTimeout(poll, 4);
      })();
    }

    return that;
  };

# 拓展模块

//拓展模块
Layui.prototype.extend = function(options){
  var that = this;

  //验证模块是否被占用
  options = options || {};
  for(var o in options){
    if(that[o] || that.modules[o]){
      error(o+ ' Module already exists', 'error');
    } else {
      that.modules[o] = options[o];
    }
  }

  return that;
};

# 事件系统

# 插件系统

# 工具方法

# getStyle

//获取节点的 style 属性值
Layui.prototype.getStyle = function(node, name){
  var style = node.currentStyle ? node.currentStyle : win.getComputedStyle(node, null);
  return style[style.getPropertyValue ? 'getPropertyValue' : 'getAttribute'](name);
};

# img

//图片预加载
Layui.prototype.img = function(url, callback, error) {   
  var img = new Image();
  img.src = url; 
  if(img.complete){
    return callback(img);
  }
  img.onload = function(){
    img.onload = null;
    typeof callback === 'function' && callback(img);
  };
  img.onerror = function(e){
    img.onerror = null;
    typeof error === 'function' && error(e);
  };  
};

# each

//遍历
Layui.prototype.each = function(obj, fn){
  var key
  ,that = this
  ,callFn = function(key, obj){ //回调
    return fn.call(obj[key], key, obj[key])
  };
  
  if(typeof fn !== 'function') return that;
  obj = obj || [];
  
  //优先处理数组结构
  if(that.isArray(obj)){
    for(key = 0; key < obj.length; key++){
      if(callFn(key, obj)) break;
    }
  } else {
    for(key in obj){
      if(callFn(key, obj)) break;
    }
  }
  
  return that;
}

# 学习资料

Github Layui (opens new window)