Giter VIP home page Giter VIP logo

ext's People

Contributors

glimis avatar

Watchers

 avatar  avatar

ext's Issues

Button结构

component

Ext.Component所有Ext组件的基类

组件主要用来封装逻辑与结构,逻辑有很多种,如果是通用样式逻辑(setHeight,setWidth),那就是所有Component的公共方法,如果是业务逻辑,我们也有更好的方式去处理,总之,封装组件,在组件中描述的大多数代码,主要是为了描述结构...想象一下,最基础的组件,应该默认怎样的结构?

component的结构

//js
Ext.create("Ext.Component", {
    renderTo: "test"
})
//html
<div id="test"></div>

运行以上代码,或许什么也看不见,没关系,通过源码查询,我们可以看到以下内容

//页面效果
<div id="test">
    <div class="x-component  x-component-default x-border-box" id="box-1009"></div>
</div>

以上内容就是Ext.Component所代表的结构,需要注意,renderTo只是将组件结构至于选择节点之中,而非使用替换,这相当重要,这意味着renderTo:Ext.getBody()不一定会显示出来,这跟其原来的结构有关(此后,若非必要,就不再贴最外层的结构),现在,我们可以简单的认为,Ext.Component就是一个div的封装(记得带上class)

autoEl

一个标签名或者DomHelper描述,用来创建Element,它将封装当前组件

autoEl属性具有修改Ext.Component结构的能力,比如

Ext.create("Ext.Component", {
    renderTo: "test",
    autoEl:{
         tag: 'p'
    }
})
<p class="x-component  x-component-default x-border-box" id="box-1009"></p>

或许Component不一定代表一个div。。。我们应该做一个简单的测试

Ext.create("Ext.Component", {
    renderTo: "test",
    autoEl:{
         tag: 'abc',
         html:'123'
    }
})
<abc class="x-component  x-component-default x-border-box" id="box-1009">123</abc>

答案已经很明显了,Ext.Component代表一个组件,其组件结构默认为一个标签(autoEl定义),该标签又默认为div,更复杂的情况我们可以配合tpl/data等方式去描述其结构(查看文章...),换句话说

  • renderTo:第一层结构,用于选择
  • autoEl:组件最外层标签
  • html,tpl/data等:标签内部结构

Button

Button是继承Component的一个具体的组件

作为组件(例子)来说,Button不算难,却又包含定义组件所有特点

Btuuon结构

创建组件,以观察Button结构

var btn=Ext.create("Ext.button.Button", {
    renderTo: "test",
})
<div id="test">
    <a class="x-btn x-unselectable x-btn-default-small x-border-box" hidefocus="on" unselectable="on" id="button-1009" tabindex="0" componentid="button-1009">
        <span id="button-1009-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="" class="x-btn-wrap x-btn-wrap-default-small ">
            <span id="button-1009-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="" class="x-btn-button x-btn-button-default-small  x-btn-no-text   x-btn-button-center ">
                <span id="button-1009-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="x-btn-icon-el x-btn-icon-el-default-small  " style=""></span>
                <span id="button-1009-btnInnerEl" data-ref="btnInnerEl" role="presentation" unselectable="on" class="x-btn-inner x-btn-inner-default-small">&nbsp;</span>
            </span>
        </span>
    </a>
</div>

第一层<div id="test">用于渲染和查询,无视之(以后真的不展示了-.-)
第二层<a class="x-btn ...">通过autoEl,修改的,标签层
第三层通过html/tpl等方式描述的内部结构层

这内部结构乍一看跟乱码一样,完全无从下手,没关系,慢慢分析

data-ref属性

我们曾经使用过childEls/renderSelectors的方法配合创建组件的子元素,没错这里也是,不出意外的话,我们可以通过btn.btnWrap等方式,去访问指定的子元素(Dom),此处无视role="presentation"

var btn=Ext.create("Ext.button.Button", {
    renderTo: "test",
})
console.log(btn.btnWrap);
console.log(btn.btnEl);
console.log(btn.btnIconEl);
console.log(btn.btnInnerEl);

此时,如果我们知道一点Ext.dom.Element的api,那对这四个节点的操作就是探囊取物(成语乱入,自行体会)

有没有感觉发现了新世界?不要太激动,我们先看一下html的经典结构

html经典结构

<div class="box">
  <div class="head"></div>
  <div class="body"></div>
</div>

这段包含了头部和主体的html模板,非常经典,经典到Ext延用了这套结构,比如btnEl与btnIconEl/btnInnerEl的关系,我无法用文字去描述,自行领悟吧

组件逻辑与结构

现在在来观察下这四个结构的名字,以此为突破口

btnWrap:整体结构,除了btnEl可能还会有其他结构
btnEl:按钮结构
btnIconEl:一定与图片相关
btnInnerEl:一定与文本内容相关

在查看一下api,比如
text:按钮中文字
icon:按钮中图标路经
我们是否可以通过那4个dom对象实现以上功能呢?

先按照api实现

var btn=Ext.create("Ext.button.Button", {
    renderTo: "test",
    text:'按钮',
    icon:'xxx.icon'
})

观察结构

<a class="x-btn x-unselectable x-btn-default-small x-border-box" hidefocus="on" unselectable="on" id="button-1009" tabindex="0" componentid="button-1009">
    <span id="button-1009-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="" class="x-btn-wrap x-btn-wrap-default-small ">
        <span id="button-1009-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="" class="x-btn-button x-btn-button-default-small x-btn-text  x-btn-icon x-btn-icon-left x-btn-button-center ">
            <span id="button-1009-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="x-btn-icon-el x-btn-icon-el-default-small  " style="background-image:url(xxx.icon);">&nbsp;</span>
            <span id="button-1009-btnInnerEl" data-ref="btnInnerEl" role="presentation" unselectable="on" class="x-btn-inner x-btn-inner-default-small">按钮</span>
        </span>
    </span>
</a>

我们发现btnInnerEl的内部增加了文本, btnIconEl的样式增加了background-image:url(xxx.icon);
很好,结合文档,查看api

var btn=Ext.create("Ext.button.Button", {
    renderTo: "test",
})
btn.btnInnerEl.setHtml('按钮');
btn.btnIconEl.setStyle("background-image","url(xxx.icon)")

看起来结果是一样的,也许我们看见了组件的内涵
没错,组件关注结构(此处指dom),其内部方法对应不同的结构(dom)的变化,data-ref是子元素(dom)的映射,用于快速获取相关的dom,现在,我们可以这样认为

Ext组件,属性用于初始化结构,方法用于修改结构

我们可以通过文档提供的api,去感受其定义的节点的区别,当然,也可以直接看源码-.-

Button源码

看html结构可以知其然,看源码完全可以知其所以然

源码二千行...嗯,注释很详细

    renderTpl:
        '<span id="{id}-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="{btnWrapStyle}" ' +
                'class="{btnWrapCls} {btnWrapCls}-{ui} {splitCls}{childElCls}">' +
            '<span id="{id}-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="{btnElStyle}" ' +
                    'class="{btnCls} {btnCls}-{ui} {textCls} {noTextCls} {hasIconCls} ' +
                    '{iconAlignCls} {textAlignCls} {btnElAutoHeightCls}{childElCls}">' +
                '<tpl if="iconBeforeText">{[values.$comp.renderIcon(values)]}</tpl>' +
                '<span id="{id}-btnInnerEl" data-ref="btnInnerEl" role="presentation" unselectable="on" ' +
                    'class="{innerCls} {innerCls}-{ui}{childElCls}">{text}</span>' +
                '<tpl if="!iconBeforeText">{[values.$comp.renderIcon(values)]}</tpl>' +
            '</span>' +
        '</span>' +
        '{[values.$comp.getAfterMarkup ? values.$comp.getAfterMarkup(values) : ""]}' +
        // if "closable" (tab) add a close element icon
        '<tpl if="closable">' +
            '<span id="{id}-closeEl" data-ref="closeEl" role="presentation"' +
                ' class="{baseCls}-close-btn"' +
                '<tpl if="closeText">' +
                    ' title="{closeText}" aria-label="{closeText}"' +
                '</tpl>' +
                '>' +
            '</span>' +
        '</tpl>',

    iconTpl:
        '<span id="{id}-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="{baseIconCls} ' +
                '{baseIconCls}-{ui} {iconCls} {glyphCls}{childElCls}" style="' +
            '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' +
            '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' +
            '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>' +
        '</span>',
    renderIcon: function(values) {
        return this.getTpl('iconTpl').apply(values);
    },
    childEls: [
        'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl'
    ],

renderTpl,绝对找不到引用的地方,其用法继承至Component,用于定义(初始化)结构
iconTpl,通过renderIcon被renderTpl引用,是Ext模板嵌套的方式(模板是组件结构的基础)
childEls,用于定义相关子元素(dom)

 setIcon: function(icon) {
        icon = icon || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldIcon = me.icon || '';

        me.icon = icon;
        if (icon != oldIcon) {
            if (btnIconEl) {
                btnIconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
                me._syncHasIconCls();
                if (me.didIconStateChange(oldIcon, icon)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldIcon, icon);
        }
        return me;
    },
    setIconCls: function(cls) {
        cls = cls || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldCls = me.iconCls || '';

        me.iconCls = cls;
        if (oldCls != cls) {
            if (btnIconEl) {
                // Remove the previous iconCls from the button
                btnIconEl.removeCls(oldCls);
                btnIconEl.addCls(cls);
                me._syncHasIconCls();
                if (me.didIconStateChange(oldCls, cls)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldCls, cls);
        }
        return me;
    },

setIcon/setIconCls,果然是通过childEl(btnIconEl),来修改更细粒度的结构
在这里我们注意到了me.fireEvent,组件创造出来的目的终究是为了业务服务,即使我们希望创造组件时不关注逻辑,但多少也需要有一个结构与逻辑组合的地方,方案有很多,而在Ext事件的解决方案中,此处的me.fireEvent就是组件与逻辑组合的地方

组件

组件

不管怎么说组件化已然成为共识,其意义在于提取可复用的内容,或许你认为提取公共内容应该称为封装对象,或许吧,大概是因为更早的语言在开发ui对象时将其称为组件的缘故

使用Ext时大多数时候都市使用or创建Ext的组件,相比于其他组建库(jqueryui,easyui...),Ext的组件包括更广泛的功能

  • 逻辑内容封装
  • 公共样式封装
  • 统一结构管理

逻辑与样式的封装是组件存在的意义,而无论多酷炫的组件,到了Ext中就必须修改成Ext的格式,这是Ext做为整体架构规范,统一管理的思路,比起直接使用Ext.Component系列,了解其实现思路多少不会有错

基于html

使用Ext不需要定义html,只需要创建组件

Ext组件包含两种简单的关系

  • 继承
  • 组合

通过这两种方式完全可以依靠原有的组件去创建新的组件,且无需使用html,当然描述组件应有的结构的方式,也并非没有

Ext.create("Ext.Component", {
    renderTo: Ext.getBody(),
    html: "&nbsp;  <a>hello world</a>"
})    

html属性,描述组件结构的属性,该属性内容会被解析,需要遵守html的约束(dom下的innerHTML)

基于模板

使用string描述html信息有很多限制,比如需要注意转移,比如换行,他并不能像html换行,必须遵守string的语法,总之,html的内容讲难以维护,下面是一种很有意思的解决方案

script标签中,type若为无法识别的类型,比如template,其内容则不会被解析,而后就可通过id等方式获取到其内部的内容

<script id='test' type='template'>
    <ul>
        <li><span>姓名:张三</span></li>
        <li><span>性别:男</span></li>
    </ul>
</script>
<script>
var html=document.getElementById('test').innerHTML;
</script>

当然将模板内容放在html中也可以,类似的方式有很多,总之就是为了解决html格式难以使用string描述的问题,Ext也有类似的解决方案-->contentEl

//js
 Ext.create("Ext.Component", {
    renderTo: "test",
    contentEl: "template"
})
//html
<p>学生信息</p>
<div id='test'></div>
<ul id='template' class='x-hidden'>
   <li><span>姓名</span></li>
    <li><span>性别</span></li>
</ul>

x-hidden是Ext提供的解决第一运行时闪烁现象的方案(使用script标签不会-.-)

基于“动态”模板

模板是结构与数据分离的产物,从效果上来看,其产生的html结果不唯一(与数据有关),其本身就是动态的,比如在例子中,可以想到,学生名应该是一个参数,他应该是“动态”的

复用上个例子中的html,将script稍微修改一下

var data = {
    name: '张三',
    sex: '男'
}
var template = document.getElementById('test').innerHTML;
var html = template.replace(/<%=([^>]*)%>/g, function(code, key) {
    return data[key];
});

最终产生的html就与数据进行了分离,但这还不够,数据可能是嵌套的,也可能是数组,这就要求结构也能拥有简单的逻辑判断(if/for),这里需要捋一下思路,在前端能够描述逻辑的语言只有js,所以这里的模板需要先转换为js,使用js的if/for进行逻辑判断,最终产生html
一个简单的模板引擎:

    var data = {
        student: [{
            name: '张三',
            sex: '男'
        }, {
            name: '李四',
            sex: '女'
        }]
    }
    var template = document.getElementById('test').innerHTML;
    var code = ['var t=[];'];
    //截取分割符,拼接js
    var codes = template.split(/<%|%>/g);
    for (var i = 0; i < codes.length; i++) {
        var c = codes[i];
        if (c.match(/for|if|else|}/)) {
            //如果为逻辑,直接添加用于执行
            code.push(c);
        } else {
            //如果为结构,放入数组中,用于展示
            if (c[0] == '=') {
                //包含=不带",用于关联数据
                code.push('t.push(' + c.substr(1) + ');');
            } else {
                //不包含=不带",用于添加结构
                code.push('t.push("' + c + '");');
            }
        }
    }
    code.push('return t.join("");');
    var htmljs = code.join('').replace(/[\r\t\n]/g, '');
    var html = new Function('data', htmljs)(data);
        <script id='test' type='template'>
        <ul>
            <%for(var i=0;i<data.student.length;i++){%>
                <li><span>姓名:<%=data.student[i].name%></span></li>
                <li><span>性别:<%=data.student[i].sex%></span></li>
                <%}%>
        </ul>
    </script>

以上是一种模板到html的简单原理,至于Ext,根据一贯的尿性,必然将其封装成了对象(Ext.Template/Ext.XTemplate)

var tpl = new Ext.XTemplate(
    '<p>Name: {name}</p>',
    '<p>Kids: ',
    '<tpl for="kids">',
        '<tpl if="age &gt; 1">',  // 注意 要被编码
            '<p>{#}: {name}</p>',  // 每一项的自动编号
            '<p>In 5 Years: {age+5}</p>',  // 基础数学
            '<p>Dad: {parent.name}</p>',
        '</tpl>',
    '</tpl></p>'
);
tpl.overwrite('test',{
    name: 'Don Griffin',
    kids: [
        { name: 'Aubrey',  age: 17 },
        { name: 'Nikol',   age: 5 }
    ]
});
<div id='test'></div>

以上就是通过Ext模板描述组件结构的方式,当然,在通用的组件父类中,也有相应的属性用以修改结构:tpl/data

Ext.create("Ext.Component", {
    renderTo:Ext.getBody(),
    "tpl":["<p>Name: {name}</p>",
        "<p>Kids: ",
        "<tpl for='kids'>",
        "<tpl if='age &gt; 1'>", 
        "<p>{#}: {name}</p>", 
        "<p>In 5 Years: {age+5}</p>",
        "<p>Dad: {parent.name}</p>",
        "</tpl>",
        "</tpl></p>"
    ],
    "data": {
        name: "Don Griffin",
          kids: [
        { name: 'Aubrey',  age: 17 },
        { name: 'Nikol',   age: 5 }
    ]
    }
})

基于设置子元素的模板

我们可以使用模板创建任何复杂的组件结构,但对于一些粒度更小,不方便在次封装的节点,操作起来并不是特别的方便,比如

<div class='box'>
    <div class='title'></div>
    <div class='content'></div>
</div>  

或许我会去修改下.title的样式/内容,显然在此封装显得繁琐,使用选择符会更加方便
当然,比起直接使用选择符(或许使用人并不了解组件的结构),Ext提供可很有爱的解决方案,子元素映射/选择,有两种描述方式

  • 选择器描述
var cmp=Ext.create('Ext.Component', {
    renderTo: Ext.getBody(),
    renderTpl: [
        '<h1 class="title">{title}</h1>',
        '<p>{desc}</p>'
    ],
    renderData: {
        title: "Error",
        desc: "Something went wrong"
    },
    renderSelectors: {
        titleEl: 'h1.title',
        descEl: 'p'
    }
});
cmp.titleEl.setStyle({color: "red"});
  • 占位符描述
var cmp=Ext.create("Ext.Component", {
    renderTo:'test',
    "childEls": ["titleEl"],
      renderData: {
        title: "Error",
        desc: "Something went wrong"
    },
        renderTpl: [
        '<h1 id="{id}-titleEl" data-ref="titleEl" class="title">{title}</h1>',
        '<p>{desc}</p>'
    ]
})

cmp.titleEl.setStyle({color: "red"});

注:无论哪一种方式子元素统一被映射为Ext.dom.Element对象

以上即为自定义结构的组件的创建

组件的继承与组合

比其自定义组件结构,更多时候我们使用继承与组合的方式去创建组件,以达到无html的目的

继承依赖于extend
组合依赖于容器的items
通常所有的组件最终会继承至Ext.Component,也就是说做为组件必然会有extend(so,如果没有,那他是什么?猜猜看),而你所依赖的Ext已存在的组件,比如Ext.panel,其内部使用tpl等方式定义结构

Ext.define('Ext.panel.Panel', {
    extend: 'Ext.container.Container',
    childEls: [
        'body'
    ],
    renderTpl: [
        '{% this.renderDockedItems(out,values,0); %}',
        '<div id="{id}-body" data-ref="body" class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>',
            ' {baseCls}-body-{ui}<tpl if="uiCls">',
                '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
            '</tpl>{childElCls}"',
            '<tpl if="bodyRole"> role="{bodyRole}"<tpl else> role="presentation"</tpl>',
            '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values);%}',
        '</div>',
        '{% this.renderDockedItems(out,values,1); %}'
    ]
}

继承当然会继承他的html结构,这就是为什么会让人感觉Ext不需要html的原因

容器(container),或者应该说是组件的容器,其最主要的功能就是组件的组合,组合出来的肯定也是组件,所以,容器必然是一个组件(继承Ext.Component),很绕口吧,换个方式描述
组合出来的组件(组合组件),使用items存储依赖的组件,其没有自己结构(html结构),依赖其他组建结构的性质,如同容器一般,故称为容器(但别忘了他首先还是一个组件),所谓的布局,指的就是组合组件下,每一个组件所处的位置(为什么非组合组件没有布局?还记得吗,我们认为那些组件的子元素为最小粒度的Dom,对其的操作内容少,只进行简单的Dom操作),这就是为什么组件没有布局(狭义的区分组件与容器,按语境理解)

Ext下的组件封装与复用

我们都知道封装的目的,但我们封装的是什么?他真的可以复用吗?

了解组件后,在回忆下我们使用组件的情况,大多数情况下我们并不会主动去描述tpl/html,而是通过组合(容器)去描述组件的结构,通过事件/方法的定义去组装组件的业务逻辑,希望用这种方式降低结构与业务的耦合,但实际中我们会发现,我们可能会需要define很多组件,他们可能只在某个地方使用,那他存在的意义是什么?有必要创建吗?
其实,你所遇见的就是业务组件/容器(不用度娘了,自创的)

业务容器:对具体业务的结构的抽象
业务组件:对具体业务的操作的抽象

没错,他们的寿命可能都是一次性的(只在一处),如果业务结构,我是说点击变双击,下拉变按钮什么的,比起在原有的组件中修改,新建一个业务组件,也是一个不错的选择

至于业务操作,指的是click,dbclick,类似这些的原生事件绑定应该且必须存在组件当中

也许你注意到了,竟然没有业务逻辑,是的,既然谈到了组件的变更,我说的是频繁的变更,将具体业务封装在组件中是不明智的,那将业务逻辑提出,又算不算明智呢?

Ext对象

对象

js自定义对象,实现继承,是一件非常麻烦的事情,下面只做简短说明

js可以自定义对象,比如

function Student(){
}

而后只需new Student()就可以创建对象
对象主要用来封装属性和方法,为了达到复用的目的,prototype,我想,你知道我的意思

function Student(name){
    this.name=name;
}
Student.prototype.getName=function(){
    return this.name;
}

自定义对象就这么简单,但还没完,面向对象有个特性叫继承,继承,你知道的,如何有逼格的将A的原型覆给B的问题

function A(){}
A.prototype.a=function(){return 'a'};
function B(){}
B.prototype.b=function(){return 'b'};

js并没有真正的实现继承的方式(至少无法很方便的去描述),我们可能需要写很多代码,比如这样(无视constructor)

function A(){}
A.prototype.a=function(){return 'a'};
function B(){}
B.prototype=new A();
B.prototype.b=function(){return 'b'};

或许我忘记了继承的定义,但我能想到java下继承的表现形式

创建子类时,会先执行父类的构造函数,

按照java的规范,在js中我们也需要执行父类的构造函数,那他可能会这样

function A(){}
A.prototype.a=function(){return 'a'};
function B(){
    A.call(this,arguments);
}
B.prototype=new A();
B.prototype.b=function(){return 'b'};

当然,要想完善js的继承是一件相当麻烦的事情,而我想表达的大致是这么个意思

  • js继承没有官方实现方法
    是的,完全没有,每次继承,都是一大段臭长的代码
  • js继承完全没有统一的规范
    很长一段时间,我都认为使用prototype就是继承,网上也有很多继承,完全不执行父类构造函数的方式,也不能称为错(没有评判标准),甚至父类构造函数与子类构造函数的优先级关系,也没有

总之,想优雅的创建js对象,不算简单

组件

我们已经聊了无数次的组件,或许你很好奇他可能长什么样子,组件首先肯定是一个对象,并且是一个会输出(渲染)html的对象,他大概这个样子

function Component(selector){
    this.selector=selector;
    this.render();
}

Component.prototype.html="<div>组件对象</div>";

Component.prototype.render=function(){
    document.getElementById(this.selector).innerHTML=this.html;
}

选择器是组件与页面的接口(依赖),至于render,完全是组件对象特有的业务方法
至于组件的继承,就像之前提到的,依赖于继承的写法,比如使用node的util.inherits的继承方式

var util = require('util');
var Component = require('./Component');
function Button() {
    Component.apply(this, arguments);
}
util.inherits(Button, Component);
Button.prototype.html="<button>按钮对象</button>";

我们复用了Component的render方法,并完成了继承,总之组件的表现形式就是一个对象,一个关注html结构,依赖选择器(或其他方式),包含很多渲染逻辑的对象

define 定义Ext对象

你知道的,自定义对象是一件非常麻烦的事情,Ext(包括其他框架)对其进行了简单的封装(语法糖,这种规格的封装,我觉得叫语法糖更贴切些-.-)
使用之前创建过的例子(A/B)

Ext.define('A', {
     a:function(){return 'a'}
 });
 var a=new A();
 console.log(a.a);

能调用,虽然实例化后的对象a好像还包含很多其他东西,无视之
从调用的方式来看,完全没有任何区别(所以说啦,那时自定义对象的封装)
感受一下自定义对象的方式,他有些像var a={a:1}这种创建对象的风格,或许,就是这种画风的转换,才让我感觉,比起封装,语法糖的叫法更合理(无视语法糖定义)

extend 继承

现在可以创建继承A对象的B..

Ext.define('B', {
    extend: 'A',
    b: function() {
        return 'b'
    }
});
var a=new B();
console.log(a.a());
console.log(a.b());

不用考虑原型,使用起来非常舒服,但这种写法没有构造函数...
有的,只是作为关键字

Ext.define('A', {
    constructor:function(){
        console.log('create a')
    },
    a:function(){return 'a'}
 });

Ext.define('B', {
    extend: 'A',
    constructor:function(){
        console.log('create b')
    },
    b: function() {
        return 'b'
    }
});
new A();
new B();

此后在进行创建,就可以看见构造函数的执行,但蛋疼的事情还没完,创建b时,a对象的构造函数并没有执行,参考下我们之前实现的方式,此处需要一个执行方法(类似于java的super),简单的修改一下B

Ext.define('B', {
    extend: 'A',
    constructor:function(){
        console.log('create b');
        this.callParent(arguments);
    },
    b: function() {
        return 'b'
    }
});

可以看到创建B时A的构造函数也执行了,我们可以发现,这种封装并没有取消super()(java语法),我们不去纠结这是实现难度还是创作理念(必须是理念啊),总之,在ext中,对父类的构造函数的执行是可控的(是否执行与执行位置)

静态方法/属性

构造函数,本身就是一个对象,所谓的静态方法,大致指的就是那种直接挂载在构造函数上的方法,比如

function A(){}
A.prototype.a=function(){return 'a'};
A.b='b';

我们可以通过A.b的方式执行,但不能通过new A().b的方式执行(你懂的),statics,ext下静态方法创建的关键字

Ext.define('A', {
    a: function() {
        return 'a'
    },
    statics: {
        b: 'b'
    }
});
console.log(A.b);

依赖

仔细看我们自己写的构造方法

var util = require('util');
var Component = require('./Component');
function Button() {
    Component.apply(this, arguments);
}
util.inherits(Button, Component);
Button.prototype.html="<button>按钮对象</button>";

此处依赖Component组件,在Ext中,这个组件正好是继承的对象,在extend中指定,如果他是Button方法所依赖的其他对象呢?(此处指未加载的对象,一般为组件or逻辑,组件是为了组合,至于逻辑,往下看-。-)

Ext.define('A2', {
    requires: [
        'A'
    ]
})

requires,干的就是这么一件事

ext对象的其他功能

闭包

Ext.define('A', function(){
    var count=1;
    return {
        a: function() {
            return count++;
        }
    }
});
new A().a();
new A().a();
new A().a();

ext对象运行创建内部的公共必包(属性方法共享,是私有方法设置的好地方)

重构(注入,原型)

js对象有多变的尿性,在任何地方修改Object.prototype都会对对象造成影响
有人会约定,js对象应该一口气定义完,不允许碎片化
有人会依赖(这种特性),使得js对象在不同的地方有不同的表现(尤其是对临时加的业务做对象的整体修改,区别于继承)
无论怎样,ext保留了这个技巧,关键字override

Ext.define('A', {
    a: 1
});

var a = new A();
console.log(a.a);

Ext.define('My.ux.Addb', {
    override: 'A',
    b: 2
});
console.log(a.b);

Ext.define('My.ux.Edita', {
    override: 'A',
    a:123
});
console.log(a.a)

也可以修改构造函数,不过那需要重新创建对象
有一个小技巧就是通过requires加载override描述的对象,用以管理逻辑

Ext.define('A', {
    requires:['My.ux.Addb'],
    a: 1
});

ext的组件

组件是一个关注html结构,依赖选择器(或其他方式),包含很多渲染逻辑的对象,简单来说就是继承Ext.Component的对象

结合曾经写过的组件,我相信,Ext.Component的constructor应该已经不算秘密了

    constructor: function(config) {
        var me = this,
            i, len, xhooks, controller;

        config = config || {};
        if (config.initialConfig) {

            // Being initialized from an Ext.Action instance...
            if (config.isAction) {
                me.baseAction = config;
            }
            config = config.initialConfig;
            // component cloning / action set up
        }
        else if (config.tagName || config.dom || Ext.isString(config)) {
            // element object
            config = {
                applyTo: config,
                id: config.id || config
            };
        }

        // Make initialConfig available early so that config getters may access it
        me.initialConfig = config;

        // Ensure that we have an id early so that config getters may access it
        me.getId();
        me.protoEl = new Ext.util.ProtoElement();
        me.initConfig(config);

        xhooks = me.xhooks;
        if (xhooks) {
            delete me.xhooks;
            Ext.override(me, xhooks);
        }

        me.mixins.elementCt.constructor.call(me);

        //<debug>
        if (!me.validIdRe.test(me.id)) {
            Ext.Error.raise('Invalid component "id": "' + me.id + '"');
        }
        if (!me.validIdRe.test(me.itemId)) {
            Ext.Error.raise('Invalid component "itemId": "' + me.itemId + '"');
        }
        //</debug>

        me.setupProtoEl();

        // initComponent, beforeRender, or event handlers may have set the style or `cls` property since the `protoEl` was set up
        // so we must apply styles and classes here too.
        if (me.cls) {
            me.initialCls = me.cls;
            me.protoEl.addCls(me.cls);
        }
        if (me.style) {
            me.initialStyle = me.style;
            me.protoEl.setStyle(me.style);
        }

        me.renderData = me.renderData || {};

        me.initComponent();

        // initComponent gets a chance to change the id property before registering
        if (!me.preventRegister) {
            Ext.ComponentManager.register(me);
        }

        me.mixins.state.constructor.call(me);
        me.addStateEvents('resize');

        controller = me.getController();
        if (controller) {
            controller.init(me);
        }

        // Move this into Observable?
        if (me.plugins) {
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }

        me.loader = me.getLoader();

        if (me.renderTo) {
            me.render(me.renderTo);
            // EXTJSIV-1935 - should be a way to do afterShow or something, but that
            // won't work. Likewise, rendering hidden and then showing (w/autoShow) has
            // implications to afterRender so we cannot do that.
        }

        // Auto show only works unilaterally on *uncontained* Components.
        // If contained, then it is the Container's responsibility to do the showing at next layout time.
        if (me.autoShow && !me.initOwnerCt) {
            me.show();
        }

        //<debug>
        if (Ext.isDefined(me.disabledClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
            }
            me.disabledCls = me.disabledClass;
            delete me.disabledClass;
        }
        //</debug>

        // If we were configured from an instance of Ext.Action, (or configured with a baseAction option),
        // register this Component as one of its items
        if (me.baseAction){
            me.baseAction.addComponent(me);
        }
    },
    initComponent: function () {
        var me = this,
            width = me.width,
            height = me.height;

        // If plugins have been added by a subclass's initComponent before calling up to here (or any components
        // that don't have a table view), the processed flag will not have been set, and we must process them again.
        if (me.plugins && !me.plugins.processed) {
            me.plugins = me.constructPlugins();
        }

        // this will properly (ignore or) constrain the configured width/height to their
        // min/max values for consistency.
        if (width != null || height != null) {
            me.setSize(width, height);
        }

        if (me.listeners) {
            me.on(me.listeners);
            me.listeners = null; //change the value to remove any on prototype
        }
    },    

constructor中包含大量的xxxEl,没错,关注el(dom)嘛
initComponent,你应该注意到了这个方法,看起来他主要用来设置宽高和初始化事件,并且在constructor中被调用
如果曾经发现组件事件无法绑定or不现实,那可能就是此处的代码没执行,简单来说就是没有this.callParent(arguments)
你应该也能感觉到,尤其是在代码me.render(me.renderTo)后,这基本上已经明示了,在this.callParent(arguments)前后,其this内容是有区别的(之前仅为配置信息,且并未渲染,之后则已成功创建,且可以在浏览器中看见)
注:莫要死磕源码,容易走火入魔

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.