本章是小程序的进阶内容。主要介绍了模块、模板和缓存的概念以及使用方法。模板是小程序中的重点和难点,它将大幅度地提高代码的复用性与可维护性,避免开发者编写重复的代码。本章也特别指出了模板与组件的区别,小程序仅仅实现了模板化而不能自定义组件,这是非常遗憾的一件事儿。缓存的应用也是小程序中的一个特 {MOD},开发者的很多业务都需要借助缓存来实现,比如用户的令牌、城市列表数据等都可以写入小程序的缓存中。本章我们还分别尝试使用ES5和ES6语法编写“数据库”访问类,开发者可以自行体会一下ES6编写Class的优越性。
5.1 将文章数据从业务中分离
现在,所有的文章数据都被强行写在post.js里,这污染了我们的业务层。我们尝试将这些数据分离到一个单独的js文件中。在项目的根目录下新建一个文件夹,命名为data。然后在data目录下新建一个js文件,命名为data.js。新的data.js文件代码如下:
代码清单 5-1 将post.js的数据代码剪切到data.js 中 data.jsvar postList = [{
object: {
date: "Jan 14 2017",
},
title: "小时候的冰棍和雪糕",
avatar: "/images/avatar/avatar-2.png",
postImg: "/images/post/post-4.jpg",
content: "冰棍儿和雪糕绝对不是一件东西,两者的价钱大不一样,......",
collectNum: { array: [108] },
messageNum: 43,
readingNum: 34,
},
{
object: {
date: "Jan 31 2077",
},
title: "最爱的女孩",
avatar: "/images/avatar/avatar-2.png",
postImg: "/images/post/post-5.jpg",
content: "就让往事随风就随风,心随你动.。。",
collectNum: { array: [746] },
messageNum: 43,
readingNum: 94,
},
{
object: {
date: "May 14 2017",
},
title: "Eason 出新歌",
avatar: "/images/avatar/avatar-2.png",
postImg: "/images/post/post-6.jpg",
content: "如果那两字没有颤动.",
collectNum: { array: [108] },
messageNum: 48927,
readingNum: 34,
}]
5.2 小程序的模块
上一小节中我们提取的数据文件data.js可以视作是小程序的一个模块,但现在还没有办法从其他文件访问这个模块。我们还需要使用module.exports向外部暴露一个接口。在data.js文件的最下部添加以下代码:
代码清单 5-2 向外部暴露模块接口 data.jsmodule.exports={
postDataList:postList
}
定义好模块后,接下来就可以在其他js文件中引用这个模块。我们需要在post.js中引入data.js这个模块。
代码清单 5-3 引入模块 post.jsvar objData=require("../../data/data.js");
Page({
data:{ },
onLoad:function(){
this.setData({
postDataList:objData.postDataList
})
}
})
代码第一行的require(path)将模块引入到post.js中,并将模块对象赋值给dataObj。随后在onLoad函数里取出postDataList数据,并进行数据绑定。使用require引用js模块时,要特别注意以下几点:
- 被引用的文件一定要带有扩展名js,这一点是不同于页面路径的。
- path路径不可以使用绝对路径,否则会报错。应该使用相对路径。
- 在JavaScript文件中声明的变量和函数只在该文件中有效,不同的文件中可以声明相同名字的变量和函数,不会互相影响。
所以,如果使用require('/data/data.js'),小程序会找不到data.js这个文件。注意为什么是dataObj.postList?因为在输出模块时,我们是将postList作为一个object的属性赋值给module.exports的,参考代码清单5-2。所以在require时,得到的也是一个object并非是postList,需要使用dataObj.postList才能获取到真实的文章数据。这样的做的好处是,object不仅可以包含postList,你还可以在data.js文件中定义除postList外的其他数据,并作为object的属性一起输出。我们在上一小节中更改了postList的数据结构,所以要调整post.wxml里{{}}的语法才可以正常显示数据。
代码清单 5-4 更改post.wxml的数据绑定语法 post.wxml
{{item.date}}
{{item.title}}
{{item.content}}
{{item.collectNum.array[0]}}
{{item.messageNum}}
{{item.readingNum}}
保存运行代码,项目正常地显示出了3篇文章数据。事实上,require只是模块化的一种方式。还可以使用ES6的Module来编写模块。开发工具默认使用babel将开发者的ES6代码转化成ES5代码。
5.3 小程序的模板化
5.3 小程序的模板化使用列表渲染展现文章列表是最好的方法吗?恐怕不是。如果其他页面同样需要显示文章列表怎么办?把代码清单5-4中的代码到处拷贝吗?这当然是最差的选择。借助一下函数这个思想。我们通常会将一些公共的、经常使用的业务逻辑提取成一个公共的函数,当在多个地方需要使用函数时,只需要调用这个函数即可完成相应的业务。使用函数的好处是不言而喻的。事实上,有一句话是这么描述软件开发的:编程世界里遇到的绝大多数问题都可以用封装的思想来解决。夸张一说,点儿来你所能看到的代码,其实全是封装过的代码。小程序也提供了一个称作模板的技术来支持对wxml组件的封装,但是这种封装仅仅只是wxml的代码片段,并没有实现像AngularJS里的完整模块儿。在AngularJS里HTML、js可以作为一个整体被封装起来。但是在小程序中,我们只能将wxml封装,无法将模板的业务逻辑(js)也封装起来。首先来看看如何使用模板,随后再讨论封装模板业务逻辑的问题。要使用模板,自然需要先新建模板文件。在/pages/post下新建目录post-item,作为模板文件目录。接着在该目录下新建2个文件:post-item-tpl.wxml和post-item-tpl.wxss。这里使用tpl来结尾,只是一种建议和习惯,并不是强制要求,开发者可以自行定义模板名称。使用模板是为了简化post.wxml中文章的写法,让文章可以成为一个单独的“组件”(但不是真的组件,只是模板),供其他多个地方使用。想想我们在使用image、text等组件时是不是很简单,只需要一个简单的
就可以实现图片的显示功能。同样,我们也可以尝试将文章编写成一个“组件”。现在,尝试将post.wxml中
标签中关于文章的代码剪切到post-item-tpl.wxml中,让这段代码成为一个可复用的“组件”。代码清单 5-5 编写文章模板 post-item-tpl.wxml
{{item.date}}
{{item.title}}
{{item.content}}
{{item.collectNum.array[0]}}
{{item.messageNum}}
{{item.readingNum}}
模板相关内容必须被包裹在标签内,使用name属性指定template模板的模板名。这个模板名将在引用模板时被使用。当定义好一个template后,可以在其他页面引用这个template。现在我们在post.wxml中引用并使用这个template使用并应用这个template.代码清单 5-6 引用postItemTpl模板 post.wxml
在post.wxml的顶部使用来引用模板。对于templatePath路径,这里需要注意,在当前版本中,可以在后面加wxml文件扩展名,也可以不加扩展名。但官方示例中是带有.wxml扩展名的,所以建议开发者带上模板文件的扩展名。引用后模板就可以在页面中使用这个模板了。在需要模板的位置使用template标签引入模板。template的is属性指定要使用哪个模板,这里我们当然要使用postItemTpl这个模板。再次类比一下函数,函数通常可以定义若干个参数,并从函数调用方传入一些数据。同样,模板也可以传入数据。通过template的data属性,可以向template传递数据。这里将wx:for得到的item传入到template里,这样就可以在template内部使用这个item了。要注意的是,向模板里传入数据,同样要使用{{}}的数据绑定语法,比如data={{item}}。