[编程]-Layui源码分析<1>

Layui源码分析

分析源码的目的

  • 学习设计框架的理念

  • 学习组件实现技巧

  • 巩固JavaScript和jQuery知识

  • 无限接近这些牛人!!!

什么是Layui

layui(谐音:类 UI) 是一套开源的 Web UI 解决方案,采用自身经典的模块化规范,并遵循原生 HTML/CSS/JS 的开发方式,极易上手,拿来即用。其风格简约轻盈,而组件优雅丰盈,从源代码到使用方法的每一处细节都经过精心雕琢,非常适合网页界面的快速开发。无需涉足前端各种工具,只需面对浏览器本身,让一切你所需要的元素与交互,从这里信手拈来。

从网站上看下载量,已经到达2160887次,即下载了216万多次,对于在程序开发的领域,有这么多次下载量总能说明问题。

设计layui的初衷

用大牛贤心说起来,当时要做layui目的很明确,就是:“由职业前端倾情打造,面向全层次的前后端开发者,易上手开箱即用的 Web UI 组件库”。贤心出发点很简单:满足服务端程序员的需求。在layui网站上,layui的特点:

  • 返璞归真。

身处在前端社区的繁荣之下,我们都在有意或无意地追逐。而 layui 偏偏回望当初,奔赴在返璞归真的漫漫征途,自信并勇敢着,追寻于原生态的书写指令,试图以最简单的方式诠释高效

  • 双面体验。

拥有双面的不仅是人生,还有 layui。一面极简,一面丰盈。极简是视觉所见的外在,是开发所念的简易。丰盈是倾情雕琢的内在,是信手拈来的承诺。一切本应如此,简而全,双重体验。

layui组件世界

layui的元素由以下组成:布局(栅格、后台布局)、颜色、字体图标、动画、按钮、表单、导航条、面包屑、选项卡、进度条、面板、静态表格、徽章、时间线、辅助元素等。

layui 提供了丰富的内置模块,他们皆可通过模块化的方式按需加载,其中包括:layer、layDate、layPage、laytpl、table、form、upload、element、tree、layeditor、rate、carousel、flow、util、code等。

Layui的模块化之路

Layui中怎么做模块,这是贤心按照js的传统创造的,是不与任何标准兼容的方案,所以很难把layui整合到其它项目中,但如果按layui为主标准,需要把其它的模块进行包装,改造成符合layui标准的模块,然后在layui中使用。模块思路采用了几年前的以浏览器为宿主的类 AMD 模块管理方式,却又并非受限于 CommonJS 的那些条条框框,它拥有自己的模式,更加轻量和简单。

layui 定义了一套更轻量的模块规范。并且这种方式在经过了大量的实践后,成为 layui 最核心的模块加载引擎。layui 通过 define方法定义模块,利用use 方法加载使用模块。所以先从define和use这两个方法说起。

LayUI的零依赖的部署方式和全模块的加载方式,就大概能知道LayUI的设计哲学了.

<link rel="stylesheet" href="/layui/css/layui.css">
<script src="/layui/layui.all.js"></script>
<script>
(function(){
	var layer = layui.layer;
	layer.msg('Hello World');
})();
</script>

JavaScript的模块化实现

通行的Javascript模块规范共有两种:CommonJSAMD

CommonJS

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程,这标志"Javascript模块化编程"正式诞生,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载:

var math = require('math');
math.add(2,3); // 5

由于一个重大的局限,使得CommonJS规范不适用于浏览器环境:

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

AMD

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

require(['math'], function (math) {

    math.add(2, 3);

  });

主要有两个Javascript库实现了AMD规范:require.jscurl.js

layui.js外层沙箱以及命名空间$

;!(function(win) {
     //用一个函数域包起来,就是所谓的沙箱
     //在这里边var定义的变量,属于这个函数域内的局部变量,避免污染全局
     //把当前沙箱需要的外部变量通过函数参数引入进来
     //只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数
    "use strict";
    
    
    win.layui = new Layui();
})( window );
  • 沙箱中第一句"use strict";

是表示使用javascript的严格模式,对于低级的浏览器,这里相当一字符串,所以兼容性是没问题的,详细的话,在阮一峰的文章Javascript 严格模式详解有介绍。

  • 为什么要创建这样一个“自调用匿名函数”呢?

通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名

空间。这点非常有用也是一个JS框架必须支持的功能。

  • 为什么要传入window呢

通过传入window变量,使得window由全局变量变为局部变量,当在layui代码块中访问window时,不

需要将作用域链回退到顶层作用域,这样可以更快的访问window;

layui.js中的框架代码

;!(function(win) {
   "use strict";
    Layui = function(){
    	this.v = '2.X'; //版本号
  	}
    //定义组件
    Layui.prototype.define = function(deps, factory){
        callback = function(){
			factory(function(app, exports){
                 setApp(app, exports);
        		//此处给layui继续挂载变量,增加callback数组属性,记录每个模块的回调函数
        		config.callback[app] = function(){
           			factory(setApp);
              	}
     	 	});
        };        
        //把相关组件加载
        that.use(deps, callback, null, 'define');
    };
    Layui.prototype.use = function(apps, callback, exports, from){
        var item = apps[0]
        function onScriptLoad(e, url){
            head.removeChild(node);            
            config.status[item] ? onCallback() : setTimeout(poll, 4);
        };
        function onCallback(){
            //此处问号表达式,多于1个app时,调用自己,进入递归调用
           	apps.length > 1 ? that.use(apps.slice(1), callback, exports, from):
            if(typeof layui.jquery === 'function' && from !== 'define'){
                return layui.jquery(function(){
            				callback.apply(layui, exports);
          				});
            };
            callback.apply(layui, exports);
        };
        //apps中的模块已经加载时
        if( apps.length === 0 || (layui['layui.all'] && modules[item]) ){
      		return onCallback(), that;
    	}
        //首次加载模块
         if(!config.modules[item]){
             //创建script的标签,追加到head中,同时绑定消息
             var node = doc.createElement('script');
             head.appendChild(node);
             if(node.attachEvent){
        			node.attachEvent('onreadystatechange', function(e){
          				onScriptLoad(e, url);
       				});
              } else {
                     node.addEventListener('load', function(e){
                  		onScriptLoad(e, url);
                	}, false);
              }
         } else {
              (function poll() {
        			onCallback()         
      			}());
         }
        return that;
    };
    
    
    
    win.layui = new Layui();
})( window );

以上就是layui中use方法和define方法的框架代码,可参照源码详细思考。

<欢迎大家讨论>

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> <link rel="stylesheet" href="/layui/css/layui.css"> <style> .content { padding: 15px; } i { margin-right: 10px; } .site-doc-icon li { width: 150px; height: 150px; line-height: 150px; margin: 0 auto 10px; text-align: center; background-color: #0099FF; cursor: pointer; color: #fff; border-radius: 50%; } .site-doc-icon li { display: inline-block; margin: 10px; } .site-doc-icon li { display: inline-block; } </style> </head> <body class="layui-layout-body"> <nav class="layui-layout layui-layout-admin"> <#include "head.ftl"> <div class="layui-side layui-bg-black"> <div class="layui-side-scroll"> <ul class="layui-nav layui-nav-tree"> <li class="layui-nav-item layui-this"> <a href="/admin/index.html"> <i class="layui-icon layui-icon-home"></i>首页 </a> </li> <li class="layui-nav-item"> <a href="/admin/user/0.html"><i class="layui-icon"></i>用户管理</a> </li> <li class="layui-nav-item"> <a href="/admin/category.html"><i class="layui-icon"></i>分类管理</a> </li> <li class="layui-nav-item"> <a href="/admin/debook.html"><i class="layui-icon"></i>书评管理</a> </li> <li class="layui-nav-item"> <a href="/admin/statistics.html"><i class="layui-icon"></i>统计占比</a> </li> </ul> </div> </div> <div class="layui-body"> <!-- 内容主体区域 --> <div class="content"> <div class="layui-col-md11" style="padding: 20px 100px;"> <blockquote class="layui-elem-quote" style="color:#000000; margin-bottom: 50px"> <i class="layui-icon"></i>欢迎来到共享图书系统 </blockquote> <div> <img style="width: 100%; height: 400px" src="/images/aa.jpg"> </div> </div> </div> </div> <#include "bottom.ftl"> </nav> </body> <script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="/js/axquery.js"></script> <script type="text/javascript" src="/js/template-web.js"></script> <script type="text/javascript" src="/layui/layui.all.js"></script> <script> layui.use([], function () { var $ = layui.jquery; //演示动画开始 $('.site-doc-icon .layui-anim').on('click', function () { var othis = $(this), anim = othis.data('anim'); //停止循环 if (othis.hasClass('layui-anim-loop')) { return othis.removeClass(anim); } othis.removeClass(anim); setTimeout(function () { othis.addClass(anim); }); //恢复渐隐 if (anim === 'layui-anim-fadeout') { setTimeout(function () { othis.removeClass(anim); }, 1300); } }); //演示动画结束 }) </script> </html>如何和后端交互的
05-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双飞雁A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值