5.4 消除template模板对外部变量名的依赖
5.4 消除template模板对外部变量名的依赖
来看一个有趣儿的问题。我们之前讲过,列表渲染中wx:for-item可以指定数组子元素的变量名。现在,尝试将代码清单5-6中的wx:for-item="item"改成wx:for-item="item1"。这将使postList数组子元素的变量名由item变成item1。
此时,如果要将数据传入到postItemTpl中,则应该设置data="item1"。
做完以上变更后,再次保存运行代码,会发现文章数据将消失,并且没有任何错误提示。没有显示数据,肯定是有问题的,再次强调很多时候数据绑定的相关问题,小程序不会做任何的错误提示。
那么,问题出在什么地方?之前代码可以正常运行是因为我们向template传入的变量名data="{{item}}",恰好和template里面数据绑定的变量名item一样,开发者可以回顾一下代码清单5-5和代码清单5-6。但一旦更改了item为item1后,template就找不到这个item了。
类比一下函数,函数的参数名是可以由函数自己自定义的,这保证了函数不受外部变量名的影响。但是template模板却并没有提供一个定义参数名的地方,没有办法更改从外部传入的item1为item。
当然,可以通过将postItemTpl这个template内部的item更改为item1来让代码重新正常运行,开发者可以自行尝试一下。
但这并不是一个好的办法,看起它非常的蠢。我们讲过模板的好处是它可以让多个调用方来调用,不可能要求每个调用方都使用同样的变量名来调用模板,这种由定义方要求调用方遵守变量名命名的做法是不太合理的。
要解决这个问题,就必须消除template对于外部变量名的依赖,可以使用扩展运算符“...”展开传入对象变量来消除这个问题。
将post.wxml中使用模板的地方更改为:
代码清单 5-7 使用扩展运算符展开对象 post.wxml
<template is="postItemTpl" data="{{...item}}"/>
接着去掉post-item-tpl.wxml文件中{{}}里所有的item。
代码清单 5-8 除去{{}}中的item post-item-tpl.wxml
<template name="postItemTpl" >
<view class="post-container">
<view class="post-author-date">
<image src="{{avatar}}" ></image>
<text> {{date}}</text>
</view>
<text class="post-title">{{title}}</text>
<image class="post-image" src="{{postImg}}" mdoe="aspectFill"></image>
<text class="post-content">{{content}}</text>
<view class="post-like">
<image src="/images/icon/wx_app_collected.png"></image>
<text>{{collectNum.array[0]}}</text>
<image src="/images/icon/wx_app_message.png"></image>
<text>{{messageNum}}</text>
<image src="/images/icon/wx_app_view.png"></image>
<text>{{readingNum}}</text>
</view>
</view>
</template>
保存并运行,文章列表可以正常显示了。
{{...item}}可以将item这个对象展开。展开之后再传入到template里,就可以保证template不再依赖item这个变量名。
5.5 include与import引用模板的区别
5.5 include与import引用模板的区别
5.3小节中,介绍了如何使用import来引用模板。小程序还提供了另外一种引入模板的方式:include。
include在使用上同import有以下区别:
import需要先引入template,然后再使用template;但include不需要预先引入,直接在需要的地方引入模板即可。
include模式非常简单,就是简单的代码替换,不存在作用域,也不能像import一样使用data传递变量。
如果要在post.wxml中使用include,我们需要做一些改动。
代码清单 5-9 使用include 引入模板 post.wxml
<block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx">
<include src="post-item/post-item-tpl.wxmml"/>
</block>
因为include无法引入包含有template标签的代码,而现在post-item-tpl.wxml里的所有代码都是被template标签包裹起来的。如果想使用include代替import,那么模板文件内就不能使用template标签。
将post-item-tpl.wxml里的template标签删除,只保留文章本身的wxml代码。再次使用调试下的【wxml】查看post.wxml页面,发现post-item-tpl.wxml代码已被成功地引入到了post.wxml中。
但现在文章的数据并没有显示出来,原因在于include无法向模板里传递变量,它仅仅只是一个占位符,当小程序运行时,会用include的src属性所指向的文件替换include自身。这一点同样可以从调试下的【wxml】面板里看到。
回顾一下代码清单5-9,假想include已经被替换成了post-item-tpl.wxml里的代码,那么要想显示出文章数据,就必须再一次在post-item-tpl.wxml中的{{}}中加入"item"这个变量名,就像代码清单5-5所做的那样,开发者可自行尝试一下。
除了在使用上有所不同,include和import还存在其他不同之处:import有作用域的概念,即只会import目标文件中定义的template,而不会import目标文件import的template。
例如:C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template。
而include就很简单了,它只是一个占位符,仅做简单的代码替换。
那么,何时使用include?何时使用import?
这里笔者的建议是,如果模板仅仅是静态wxml,不涉及数据的传递,可以使用include。但如果模板涉及数据绑定,还是建议使用import。所以,这里选择使用import来引用postItemTpl模板
5.6 CSS的模块化
5.6 CSS的模块化
在之前的几个小节里,已经成功地将wxml代码做成了模板。既然是模板,就应该有模板的样式。我们当然可以维持现在的代码不做改变,因为现在整个项目运行正常,但这样并不合理。样式同样应该作为模板的一部分被“打包”起来。
将post.wxss中同文章相关的样式(所有以post-开头的样式)全部剪切到post-item-tpl.wxss文件中,post.wxss文件只留下swiper组件相关的样式。
代码清单 5-10 postItemTpl的样式 post-item-tpl.wxss
.post-container{
flex-direction: column;
display: flex;
margin:20rpx 0 20rpx;
background-color: #fff;
border-bottom: 1px solid #ededed;
padding-bottom:5px;
}
.post-author-date{
margin: 10px 0px 20px 10px;
display:flex;
flex-direction: row;
align-items: center;
}
.post-author-date image{
width:60rpx;
height: 60rpx;
}
.post-author-date text{
margin-left: 30px;
}
.post-image{
width: 100%;
height:340rpx;
margin-bottom:15px;
}
.post-title{
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0 0 10px 10px;
}
.post-content{
color:#666;
font-size: 26rpx;
margin:0 0 20rpx 20rpx;
letter-spacing: 2rpx;
line-height: 40rpx;
}
.post-like{
display: flex;
flex-direction: row;
font-size: 13px;
line-height: 16px;
margin-bottom: 10px;
align-items: center;
}
.post-like image{
height: 16px;
width: 16px;
margin-right: 8px;
}
.post-like text{
margin-right:20px;
}
在定义了postItemTpl后,我们需要在post.wxml中引用它。同样,当定义了模板的wxss文件后,也需要在post.wxss文件中引用它。
引用样式文件的语法是@import "src"。在post.wxss文件的顶部添加如下代码:
代码清单 5-11 引入模板样式文件 post.wxss@import"post-item/post-item-tpl.wxss";
swiper{
width:"100%";
height: 500rpx;
}
swiper image{/*设置轮播图片宽度*/
width:"100%";
height:500rpx;
}
在引入CSS文件时,既可以是以上代码中所使用的相对路径,也可以是绝对路径.
5.7 令人遗憾的模板化而非组件化
在组件化的编程思维里,一个前端组件必须要同时满足视图层代码的组件化和逻辑层代码的组件化。在小程序里勉强可以实现view的组件化(template模板),但是模板却不可以实现业务逻辑js代码的组件化。
开发者可以自行尝试一下,如果在/pages/post/post-item下再新建一个post-item-tpl.js文件,尝试将此文件作为模板的业务逻辑代码是行不通的,小程序无法自动运行这个js文件。
也就是说,小程序只实现了模板化而并没有实现组件化。当然,我们同样可以像引入wxss样式文件一样,在post.js中通过require来引用模板的js文件,这样可以将模板的业务代码集中在一个模块儿中。
但对比一下官方提供的image、swiper等组件,他们的业务逻辑并不需要在js代码里引入,而是被很好地封装起来了,我们只需要在wxml中添加这些组件就可以很好地应用组件。同时,这些组件的数据交互也是通过组件的自定义属性来传递的,比如swiper组件的autoplay和interval等属性。
很期待官方能够支持自定义组件,而不仅仅是现在的模板。自定义组件将大幅提高代码的复用性,并且使业务代码变得更加简洁。在官方的一个Q&A列表里,提到了打算支持自定义组件,但并没有说明准确的时间点。