Ques是一套组件化系统,解决如何定义、嵌套、扩展、使用组件。

传统开发模式的痛点

  • 无法方便的引用一个组件,需要分别引用其JavascriptTemplateCSS文件
  • 我们期望能以MV*的方式去写代码,结果发现只有JavascriptMV*
  • UI库打包成一坨(类似Bootstrap),但是实际上UI库伴随产品迭代在反复变更,每次打开网站,用户依然反复下载UI库
  • CSS没有命名空间导致两个组件容易冲突
  • 组件无法嵌套或者无法扩展,所以实际上组件根本无法复用
  • 组件无法复制后可用,在组件无法嵌套或无法扩展的情况下,连定制一个组件都困难连连
  • 每次性能优化都将代码弄的很恶心,不好维护

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>
&lt;ui-button class=&quot;ui-default&quot;&gt;DEFAULT&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-success&quot;&gt;SUCCESS&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-info&quot;&gt;INFO&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-warning&quot;&gt;WARNING&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-danger&quot;&gt;DANGER&lt;/ui-button&gt;
</third-code>
  • 效果:

备注
  • 第三方Component需要实现两个接口bindunbind,即在容器创建时需要绑定什么,当容器删除时需要解绑什么。this会带来必要的东西,例如容器、父级ViewModel等等。

组件嵌套

当组件可以嵌套,组件件可以拆成较小的颗粒,使得复用性大大提升。

下面我们是一个codeclick组件,其用途是高亮展示代码片段,点击代码弹出dialog,也就是说我们准备嵌套上面做出来的third-codedialog组件:

定义
  • CSS文件:
/** 可以给组件定义一些特殊样式,但为了简单我们什么也不做 **/
  • 模版文件:
<div>
<third-code q-ref="code">
<content></content>
</third-code>
<dialog q-ref="dialog">
<header>欢迎使用Ques</header>
<article>你点击了代码</article>
</dialog>
</div>
  • Javascript文件:
var $ = require('jquery');

module.exports = {
data: {},
ready: function () {
var code = this.$.code,
dialog = this.$.dialog;
// 点击代码,弹出dialog
$(code.el).on('click', function () {
dialog.show();
});
}
};
效果
  • 在页面上引用:
<codeclick>
&lt;ui-button class=&quot;ui-default&quot;&gt;DEFAULT&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-success&quot;&gt;SUCCESS&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-info&quot;&gt;INFO&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-warning&quot;&gt;WARNING&lt;/ui-button&gt;
&lt;ui-button class=&quot;ui-danger&quot;&gt;DANGER&lt;/ui-button&gt;
</codeclick>
  • 展示:

备注
  • 我们看到<content>标签另一个神奇的用法是可传递,我们从third-code传递到codeclick,再传递到最外部。使得我们可以在最外部改third-code内部的节点。

  • 我们注意到q-ref本来是Q.js用于组件嵌套从母Component(为了和扩展中的父Component其分开来,这里称之为母Component)拿到子Component的引用,同样可以拿到第三方Component的引用。

组件扩展

组件可扩展,则差别不大的组件可以继承同一个父组件。

下面dialog组件扩展的例子,效果是弹出一个dialog,要求输入内容:

定义
  • CSS文件:
/** 同样为了简单我们什么也不做 **/
  • 模版文件:
<dialog extend>
<header>
<h2>欢迎使用Ques</h2>
</header>
<article>
<p>请输入要设置的值</p>
<ui-input value="" q-model="curVal" q-on="keyup: submit | key enter" q-focus="focus"></ui-input>
</article>
</dialog>
  • Javascript文件:
var filters = require('filters');

module.exports = {
methods: {
submit: function () {
if (!this.curVal) {
this.$set('focus', true);
} else {
this.$emit('submit', this.curVal);
this.$set('curVal', '');
this.hide();
}
},
show: function () {
// call super.show
this.constructor.super.options.methods.show.call(this);
this.$set('focus', true);
}
},
directives: {
focus: function (val) {
val && this.el.focus();
}
},
filters: {
key: filters.key
}
};
效果
  • 在页面上引用inputval
<inputval id="my-dialog"></inputval>
  • 在Controller调用其show方法:
var Q = require('Q');

Q.get('#my-dialog').show();
  • 则页面弹出一个弹出,要求输入值:

备注
  • 这里我们引入extend属性,用于表示该组件继承哪个组件。

  • 我们还复写了dialogsubmitshow方法,并且可以调用其父Componnet的方法,如:

this.constructor.super.options.methods.show.call(this);

嵌套使用扩展组件

我们可以嵌套使用扩展后的组件。

下面是一个复杂的例子,一个按钮点击后弹出inputval,输入后alert出输入的内容。

定义
  • CSS文件
.$__anchor {
padding: 13px 35px 17px;
box-shadow: inset 0 -4px 0 #2a6496;
border: 0;
color: #fff;
transition: all .2s ease-in-out;
background-color: #337ab7;
border-color: #2e6da4;
display: block;
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;
border-radius: 4px;
text-decoration: none;
margin: 0 auto;
} .$__anchor:hover,
.$__anchor:active
.$__anchor:focus {
background-color: #286090;
border-color: #204d74;
}
  • 模版文件:
<div>
<a href="javascript:void(0);" class="$__anchor" q-on="click: setMessage">
<content></content>
</a>
<inputval q-ref="input"></inputval>
</div>
  • Javascript文件:
module.exports = {
data: {},
methods: {
setMessage: function (e) {
var self = this;
this.$.input.$once('submit', function (value) {
value && alert('输入了:' + value);
});
this.$.input.show();
}
}
};
效果
  • 在页面引用:
<clkme>请点击我</clkme>
  • 效果:

DIY组件

DIY组件允许页面通过Markup的方法引用NodeJS组件,这样我们可以使用该NodeJS组件分析我们的代码来做一些神奇的事情。

例如我们提供的diy-preload组件提供的CGI预加载方案,整个过程没有改变开发人员的编码体验,只是简简单单标记一下哪个CGI需要预加载,则系统会自动预加载CGI。

使用
  • 在页面引入diy-preload组件
<diy-preload></diy-preload>
  • 在页面对应的数据层配置文件,这里我们的规范是该文件名为db.*.js,的对应CGI设置成preload = true:
    var DB = require('db');

    DB.extend({
ke: DB.httpMethod({
url: 'http://ke.qq.com/cgi-bin/index_json',
type: 'JSONP',
preload: true
})
}) module.exports = DB;
  • diy-preload组件会找到db.*.js,然后分析出什么CGI需要预加载,在<diy-preload>标签对应的位置插入预加载脚本。整个过程开发人员感知不到,体验上还是调取一个CGI,但是实际上在页面文档打开后CGI请求立刻发出,而不是等待Javascript加载完后才发出。

Ques前端组件化体系的更多相关文章

  1. 从DOM操作看Vue&React的前端组件化,顺带补齐React的demo

    前言 接上文:谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo 上次写完博客后,有朋友反应第一内容有点深,看着迷迷糊糊:第二是感觉没什么使用场景,太过业务化,还不如直接写Vue ...

  2. 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo

    前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的话分工会更细,比如携程: 携程app = 机票频道 + 酒店频道 + 旅游频道 + ..... ...

  3. Vue.js:轻量高效的前端组件化方案

    转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...

  4. AppBoxFuture(六): 前端组件化开发

      前面几篇都是在介绍结构化与非结构化的数据存储,本篇换换口味介绍一下框架是如何实现前端组件化开发的.首先得感谢Vue.ElementUI等优秀的前端开源项目,这些项目帮助作者快速实现了框架的两个前端 ...

  5. 如何通过 Vue+Webpack 来做通用的前端组件化架构设计

    目录:   1. 架构选型     2. 架构目录介绍     3. 架构说明     4. 招聘消息 目前如果要说比较流行的前端架构哪家强,屈指可数:reactjs.angularjs.emberj ...

  6. 前端组件化Polymer深入篇(1)

    在前面的几节里面简单的介绍了一下Polymer的基本功能,但还有一些细节的东西并没有讨论,所有打算花点时间把Polymer的一些细节写一下. new和createElement有区别吗? <sc ...

  7. 前端组件化Polymer入门教程(1)——初识&&安装

    前端组件化Polymer入门教程目录: 前端组件化Polymer入门教程(1)--初识&&安装 前端组件化Polymer入门教程(2)--快速入门 前端组件化Polymer入门教程(3 ...

  8. 前端组件化-Web Components【转】

    以下全部转自:http://www.cnblogs.com/pqjwyn/p/7401918.html 前端组件化的痛点在前端组件化横行的今天,确实极大的提升了开发效率.不过有一个问题不得不被重视,拟 ...

  9. Webpack+Vue+ES6 前端组件化开发mobile-multi-page应用实战总结

    本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws 一.写在前面 项目上线有一段时间了,一个基于webpack+vue+ES6的手机端多页面应用 ...

随机推荐

  1. 12.我们不是在真空里谈软件工程, 软件要运行在硬件芯片上面, 下面看看一个计算机芯片的发展历史: http://perspectives.mvdirona.com/2014/09/august-21-2014-computer-history-museum-presentation/ http://mvdirona.com/jrh/TalksAndPapers/DileepBhandar

    电脑芯片的诞生和发展是20世纪最伟大的发明之一,芯片技术决定了计算机升级换代的速度,决定了计算机小型化实现的程度,决定了计算机智能化的程度,决定了计算机普及化的应用深度. 1971年11月15日,英特 ...

  2. JavaScript 高性能笔记

    浏览器解析 JavaScript .CSS .DOM 时,一般都是单线程解析,所以,引用外部文件时的位置不同,UE体验也不同. 下面是 Yahoo 大牛 Nicholas C. Zakas 的 < ...

  3. javascript的原型和继承(1)

    原型与继承是javascript中基础,重要而相对比较晦涩难解的内容.在图灵的网上看到一篇翻译过的文章,有参考了一些知名博客.我自己总结了几篇.通过这次的总结,感觉自己对原型和继承的认识又增加了很多, ...

  4. MVC项目创建与项目结构介绍

    一.创建MVC项目 打开VS202,点击:文件—>新建—>项目—>Web—>Asp.Net MVC 4 Web应用程序 填好项目名称.解决方案名称和文件存放位置,然后点击确定, ...

  5. mysq双主模式

    准备环境:服务器操作系统为RHEL6.4 x86_64,为最小化安装.主机A和主机B均关闭防火墙和SELINUX ,IP地址分别为192.168.131.129和192.168.131.130,MyS ...

  6. hdu 4647 - Another Graph Game(思路题)

    摘自题解: 若没有边权,则对点权从大到小排序即可.. 考虑边,将边权拆成两半加到它所关联的两个点的点权中即可. ..因为当两个人分别选择不同的点时,这一权值将互相抵消. 代码如下: #include ...

  7. Block 传值

    A 界面: - (IBAction)gotoVC:(id)sender { //必须在事件发生时调用Block,每次Block对应一次初始化 cvc = [[CViewController alloc ...

  8. Jenkins2 - 下载与启动

    文章来自:http://www.ciandcd.com 文中的代码来自可以从github下载: https://github.com/ciandcd 本文将引导jenkins初学者安装和配置jenki ...

  9. Could not load file or assembly 'System.Data.SQLite' or one of its dependencies. 试图加载格式不正确的程序。

    出现上述问题的原因是,所加载的程序集中有32位的,也有64位的,IIS 7 程序池 在Windows下.Net FrameWork是64位的,要想正确使用,需要对程序池进行配置.如下图所示:

  10. JavaScript、for循环语句知识巩固,while(){}语句以及do{}while()语句以及switch()语句

    一.for循环语句练习 关于for循环存在的两个问题类型 穷举:在不知道什么情况下才真的寻要我们的结果,自能让我们一个个走一遍. 迭代:在现有的条件根据规律不断求解,中间情况,最终推测出来的结果 1. ...