Skip to content

allskystar/ECUI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

初始 ECUI

什么是 ECUI

ECUI 是以 js es5、css 语法为基础,基于面向对象理念设计的一套企业级前端开发语言。

ECUI 的特点与优势

  • 支持一套代码多平台发布,开发者编写一套代码,可发布到iOS、Android、Web(响应式)等多个平台;
  • 控件对象化处理,支持对象封装、继承、接口等抽象模式,既保证了较高的扩展性,同时使得对象间解耦,减少多人开发导致的控件冲突等问题;
  • 支持单页应用 开发,支持 ES6+ 语法。(打包工具已采用 uglify-es )
  • 提供了 pc 和 移动端 开发 常用控件(自带默认样式);
  • 通过src/esr.js来统一管理路由;
  • 通过src/core.js来对事件与状态进行统一管理;
  • 通过src/etpl.js 来实现模板渲染;
  • 通过src/adapter.js 来兼容第三方库;
  • 通过src/control 来扩展原生 DOM 节点的标准事件;

快速上手

这里 只介绍在mac环境下 如何快速 从0搭建一个 ecui 项目,并着手进行日常开发。

环境准备

  • node/v9.3.0 以上版本 请用户自行百度安装
  • npm/6.1.0 以上版本 请用户自行百度安装
  • nginx 请用户自行百度安装

构建依赖

前置假定 node 已安装好,从环境中可以找到node (无论是位于 /usr/local/bin/node 还是PATH中能找到)

 >>> npm install [email protected] -g     ### js 代码检测器 node-jslint version: 0.12.0  JSLint edition 2013-08-26; 稍后需用欧阳改造后的 lib/jslint.js
 >>> npm install [email protected] -g    ### css 检测器
 >>> npm install [email protected] -g       ### css 语法高亮 
 >>> npm install [email protected] -g   ### 高效率压缩 js 文件 ,打包工具,参见 https://github.com/mishoo/UglifyJS2/tree/harmony

安装完jslint后,找到安装路径(mac下默认安装路径:/usr/local/lib/node_modules),使用ECUI项目下的ECUI/tools/jslint.js替换掉安装的jslint中lib目录下的jslint.js文件( /usr/local/lib/node_modules/jslint/lib/jslint.js)

nginx配置

若您的项目不直接在 ECUI 框架根目录下,则需要配置 nginx ,以便您的本地项目可以访问到 ECUI 控件库。 nginx 安装成功以后,在 mac 中打开终端, 执行以下命令:

// 打开 nginx.conf 配置文件后,参照 附录 中的 nginx配置示例, 修改 框架和项目 root 路径和文件名即可。
open /usr/local/etc/nginx/nginx.conf
// 第一次启动,需要root 权限,输入密码
sudo nginx
// 若是启动不成功,则需要多次需改nginx.conf,使用如下命令 重启
sudo nginx -s reload

获取 框架源码

建议先创建一个工作目录,把框架源码和项目代码 都放在该目录下:

// 创建并进入该目录
mkdir work && cd work
// 下载源码
git clone https://github.com/allskystar/ECUI.git

本地生成文档

cd ECUI 在ECUI项目目录下执行 java -jar ecui-doc.jar

创建 项目目录

您可采用以下几种方式,完成您的项目创建

  • 使用脚本 :使用 ECUI下的 generator-ecui.sh 脚本创建项目(脚本长久未维护,可能存在问题)
  • 解压 ECUI下的 ecui-demo.zip 文件,作为您的初试项目
  • 手动按照框架对目录和文件命名的要求一一创建:
  • 您也可以在您已有项目中引用 ECUI 框架

使用脚本 创建 项目目录

打开终端,在 work目录下执行以下命令:

// 初始化项目并且创建路由 demo.list 和 demo.detail,并且在index中添加路由的链接
./ECUI/generator-ecui.sh  -i -r demo.list,demo.detail

手动搭建

在 work 按照以下目录层次创建对应文件,并按照实例 引入 ecui 相关文件:

demo(项目目录)
           |
           |_ _ _
                |_ _ _ _  helloworld
                |       |
                |       |_ _ _ _  helloworld.js
                |       |
                |       |_ _ _ _  route.helloworld.demo.html
                |       |
                |       |_ _ _ _  route.helloworld.demo.js
                |       |
                |       |_ _ _ _  route.helloworld.demo.css
                |
                |_ _ _ _  index.html
                |
                |_ _ _ _  index.js
                |
                |_ _ _ _  index.css

HTML引入 ECUI 示例

    <link rel="stylesheet/less" type="text/css" href="ecui.css" />
	<link rel="stylesheet/less" type="text/css" href="index.css">
    <script type="text/javascript" src="options.js"></script>
    <script type="text/javascript" src="ecui.js"></script>
    <script type="text/javascript" src="index.js"></script>

打开浏览器进行访问

根据 nginx.conf 配置的端口号和项目名称,然后在浏览器中输入,如:

http://localhost:9006

开始开发

使用您喜欢的编辑器 去打开 项目和框架目录即可,有时需要去 看一下框架 源码,这样避免在 业务代码中 做 重复的实现。对于pc和移动端开发的不同,请查看有关教程实例或者联系框架开发者!

ECUI 的基本语法

ECUI 的目录结构

请参考下图(DEMO 目录结构):

demo(项目目录)
           |
           |_ _ _
                |_ _ _  demo(文件夹) ------------  demo 目录
                |     |
                |     |_ _ _  _define_.js  --------  demo 目录下全局js,可用于路由加载
                |     |
                |     |_ _ _  _define_.css  -------  demo 目录下全局css
                |     |
                |     |_ _ _  route.demo.html  ---  demo 页面
                |     |
                |     |_ _ _  route.demo.js  -----  demo 页面相应的js文件
                |     |
                |     |_ _ _  route.demo.css  ----  demo 页面相应的css样式
                |     | 
                |     |_ _ _ _ _ _ childdemo(文件夹)  ------------  childdemo子目录
                |                  |               
                |                  |_ _ _ _define_.js  -------------  demo子目录js
                |                  |               
                |                  |_ _ _ _define_.css  ------------  demo子目录css
                |                  |               
                |                  |_ _ _ route.childdemo.html
                |                  |               
                |                  |_ _ _ route.childdemo.js
                |                  |               
                |                  |_ _ _ route.childdemo.css                
                |
                |_ _ _  index.html --------------------- 首页 html
                |
                |_ _ _  index.js   --------------------- 首页对应的 js 文件,全局适用
                |
                |_ _ _  index.css  --------------------- 首页相应的 css 样式,全局适用

注意事项

  • xxx.html、index.js、index.css 为该 xxx 页面的一组文件,请保持命名规范
  • define.js 为必要文件,框架根据该文件进行路由扫描,请勿更改文件名
  • childdemo 下页面问 demo 的子路由,同级路由写在同一文件夹下即可

路由管理

ECUI 支持路由与子路由操作,程序按照注册路由对应的目录结构实现路由模式。 ECUI 的路由在每次跳转(也即 url hash变化)时,计算当前路由以及目标路由。与 vue 框架不同的是,vue 仅支持页面级别的缓存,如果同一个页面复用,后面的缓存就会覆盖前者。而 ECUI 通过hash处理,每个页面均为独立的新路由,不会导致定义的复用页面的数据丢失或错乱。举个典型的例子,从列表访问详情页,详情页再打开列表,数次循环。 VUE 框架中,往往这时候 back 到第一个列表页时,当时页面填写的筛选数据等数据就丢失了。而 ECUI 每 i 次打开的列表页为 xxxx~ HISTORY=i , 这个机制可以保存所有页面的缓存数据。 基于上述底层逻辑,ECUI 提供了自动缓存、回填的接口,无需任何额外开发,便可完成。

路由注册

如 demo 中所示,路由的注册仅需以下三步:

  • 步骤一:在 xxx.js ( demo 中为 route.demo.js ) 中调用 ecui.esr.addRoute() 方法添加路由。

addRoute 方法代码介绍

        /**
         * 添加路由信息。
         * @public
         *
         * @param {string} name 路由名称
         * @param {object} route 路由对象
         */
        addRoute: function (name, route) {}
route 对象关键参数介绍
  • route.model : xxx
  • route.main : 页面所属容器
  • route.view : 页面所属试图
  • route.onbeforerequest : 页面发生请求前事件、方法集合
  • route.onbeforerender : 页面加载前事件、方法集合
  • route.onafterrender : 页面加载后事件、方法集合

addRoute 调用示例

ecui.esr.addRoute('demo', {
    model: [''],
    main: 'main',
    view: 'demo_demo',
    onbeforerequest: function (context) {
    },
    onbeforerender: function (context) {
    },
    onafterrender: function () {
        // ecui.get('button_demo').onclick = function () {
        //     ecui.esr.redirect('/demo/childdemo');
        // };
    },
});

  • 步骤二:在 xxx.html 文件头,加上 “<!-- target: 视图名 -->”,* *视图名务必与第一步注册路由时,route.view 定义的视图名一致**,如下:
<!-- target: demo_childdemo -->
<div class="assess-demo-content">
    <a href="#/demo/demo">go-demo</a>
</div>
  • 步骤三:在同级目录的 define.js 中调用 ecui.esr.loadRoute() 方法 加载该路由,代码如下:
ecui.esr.loadRoute('childdemo');

子路由

在 ECUI 框架中,子文件夹内注册的路由,自动视为子路由。逐级延伸。

路由跳转

您可以通过 a 标签跳转页面,或者调用 ecui.esr.redirect() 方法完成路由跳转。

a标签跳转路由代码示例:

    <a href="#/demo/childdemo/childdemo">go-childdemo</a>

redirect 跳转路由代码示例:

    ecui.get('button_demo').onclick = function () {
        ecui.esr.redirect('/demo/childdemo/childdemo');//路由构成为 文件夹路径+路由名
    };

控件的定义和继承

ECUI通过core.js实现继承和接口处理能力。 调用 core.js 的 inherits()方法定义控件对象,并指定其继承对象。 子类可以直接拥有父类的所有方法,以及可以使用父类定义的全局变量。要想重写父类方法,只需在声明子类时,在“子控件的构造函数” constructor 参数中定义即可。子类若想调用父类的方法,可通过 super.xxx() 进行调用。

inherits 方法代码介绍

        /**
         * 控件继承。
         * 如果不指定类型样式,表示使用父控件的类型样式,如果指定的类型样式以 * 符号开头,表示移除父控件的类型样式并以之后的类型样式代替。生成的子类构造函数已经使用了 constructor/TYPES/CLASS 三个属性,TYPES 属性是控件的全部类型样式,CLASS 属性是控件的全部类型样式字符串。
         * @public
         *
         * @param {function} superClass 父控件类
         * @param {boolean} singleton 是否单例
         * @param {string} type 子控件的类型样式
         * @param {function} constructor 子控件的标准构造函数,如果忽略将直接调用父控件类的构造函数
         * @param {object} ... 控件扩展的方法
         * @return {function} 新控件的构造函数
         */
        inherits: function (superClass, singleton, type, constructor) {}

控件的定义代码示例一

ui.Control 控件

    /**
     * 基础控件。
     * 基础控件 与 ECUI状态与事件控制器 共同构成 ECUI核心。基础控件扩展了原生 DOM 节点的标准事件,提供对控件基础属性的操作,是所有控件实现的基础。
     * options 属性:
     * id          名称,指定后可以使用 ecui.get([id]) 的方式获取控件
     * uid         唯一标识符,不可自行定义,系统自动生成
     * primary     主元素需要绑定的样式
     * capturable  是否接收交互事件,如果设置不接收交互事件,交互事件由控件的父控件处理,缺省值为 true
     * disabled    是否失效,如果设置失效,控件忽略所有事件,缺省值为 false
     * focusable   是否允许获取焦点,如果设置不允许获取焦点,控件的交互事件不会改变当前拥有焦点的控件,用于自定义滚动条,缺省值为 true
     * userSelect  是否允许选中内容,缺省值为 true
     * @control
     */
    ui.Control = core.inherits(
        null, //Control 是所有控件对象的父类,它没有父类对象
        function (el, options) {
            this._eMain = this._eBody = el;
            if (options.primary) {
                el.className = (options.id || '') + ' ' + el.className + options.primary;
            }
            // svg classname 是数组 不能做trim操作
            if (typeof el.className === 'string') {
                this._sClass = el.classList[0];
            }
            this._bDisabled = !!options.disabled;
            this._bCapturable = options.capturable !== false;
            this._bUserSelect = options.userSelect !== false;
            this._bFocusable = options.focusable;
            this._bGesture = true;
            this._sSubType = '';
            this._aStatus = ['', ' '];
            this._sWidth = el.style.width;
            this._sHeight = el.style.height;
            this._hResize = util.blank;
            if (!options.main) {
                this._UIControl_oHandler = unitReadyHandler.bind(this);
            }
        },
        {} //此处为 Control 组件定义的事件触发,因代码过长,不在此处展示。
    );

控件的定义代码示例二

Title控件

    /**
     * 标题栏部件。
     * 继承自 ui.Control(所有控件均为 ui.Control 的子类)
     * 
     * @unit
     */
    Title: core.inherits( //通过core.inherits() 实现空间继承
        ui.Control,
        {
            /**
             * 标题栏激活时触发拖动,如果当前窗体未得到焦点则得到焦点。
             * @override
             */
            $activate: function (event) {
                _super.$activate(event);
                core.drag(this.getParent(), event);
            }
        }
    )

接口

ECUI中定义的接口,是指抽象出对象的共有行为(也就是抽象出各种方法),在对象定义的时候,它实现了哪些接口,相当于直接拥有了这些接口的方法。以此实现控件方法的横向扩展,减少重复造轮子的同时,便于一次维护,多方受用。 控件扩展的方法遵循后声明覆盖前者标准,即,相同方法,仅后声明的生效。

接口声明

调用 core.js 的 interfaces()方法声明接口

interfaces 方法代码介绍

    /**
     * 接口声明。
     * @public
     *
     * @param {string} name 接口名称
     * @param {Array} superClass 接口的基类的数组
     * @param {object} methods 接口的方法集合
     * @param {function} interceptor 拦截器
     * @return {Interface} 接口定义
     */
    interfaces: function (name, superClass, methods, interceptor) {

接口实现

接口某种意义上,就是控件的扩展方法。所以,在定义控件的inherits()方法,将接口传入“控件扩展的方法”即可实现该接口。同时,可以通过继续传入自定义方法,来实现接口方法的重写。 可使用 _class.xxx() 调用接口链上之前接口里的方法。

接口实现代码示例

    /**
     * 下拉框控件。
     * 扩展了原生 SelectElement 的功能,允许指定下拉选项框的最大选项数量,在屏幕显示不下的时候,会自动显示在下拉框的上方。在没有选项时,下拉选项框有一个选项的高度。下拉框控件允许使用键盘与滚轮操作,在下拉选项框打开时,可以通过回车键或鼠标点击选择,上下键选择选项的当前条目,在关闭下拉选项框后,只要拥有焦点,就可以通过滚轮上下选择选项。
     * options 属性:
     * optionSize     下拉框最大允许显示的选项数量,默认为5
     * required       是否必须选择
     * @control
     */
    ui.Select = core.inherits(
        ui.abstractSelect,
        'ui-select',
        function (el, options) {
            _super(el, options);
            // 初始化下拉区域最多显示的选项数量
            this._nOptionSize = options.optionSize || 10;
        },
        {},
        ui.iPopup, ui.iResource,//这里实现了 ui.iPopup、ui.iResource 两个接口
        {}//这里可以传入方法重写ui.iPopup的方法。同时可以自定义控件方法
    );

私有变量

通过在属性名前加 下划线“_”(如 _name),可将该属性定义为私有变量,私有变量仅改类可以访问,其子类无法访问。因此,为避免程序异常,请勿在该类方法之外的地方引用私有变量。 顺带一提,框架通过以下两种方式实现变量的绝对私有,保障私有变量隔离:

  • 调试过程中,框架提供了pushCaller与popCaller方法检查私有化属性
  • 打包编译后,私有化属性会重新命名

全局变量

将变量命名以_ECUI_开头,即可认定为全局变量,全局变量可作用于该对象及其所有子类访问。

附录

nginx配置示例

请注意,不要把 filepath 、root 之类的目录位置原封不动的照抄! 根据你实际情况来填写

该配置示例要求 框架和业务项目的目录 在同一目录下,主要更改以下地方:

  • 将 root 后面的路径 改为 您电脑上 ecui 框架或业务项目 的对应目录;

nginx 配置时小技巧

请各位同事在研发态 给本机 nginx 静态资源的 location 段加一下 Cache-Control 控制,研发时,一些 css html jpg 废不了太多流量

location ~ /你的路径规则   {
  add_header Cache-Control 'no-store,no-cache,must-revalidate';
  add_header xx-via   'developer-你的名' ;   ## 这样可以明确的从http响应头 感知到 静态资源来自你机器
  ……
}

下方是一个简易的配置文件

#控制工作进程数
worker_processes  1;
error_log  /tmp/nginx_error.log  notice;
#包含nginx中所有处理连接的设置
events {
  #工作进程的最大连接数量
  worker_connections  1024;
}


#控制 nginx http处理的所有核心特性
http {
    #该文件内定义指定文件头所对应的文件格式
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    #指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on。
    #如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度,降低系统负载
    sendfile        on;

    #keepalive_timeout 参数是一个请求完成之后还要保持连接多久,不是请求时间多久,
    #目的是保持长连接,减少创建连接过程给系统带来的性能损耗,类似于线程池,数据库连接池。
    keepalive_timeout  65;

    #本地开发时用于请求项目的静态文件,需要在hosts中配置 127.0.0.1 为localhost(Mac一般默认配置)
    #否则通过127.0.0.1加 文件路径进行访问

    server {
        listen       9006;      # 一般习惯设置80,但很多开发者 本机80端口常被占
        server_name  localhost; # 请确认是本机,最好解析为127 以免走入 ipv6型回路
        #配置默认请求为 ecui-demo 的静态资源
        set $root '/Users/chentiancheng/IdeaProjects/ecui-demo';
        set $ecui_root '/Users/chentiancheng/IdeaProjects/ECUI';
        root   $root; #项目所在的路径
        index  index.html index.htm;#默认请求的文件

        # ECUI文件本地调试
        location ~ ^/(ie-es5\.|ecui\.|compatible-2\.0\.0|options\.|common\.|update\.|common/|tools/|css/|src/|images/ecui/).*$ {
            if ( !-f $document_root$uri ) {
                  proxy_pass http://localhost:8000;
            }
            root $root;
            index index.html index.htm;
        }

        location ~ /(_layer_\.js|_define_\.(css|html)|layer\.[^/]+\.(js|css|html))$ {
            if ( !-f $document_root$uri ) {
                # default_type text/html;
                add_header Content-Type 'text/html; charset=utf-8';
                return 200 '';
            }
            root $root;
        }

        # 打包后工程访问
        location ~ ^/output-.*/[/\w-]+$ {
            rewrite /output-(.*) /$1;
        }

        location = / {
            root   $root;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }

    #控制开发环境中用于请求的ecui有关文件
    server {
        listen       8000;

        set $root '/Users/chentiancheng/IdeaProjects/ecui-demo';
        set $ecui_root '/Users/chentiancheng/IdeaProjects/ECUI';

        location / {
            # 引入文件所在的路径
            root   $ecui_root;
        }
    }

}

About

企业级Web UI控件库

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published