layui 首个版本发布于 2016 年,layui 官网于 2021年10月13日 下线。新版下载、文档和示例在内的所有框架日常维护工作,全部迁移到 Github 和 Gitee。

对于下架,作者贤心在知乎的回答大概是:

  • ES6 的语言、语法、功能特性的提升,IE 的主动退场,对于内置了简化 DOM 操作及浏览器兼容性问题的 jquery 的 layui 来说失去了它的核心优势。与此同时,其他的框架上架,layui 决定放手
  • layui 是 ES3/ES5 时代的产物,对于 DOM 查询、数据的动态变更,效率已不合时宜。重新维护起来也更难
  • 缺乏良性的生态支撑和“外界干扰”,作者贤心对此失去信心(有用户猜测是赚不到钱 狗头.jpg,而也认为白嫖党太多,国内的知识产权付费观念有待提升 严肃.jpg)

不管咋样吧,感恩作者这几年的付出(鞠躬.gif)

layui 官网是这样描述这个框架的:layui 兼容人类正在使用的全部浏览器(IE6/7除外),可作为 Web 界面速成开发方案。(我有足够的理由怀疑官网瞧不起 IE 低版本并且有证据)

iShot2021-10-08 17.07.45.png

库 vs 框架

在之前的学习中听说过一些说法:bootstrap 框架、lodash 库、jquery 库,那么库和框架的共同点好像都在于:

  1. 来源:都是其他开发人员写好的、现成的
  2. 使用:需要去引入文件
  3. 开发:需要去查看文档来明确用法

区别则在于:

  • 库是用来提供一些方法(函数)的集合,避免重复定义相同功能的函数,并具有一定的模式兼容性
  • 而所谓框架,更应该是规范开发者按照框架的设计去做一些事,而非简单的工具集的概念。框架可以提供相应的库或者基于库来实现,但库一般不会具备框架的规范性

再往简单了想,函数库是对于数据的操作,像得到随机数、截取、乱序等,而框架是可以直接解决静态页面的问题,快速搭建网站的模板。

安装

  1. 官网 官网 官方文档镜像链接
  2. Git 仓库下载 Git 链接
  3. github github 链接
  4. npm
  5. CDN

layui 有 CSS、JS、组件库,文件间相互关联,所以建议使用本地文件包,而非使用 bootCDN。

引入 【layui.js】 文件支持的语法与引入 【layui.all.js】 文件支持的语法略有不同

layui.js 需要先“引入”再支持功能: layui.use(‘layer’, function () { // layui.use(模块名称,回调) layui.layer.msg(222); });
不支持: layui.layer.msg(111); 而使用 layui.all.js 两种语法都支持

起步

引入了样式文件和脚本文件以后就可以从官网组件开始了。

  1. <head>
  2. ...
  3. <link rel="stylesheet" href="./layui/css/layui.css">
  4. </head>
  5. <body>
  6. <script src="./layui/layui.all.js"></script>
  7. <!--
  8. <script src="./layui/layui.all.js"></script>
  9. -->
  10. </body>

样式起步从按钮开始

拷贝示例代码,并挂类(跟 bootstrap 不能说一毛一样,只能说毫无差别。 狗头.jpg)
iShot2021-10-09 11.44.43.png

模块化使用方式

iShot2021-10-09 15.48.55.png
与引入了 jQuery 文件就拥有 jQuery($)核心对象一样,引入了 layui 的 js 文件就有 layui 核心对象。
layui.use() 方法就表示使用模块。
假设当前引入了 layui.js 文件,那么在使用模块时,就要先.use( ) 使用某个模块:

// 语法
layui.use('modName',function(){
    // 具体代码
});

弹出层 layer

layui.use('layer', function(){
  var layer = layui.layer;
  layer.msg('hello');
});  

// 等同于一个链式操作
layui.use('layer', function () {
  layui.layer.msg('Hello world');
});

2019-08-06 22.54.02.gif
layui 内置了 jQuery,就可以直接使用 jQuery 语法获取到某个元素节点

const $ = layui.$;
$('#btn').click(function{}); // 可以使用 jquery 的方法了

结合一下点击事件 + 弹出层:

const $ = layui.$;
$('#btn').click(function () {
  layui.use('layer', function () {
    layui.layer.msg(111);
  });
});

日期与时间 laydate

layui.use('laydate', function () {
  layui.laydate.render({
    elem: '#testLaydate', // 绑定到某一个元素,这里是一个 input 框
  });
});

iShot2021-10-09 15.33.04.png
将 key type 还可以设置为 time 显示为时间选择
iShot2021-10-09 15.33.41.png
如果需要使用多个模块,将 use 方法的第一个参数改为数组类型就可以了:

layui.use(['mod1', 'mod2'], mod1callback, mod2callback);

直接使用 layui.all.js (新版本已经没有该文件)

直接引入 layui.all.js 文件的话,就可以省略了.use()的过程。

// 弹出层
layui.layer.msg('省略了 .use 过程');

// 时间与日期
layui.laydate.render({
  elem: '#time'
});

// 颜色选择器
layui.colorpicker.render({
  elem: '#p1'
});

页面元素 规范公共基础类、公共属性

内置了一些公共类,类似于 bootstrap,直接给元素挂上类即可。
还定义了一些公共属性(这里并不会列举!自己去官网查看!)

布局

布局依旧按照 bootstrap 的栅格式布局,所以样式部分自行查看官方文档。
iShot2021-11-30 23.12.59.png

快速搭建一个后台管理系统

一个后台管理系统的布局通常长这样:
15651798185075.jpg
在官方文档【布局】-【管理系统界面布局】- 【预览布局效果】- 【查看该布局代码】有完整代码可供快速搭建网页结构:

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>layout 管理系统大布局 - Layui</title>
    <link rel="stylesheet" href="./layui/css/layui.css">
  </head>

  <body>
    <div class="layui-layout layui-layout-admin">
      <div class="layui-header">
        <div class="layui-logo layui-hide-xs layui-bg-black">layout demo</div>
        <!-- 头部区域(可配合layui 已有的水平导航) -->
        <ul class="layui-nav layui-layout-left">
          <!-- 移动端显示 -->
          <li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft">
            <i class="layui-icon layui-icon-spread-left"></i>
          </li>

          <li class="layui-nav-item layui-hide-xs"><a href="">nav 1</a></li>
          <li class="layui-nav-item layui-hide-xs"><a href="">nav 2</a></li>
          <li class="layui-nav-item layui-hide-xs"><a href="">nav 3</a></li>
          <li class="layui-nav-item">
            <a href="javascript:;">nav groups</a>
            <dl class="layui-nav-child">
              <dd><a href="">menu 11</a></dd>
              <dd><a href="">menu 22</a></dd>
              <dd><a href="">menu 33</a></dd>
            </dl>
          </li>
        </ul>
        <ul class="layui-nav layui-layout-right">
          <li class="layui-nav-item layui-hide layui-show-md-inline-block">
            <a href="javascript:;">
              <img src="http://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg"
                   class="layui-nav-img">
              tester
            </a>
            <dl class="layui-nav-child">
              <dd><a href="">Your Profile</a></dd>
              <dd><a href="">Settings</a></dd>
              <dd><a href="">Sign out</a></dd>
            </dl>
          </li>
          <li class="layui-nav-item" lay-header-event="menuRight" lay-unselect>
            <a href="javascript:;">
              <i class="layui-icon layui-icon-more-vertical"></i>
            </a>
          </li>
        </ul>
      </div>

      <div class="layui-side layui-bg-black">
        <div class="layui-side-scroll">
          <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
          <ul class="layui-nav layui-nav-tree" lay-filter="test">
            <li class="layui-nav-item layui-nav-itemed">
              <a class="" href="javascript:;">menu group 1</a>
              <dl class="layui-nav-child">
                <dd><a href="javascript:;">menu 1</a></dd>
                <dd><a href="javascript:;">menu 2</a></dd>
                <dd><a href="javascript:;">menu 3</a></dd>
                <dd><a href="">the links</a></dd>
              </dl>
            </li>
            <li class="layui-nav-item">
              <a href="javascript:;">menu group 2</a>
              <dl class="layui-nav-child">
                <dd><a href="javascript:;">list 1</a></dd>
                <dd><a href="javascript:;">list 2</a></dd>
                <dd><a href="">超链接</a></dd>
              </dl>
            </li>
            <li class="layui-nav-item"><a href="javascript:;">click menu item</a></li>
            <li class="layui-nav-item"><a href="">the links</a></li>
          </ul>
        </div>
      </div>

      <div class="layui-body">
        <!-- 内容主体区域 -->
        <div style="padding: 15px;">内容主体区域。记得修改 layui.css 和 js 的路径</div>
      </div>

      <div class="layui-footer">
        <!-- 底部固定区域 -->
        底部固定区域
      </div>
    </div>
    <script src="./layui/layui.js"></script>
    <script>
      layui.use(['element', 'layer', 'util'], function () {
        var element = layui.element,
            layer = layui.layer,
            util = layui.util,
            $ = layui.$;

        //头部事件
        util.event('lay-header-event', {
          //左侧菜单事件
          menuLeft: function (othis) {
            layer.msg('展开左侧菜单的操作', { icon: 0 });
          },
          menuRight: function () {
            layer.open({
              type: 1,
              content: '<div style="padding: 15px;">处理右侧面板的操作</div>',
              area: ['260px', '100%'],
              offset: 'rt', //右上角
              anim: 5,
              shadeClose: true
            });
          }
        });
      });
    </script>
  </body>

</html>

在官方布局基础上,修改了一些符合我们需要的部分:

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>layout 管理系统大布局 - Layui</title>
    <link rel="stylesheet" href="./layui/css/layui.css">
  </head>

  <body>
    <div class="layui-layout layui-layout-admin">
      <!-- 头部导航 -->
      <div class="layui-header layui-bg-cyan">
        <div class="layui-logo ">一个学生管理系统</div>
        <!-- 头部区域(可配合layui 已有的水平导航) -->
        <ul class="layui-nav layui-layout-left">
          <li class="layui-nav-item layui-hide-xs"><a href="">商品管理</a></li>
          <li class="layui-nav-item">
            <a href="javascript:;">其他内容</a>
            <dl class="layui-nav-child">
              <dd><a href="">其他内容1</a></dd>
              <dd><a href="">其他内容2</a></dd>
              <dd><a href="">其他内容3</a></dd>
            </dl>
          </li>
        </ul>
        <ul class="layui-nav layui-layout-right">
          <li class="layui-nav-item layui-hide layui-show-md-inline-block">
            <a href="javascript:;">
              <img src="http://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img">
              admin
            </a>
            <dl class="layui-nav-child">
              <dd><a href="">退出登录</a></dd>
            </dl>
          </li>
        </ul>
      </div>
      <!-- 左侧菜单 -->
      <div class="layui-side layui-bg-cyan">
        <div class="layui-side-scroll">
          <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
          <ul class="layui-nav layui-nav-tree layui-bg-cyan" lay-filter="test">
            <li class="layui-nav-item layui-nav-itemed">
              <a class="" href="javascript:;">学生模块</a>
              <dl class="layui-nav-child">
                <dd><a href="javascript:;">学生管理</a></dd>
                <dd><a href="javascript:;">班级学生</a></dd>
                <dd><a href="javascript:;">课程学生</a></dd>
              </dl>
            </li>
            <li class="layui-nav-item">
              <a href="javascript:;">退换货记录</a>
              <dl class="layui-nav-child">
                <dd><a href="javascript:;">退货记录</a></dd>
                <dd><a href="javascript:;">换货记录</a></dd>
              </dl>
            </li>
            <li class="layui-nav-item"><a href="javascript:;">库存清点</a></li>
            <li class="layui-nav-item"><a href="javascript:;">商品发布</a></li>
          </ul>
        </div>
      </div>
      <!-- 右侧内容主体 -->
      <div class="layui-body">
        <!-- 内容主体区域 -->
        <div style="padding: 15px;">内容主体区域</div>
      </div>

      <!-- 底部固定区域 -->
      <div class="layui-footer layui-word-aux">&copy; 哼,总有人想剽窃我的智慧</div>
    </div>

    <script src="./layui/layui.all.js"></script>
    <script>
      layui.use(['element', 'layer', 'util'], function () {
        var element = layui.element,
            layer = layui.layer,
            util = layui.util,
            $ = layui.$;

        //头部事件
        util.event('lay-header-event', {
          //左侧菜单事件
          menuLeft: function (othis) {
            layer.msg('展开左侧菜单的操作', { icon: 0 });
          },
          menuRight: function () {
            layer.open({
              type: 1,
              content: '<div style="padding: 15px;">处理右侧面板的操作</div>',
              area: ['260px', '100%'],
              offset: 'rt', //右上角
              anim: 5,
              shadeClose: true
            });
          }
        });
      });
    </script>
  </body>

</html>

显示的主题颜色从 black 变成了 cyan,不要问为啥,就是我乐意。
iShot2021-10-10 22.56.34.png
还写了一个彩色的,不服?
iShot2021-11-30 23.22.02.png

常用功能

导航

截动图截小了,字不重要,凑合看功能:
导航效果1.gif
之前使用过 iframe 实现点击后显示相应内嵌页面的效果,现在可以使用新的方式。

首先需要新建几个新的 html 文件(根据需求创建)这里我创建了 student.html、class.html、course.html 并且只在各文件中书写所需的语义化标签,没有其他的 doctype 之类的东西,因为这些内容最终会被嵌入到index.html,所以只需要书写具体内容即可。
iShot2021-10-10 23.49.46.png

左侧菜单代码段:

<!-- 左侧菜单 -->
<div class="layui-side layui-bg-cyan">
  <div class="layui-side-scroll">
    <ul class="layui-nav layui-nav-tree layui-bg-cyan" lay-filter="test" id='navTree'>
      <li class="layui-nav-item layui-nav-itemed">
        <a class="" href="javascript:;">功能模块</a>
        <dl class="layui-nav-child">
          <dd><a href="javascript:;" data-id='student'>学生管理</a></dd>
          <dd><a href="javascript:;" data-id='class'>班级管理</a></dd>
          <dd><a href="javascript:;" data-id='course'>课程管理</a></dd>
        </dl>
      </li>
     ...
    </ul>
  </div>
</div>

要获取到具体点击的某一个选项,所以将 3 个 li 设置上 data-id,值与刚才新建的各文件名相同(8、9、10行代码),再为父级元素设置书写事件委托。

点击事件写好后,使用 load() 发送 ajax 请求(目前就用就完了别管那么多,异步那块儿会再详细见到 ajax 的小黄人 xhr 对象的),由于涉及到了 ajax 请求,本地打开会提示跨域的错误,所以这里需要采用服务器的形式打开

const $ = layui.$;
$('#navTree').click(function (event) {
  let pageId = event.target.dataset.id; // 获得当前点击的哪个选项
  $('#content').load(`./${pageId}.html`); // $('#content') 为显示区域元素节点
});

编辑器里选择 open with live server,不能用 open in default broswer(凶狠.jpg)
iShot2021-10-10 23.46.26.png

表格和分页

文档跑 这一页 来了,layui 最牛*的就是把表格的分页和排序功能内置了!
那么既然要显示表格,那么 student.html 里自然就该出现 table 标签了。再依照文档语法、利用 mock 生成一些随机数据:

<!-- 这是在 student.html -->

<table lay-filter='studentList' id='studentList'></table>
<!-- 引入的 mock 文件已经引在了 index.html  -->
<script>
  // 生成随机数据,内容在语雀 Mock 那一篇都有讲解
  let data = Mock.mock({
    "list|27": [{
      "id|+1": 1,
      "name": "@cname()",
      "age|16-22": 1,
      "gender|1": ["男", "女"],
      "score|0-100": 1,
      "address": "@county(true)",
      "phoneNumber": /^1[3458]\d{9}$/
    }]
  });

  layui.table.render({
    elem: '#studentList',  // 绑定节点
    data: data.list, // value 为一个 array,数据从 mock 来
    page: true, //开启分页
    cols: [[ //表头
      { field: 'id', title: '学号', sort: true, fixed: 'left' },
      { field: 'name', title: '姓名' },
      { field: 'age', title: '年龄', sort: true },
      { field: 'gender', title: '性别', sort: true },
      { field: 'score', title: '成绩', sort: true },
      { field: 'address', title: '城市', width: 300, },
      { field: 'phoneNumber', title: '电话号码', sort: true },
    ]]
  });
</script>

iShot2021-10-11 10.05.50.png

2019-08-08 09.43.23.gif

选项卡

在 【页面元素】-【选项卡】的 ‘默认 Tab 选项卡’ 的代码里可以看出整个文档结构。

将官网实例代码 copy 到 index.html 的 content 部分,观察:一级 ul 的内容和兄弟元素 div 内容之间的关系。

那回到管理系统,在 内容主体区域 就可以准备出选项卡的部分了,由于选项卡每个选项的个数是不定的、底下二级显示的内容也是不定的,所以只需要留有父级容器即可:

<!-- 右侧内容主体 -->
<div class="layui-body">

  <!-- 选项卡 -->
  <div class="layui-tab" lay-allowClose="true" lay-filter="tab-switch">
    <!-- 一级 -->
    <ul class="layui-tab-title"></ul>

    <!-- 二级 -->
    <div class="layui-tab-content"></div>
  </div>
</div>

动态的选项卡

接下来在左侧菜单的选项中,点击后,不再是调用 .load() 方法,而是应该添加选项卡。动态的往 content 里填充内容。所以就不再需要 <div style="padding: 15px;" id='content'></div> 这个 content 了。(注释掉 content )

在 .layui-tab-title 的一级 ul 里就可以动态添加选项卡内容了:

// 左侧菜单点击事件
$('#navTree').click(function (event) {
  let pageId = event.target.dataset.id; // 获得当前点击的哪个选项 student|class|cource

  /*
      实参说明:动态传入点击的 li 的内容 “学生管理|班级管理|课程管理”,渲染的 html 文件名,data-id
  */ 
    addTab(event.target.innerText, `./${pageId}.html`, pageId); 
});

// 参数接收:点击的 li 的标题内容“学生管理|班级管理|课程管理”,渲染的 html 文件名、data-id
function addTab(innerText, url, pageId) {

}

在函数内部应该考虑一个问题:
用户在点击以后,自然会新增一页选项卡,但并不是每点击一次就会新增一页。
例如先点击了‘学生管理’,就会新增一个学生管理的选项页;再点击‘班级管理’,又会新增一个班级管理的选项页。此时,又点回到‘学生管理’,应该再新开学生管理的选项页吗?

答案是否定的,对于已有的选项页,就不该再被作为新增了,而是被打开。所以就应该经过判断。

来分析一下选项卡标签页的文档结构:
iShot2021-12-01 22.30.07.png
iShot2021-12-01 22.21.34.png

// ----------------------------- 这一截同上,避免扰乱视线可以不看 ------------------
const $ = layui.$;
$('#navTree').click(function (event) {
  let pageId = event.target.dataset.id; 
  addTab(event.target.innerText, `./${pageId}.html`, pageId);
});
// ----------------------------------------------------------------------------

// 参数:点击的 li 的标题内容“学生管理、班级管理、课程管理”,渲染的 html 文件名、data-id
function addTab(innerText, url, pageId) {

  // 判断父元素 ul 是否已存在相同 data-id 值的后代 li
  if ($(`.layui-tab-title>li[lay-id="${pageId}"]`).length > 0) { // length 为 1,则表示已打开相应标签页。就切换至已打开的标签页
    layui.element.tabChange('tab-switch', pageId);// [相关动态操作]里有方法说明
  } else { // 该标签页还没有开启过,新增一个标签页
    layui.element.tabAdd('tab-swtich', { // 【这是一个秘密已做好决定,让你们拷!】
      title: innerText, 
      content: '选项卡的内容', //支持传入html
      id: pageId
    });
  }
}

QQ20211201-225041.gif
二级内容会在第二次点击时才出现,解决方案 tabAdd() 后立马 tabChange()。

选项卡内容

从上面 else 的 tabAdd() 参数中还需要二级选项卡的内容展示(18行)。应该是被动态获取出来的,所以发请求去获取:

// ----------------------------- 这一截同上,避免扰乱视线可以不看 ------------------
const $ = layui.$;
$('#navTree').click(function (event) {
  let pageId = event.target.dataset.id; 
  addTab(event.target.innerText, `./${pageId}.html`, pageId);
});
// ----------------------------------------------------------------------------

// 参数:点击的 li 的标题内容“学生管理、班级管理、课程管理”,渲染的 html 文件名、data-id
function addTab(innerText, url, pageId) {
  // 判断父元素 ul 是否已存在相同 data-id 值的后代 li
  if ($(`.layui-tab-title>li[lay-id="${pageId}"]`).length > 0) { // length 为 1,则表示已打开相应标签页。就切换至已打开的标签页
    layui.element.tabChange('tab-switch', pageId);// [相关动态操作]里有方法说明
  } else { // 该标签页还没有开启过,新增一个标签页
    s// ajax 的请求也是先照着写不要过多纠结
    $.ajax({
      url: url,
      type: 'GET',
      success: function (data) {
        // layui.element.tabAdd(filter, options); //options 参数类型为对象
        console.log('请求回来的data', data);
        layui.element.tabAdd('tab-switch', {
          title: innerText,//'选项卡的标题'
          content: data,//'选项卡的内容',支持传入html
          id: pageId, //'选项卡标题的lay-id属性值'
        });
        // 新建完成后,就显示到该标签页
        layui.element.tabChange('tab-switch', pageId);
      }
    });
  }
}

选项卡.gif

其他更多需求看需求文档吧……我累了。
没事儿,我是自愿写文档的。嗯。是的。

更新一个小提示

看看官网里的这句话,或许在某些地方~能帮你大忙(偷笑.gif)
iShot2021-12-03 15.02.17.png