
简介这是一份适合前端开发者使用的 layui 2.5.5 框架压缩包开源轻量用于快速搭建响应式网页与后台管理界面。资源共收录一百一十四个文件以二十二个 JavaScript 模块脚本、五个层叠样式表、七十五张动态演示图片为主另附图标字体及少量位图资源压缩后仅有约六百一十五千字节便于导入项目目录中包含核心样式与脚本、日期选择等组件模块、可部署的打包产物以及参考文档和示例结构分明。目前该资源已有一千二百一十七人学习下载通过组件系统能轻松使用表格、表单、弹窗等功能配合兼容 jQuery 的接口可动态控制组件行为。参照文档与示例还能自定义主题和扩展模块适合从入门到进阶的开发者提升界面构建效率缩短项目周期。1. 老项目还在用 layuiv2.5.5 这版压缩包值得原样留一份接手一个维护了三年的后台系统打开页面看到的是熟悉的下拉菜单和表格F12 一看控制台还在引用 layui-v2.5.5。官方文档早就切到新版老接口的渲染行为和 3.x 不完全一致这时候翻出本地的 layui-v2.5.5.rar解压、对着 docs 目录里的原版文档排查是最快的路径。layui 这个前端 UI 框架在 2.x 时代是很多后端管理系统的标配v2.5.5 处于 2.x 的中期版本模块化机制稳定组件行为经典适合用来维护存量项目、学习模块化设计也适合想脱离构建工具、直接打开浏览器就能跑业务的前端新手。压缩包里是完整的官方发布包源码、示例、文档都齐不需要联网找资源解压后本地就能跑示例。2. 弄清模块化运行机制use 与 render 是这套框架的两个轮子layui 的 2.x 运行机制里最值得先搞懂的不是某个组件的 API而是“加载模块”和“渲染视图”这两条线。模块线决定代码什么时候能执行渲染线决定 DOM 什么时候变成 UI 组件。两条线交织在一起就是这套框架几乎所有诡异行为的根源。新手容易直接把所有 JS 堆在页面底部结果一刷新页面发现 select 没有样式、表格是空的本质都是没理解这两条线的时序。2.1 模块加载器layui.define 与 layui.use 的分工先讲加载原理。layui.js 本身只包含加载器和一些基础工具真正干活的是 layer、form、table 这些模块文件。页面引入 layui.js 后通过 layui.use 注册需要加载的模块加载器会把对应的 JS 文件从模块目录里动态拉出来执行。模块目录默认是 layui 目录下的 modules也可以自己改。这个设计本质上就是一个浏览器端的 CommonJS 方案但不需要 npm、不需要构建工具丢到任意静态服务器上就能跑这是它在后台系统里经久不衰的核心原因。// 修改模块加载的基础路径常见于把静态文件做了 CDN 分发的场景 layui.config({ base: /static/assets/lib/layui/modules/ // 必须指向模块所在的目录注意结尾斜杠 }); // 按需加载 form 和 layer回调里的代码在模块初始化后才执行 layui.use([form, layer], function () { var form layui.form; // 从 layui 对象上取模块实例 var layer layui.layer; // 同一个模块只加载一次重复 use 不会重复请求 // 这里才能放心使用 form.render、layer.msg 等方法 layer.msg(模块已就绪); });这段代码里 base 路径是常见的翻车点路径写错会直接报“模块加载失败”。layui.use 的第一个参数是模块名数组第二个参数是回调函数只有所有依赖模块加载完成后回调才会执行。如果团队里有人自己写了通用模块就要用 layui.define 来定义模块名就是文件名去掉 .js别的页面就能通过 layui.use 引用了。// 在 static/js/myModule.js 里定义一个业务模块 layui.define([table, form], function (exports) { var table layui.table; var form layui.form; // 模块内部逻辑比如封装一个通用的表格初始化方法 function initTable(options) { table.render(options); } // 通过 exports 导出模块名是文件名去掉 .js即 myModule exports(myModule, { initTable: initTable }); });这里有个细节use 回调里的代码不会立即执行要等依赖模块全部加载完成所以模块化对页面性能有利但也引入了异步时序概念。没有构建工具的情况下这种设计比 ES Module 在浏览器里的兼容性更稳定不需要处理 typemodule 的跨域问题。我一般会在公共页面的底部统一写一个 use把所有页面级的初始化逻辑放进去避免多个 use 嵌套导致回调顺序混乱。2.2 自动渲染与手动渲染为什么动态生成的 select 不生效官方文档里经常看到 form.render 和 table.render。前者是手动渲染后者是实例化。2.x 的设计中form 依赖自动渲染也就是 use(form) 后会扫描页面里 classlayui-form 的容器把内部的 select、checkbox、radio 替换成 UI 结构。table 不走自动渲染必须调用 table.render 才会生成表格。两种渲染模式的区别决定了动态操作 DOM 时该调用哪个方法。// 动态往表单容器里插入新的选项 var html select namecityoption北京/optionoption上海/option/select; $(#cityWrap).append(html); // 手动重新渲染 select 区域否则刚才的 select 还是原生控件样式 form.render(select);form.render 的第一个参数是指定渲染类型第二个参数是表单的 lay-filter 值比如 form.render(select, demoForm) 只渲染指定表单避免大范围的 DOM 重绘。之前的项目里出过一次问题动态插入了一个 checkbox 组没调用 form.render数据提交时拿不到值因为 DOM 结构还是隐藏的原生控件layui 的样式层没接管。table 则是每次调用 render 都会发起请求或读取数据并填充表格重复调用会产生重复请求这是高频踩坑点。2.3 v2.5.5 的模块概览哪些是日常主力、哪些是低频v2.5.5 内置模块包括 layer、laydate、form、table、tree、upload、element、rate、carousel、flow、util、laytpl、laypage。日常后台开发用 form、table、layer、laydate 最多element 会在导航和 Tab 切换时用到upload 和 tree 在权限配置、文件管理场景出现。3.x 引入了新的构建体系和组件写法从维护成本角度选型一个已经在跑的 2.x 系统没必要升 3.x组件 API 不兼容的代价远超收益。模块主要用途使用频率form表单控件渲染、验证、提交拦截极高table数据表格、分页、排序、操作列极高layer弹层、消息提示、加载层极高laydate日期与时间范围选择高upload文件上传、拖拽上传中tree树形菜单、权限树中element导航、Tab、进度条中laytpl模板引擎配合 table 自定义列低但实用flow流加载滚动到底部加载低laypage独立分页组件低3. 把资源包落地到工程目录取舍与三种引入方式拿到压缩包后不要直接整个扔进工程先把目录结构看清楚。解压后是一个典型的发布包包含 dist、src、docs 等目录生产环境只需要部分内容。布局合理的话整个静态资源加载下来也就几百 KB对后端管理系统完全够用。3.1 解压后该留哪些目录dist、src、docs 的取舍dist 是发布目录生产环境只保留这个目录就够。src 是未压缩源码适合想调试框架内部逻辑的开发者单纯使用的话可以删掉。docs 里是离线的 HTML 文档维护老项目时非常有价值官方文档如果更新了版本老接口细节查不到本地文档就是后悔药。下面是解压后主要内容的用途说明路径内容部署建议dist/layui.css全部组件样式必须保留dist/layui.js模块加载器与基础库必须保留dist/layui.all.js全量打包版一次加载所有模块调试可用正式环境建议按需加载dist/font图标字体文件必须保留路径错了图标会消失docs离线官方文档本地排查用不用部署到服务器src未压缩的源码有改动需求的留只是使用可删部署的时候我习惯把整个 dist 目录改名为 layui放在静态资源根目录下的 lib 文件夹里和 echarts、jquery 并列。这样页面路径是 /lib/layui/layui.js模块路径默认是 /lib/layui/modules/清晰且不用额外配置 base。3.2 本地引入与在线引入两种方式怎么切引入 layui 有两条路本地文件引入和公共 CDN 引入。本地引入的优势是稳定内网环境也能用版本锁定不出意外。在线引入省流量但国内公共 CDN 的更新策略不可控一旦源站调整资源路径线上样式可能直接崩掉。我维护的项目都强制走本地引入公司内网服务器不一定允许外网请求。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title后台登录页/title link relstylesheet href/lib/layui/layui.css /head body !-- 页面内容 -- script src/lib/layui/layui.js/script /body /html如果非要用 CDN先把 layui.js 下载到本地看它引用的模块路径是相对路径还是绝对路径再决定页面里能否直接换 CDN 地址。这里有个细节layui.all.js 适合开发调试时快速看效果但正式环境不建议用它等于把所有模块一次性加载失去了按需加载的流量优势。生产环境直接用 layui.js 配合 use 按需加载。3.3 第一个可直接跑的页面登录页布局与表单初始化做一个登录页是理解 layui 工作流程最直接的方式。页面里有 form 表单、输入框、验证规则、提交拦截覆盖了 form 模块的主要用法。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title后台登录/title link relstylesheet href/lib/layui/layui.css style body { background: #f2f2f2; } .login-box { width: 360px; margin: 120px auto; padding: 30px; background: #fff; border-radius: 6px; } /style /head body div classlogin-box form classlayui-form lay-filterloginForm div classlayui-form-item input typetext nameusername lay-verifyrequired placeholder用户名 classlayui-input /div div classlayui-form-item input typepassword namepassword lay-verifyrequired placeholder密码 classlayui-input /div div classlayui-form-item button classlayui-btn layui-btn-fluid lay-submit lay-filterloginSubmit登 录/button /div /form /div script src/lib/layui/layui.js/script script layui.use([form, layer], function () { var form layui.form; var layer layui.layer; // 监听登录表单提交这里的 lay-filter 必须与按钮上的 lay-filter 一致 form.on(submit(loginSubmit), function (data) { var field data.field; if (field.username admin field.password 123456) { layer.msg(登录成功); } else { layer.msg(账号或密码错误); } return false; // 阻止表单原生提交 }); }); /script /body /html这个登录页里 lay-verifyrequired 是内置验证规则空值提交时会有默认提示。lay-submit 告诉 form 模块这个按钮是提交按钮点击时触发 submit 事件。data.field 是序列化后的表单数据注意表单里面的 input 必须有 name 才会出现在 field 里。return false 就是通知 layui 拦截原生提交改成走 AJAX。4. 高频组件实战table、layer、form 的组合用法把三个主力组件串起来基本就能做 80% 的后台管理页面。table 负责数据展示layer 负责交互反馈和弹层表单form 负责校验和收集数据。很多看上去复杂的页面拆开就是这三样东西的组合。4.1 数据表格从静态到接口table.render 的参数体系table.render 是全参数实例化的核心参数是 elem、url、cols、page。elem 指向表格容器url 是数据接口cols 是列配置数组page 开启分页。第一次用容易忽略的是默认接口数据格式框架要求返回 code 为 0、msg 为提示信息、count 为总数、data 为列表数组。后端没按这个格式返回时表格会一直空着接口明明 200。table.render({ elem: #demoTable, // 表格容器选择器 id: demoTableId, // 表格实例 ID后续 reload 用 url: /api/user/list, // 数据接口地址 page: true, // 开启分页 limit: 10, // 每页显示条数 cols: [[ { field: id, title: ID, width: 80 }, { field: name, title: 姓名 }, { field: status, title: 状态, templet: function (d) { return d.status 1 ? 启用 : 停用; }}, { field: role, title: 角色 } ]], parseData: function (res) { // 后端返回的数据结构可能与框架默认不同在这里做一次映射 return { code: res.code, msg: res.msg, count: res.data.total, data: res.data.rows }; } });cols 里的 templet 支持函数和模板字符串函数参数 d 是当前行数据适合做状态转换、拼接操作按钮。id 参数给表格起个名字之后可以用 table.reload(demoTableId, {...}) 带条件搜索不用重新 render。limit 如果不填默认是 10需要和分页组件的 page 参数配合。4.2 layer 弹层分类与回调type 参数决定弹层形态layer.open 是最底层的方法所有弹层都可以用它实现根据 type 参数区分类型。0 是信息框1 是页面层2 是 iframe 层3 是加载层4 是 tip 层。日常最多的是 type 1 和 type 2。type 1 适合承载页面上的隐藏模板type 2 适合嵌入一个独立页面比如编辑表单。// 打开一个页面层content 传页面里已有的模板元素 var index layer.open({ type: 1, // 1 表示页面层 title: 用户详情, content: $(#detailTpl), // 必须是页面里存在的 DOM 元素 area: [600px, 400px], // 弹层宽高 btn: [保存, 取消], yes: function (layero) { // 第一个参数是当前弹层的 index第二个才是 layero注意顺序 layer.close(index); }, btn2: function (layero) { return false; // 返回 false 阻止默认关闭 } });这里有个容易被文档绕晕的地方官方示例里 yes 回调写法是 function(index, layero)实际上 open 方法返回的 index 可以直接用在 close 里回调里的第一个参数同样是当前弹层的 index第二个参数是弹层的 DOM 对象。按钮不响应多半是回调参数顺序理解错了。弹层内的动态 HTML 里如果有按钮绑定事件需要在 success 回调里处理或者用事件委托绑定到 document 上。4.3 form 自定义验证与提交拦截不只是 requiredform 模块支持内置验证和自定义验证。内置规则包括 required、phone、email、url、number、date 等写在 lay-verify 属性里多个规则用竖线分隔。自定义规则通过 form.verify 注册规则名在 lay-verify 里引用返回字符串就是错误提示。input typetext namephone lay-verifyrequired|customPhone placeholder手机号 classlayui-input// 注册自定义验证规则 form.verify({ customPhone: function (value) { if (!/^1[3-9]\d{9}$/.test(value)) { return 手机号格式不对; } } }); // 监听提交data.field 是表单数据对象 form.on(submit(loginSubmit), function (data) { var field data.field; // 这里可以发起 AJAX 提交 return false; // 阻止默认提交行为 });lay-verify 里的规则顺序会影响提示优先级required 放前面空值先提示必填再提示格式。form.on 是事件监听的统一入口第一个参数是事件类型加括号里的过滤器值与表单的 lay-filter 或按钮的 lay-filter 对应。动态表单回显可以用 form.val(filterName, {}),传入表单的 lay-filter 和字段键值对执行后会自动渲染选中状态。5. 避坑排查v2.5.5 最常见的五个翻车现场前面章节覆盖了正常用法但实际项目里踩的坑比功能本身多。下面五个问题是我在多个老项目维护里反复遇到的每一条都见过不止一次。按“现象 → 原因 → 解决”记录排查时直接对照。5.1 表格一直空白Network 里接口却是 200【现象】table.render 执行后页面只有表头没有数据行控制台也没报错。 【原因】后端返回的数据结构不是 {code:0, msg:, count:100, data:[]}。最常见是 code 为 200或者数据在 data.list 里框架解析不到 data 字段。 【解决】两种修复思路一是改后端接口但老系统动接口成本高二是前端用 response 和 parseData 做适配。table.render({ elem: #demoTable, url: /api/user/list, response: { statusCode: 200 // 后端返回的 code 是 200不是默认的 0 }, parseData: function (res) { return { code: res.code, count: res.data.total, data: res.data.list }; } });注意 response 里的 statusCode 会改变框架判断成功响应的标准parseData 负责把数据结构映射成框架认的格式。排查顺序是先在浏览器里看接口原始返回再对照表格配置的 response 和 parseData。5.2 弹层的按钮点了没反应【现象】layer.open 渲染出的弹层按钮点击后既不触发回调也不关闭。 【原因】回调函数里的 this 指向变了导致拿不到正确的 index或者事件绑定到了弹层外部的元素上。 【解决】用变量保存 index在 yes 里通过变量关闭不要依赖 this。正确写法如下。var index layer.open({ type: 1, content: $(#formTpl), btn: [确定, 取消], yes: function (layero) { layer.close(index); // 用外部闭包保存的 index } });如果弹层内容里有动态生成的按钮要在 success 回调里绑定事件或者用事件委托时注意选择器要匹配弹层内的元素而不是整个 document避免外部同类型元素被误触发。5.3 laydate 范围选择只能选一天或者直接失灵【现象】laydate.render 配了 range: true但点击输入框时没有出现范围选择的双日历面板或者选了开始日期后结束日期无法联动。 【原因】laydate 需要用 elem 指向一个可见的 input如果 input 处于 display:none 或者被遮挡日期面板定位会异常。另一个常见原因是页面里引用了两个版本的 laydate后加载的覆盖了先加载的。 【解决】确保 input 可见再调用 render。如果必须隐藏日期输入框可以用一个文本按钮触发把日期传递到隐藏字段。laydate.render({ elem: #dateRange, range: true, // 开启范围选择 theme: #1E9FFF });版本冲突问题我一般在页面里全局搜一下 laydate.js 的引用保证只保留 dist 下的一个。排查顺序是先看 input 是否可见再看浏览器控制台有没有 laydate 相关的报错最后检查是否重复加载。5.4 自定义 JS 文件里直接用 layui.table 报 undefined【现象】在 common.js 里直接写 layui.table.render(...)浏览器报 Cannot read property table of undefined。 【原因】layui.table 模块尚未加载完成layui 对象上还没有挂载 table 实例。layui 的核心 JS 引入后layui 命名空间是存在的但模块需要 use 之后才有。 【解决】把所有依赖 layui 组件的代码包在 layui.use 里。// 错误示例直接使用 // layui.table.render({...}); // 正确示例包在 use 回调里 layui.use([table], function () { var table layui.table; table.render({...}); });如果业务 JS 文件本身是自定义模块就用 layui.define 包裹内部用 layui.table 获取依赖。另一个误用是在 use 回调外面读取 layui.form只会拿到 undefined因为 form 模块还没加载。5.5 图标显示成方框或空白【现象】页面上的 layui-icon 字体图标变成方块表格操作列的排版乱掉。 【原因】CSS 里通过 font-face 引用的字体文件路径不对通常是把 layui.css 移动了位置但没有同步移动 font 目录或者把 CSS 和 JS 分别做了 CDN 分发导致请求跨域。 【解决】保证 font 目录与 layui.css 同级查看 CSS 文件里 font-face 的 url 相对路径确认字体文件能直接访问。我一般把整个 dist 目录原封不动地改名部署而不是单独挑出 CSS 和字体这样层级关系不会乱。# 检查 font 目录内容至少应该有 iconfont.eot、iconfont.svg、iconfont.ttf、iconfont.woff ls -la /lib/layui/font/排查时打开浏览器 Network 面板找到字体文件的请求看是 404 还是被 CORS 拦了。如果是 404就是路径问题如果是跨域报错需要调整静态资源服务器的 CORS 头或者把字体文件放到同域。6. 进阶技巧把业务逻辑封装成私有模块到这一步layui 的日常使用已经通了再往上走就是工程化层面的封装。v2.5.5 的 layui.define 机制可以把项目自己的业务方法封装成模块配合 laytpl 做模板拆分让代码结构更清晰。// 封装项目自己的表格工厂 layui.define([table, layer], function (exports) { var table layui.table; var layer layui.layer; function init(url, cols, elem) { return table.render({ elem: elem, url: url, page: true, cols: cols, done: function (res) { if (res.code ! 0) { layer.msg(数据加载失败); } } }); } exports(tableFactory, { init: init }); });页面里使用时先 layui.use([tableFactory])然后直接调用 tableFactory.init(...) 初始化表格。这样做的收益是如果后续要统一调整表格的默认参数比如默认 limit 改成 20、加统一错误提示只需要改模块内部的配置不用全量替换页面代码。配合 laytpl 可以在表格的 templet 里渲染复杂模板而不用写一堆字符串拼接。var laytpl layui.laytpl; var tpl # if(d.status 1){ #span classlayui-badge layui-bg-green启用/span# } else { #span classlayui-badge layui-bg-gray停用/span# } #; table.render({ elem: #demoTable, cols: [[ { field: status, title: 状态, templet: function (d) { var html ; laytpl(tpl).render(d, function (str) { html str; }); return html; }} ]] });性能方面的建议正式环境始终用 layui.js 按需加载不要用 layui.all.js页面上同时用到的模块尽量合并到一个 use 里如果后台系统页面很多静态资源缓存策略里把 layui 的 CSS 和 JS 设成强缓存文件名不变的情况下浏览器不会重复下载。我维护的那个某跨平台系统几十个页面都依赖 v2.5.5靠这套封装和缓存策略页面加载几乎没有因为框架产生过性能问题。从那以后我每次把 layui 这类压缩包解压进工程都强制先跑一遍官方示例确认静态资源路径和模块依赖都正常再动业务代码这个习惯帮我省去了大量排查时间。希望帮到你。本文还有配套的精品资源点击获取