laydate源码解读

create on in source_code with 1 comment and 228 view

laydate.js是layui中的日期组件,是layui三大组件之一。

它的主要功能有:

  • 年选择器
  • 年月选择器
  • 日期选择器
  • 时间选择器
  • 日期时间选择器

它的优点有:

  • 支持范围选择(即双控件)
  • 自定义日期格式解析,合法校正机制
  • 主题简约,灵活多样
  • 零依赖

laydate官网

分析源码结构

本文分析的版本是layDate 5.0.7.

laydate.js源码结构图

图1.1 laydate.js源码结构图

laydate.js中,定义了laydate变量作为核心对象,与其平级的变量还有LAY、lay、getEndDate、Class、ready、isLayui、thisDate以及字符常量,它们的用途如下:

  • LAY 拥有选择器等多功能的构造函数,其原型链继承于Array对象,并新增多个自定义方法。
  • lay LAY构造函数的实例,并扩展了多个方法
  • getEndDate 函数,用于获取某月的最后一天
  • Class 日期组件核心接口的构造函数
  • ready 定义日期组件初始化前的方法
  • isLayui 判断是否使用layui
  • thisDate 函数,操作当前实例

laydate对象拥有以下属性:

  • render 实例化Class,并调用thisDate,
  • v 版本号
  • config 配置
  • index html中每个laydate组件的标识
  • set 设置全局配置
  • ready 主体CSS等待事件
  • path ready.getPath的引用,获取laydate.js的引入路径

加载与执行流程

判断加载方式

  //加载方式
  isLayui ? (
    laydate.ready()
    ,layui.define(function(exports){ //layui加载
      laydate.path = layui.cache.dir;
      exports(MOD_NAME, laydate);
    })
  ) : (
    (typeof define === 'function' && define.amd) ? define(function(){ //requirejs加载
      return laydate;
    }) : function(){ //普通script标签加载
      laydate.ready();
      window.laydate = laydate
    }()
  );

laydate.js支持layui加载,模块化加载以及script标签加载。

载入CSS配件

ready: function(fn){
      var cssname = 'laydate', ver = ''
      ,path = (isLayui ? 'modules/laydate/' : 'theme/') + 'default/laydate.css?v='+ laydate.v + ver;
      isLayui ? layui.addcss(path, fn, cssname) : ready.link(path, fn, cssname);
      return this;
    }

执行laydate对象的ready属性方法,然后再方法中调用layui.addcss或ready.link方法,加载css文件。

执行ladate.render

该步骤又开发人员手动调用,并传入options配置。
源码如下:

 //核心接口
  laydate.render = function(options){
    var inst = new Class(options);
    return thisDate.call(inst);
  };

这里做了两件事,首先实例化Class,然后使用实例调用thisDate。

构造函数Class如下:

Class = function(options){
    var that = this;
    that.index = ++laydate.index;
    that.config = lay.extend({}, that.config, laydate.config, options);
    laydate.ready(function(){
      that.init();
    });
  }

这里做了3件事,添加index标识options配置对象合并初始化

这里执行read方法与第2步不同的是,其传入了回调函数作为参数。所以,在ready方法中,若执行了ready.link方法,则会判断css是否加载完毕:

      //轮询css是否加载完毕
      (function poll() { 
        if(++timeout > 8 * 1000 / 100){
          return window.console && console.error('laydate.css: Invalid');
        };
        parseInt(ready.getStyle(document.getElementById(id), 'width')) === 1989 ? fn() : setTimeout(poll, 100);
      }());

判断css是否完成的原理是:
在css中对加载该css的link标签设置了width为1989px,所以如果css加载完成,那么link标签的width属性必然为1989。最后,只要判断该值等于1989,则说明css加载完成。

若css加载完成,则执行回调函数,开始执行初始化。

初始化

初始化方法为Class.prototype.init,该方法对数据进行了预先处理,包括

  • 设置日期范围分隔符
if(options.range === true) options.range = '-';
  • 根据不同type,初始化默认format
    if(options.format === format.date){
      options.format = format[options.type];
    }
  • 将日期格式转化成数组
 that.format = options.format.match(new RegExp(dateType + '|.', 'g')) || [];
  • 生成正则表达式
  • 不是input|textarea元素,则默认采用click事件
  • 设置唯一KEY
    if(!options.elem.attr('lay-key')){
      options.elem.attr('lay-key', that.index);
      options.eventElem.attr('lay-key', that.index);
    }
  • 记录重要日期
  • 获取限制内的日期
  • 默认赋值
    if(options.value){
      if(options.value.constructor === Date){
        that.setValue(that.parse(0, that.systemDate(options.value))); 
      } else {
        that.setValue(options.value); 
      }
    }

完成以上工作后,则开始进行控件主体渲染。

渲染

渲染方法为Class.prototype.render,该方法负责日期组件的界面渲染。
日期组件分为4大区域:

  • 主面板
  • 主区域
  • 底部区域
  • 单双日历区域
    • 头部区域
    • 左右切换
    • 日历内容区域
    • 生成年月选择
    • 表格

除了渲染这些区域外,还要进行的工作有:

  • 自定义主题
  • 组件定位
  • 初始校验
  • 监听日期切换事件

相关实践

在一个需求中,需要让用户可进行日期的选择,综合考虑下,选择了该插件。
在开发过程中,使用该插件非常快捷的完成了我们的需求开发,这非常的好,但是,当需要上线时,问题就出现了,什么问题呢?

问题

由于项目上线时,需要通过gulp对多个js/css文件进行打包并压缩合并到一个js/css文件中,这样可避免一个页面发起多个http请求。而laydate.js是需要引入laydate.css以及相关的字体文件的,并且这些文件会固定存放在当前default目录下,所以,打包后,由于laydate.js源代码文件目录的改动,导致其找不到css和字体文件。

解决方法

  1. 调整项目文件路径
  2. 修改源码

刚开始的想法是调整项目的文件路径,但是这样会导致整个项目的静态文件目录大改动,可能会出现意想不到的问题。为了保险起见,最后采用了第二种方法。

思路如下:
开发环境下,有以下源文件:

  • page.js
  • page.css
  • laydate.js
  • default/laydate.css
  • default/font/*

上线后,打包文件为:

  • common.js
  • common.css
  • font/*

所以,对于源码的更改,要做的工作是:

  1. 将common.js与common.css相关联
  2. common.js执行时,需检查common.css是否加载完成。

第一步,将两标签使用layuicss-laydate进行一一对应:

<link rel="stylesheet" id="layuicss-laydate" href="common.css" />
<script merge="layuicss-laydate" src="common.js"></script>

第二步,修改ready.getPath和ready.link方法:

      + getPath: function(){
      +   var js = document.scripts, script = js[js.length - 1], jsPath = script.src;
      +   var customcssId = script.getAttribute('merge');
      +   return customcssId === null ? jsPath.substring(0, jsPath.lastIndexOf('/') + 1): customcssId === 'layuicss-laydate'? customcssId:''
      + }()
      //载入CSS配件
      link: function(href, fn, cssname){
        //未设置路径,则不主动加载css
        if(!laydate.path) return;

        + var id = '', timeout = 0;
        + // 自定义css
        + if(laydate.path.indexOf('/') === -1){
        +     id = laydate.path;
        + }else{
            var head = document.getElementsByTagName("head")[0], link = document.createElement('link');
            ...
        +   id = 'layuicss-'+ app;
            ...
            if(!document.getElementById(id)){
                head.appendChild(link);
            }
        + }
	...

为laydate.js添加自定义css的方法,然后将common.css作为laydate的自定义css。当执行laydate.js源码并开始进行渲染时,则会先去检测common.css是否加载完成。

😁😂😃😄😅😆😇😈😉😐😑😒😓😔😕😖😗😘😙😠😡😢😣😤😥😦😧😨😩😰😱😲😳😴😵😶😷😸😹🙀🙁🙂🙃🙄🙅🙆🙇🙈
🙂
评论列表
  • 想知道怎样在laydate默认加载时把时分选择也显示出来呢? 求解答 谢谢 [email protected]

    reply
    • luo

      @ banana dish 你说的是日期时间选择器吗 https://www.layui.com/demo/laydate.html

      reply