首页 > 其他分享 >webman admin 中的 Layui 使用说明

webman admin 中的 Layui 使用说明

时间:2024-06-13 15:54:18浏览次数:28  
标签:function item webman layui apps admin 模块 Layui config

目录

Layui 背景

Layui 是一个上古框架,jQuery 时代末期 Vue 初期的作品,早期因为 layout.js 弹出层起家,在作者的努力下,成体系的开发了一套常用 web 应用组件
现在这个框架已经不推荐新项目入坑了,除非你负责项目的全栈,需要权衡开发效率(现在依然有很多 MMR 上万刀的独立开发者在使用 jQuery 开发应用)
此记录主要为了配合 webman-admin 使用,提供一个快速查询手册的目录,万一哪天我忘记了回来就直接看这里

生命周期

框架运行非常简单,浏览器载入就执行,没有复杂的 SPA 组件生命周期
web 生命周期:浏览器载入页面 -> html 结构持续加载标签 -> 加载到 script 标签就开始执行(通常会将 script 标签置于 html 文件的结尾)
Layui 在这里被配置为当 html 结构加载完毕后执行,类似于 jQuery 当中的

$(document).ready(function() {
	// your code here
});

就算你将 script 标签置于 html 文档开头,也会等待所有 DOM 加载完毕

底层方法

通用 Common

名称 接口 说明 链接
全局配置 layui.config(options: {base: string, version: bool, dir: string, debug: bool} = {}) 在 Layui 模块使用之前,采用该方法进行一些全局化的基础配置 link
链接解析 var url = layui.url(href: string = location.href); 该方法用于将一段 URL 链接中的 pathname、search、hash 等属性进行对象化处理。
如果只是解析 URL 的话不推荐用这个,拿来开发单页应用可使用,因为新的浏览器已经有原生标准 API 支持这项功能,可以查看 MDN 的链接: URL
本地存储 layui.data(key: string, settings: {[key:string]: any})
layui.sessionData(key: string, settings: {[key:string]: any})
data 表示 localStoragesessionData 等于 sessionStorage,两者用法一致
这个封装就是将对象 settings 和原生简单的 JSON.parse, JSON.stringify 了一下,可用可不用
浏览器信息 var device = layui.device(); 可利用该方法对不同的设备进行差异化处理

模块化相关 Modular

名称 接口 说明 链接
定义模块 layui.define([modules], callback)
加载模块 layui.use([modules], callback)
扩展模块 layui.extend(obj)
弃用模块 layui.disuse([modules]) v2.7+
获取回调 layui.factory(modName) 获取 layui.define([], callback) 中的参数 callback

事件 Event

名称 接口 说明 链接
阻止事件冒泡 layui.stope(e) js 原生 event.preventDefault()
增加自定义模块事件 layui.onevent(modName, events, callback) 一般在内置组件中使用,类似 jQuery 中的 $.on()
执行自定义模块事件 layui.event(modName, events, params) 搭配 onevent 使用,类似 jQuery 中的 $.trigger()
事件注销 layui.off(events, modName) 注销模块相关事件,类似 jQuery 中的 $.off()
防抖包装 layui.debounce(fn, wait) fn 按指定毫秒 wait 延时执行
节流包装 layui.throttle(fn, wait) 限制 fn 在指定毫秒 wait 内不重复执行

模块系统入门

Layui 模块系统与 vue 模块的区别

vue 当前通常作为单文件组件定义,并且使用的是标准的 ES5 模块系统,具有把 htmljscss 合为一个组件的组件系统。
Layui 更为简单,仅仅只是封装 js 模块,它与 htmlcss 是离散的,内聚性较弱,可在不同的 html 文档当中随意引用 js 模块,在你引用了 Layui 模块的地方再单独加载 css 与编写 html 标签。

// 定义模块(通常单独作为一个 JS 文件)
layui.define([mods], function(exports){
  exports('mod1', api);
});
// 使用模块
layui.use(['mod1'], function(args){
  var mod1 = layui.mod1;
});
// 嵌套式使用模块
layui.use(['mod1'], function(args){
  var mod1 = layui.mod1;
  layui.use(['mod2'], function(args2) {
	  var mod2 = layui.mod2;
  })
});

理解 layui.use

layui.use 的使用方法在文档中有介绍link
简而言之这个的用法就是为了控制模块加载,不需要你每次都手动在 html 文件中引入 script 标签,Layui 处理了你重复引入的问题以及异步引入问题
核心在于执行 callback(加载 apps 参数内的模块只是为了服务 callback)
其实你可以不用深究 layui.use 到底干什么的,你只需要把 callback 的业务流当作以前的 jQuery 使用就行

// 你只需要把 callback 的业务流当作以前的 jQuery 的
$(function() {

})

代码解释

source code
注意:我去掉了作者的注释,加上了我自己的注释,你可以打开上述链接对照理解

  /**
   * 源码,在 Layui 原型链上直接定义了 use 函数
   * @apps string | array<string> 表示需要加载的模块
   * @callback function 模块加载完毕后执行的函数
   * @exports
   * @from 
   */
  Layui.prototype.use = function(apps, callback, exports, from){
    var that = this;
    var dir = config.dir = config.dir ? config.dir : getPath;
    var head = doc.getElementsByTagName('head')[0];

	// 1) apps 值调整
    apps = function(){
      // 字符串包装为 [string]
      if(typeof apps === 'string'){
        return [apps];
      }
      // apps 如果是 function,则重载 use 函数参数为 function(callback, exports, from)
      else if(typeof apps === 'function'){
        callback = apps;
        return ['all'];
      }
      /**
       * 这种写法不好,不要学,好的办法是接一个 `else { return apps }`,这样更清晰
       * 这里把 apps 理解为是一个数组加载模块形式,比如 layui.use([jquery, mod1, mod2], ...)
       */
      return apps;
    }();

	// 2) 解决 jQuery 加载冲突问题
    if(win.jQuery && jQuery.fn.on){ // 检测在根环境 window 中是否加载了 jQuery 对象
      that.each(apps, function(index, item){ // 如果用户在 apps 中指明加载 jQuery 且 window 已有 jQuery,则剔除掉即将加载的 jQuery 组件
        if(item === 'jquery'){
          apps.splice(index, 1); // 这是 js 中常见的剔除操作,splice 会直接修改 apps 对象。
        }
      });
      layui.jquery = layui.$ = jQuery; // 将 win 环境中的 jQuery 引用,链接到 layui.jquery 中
    }

    var item = apps[0]; // 取出第一个组件
    var timeout = 0;

    exports = exports || []; // 对 exports 的再处理,这种初始化操作最好放在函数的开始位置,也是 js 的常见惯用法:a || b || c,表示如果 a 和 b 无效,则返回一个 c。

    // config 这个变量声明于 use 函数外,这里需要获取到 host,用 host 拼接出完整的 url 给 <script/> 的 src 属性
    config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//)||['//'+ location.host +'/'])[0];

    /**
     * 当脚本加载时
	 * 会在页面中创建一个 script 标签,并且附加了一个 event 为 load 的事件,当这个 load 执行的时候,会执行一次这个函数
	 * 可以看出整个 `layui.js` 文件当中只有 script 标签引用了一次 onScriptLoad,这个函数声明的位置距离调用处非常远,用现在时髦的话来说就是关注点分离严重
     * 可以考虑挪到距离调用处较近的地方
     */
    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); // 这里的 node 就是下面的 script 标签,你在这里往上查查不到(这就是我说为什么要在离使用它最近的地方声明)
        (function poll() { // 这里用立即执行表达式主要是为了异步执行,避免阻塞
          if(++timeout > config.timeout * 1000 / 4){
            return error(item + ' is not a valid module', 'error');
          }
          config.status[item] ? onCallback() : setTimeout(poll, 4); // 如果执行失败那么就尝试再执行一次
        }());
      }
    }

    
    /**
     * use 函数的核心功能,这里执行了一个线性迭代过程
     * 重复的调用 use,直到所有 apps 中的模块被处理完毕(加载 script 标签)
     */ 
    function onCallback(){
      exports.push(layui[item]);
      apps.length > 1 // 如果加载了超过一个以上的模块
		? that.use(apps.slice(1), callback, exports, from) // 这里比较关键,use 函数会进行一个
        : (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(/^\{\/\}/, '');

    /**
     * config.modules 为模拟物理路径,存储的是 key: url,比如 config.modules['jquery'] = url
     * 这里表示如果 config.modules 当中没有这一项,则记录该扩展模块的 url
     */
    if(!config.modules[item] && layui[item]){
      config.modules[item] = url;
    }

    /**
     * 加载模块的方法
     * 创建一个 script 标签,然后给一个 src,直接插入到 HTML 文档当中,怎么样,简单吧?
     */
    if(!config.modules[item]){
      var node = doc.createElement('script');

      /**
       * 关于 async 的说明
       * https://developer.mozilla.org/en-US/docs/Games/Techniques/Async_scripts
       */
      node.async = true;
      node.charset = 'utf-8';
      node.src = url + function(){
		// config.versoin 表示用于更新模块缓存,默认 false。若设为 true,即让浏览器不缓存。
        var version = config.version === true
        ? (config.v || (new Date()).getTime())
        : (config.version||'');
        return version ? ('?v=' + version) : '';
      }();

	  // 在 html 文件的 head 标签内插入这个 script,因为配置了延迟到 html dom 加载完毕,所以不用担心顺序问题。
      head.appendChild(node);

	  // 这里的判断主要是处理兼容性问题,有的浏览器是 attachEvent(IE)
      if(node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera){
        node.attachEvent('onreadystatechange', function(e){
          onScriptLoad(e, url);
        });
      } else {
        // 标准浏览器的 API 为 addEventListener,当脚本装载的时候,执行 onScriptLoad 函数
        node.addEventListener('load', function(e){
          onScriptLoad(e, url);
        }, false);
      }
	  // config.modules 保存一个 hash
      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);
      }());
    }

    // 返回内部 this 的别名 that
    return that;
  };

jQuery 快速入门
Layui

标签:function,item,webman,layui,apps,admin,模块,Layui,config
From: https://www.cnblogs.com/ninkaki/p/18246069

相关文章

  • 我不允许你们不知道,这么优秀的开源项目!smart-admin
    概述都2024年了,我不允许你们不知道的优秀开源项目,他是国内首个满足网络安全数据安全三级等保支持登录限制支持加解密等等一系列安全措施的开源项目!这个开源项目叫smart-admin是由1024创新实验室开发的。官网官网地址:https://smartadmin.vip/源码地址:https://gi......
  • pgAdmin未授权命令执行漏洞(CVE-2022-4223)
    ​https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v5.7/source/pgadmin4-5.7.tar.gz 下载pgadmin5.7的源码首先从代码层面进行分析接口 /validate_binary_path​最后调用了 subprocess.getoutput(​来执行了命令这一部分代码是对传入的路径进行检测,如果是在linu......
  • Sz-Admin | SpringBoot3 JDK21 Vue3开源后台RBAC管理系统 | 2024年好用的开源RBAC管理
    简介接触了很多优秀的开源和闭源项目,在使用过程中也发现一些问题,不甘满足的我遂产生了想法:于是利用休息时间编写了一套后台管理系统,它灵活、简洁、高效,拥抱最新的技术,因此Sz-Admin便诞生了,也意为升职Admin,升职加薪节节高。SzAdmin,一个基于SpringBoot3、Vue3和El......
  • eladmin-mp 低代码生成后台页面和服务端java代码接口调研
    概述总体这个项目前端使用vue2,后端使用springbootgit地址https://github.com/elunez/eladmin-mp预览地址https://eladmin.vip/demo/#/sys-tools/generator数据表配置低代码下载生成后的前端代码示例生成的后端代码示例:这里可以配置生成自动放在项目的目录......
  • 宝塔面板部署ruoyi-admin_jar(java项目)
    1.创建文件夹,上传jar文件:/www/wwwroot/域名/ruoyi-admin_jar2.点击网站-》添加Java项目3.选择已上传的jar文件-》添加对应域名-》配置后端路径:/prov-api,配置前端路径:/www/wwwroot/域名/dist(其他的默认)4.点击确认,等待一下,尝试访问(报错:404前端路径不对,502端口配置不对,401后端api......
  • FastAdmin 后端控制器与前端页面传参
    1.菜单让链接带参 2.控制器传参数到前端JS$this->assignconfig('tab',$tab); 3.JS传参回后端index_url:'contract/contract/index/tab/'+Config.tab, ......
  • Windows Server 2022 中 wbadmin 工具
    WindowsServer2022中wbadmin工具的初级应用大纲:1.理解wbadmin工具介绍wbadmin工具及其作用。解释wbadmin在WindowsServer2022中的重要性和作用。2.基本备份操作学习如何使用wbadmin工具执行基本备份操作。包括备份到本地磁盘或网络共享的示例。3.......
  • hm头条-admin
    本节目标完成项目项目介绍验证码登录统一处理请求富文本编辑器频道下拉菜单封面上传文章列表展示筛选功能分页功能删除功能编辑文章(回显)编辑文章(保存)退出登录项目介绍介绍头条数据管理平台:对IT资源移动网站的数据进行管理移动网站(演示):极客园主要功能登录......
  • 如何初始化 FIrebase 云函数,以便使用凭据和 JSON 验证 Firebase Admin SDK 服务账户?
    我觉得我已经阅读了所有可用的资料,但我仍然无法理解这一点。我非常喜欢Google的产品,但有时其文档的简洁性令人头疼。我阅读了这个令人难以置信的雄辩答案,这个答案的作者和我一样毫无头绪,但他觉得有必要写一本循序渐进的儿童指南。不幸的是,他的回答过于针对他的项目,而不是我的项......
  • 通过admin监视任务
    通过admin监视任务在控制台监控任务执行情况,还不是很方便,最好是能够通过web界面看到任务的执行情况,如有多少任务在执行,有多少任务执行失败了等这个Celery也是可以做到了,就是将任务执行结果写到数据库中,通过web界面显示出来。安装插件pipinstalldjango-celery-results注册......