应用场景

日常开发中,会经常遇到一些重复性很强的内容,比方说活动页中的规则、协议弹窗,样式差不多的产品展示等等。这种情况下,可以将这部分常用部分抽离开出来通过按需请求实现多页面共用,提升了开发效率。
可作为组件的特点:

  • 重复性:作为组件首先满足的特点肯定是重复性,如果这个组件做出来只可能在一个页面中使用,其意义也不大。可能被多个页面或一个页面多次使用的内容肯定是优先考虑作成组件的。
  • 关联性:组件可以跟主页有一定的关联,主要体现在css跟js的共用上,这点是组件跟iframe的主要区别之一。组件跟页面之间可以互相使用样式和方法,实现互利互惠。
  • 有一定“份量”: 这里的“份量”指的是代码量、节点数、逻辑复杂度。假设组件只有几句”Hello world!”,那也没意义,并且反而会因为增加请求量弊大于利;相反如果组件过于复杂,那么请求时间也会造成不良的用户体验。所以作为组件,有足够“份量”才能倍有面子。
  • 非”首屏”显示:组件是需要请求的,所以为了首屏更快得显示,页面加载进去时的内容最好不要通过请求异步加载。

一.开启——三行代码实现组件复用

1.首先我们先随意写个主页面,index.html
1
2
3
4
5
6
7
8
9
<section class="m-container f-tc">
<h1>Hello world</h1>
<p>我不是组件,我是主页面。</p>
</section>

<!-- 组件内容 -->
<section class="g-mt50 f-tc j-renderCtn">
看什么,还没内容呢
</section>

其中“j-renderCtn”是我们将要渲染组件的地方。

2.我们再写个组件,components/test.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.m-t_tit{
text-decoration: underline;
color: #09f;
}
.m-t_btn{
width: 50%;
height: 30px;
line-height: 30px;
}
</style>

<p class="m-t_tit">看到了没,我是组件</p>
<button class="u-btn btn_yellow m-t_btn g-center">点击</button>

<script type="text/javascript">
$('.m-t_btn').on('click', function () {
alert('你点了组件里的按钮');
});
</script>

组件里写了组件的样式、节点、事件。

3.实现组件加载,三行代码来了,index.html:
1
2
3
4
5
6
7
8
<script type="text/javascript">
/*
* 说三行就三行,多一行算我输
*/
$.get('components/test.html', function (htmldata) {
$('.j-renderCtn').html(htmldata);
});
</script>
效果图:

(渲染前):
渲染前
(渲染后):
渲染后
(点击按钮事件):
点击按钮

通过ajax请求获取组件内的代码放到事先准备的容器内。看起来是不是很low,low吗?确实low。

demo地址:demo

目前的组件开发可吐槽的点太多了。好,那咱就改进改进。。。

二.发展——灵活性,自定义填充内容

现在的组件有一个很严重的问题,文字内容什么都是写死的,那么很尴尬的,差不多的组件如果只是文案或颜色有所不同现在这情况只能再添加个组件。这样的组件太弱了我不要,所以首先就来实现内容的灵活性。
思路:正则匹配,变量替换。咱ajax请求回来的数据不就是字符串么,字符串就好办了嘛

1.变量替换

在组件中,我们以”{ {“开头,”} }“结尾,中间为变量的形式定义的内容,可以被定义变量所替换,若取不到变量则用空字符串替换内容

1
{{变量名}}

组件,components/test.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
.m-t_tit{
text-decoration: underline;
color: #09f;
}
.m-t_btn{
width: 50%;
height: 30px;
line-height: 30px;
background-color: {{btnColor}}
}
</style>

<p class="m-t_tit">看到了没,我是{{name}}</p>
<p>{{content}}</p>
<button class="u-btn btn_yellow m-t_btn g-center">点击</button>

<script type="text/javascript">
$('.m-t_btn').on('click', function () {
alert('你点了组件里的按钮,并且content是{{content}}');
});
</script>

这里有btnColor,name,content三个变量。

主页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
/*
* step2——自定义内容
*/
var testObj = {
name: 'Micheal',
content: 'This is a component.',
btnColor: '#f00'
};
var reg = /{{(.*)}}/gi;

$.get('components/test.html', function (htmldata) {
var _html = htmldata.replace(reg, function (matches, m1) {
var _m1 = m1.trim();
return testObj[_m1] || ''
});
$('.j-renderCtn').html(_html);
});
</script>

实现效果:
(渲染后):
渲染后
(点击按钮事件):
点击按钮
可以看到css、html、js里的变量都成功被替换了

demo地址:demo

2.列表渲染

当组件是一个表格或是一个包含标签显示的产品时,列表数量跟标签数量都是不确定的,因此我们还要加个循环
循环以

1
2
3
4
5
{{r-for="数组变量"}}
<h4>{{变量1}}</h4>
<p>{{变量2}}</p>
...
{{r-end}}

语法形式编写,当数组变量Arr的各项内容为对象时,变量位置替换为相应的属性(如Arr[index].name),否则变量位置替换为该项数组(如Arr[index])。

组件——components/testtable.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
<table class="m-table">
<thead>
<tr>
<th>title1</th>
<th>title2</th>
<th>title3</th>
<th>title4</th>
<th>title5</th>
</tr>
</thead>
<tbody>
{{r-for="list"}}
<tr>
<td>{{value1}}</td>
<td>{{value2}}</td>
<td>{{value3}}</td>
<td>{{value4}}</td>
<td>{{value5}}</td>
</tr>
{{r-end}}
</tbody>
</table>

<ul class="m-listul">
{{r-for="ularr"}}
<li>{{item}}</li>
{{r-end}}
</ul>
...

主页面,为了方便这里干脆写了个jQuery函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
* @param {Object} options: 组件参数
* {String} options.url: 组件路径;
* {object} options.value: 数据内容;
*/
$.fn.renderComponent = function (options) {
if (!options || !options.url) return false;

var $t = $(this),
_value = options.value;
var reg = /{{(.*)}}/gi,
listReg = /{{r-for="(\w*)"}}/gi;

// 渲染列表获取html
var getListHtml = function (key, html) { // key列表数组变量,html原html
var _reg = new RegExp('{{r-for="' + key + '"}}([\\s\\S*]+?){{r-end}}');

return html.replace(_reg, function (matches, modulehtml) {
var _resultHtml = '';
for (var i = 0; i < _value[key].length; i++) {
_resultHtml += modulehtml.replace(reg, function (_matches, arritem) {
arritem = arritem.trim();
return (typeof _value[key][i] == 'object' ? _value[key][i][arritem] : _value[key][i]) || ''
});
}

return _resultHtml;
});
};
// 请求组件
$.get(options.url, function (htmldata) {
var _listKeys = [];

// 替换列表内容
htmldata.replace(lsitReg, function (matches, m1) {
_listKeys.push(m1);
});

for (var i in _listKeys) {
htmldata = getListHtml(_listKeys[i], htmldata);
}

// 替换其他变量
htmldata = htmldata.replace(reg, function (matches, m1) {
var _m1 = m1.trim();
return _value[_m1] || '';
});

$t.html(htmldata);
});
};

调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$('.j-renderCtn').renderComponent({
url: 'components/testtable.html',
value: {
name: 'Micheal',
content: 'This is a component.',
btnColor: '#f00',
list: [
{ value1: 01, value2: 02, value3: 03, value4: 04, value5: 05 },
{ value1: 11, value2: 12, value3: 13, value4: 14, value5: 15 },
{ value1: 21, value2: 22, value3: 23, value4: 24, value5: 25 },
{ value1: 31, value2: 32, value3: 33, value4: 34, value5: 35 },
{ value1: 41, value2: 42, value3: 43, value4: 44, value5: 45 },
{ value1: 51, value2: 52, value3: 53, value4: 54, value5: 55 }
],
ularr: ['li1', 'li2', 'li3']
}
});

实现效果:
(渲染后):
渲染后

demo地址:demo
github:github

至此组件有了变量跟列表渲染,瞬间感觉灵活了。

本篇内容初步介绍了前端组件化的开发模式,当前的组件函数具备了变量及循环的功能,但其还存在着很多缺陷,例如一个页面多次引用组件会浪费请求,组件中的css、js可能会影响整个页面等等。下篇将为其进行改善,添加其本地缓存等功能。

谢谢~