glimis / ext Goto Github PK
View Code? Open in Web Editor NEWext普及
ext普及
Ext.Component所有Ext组件的基类
组件主要用来封装逻辑与结构,逻辑有很多种,如果是通用样式逻辑(setHeight,setWidth),那就是所有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)
一个标签名或者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等方式去描述其结构(查看文章...),换句话说
Button是继承Component的一个具体的组件
作为组件(例子)来说,Button不算难,却又包含定义组件所有特点
创建组件,以观察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"> </span>
</span>
</span>
</a>
</div>
第一层<div id="test">
用于渲染和查询,无视之(以后真的不展示了-.-)
第二层<a class="x-btn ...">
通过autoEl,修改的,标签层
第三层通过html/tpl等方式描述的内部结构层
这内部结构乍一看跟乱码一样,完全无从下手,没关系,慢慢分析
我们曾经使用过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的经典结构
<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);"> </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,去感受其定义的节点的区别,当然,也可以直接看源码-.-
看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"> </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系列,了解其实现思路多少不会有错
使用Ext不需要定义html,只需要创建组件
Ext组件包含两种简单的关系
通过这两种方式完全可以依靠原有的组件去创建新的组件,且无需使用html,当然描述组件应有的结构的方式,也并非没有
Ext.create("Ext.Component", {
renderTo: Ext.getBody(),
html: " <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 > 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 > 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操作),这就是为什么组件没有布局(狭义的区分组件与容器,按语境理解)
我们都知道封装的目的,但我们封装的是什么?他真的可以复用吗?
了解组件后,在回忆下我们使用组件的情况,大多数情况下我们并不会主动去描述tpl/html,而是通过组合(容器)去描述组件的结构,通过事件/方法的定义去组装组件的业务逻辑,希望用这种方式降低结构与业务的耦合,但实际中我们会发现,我们可能会需要define很多组件,他们可能只在某个地方使用,那他存在的意义是什么?有必要创建吗?
其实,你所遇见的就是业务组件/容器(不用度娘了,自创的)
业务容器:对具体业务的结构的抽象
业务组件:对具体业务的操作的抽象
没错,他们的寿命可能都是一次性的(只在一处),如果业务结构,我是说点击变双击,下拉变按钮什么的,比起在原有的组件中修改,新建一个业务组件,也是一个不错的选择
至于业务操作,指的是click,dbclick,类似这些的原生事件绑定应该且必须存在组件当中
也许你注意到了,竟然没有业务逻辑,是的,既然谈到了组件的变更,我说的是频繁的变更,将具体业务封装在组件中是不明智的,那将业务逻辑提出,又算不算明智呢?
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对象,不算简单
我们已经聊了无数次的组件,或许你很好奇他可能长什么样子,组件首先肯定是一个对象,并且是一个会输出(渲染)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结构,依赖选择器(或其他方式),包含很多渲染逻辑的对象
你知道的,自定义对象是一件非常麻烦的事情,Ext(包括其他框架)对其进行了简单的封装(语法糖,这种规格的封装,我觉得叫语法糖更贴切些-.-)
使用之前创建过的例子(A/B)
Ext.define('A', {
a:function(){return 'a'}
});
var a=new A();
console.log(a.a);
能调用,虽然实例化后的对象a好像还包含很多其他东西,无视之
从调用的方式来看,完全没有任何区别(所以说啦,那时自定义对象的封装)
感受一下自定义对象的方式,他有些像var a={a:1}
这种创建对象的风格,或许,就是这种画风的转换,才让我感觉,比起封装,语法糖的叫法更合理(无视语法糖定义)
现在可以创建继承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.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
});
组件是一个关注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内容是有区别的(之前仅为配置信息,且并未渲染,之后则已成功创建,且可以在浏览器中看见)
注:莫要死磕源码,容易走火入魔
###a
b
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.