Yb-components Yb-components
首页
开发规范
yb-cli
  • 开发指南
  • 更新日志
  • 组件
关于
首页
开发规范
yb-cli
  • 开发指南
  • 更新日志
  • 组件
关于
  • yb-cli
  • web项目开发模板
  • vue组件包开发模板
  • Layui项目开发模板
    • 技术栈
    • 安装依赖
    • 命令
    • git 规范说明
    • 目录结构
    • 开发环境说明
    • components 组件
  • yb-cli
zougt
2023-03-06

Layui项目开发模板

通过 yb-cli 快速创建的 create-layui-template,目的用于开发兼容 IE9 以下的简单页面(IE8+),最好只用 ES3 的语法,因为这里没有使用 babel 转译语法,可以在 can i use (opens new window)查看语法的兼容情况。

不过 yb-layui-extend/libs 提供了一些 polyfill:

│ │ ├── yb-layui-extend # yb-layui-extend 组件包的内容,自动从node_modules拷贝进来,请勿在此目录人为添加任何东西
│ │ │   ├── components # yb-layui-extend 提供的按需加载的layui模块
│ │ │   ├── libs # 放在yb-layui-extend里面的一些资源
│ │ │   │   ├── layui
│ │ │   │   ├── array-polyfils.js
│ │ │   │   ├── console-no-error.js
│ │ │   │   ├── promise.plyfill.js
│ │ │   │   └── url-search-params.js
1
2
3
4
5
6
7
8

# 技术栈

jquery v1.x + layui v2.x + scss

开发环境用 gulp + browser-sync 搭建 (封装在 yb-layui-extend/scripts)

# 安装依赖

# 安装依赖
npx yarn
1
2

# 命令

# 启动服务
npm run dev

# 打包
npm run build

# 打包后也启动服务
npm run serveBuild

# eslint 检测
npm run lint
1
2
3
4
5
6
7
8
9
10
11

# git 规范说明

开发分支:dev,稳定分支:master , 始终由 dev 向 master 合并,tag 的版本号规范遵循语义化版本号 (opens new window)

# 目录结构

├── /.husky/ # husky管理git hooks的目录,默认添加了 pre-commit 和 commit-msg 两个钩子
├── /dist/ # 项目打包输出目录,也可以用package.json的name作为打包目录,在ybEasy.config.js中配置outputDir
├── /src/  # 项目开发源码目录
│ ├── /components/ # 此工程支持按需加载的组件目录,遵循layui模块规范
│ │ ├── Api # 把后端api接口管理成模块
│ │ │   └── main.api.js # 其中一个接口api模块案例
│ │ ├── buttonShow # 一个组件目录案例,目录名称其实不重要,为了直观,最好也跟模块名称对应
│ │ │   ├── buttonShow.scss # 当前组件对应的scss,文件名必须跟模块名称对应,与模块同名就可以按需加载编译后的.css,为了避免样式类名冲突,请规范类名只应用于当前组件
│ │ │   ├── buttonShow.js # 不具备html模板写法的layui模块,文件名必须跟模块名称对应,不可以同时存在 buttonShow.js 和  buttonShow.html
│ │ │   └── buttonShow.html # 具备html模板写法的layui模块,方便使用layui的模板引擎,文件名必须跟模块名称对应,不可以同时存在 buttonShow.js 和  buttonShow.html
│ ├── /pages/ # 页面目录
│ │ ├── page1 # 页面1
│ │ │   ├── /images/ # 当前页面独有的图片资源
│ │ │   ├── index.scss # 当前页面独有的scss,当前目录可以多个.scss,文件名可以随意
│ │ │   ├── index.js # 当前页面独有的js,当前目录可以多个.js,文件名可以随意
│ │ │   └── index.html # 如果用当前目录名作为访问路径的一部分,文件名必须是index
│ │ ├── /images/ # 当前页面独有的图片资源
│ │ ├── index.scss # 当前页面独有的scss,当前目录可以多个.scss,文件名可以随意
│ │ ├── index.js # 当前页面独有的js,当前目录可以多个.js,文件名可以随意
│ │ └── index.html  # pages 第一层也可以是一个页面的目录,访问路径是 / 时,文件名必须是index
│ ├── /libs/ # 多个页面共用的第三方js库,如jquery、layui
│ │ ├── yb-layui-extend # yb-layui-extend 组件包的内容,自动从node_modules拷贝进来,请勿在此目录人为添加任何东西
│ │ │   ├── components # yb-layui-extend 提供的按需加载的layui模块
│ │ │   ├── libs # 放在yb-layui-extend里面的一些资源
│ │ │   │   ├── layui
│ │ │   │   ├── array-polyfils.js
│ │ │   │   ├── console-no-error.js
│ │ │   │   ├── promise.plyfill.js
│ │ │   │   └── url-search-params.js
│ │ │   └── scss # yb-layui-extend 提供的scss,如 var.scss
│ │ └── aaa.js # 其他当前工程的不会变化的js,这些js的文件名可以随意,会全部加载
│ ├── /scss/  # 多个页面共用的scss
│ │ └── index.scss
│ ├── /images/ # 多个页面共用的图片资源
│ ├── /utils/ # 多个页面共用的js
│ │ ├── httpAjax.js # httpAjax 模块,使用$.ajax,封装后端接口返回数据统一处理等
│ │ └── index.js # utils 模块,当前工程的工具模块
├── .gitgnore # git忽略检测的配置
├── commitlint.config.js # commitlint配置
├── package.json #
├── README.md # 每个git工程要写说明
├── gulpfile.js # gulp 配置
└── ybEasy.config.js # 定义的打包脚本的配置,配置反代理等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 开发环境说明

/pages/ 内的 html 模板应该是:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="pragma" content="no-cache" />
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="0" />
        <title>事中提醒</title>
        <!-- inject:libs:css -->
        <!-- endinject -->
        <!-- inject:components:css -->
        <!-- endinject -->
        <!-- inject:css -->
        <!-- endinject -->
        <!-- inject:libs:js -->
        <!-- endinject -->
        <!-- inject:_components_extend_:js -->
        <!-- endinject -->
        <!-- inject:utils:js -->
        <!-- endinject -->
    </head>

    <body>
        <div id="app"></div>
        <!-- inject:js -->
        <!-- endinject -->
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

不需要手动引入 js、css 文件,只管写 html 内容,当启动服务会自动插件对应的 js、css,也就是 html 模板中 inject 注释的作用。

如果不需要引入某部分的 js、css 文件,去掉对应的 inject 注释即可。

npm run dev 后访问页面例如:

http://localhost:2080/ ==> /pages/index.html http://localhost:2080/page1 ==> /pages/page1/index.html

# 页面独立的 js 文件的依赖关系

由于 inject 无法自动确认 js 的顺序,所以同一级目录的 js 不应该存在依赖关系,如下 a.js 和 b.js 不能有互相依赖关系。

或者通过对 js 文件名称的排序来确定依赖,例:a.js 排在前面,b.js 排在后面

├── /src/  # 项目开发源码目录
│ ├── /pages/ # 页面目录
│ │ ├── page1 # 页面1
│ │ │   ├── a.js # 当前页面js
│ │ │   ├── b.js # 当前页面js
│ │ │   └── index.html
1
2
3
4
5
6

跨目录的 js,目录深的.js 排前面,即始终 page1/b.js 会在 page1/deep/a.js 后面的,可以通过目录深浅形成依赖关系

├── /src/  # 项目开发源码目录
│ ├── /pages/ # 页面目录
│ │ ├── page1 # 页面1
│ │ │   ├── deep #
│ │ │   ├── ├── a.js # 当前页面js
│ │ │   ├── b.js # 当前页面js,可以依赖deep/a.js
│ │ │   └── index.html
1
2
3
4
5
6
7

# JS 作用域规范

这里无法使用任何的模块系统,所以一个 js 尽量暴露少的全局变量,js 内应该用闭包隔离作用域,通过 layui.define 模块声明,或者 layui.use 调用模块,或者 window 挂载少量的全局变量

├── /src/  # 项目开发源码目录
│ ├── /pages/ # 页面目录
│ │ ├── page1 # 页面1
│ │ │   ├── A-commonMethods.js # 当前页面js
│ │ │   ├── index.js # 当前页面js
│ │ │   └── index.html
1
2
3
4
5
6
// A-commonMethods.js
layui.define(['main.api'], function (exports) {
    var mainApi = layui['main.api'];
    exports('commonMethods', {
        getData: function () {
            return mainApi.getAllDict();
        },
    });
});

// (function (window) {
//     var a = '666';
//     function getNname() {
//         return a;
//     }
//     function setName(arg) {
//         a = arg;
//     }
//     // 挂载全局变量
//     window.commonMethods = {
//         getNname: getNname,
//         setName: setName
//     };
// })(window);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// index.js
layui.use(['form', 'commonMethods'], function () {
    var commonMethods = layui['commonMethods'];
    commonMethods.getData().then(function () {});
});
// (function (window) {
//     // 调用commonMethods.js暴露的方法
//     var a = window.commonMethods.getName();
// })(window);
1
2
3
4
5
6
7
8
9

# components 组件

components 目录无需关心文件顺序,支持按需加载到页面:

│ ├── /components/ # 此工程支持按需加载的组件目录,遵循layui模块规范
│ │ ├── Api # 把后端api接口管理成模块
│ │ │   └── main.api.js # 其中一个接口api模块案例
│ │ ├── buttonShow # 一个组件目录案例,目录名称其实不重要,为了直观,最好也跟模块名称对应
│ │ │   ├── buttonShow.scss # 当前组件对应的scss,文件名必须跟模块名称对应,与模块同名就可以按需加载编译后的.css,为了避免样式类名冲突,请规范类名只应用于当前组件
│ │ │   ├── buttonShow.js # 不具备html模板写法的layui模块,文件名必须跟模块名称对应,不可以同时存在 buttonShow.js 和  buttonShow.html
│ │ │   └── buttonShow.html # 具备html模板写法的layui模块,方便使用layui的模板引擎,文件名必须跟模块名称对应,不可以同时存在 buttonShow.js 和  buttonShow.html
1
2
3
4
5
6
7

# layui.define 与 exports

一个组件(.js 或者.html)至少要有一个 layui.define ,和 exports("对应文件名",{});

# html 模板块

使用 <script id="demo" type="text/html"></script> 包裹,必须要有 id 和 type ,id 不会在实际渲染中存在,主要用于构建编译组件时,在 js 代码中 'html(demo)' 获取这个模板

案例: buttonShow.html

<script id="demo" type="text/html">
    <div class="button-show-wrapper layui-btn-container">
        <button type="button" class="layui-btn layui-btn-primary">
            {{ d.data.primaryName }}
        </button>
    </div>
</script>
<script>
    layui.define(['laytpl', 'jquery', 'layer'], function (exports) {
        var laytpl = layui.laytpl;
        var layer = layui.layer;
        var $ = layui.jquery;

        var btnClick = function () {
            layer.msg('弹窗', {
                offset: 'rt',
            });
        };
        var templates = {
            //  id="demo" type="text/html" 对应的 'html(demo)'
            demo: 'html(demo)',
        };
        exports('buttonShow', {
            templates: templates,
            /**
             *
             * @param {object} options
             * @param {jquery|string} options.el html容器, jquery对象或者css选择器
             * @param {object} options.data 渲染数据
             * @returns
             */
            render: function (options) {
                var elIsJquery = options.el instanceof $;
                if (!elIsJquery && typeof options.el !== 'string') {
                    throw Error('el must be a jquery object or css selector');
                }
                var $el = elIsJquery ? options.el : $(options.el);
                // 通过事件委托给按钮绑定事件,重复调用render就会重复绑定,这里要先移除,再绑定
                $el.off('click', '.layui-btn', btnClick);
                $el.on('click', '.layui-btn', btnClick);
                // 编译成html
                var html = laytpl(templates['demo']).render({
                    data: $.extend(
                        {
                            primaryName: '原始按钮',
                        },
                        options.data || {}
                    ),
                });
                $el.html(html);
            },
        });
    });
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 写一个组件

根据以上基础能力,每个组件应该有相同的特性,在 yb-layui-extend 提供了一个"ybCreate"模块,可以声明式的定制我们的组件

案例:ybButton.html

<script id="demo" type="text/html">
    <button
        type="button"
        class="layui-btn layui-btn-{{d.type}} {{d.className}}"
        style="{{d.style}}"
    >
        {{# if(d.iconType&&d.iconPosition==="before"){ }}
        <i class="layui-icon {{d.iconType}}"></i>
        {{# } }} {{- d.name||d.content }} {{#
        if(d.iconType&&d.iconPosition==="after"){ }}
        <i class="layui-icon {{d.iconType}}"></i>
        {{# } }}
    </button>
</script>
<script>
    layui.define(['laytpl', 'jquery', 'layer', 'ybCreate'], function (exports) {
        var laytpl = layui.laytpl;
        var ybCreate = layui.ybCreate;
        var $ = layui.jquery;
        var templates = {
            demo: 'html(demo)',
        };
        var createConfig = {
            // 组件名称,与文件名对应即可
            name: 'ybButton',
            // 初始化组件属性
            data: function () {
                return {
                    // 定义按钮名称
                    name: '',
                    // 定义按钮样式类型, 'primary'||'normal'||'warm'||'danger'
                    type: 'default',
                    // 按钮的点击事件的回调方法
                    onClick: null,
                    // icon 位置,'before'||'after'
                    iconPosition: 'before',
                    // layui的小图标名称,为空则不显示小图标
                    iconType: '',
                    // 是否禁用
                    disabled: false,
                };
            },
            // 实例创建时
            created: function (opt) {
                console.log(opt);
            },
            // 渲染视图,创建实例后和setData,会调用render
            render: function () {
                // 编辑模板后得到html,用 this.data 获取最新的组件属性
                var props = $.extend({}, this.data);
                var disabled = this.data.disabled;
                if (disabled) {
                    props.type = 'disabled';
                }
                var html = laytpl(templates['demo']).render(props);
                return html;
            },
            // render之后进行新dom挂载,执行mounted一次
            mounted: function () {
                this.bindClick();
            },
            // setData 会调用 updated
            updated: function () {
                this.bindClick();
            },
            // 重复调用render,意味着原html将被替换,绑定的事件之类需要释放,所以这里调用render之前也会自动this.destroy(),进入 destroyed 回调
            destroyed: function () {
                // 解绑事件
                if (
                    !this.data.disabled &&
                    typeof this.data.onClick === 'function'
                ) {
                    this.$el.off('click', this.data.onClick);
                }
            },
            methods: {
                bindClick: function () {
                    // 绑定按钮事件
                    if (
                        !this.data.disabled &&
                        typeof this.data.onClick === 'function'
                    ) {
                        this.$el.on('click', this.data.onClick);
                    }
                },
            },
        };
        exports(createConfig.name, {
            // 当前组件所有模板
            templates: templates,
            // 组件的初始化方法
            create: ybCreate.getComponentCreate(createConfig),
        });
    });
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

在另外组件中使用 ybButton

<script id="demo" type="text/html">
    <div>
        <div button-key="button1"></div>
        <div button-key="button2"></div>
    </div>
</script>
<script>
    layui.define(
        ['laytpl', 'jquery', 'layer', 'ybCreate', 'ybButton'],
        function (exports) {
            var laytpl = layui.laytpl;
            var ybCreate = layui.ybCreate;
            // var $ = layui.jquery;
            // var ybButton = layui.ybButton;
            var templates = {
                demo: 'html(demo)',
            };
            var createConfig = {
                name: 'exampleButtonUse',
                // 初始化组件属性
                data: function () {
                    return {};
                },
                // 实例创建时
                created: function (opt) {
                    console.log(opt);
                },
                // 渲染视图,创建实例后和setData,会调用render
                render: function () {
                    var that = this;
                    // 编辑模板后得到html,this.data 获取最新的组件属性
                    var html = laytpl(templates['demo']).render(that.data);
                    return html;
                },
                mounted: function () {
                    var that = this;
                    // 按钮组件的实例存到 this.childComponents[alias], 如果没有 alias ,就是 this.childComponents['[button-key="button1"]']
                    // this.destroy()会对this.childComponents的实例逐个调用 this.childComponents[alias].destroy()
                    this.execChildCreate({
                        componentName: 'ybButton',
                        selector: '[button-key="button1"]',
                        alias: 'button1',
                        data: {
                            name: '第一个按钮',
                            iconType: 'layui-icon-addition',
                            // 点击后,修改第二个按钮的名称,使用组件实例的 setData 方法
                            onClick: that.buttonClick1,
                        },
                    });
                    this.execChildCreate({
                        componentName: 'ybButton',
                        selector: '[button-key="button2"]',
                        alias: 'button2',
                        data: {
                            name: '第二个按钮',
                            iconType: 'layui-icon-addition',
                            // 点击后,修改第一个按钮的名称,使用组件实例的 setData 方法
                            onClick: that.buttonClick2,
                        },
                    });
                },
                // 更新后的回调,setData之后
                // updated:function(){},

                // 销毁一些事件和子组件的事件和内容等,this.destroy()会触发回调
                destroyed: function () {
                    // var that = this;
                },

                methods: {
                    buttonClick1: function () {
                        this.childComponents['button2'].setData({
                            name: '修改后第二个按钮名称',
                        });
                    },
                    buttonClick2: function () {
                        this.childComponents['button1'].setData({
                            name: '修改后第一个按钮名称',
                        });
                    },
                },
            };
            exports(createConfig.name, {
                // 当前组件所有模板
                templates: templates,
                // 组件的初始化方法
                create: ybCreate.getComponentCreate(createConfig),
            });
        }
    );
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

组件内部可用的东西:

# this.data

类型:object

最新的组件属性

# this.execChildCreate

类型:function

执行子组件的创建

this.execChildCreate({
    // 组件名称,必填
    componentName: 'ybButton',
    // 容器的选择器,必填
    selector: '[button-key="button2"]',
    // 别名,选填,用于 this.childComponents[alias] 获取子组件的实例,如果没有alias,就是this.childComponents[selector]
    alias: 'button2',
    // 组件create方法的其他初始属性
    data: {
        name: '第二个按钮',
        iconType: 'layui-icon-addition',
        // 点击后,修改第一个按钮的名称,使用组件实例的 setData 方法
        onClick: that.buttonClick2,
    },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# this.childComponents

类型:object

所有子组件的实例,凡是调用 this.execChildCreate 后存储的,用法:this.childComponents[alias||selector]

this.childComponents['button2'].setData({
    name: '新按钮名称',
});
1
2
3

# this.childSelectors

类型:object

所有子组件容器的选择器,凡是调用 this.execChildCreate 后存储的,用法:this.childSelectors[alias||selector]

console.log(this.childSelectors['button2']);
// '[button-key="button2"]'
1
2
上次更新: 2023/05/15, 20:09:55
vue组件包开发模板

← vue组件包开发模板

Theme by Vdoing | Copyright © 2021-2025 YB-GZ | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式