本文作者:IMWeb 杨文坚 原文出处:IMWeb社区 未经同意,禁止转载 Ques是一套组件化系统,解决如何定义、嵌套、扩展、使用组件。 项目地址:https://github.com/miniflycn/Ques
传统开发模式的痛点
- 无法方便的引用一个组件,需要分别引用其
Javascript
、Template
、CSS
文件 - 我们期望能以
MV*
的方式去写代码,结果发现只有Javascript
是MV*
- UI库打包成一坨(类似
Bootstrap
),但是实际上UI库伴随产品迭代在反复变更,每次打开网站,用户依然反复下载UI库 CSS
没有命名空间导致两个组件容易冲突- 组件无法嵌套或者无法扩展,所以实际上组件根本无法复用
- 组件无法复制后可用,在组件无法嵌套或无法扩展的情况下,连定制一个组件都困难连连
- 每次性能优化都将代码弄的很恶心,不好维护
- 可能没法支持IE6,例如React、Vuejs、Polymer这些方案IE6肯定不支持
UI组件
UI组件
是用来专门做UI的组件,其特点为只有模版、样式文件。系统会根据用户在页面已使用的UI组件
动态引用其依赖的资源。注意,UI组件
的前缀必须是ui-
。
下面是一个简单的ui-button
的例子:
定义
- CSS文件
.ui-button {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
text-transform: none;
-webkit-appearance: button;
overflow: visible;
margin: 0;
}
.ui-default {
color:#333;
background-color:#fff;
border-color:#ccc
}
.ui-default.focus,.ui-default:focus {
color:#333;
background-color:#e6e6e6;
border-color:#8c8c8c
}
.ui-default:hover {
color:#333;
background-color:#e6e6e6;
border-color:#adadad
}
// 后面可添加更多样式的按钮
- 模版文件
<button class="ui-button">
<content></content>
</button>
效果
- 在页面上直接引用:
<ui-button class="ui-default">DEFAULT</ui-button>
<ui-button class="ui-success">SUCCESS</ui-button>
<ui-button class="ui-info">INFO</ui-button>
<ui-button class="ui-warning">WARNING</ui-button>
<ui-button class="ui-danger">DANGER</ui-button>
- 展示
这样我们就实现了一个ui-button
组件,其可以在任意其他组件中嵌套使用。其依赖的资源会动态引用,也就是说,只有我们使用了ui-button
他的模版和样式才会被引入。
备注
- 由于我们使用了强大的cssnext,所以CSS吐出来的时候会根据配置转换成兼容版本,也就是说我们只需要按照标准去写CSS,系统会自动帮我们适配:
.ui-button {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
text-transform: none;
-webkit-appearance: button;
overflow: visible;
margin: 0;
}
.ui-default {
color:#333;
background-color:#fff;
border-color:#ccc
}
.ui-default.focus,.ui-default:focus {
color:#333;
background-color:#e6e6e6;
border-color:#8c8c8c
}
.ui-default:hover {
color:#333;
background-color:#e6e6e6;
border-color:#adadad
}
- 注意到我们引入了Shadow DOM中的
<content>
标签,<content>
标签作为Component内部的插入点(或者可以理解成占位符),当外部引用该Component时可以从外部向内部插入节点,例如:
<ui-button class="ui-default">DEFAULT</ui-button>
则表示向Component的插入点插入DEFAULT这个文字节点。关于<content>
标签我们后面还会提到其高级应用。
Component
Component是最常见的组件,其拥有模版、样式以及逻辑文件,使得这种Component更像一个自定义的元素(Custom Element)。体验上像引入一个
<input>
标签一样,我们可以设置她的值,绑定她的事件,调用她的函数。
下面是一个dialog
组件的例子:
定义
- CSS文件:
.$__mask {
position: fixed;
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
left: 0px;
top: 0px;
z-index: 999;
background-color: rgba(0,0,0,.6) !important;
display: none;
}
.$__mask.show {
display: block;
}
.$__$ {
position: fixed;
top: 10%;
opacity: .5;
left: 50%;
width: 490px;
margin-left: -245px;
z-index: 999;
background: #fff;
font-size: 14px;
border-radius: 4px;
overflow: hidden;
transition: all 200ms ease-in-out;
}
.$__mask .$__$.show {
top: 50%;
opacity: 1;
}
.$__$ .header {
height: 30px;
line-height: 30px;
text-indent: 12px;
background: #039ae3;
color: #fff;
font-size: 14px;
}
.$__$ .body {
padding: 30px 40px;
position: relative;
line-height: 24px;
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
.$__$ .msg {
margin-left: 66px;
position: relative;
top: 10px;
word-break: break-all;
}
.$__$ .bottom {
margin: 20px;
text-align: right;
}
.icon-info-large {
background: url(http://9.url.cn/edu/img/sprite/common.a8642.png) -41px -276px no-repeat;
width: 36px;
height: 36px;
display: block;
float: left;
margin-top: 4px;
}
- 模版文件:
<div class="$__mask" q-class="show: isShow">
<div class="$__$">
<div class="header">
<content select="header *"></content>
</div>
<div class="body">
<div class="icon-info-large"></div>
<div class="msg">
<content select="article *"></content>
</div>
</div>
<div class="bottom">
<ui-button class="ui-info" q-on="click: submit">确定</ui-button>
<ui-button class="ui-default" q-on="click: cancel">取消</ui-button>
</div>
</div>
</div>
- Javascript文件:
var $ = require('jquery');
module.exports = {
data: {
isShow: false
},
methods: {
submit: function () {
this.$emit('submit');
},
cancel: function () {
this.$emit('cancel')
.hide();
},
show: function () {
this.$set('isShow', true);
},
hide: function () {
this.$set('isShow', false);
}
},
ready: function () {
var dialog = $('.$__$', this.$el);
this.$watch('isShow', function (val) {
if (val) {
setTimeout(function () {
dialog.addClass('show');
}, 20);
} else {
dialog.removeClass(dialog, 'show');
}
}, false, true);
}
}
效果
- 在页面直接引入
<dialog>
:
<dialog id="my-dialog">
<header>
欢迎使用Ques
</header>
<article>
Hello World!
</article>
</dialog>
- 则可以在Controller中直接使用,例如拿到其实例,再调用其
show
方法,将其展示:
var Q = require('Q');
Q.get('#my-dialog')
.show();
- 展示
备注
- 由于CSS没有命名空间,所以我们引入了两个
$__
和__ 两个占位符来充当命名空间,系统会自动转换成当前Component的名字,所以CSS最终变成:
.dialog__mask {
position: fixed;
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
left: 0px;
top: 0px;
z-index: 999;
background-color: #000000 !important;
background-color: rgba(0,0,0,.6) !important;
display: none;
}
.dialog__mask.show {
display: block;
}
.dialog {
position: fixed;
top: 10%;
opacity: .5;
left: 50%;
width: 490px;
margin-left: -245px;
z-index: 999;
background: #fff;
font-size: 14px;
border-radius: 4px;
overflow: hidden;
-webkit-transition: all 200ms ease-in-out;
transition: all 200ms ease-in-out;
}
.dialog__mask .dialog.show {
top: 50%;
opacity: 1;
}
.dialog .header {
height: 30px;
line-height: 30px;
text-indent: 12px;
background: #039ae3;
color: #fff;
font-size: 14px;
}
.dialog .body {
padding: 30px 40px;
position: relative;
line-height: 24px;
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
.dialog .msg {
margin-left: 66px;
position: relative;
top: 10px;
word-break: break-all;
}
.dialog .bottom {
margin: 20px;
text-align: right;
}
.icon-info-large {
background: url(http://9.url.cn/edu/img/sprite/common.a8642.png) -41px -276px no-repeat;
width: 36px;
height: 36px;
display: block;
float: left;
margin-top: 4px;
}
可以看见$__
被转换成了dialog__
,而__ 被转换成了dialog
。
- 逻辑层我们使用了MVVM库Q.js,这里就不细说了。
- 这里还用到
<content>
标签的高级功能,select
属性。select属性是用来选择外部符合选择器的节点进行替换。例如:
<content select="header *"></content>
的意思是选择外部<header>
标签内所有东西进行替换,所以“欢迎使用Ques”就被替换进去了。
第三方Component
第三方Component是一套兼容方案,使得业务方不依赖
Q.js
也能定义一个Component,但是由于系统无法控制第三方组件DOM的作用域,以及实现其扩展似乎没啥意思,所以第三方无法嵌套和扩展。总的来说第三方Component本质上就是系统给第三方一个容器,他在里面干什么,系统就不管了。第三方组件一定以third-
为前缀。
下面是一个高亮代码third-code
的例子:
定义
- CSS文件:
.$__pre {
width: 900px;
margin: 10px;
}
/** 引入hightlight.js的css库 **/
@import "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/default.min.css";
- 模版文件:
<pre class="$__pre">
<code>
<content></content>
</code>
</pre>
- Javascript文件:
module.exports = {
bind: function () {
var el = this.el,
script= document.createElement('script');
script.onload = function () {
hljs.highlightBlock(el);
}
script.src = '//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/highlight.min.js';
document.body.appendChild(script);
},
unbind: function () {}
};
效果
- 引入
third-code
:
<third-code>
<ui-button class="ui-default">DEFAULT</ui-button>
<ui-button class="ui-success">SUCCESS</ui-button>
<ui-button class="ui-info">INFO</ui-button>
<ui-button class="ui-warning">WARNING</ui-button>
<ui-button class="ui-danger">DANGER</ui-button>
</third-code>
- 效果:
备注
- 第三方Component需要实现两个接口
bind
和unbind
,即在容器创建时需要绑定什么,当容器删除时需要解绑什么。this会带来必要的东西,例如容器、父级ViewModel等等。
下文
http://imweb.io/topic/55abc7b66ee095884b704c2f