这个主要是来开发book的这个大模块的,看看如何优雅的开发出booked模块!
一、book模块的创建
这个就很简单了,创建一个大的框架是很简单的
二、组件的编写
(1)wxml组件页面的编码
首先是将一本书的展示看做是一个组件,然后就是循环展示所有请求的书籍的信息,所以需要把一本书当做一个组件来制作,这样就能比较合理的解决这个问题!
1 // book组件的页面的代码 2 <view class="container"> 3 <image src="{{book.image}}"></image> 4 <view class="description"> 5 <text class="title">{{book.title}}</text> 6 <text class="author">{{book.author}}</text> 7 <view class="foot"> 8 <text class="footer">{{book.fav_nums}} 喜欢</text> 9 </view>10 </view>11 </view>
将这个book组件引用到page中去,给用户展示书籍的信息,需要在book.json 中引入,并且将组件的写到book.wxml页面代码中,这里暂时只是展示一个book组件
1 // page中的book.json 中引入book组件 2 { 3 "usingComponents": { 4 "v-book":"/components/book/index" 5 } 6 } 7 8 // page中的book.wxml中引入v-book标签 9 <v-book book="{{books[0]}}" />10 11 // page中的book.js中操作数据,将数据传递到页面属性中,只写主要的生命周期函数12 import {13 BookModel14 } from ‘../../models/book.js‘;15 16 // 实例化BookModel对象17 const bookModel = new BookModel();18 19 Page({20 21 /**22 * 页面的初始数据23 */24 data: {25 // 服务器请求的数据 book的集合26 books:[]27 },28 29 /**30 * 生命周期函数--监听页面加载31 */32 onLoad: function(options) {33 // 这种写法才能规避回调地狱的34 bookModel.getHotList()35 .then(res => {36 // 这种写法不完善 只是做了赋值 页面无法获取到37 // this.data.books = res38 this.setData({39 books:res40 })41 })42 43 },
(2)book组件样式的编码
这个啊,让人头疼的,我是写不出来,哈哈
1 .container{ 2 margin-top: 30rpx; 3 display: flex; 4 position: relative; 5 box-shadow: 2px 2px 3px #e3e3e3; 6 flex-direction: column; 7 width: 240rpx; 8 height: 360rpx; 9 }10 11 /* 书籍封面的样式 */12 .container image {13 width: 100%;14 height: 100%;15 border-radius: 2px;16 }17 18 .description{19 width: 216rpx;20 position: absolute;21 bottom: 0;22 background-color: #fff;23 padding: 5rpx 10rpx 8rpx 15rpx;24 font-size: 24rpx;25 display: flex;26 flex-direction: column;27 border-bottom-left-radius: 2px;28 border-bottom-right-radius: 2px;29 }30 31 .title{32 margin-top: 10rpx;33 text-overflow: ellipsis;34 white-space: nowrap;35 overflow: hidden;36 }37 38 .author{39 font-size: 20rpx;40 color: #999999;41 margin-bottom: 10rpx;42 text-overflow: ellipsis;43 white-space: nowrap;44 overflow: hidden;45 }46 47 .foot{48 font-size: 20rpx;49 display: flex;50 flex-direction: row;51 justify-content: flex-end;52 }53 54 .footer{55 color: 56 }
三、组件的应用
因为书籍是使用的一组的书籍组件,如何来把服务器上传回来的所有的书籍信息全部显示出来,这个就是我们需要考虑的,需要完成的
小程序中肯定是存在类似for循环的,那就是wx:for 但是在小程序中并不是叫做for循环,而是叫做列表渲染
(1)列表渲染
1 <block wx:for="{{books}}">2 <v-book book="{{item}}" />3 </block>
(2)整体页面布局
这个就是pages中的book页面的代码了,主题包含一个搜索栏,以及下面的图书列表
1 <view class="container"> 2 <view class="header"> 3 <view class="box"> 4 <image src="/images/icon/search.png"></image> 5 <text>搜索书籍</text> 6 </view> 7 </view> 8 <view class="sub-container"> 9 <image src="/images/book/quality.png" class="head-img"></image>10 <view class="books-container">11 <block wx:for="{{books}}">12 <v-book book="{{item}}" />13 </block>14 </view>15 </view>16 </view>
(3)整体的样式代码
看一下如何设计样式来让这个页面看起来那么舒服,这是最难的吧,充分灵活的使用flex布局来实现这样的样式
1 .container{ 2 display: flex; 3 flex-direction: column; 4 align-items: center; 5 width:100%; 6 } 7 8 .sub-container{ 9 display: flex;10 flex-direction: column;11 align-items: center;12 background-color: #f5f5f5;13 margin-top:100rpx;14 /* z-index:0; */15 } 16 17 .books-container{18 margin-top:10rpx;19 display: flex;20 flex-direction: row;21 flex-wrap: wrap;22 padding: 0 90rpx 0 90rpx;23 justify-content: space-between;24 }25 26 .books-container v-book{27 margin-bottom: 30rpx;28 }29 30 .box{31 display:flex; 32 flex-direction: row;33 justify-content: center;34 align-items: center; 35 border-radius: 50px;36 background-color: #f5f5f5;37 height: 34px;38 width:700rpx;39 color:#999999;40 }41 42 .header{43 /* fixed 是使得容器固定 */44 position: fixed; 45 background-color: #ffffff;46 height:100rpx;47 width:100%;48 border-top:1px solid #f5f5f5;49 border-bottom:1px solid #f5f5f5;50 display: flex;51 flex-direction: row;52 align-items: center;53 justify-content: center;54 box-shadow:0 0 3px 0 #e3e3e3; 55 z-index: 99;56 }57 58 .head-img{59 width:106rpx;60 height:34rpx;61 margin-top:40rpx;62 }63 64 .box image{65 margin-right:10px;66 width:14px;67 height:14px;68 margin-bottom:-2px;69 }
四、book组件中业务逻辑的实现
这个主要是实现功能就是从book列表页面用户直接点击之后,跳转到书籍的详细信息的页面,这个该如何实现,是在组件中实现跳转逻辑,还是在页面上实现,如何取舍,如何选择,是选择组件的通用性呢?还是选择组件的设计实现简单呢?如何来写这个逻辑代码?
1、不考虑组件的通用性
不考虑组件通用性的话,就直接在组件中实现页面的跳转就OK了,具体的实现代码如下:
1 // 组件中的wxml文件 2 <view bind:tap="onTap" class="container"> 3 </view> 4 5 // 组件中的js文件 小程序中的navigateTo实现跳转 6 methods: { 7 onTap(event){ 8 const bid = this.properties.book.id; 9 wx.navigateTo({10 url: `/pages/book-detail/book-detail?bid=${bid}`,11 })12 }13 }
2、考虑组件通用性的
五、book详细信息的开发
1、小程序中的编译模式
为了方便开发,让小程序每次编译之后都会是书籍详细信息的页面,我们可以添加编译模式,来控制编译之后的启动页面,这样有利于提高开发效率:
选择自己定义的编译模式:
2、具体book详情页面的开发
首先把详情页面的样式也页面的代码进行编写,这里就是没有按照顺序来整理出来代码,直接把完整的代码都拿出来吧
首先是详情页面的wxml文件中静态页面代码:(这里不是完整的,下面的点评功能没有实现)
1 <wxs src="../../util/filter.wxs" module="util"/> 2 <view class="container"> 3 <!-- 头部信息 --> 4 <view class="head"> 5 <image src="{{book.image}}"></image> 6 <text class="title">{{book.title}}</text> 7 <text class="author">{{book.author}}</text> 8 </view> 9 <!-- 短评 -->10 <view class="sub-container">11 <text class="headline">短评</text>12 <view class="comment-container">13 <block wx:for="{{util.limit(comments,10)}}" wx:key="">14 <v-tag tag-class="{{index==0?‘ex-tag1‘:‘‘ || index==1?‘ex-tag2‘:‘‘}}" text="{{item.content}}">15 <text class="num" slot="after">{{"+" + item.nums}}</text>16 </v-tag>17 </block>18 </view>19 <!-- 内容简介 -->20 <view class="sub-container">21 <text class="heading">内容简介</text>22 <text class="content" decode="{{true}}">{{util.format(book.summary)}}</text>23 </view>24 </view>25 <!-- 书籍出版信息 -->26 <view class="sub-container">27 <text class="heading">书籍信息</text>28 <view class="detail-container">29 <view class="vertical description">30 <text>出版社</text>31 <text>出版年</text>32 <text>页数</text>33 <text>定价</text>34 <text>装帧</text>35 </view>36 <view class="vertical">37 <text>{{book.publisher}}</text>38 <text>{{book.pubdate}}</text>39 <text>{{book.pages}}</text>40 <text>{{book.price}}</text>41 <text>{{book.binding}}</text>42 </view>43 </view>44 </view>45 </view>·
接下来是wxss样式的代码:
1 .container { 2 background-color: #f5f5f5; 3 width: 100%; 4 } 5 6 .head { 7 background-color: #fff; 8 padding-top: 40rpx; 9 padding-bottom: 40rpx; 10 display: flex; 11 flex-direction: column; 12 align-items: center; 13 } 14 15 .title { 16 color: #2f2f2f; 17 margin-top: 20rpx; 18 font-size: 38rpx; 19 font-weight: 600; 20 } 21 22 .author { 23 font-size: 28rpx; 24 color: #999; 25 } 26 27 .head image { 28 width: 200rpx; 29 height: 300rpx; 30 box-shadow: 2px 2px 3px #e3e3e3; 31 } 32 33 .sub-container { 34 width: 690rpx; 35 display: flex; 36 flex-direction: column; 37 align-items: center; 38 margin-top: 30rpx; 39 background-color: #fff; 40 padding: 30rpx; 41 } 42 43 .headline { 44 font-size: 30rpx; 45 font-weight: 600; 46 color: #2f2f2f; 47 margin-bottom: 20rpx; 48 } 49 50 .comment-container { 51 display: flex; 52 flex-direction: row; 53 flex-wrap: wrap; 54 } 55 56 .comment-container v-tag { 57 margin-right: 15rpx; 58 margin-bottom: 10rpx; 59 } 60 61 .num { 62 margin-left: 10rpx; 63 font-size: 22rpx; 64 color: #aaa; 65 } 66 67 .content{ 68 text-indent: 58rpx; 69 font-weight: 500 70 } 71 72 /* 给标签前两个设置背景色 这种设置违背了组件的封装原则的*/ 73 74 /* 这里引入了小程序中的externalClasses来进行自定义组件的样式的设置 */ 75 76 /* .comment-container > v-tag:nth-child(1) > view{ 77 background-color: #fffbdd; 78 } 79 80 .comment-container > v-tag:nth-child(2) > view{ 81 background-color: #eefbff; 82 } */ 83 84 /* !important 强制提高外部样式的权限 */ 85 86 .ex-tag1 { 87 background-color: #fffbdd !important; 88 } 89 90 .ex-tag2 { 91 background-color: #eefbff !important; 92 } 93 94 /* 书籍出版信息样式 */ 95 .detail-container{ 96 width: 100%; 97 display: flex; 98 flex-direction: row; 99 justify-content: flex-start;100 margin-bottom: 100rpx;101 font-size: 28rpx;102 color: #666;103 }104 105 .vertical{106 display: flex;107 flex-direction: column;108 }109 110 .description{111 color: #999;112 margin-right: 30rpx;113 }
最后是book详情页面的js代码:
1 import { 2 BookModel 3 } from ‘../../models/book.js‘; 4 5 // 实例化BookModel对象 6 const bookModel = new BookModel(); 7 8 Page({ 9 10 /**11 * 页面的初始数据12 */13 data: {14 comments:[],15 book:null,16 likeStatus:false,17 likeCount:018 },19 20 /**21 * 生命周期函数--监听页面加载22 */23 onLoad: function (options) {24 // 从外部页面传递过来的参数id25 const bid = options.bid;26 console.log(bid);27 const detail = bookModel.getDetail(bid);28 const comments = bookModel.getComments(bid);29 const likeStatus = bookModel.getLikeStatus(bid);30 31 // 利用promise的then的回调获取数据32 detail.then(res => {33 // console.log(res);34 this.setData({35 book:res36 })37 })38 39 comments.then(res => {40 // console.log(res);41 this.setData({42 comments:res.comments43 })44 })45 46 likeStatus.then(res =>{47 // console.log(res);48 this.setData({49 likeStatus:res.like_status,50 likeCount:res.fav_nums51 })52 })53 54 },
3、小程序插槽slot
这里的插槽很适合来做自定义组件的,这种用法非诚灵活,感觉真的有一种美妙的感觉-slot的官方介绍:
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#组件wxml的slot
其实文档中已经写的很清楚了,但是还是看一下老师的讲解,毕竟在实战中引用才是最重要的,如何将理论的东西应用到实战中,是很值得思考的东西
1 <view class="container">2 <slot name="before"></slot>3 <text>{{text}}</text>4 <!-- 微信小程序中的 插槽 slot -->5 <slot name="after"></slot>6 </view>
这个其实就相当于占位符,我事先在这里占有一个位置,随时等待有人来占有这个地方,但是如果没人来占,对总体的布局也不会产生影响,有人的话,我就要把这个人给你们展示出来了,这个人就和组件中的其他的标签融为一体了,注意这里需要指定slot的name值,因为在page中是需要根据name值来找具体哪个slot的
看page中如何插入到插槽中slot的:
1 <view class="sub-container">2 <text class="headline">短评</text>3 <view class="comment-container">4 <block wx:for="{{comments}}" wx:key="">5 <v-tag text="{{item.content}}">6 <text class="num" slot="after">{{"+" + item.nums}}</text>7 </v-tag>8 </block>9 </view>
这里是将text标签插入到插槽中,那么插槽中就会被一个text标签完全的占有了,你看到就是text标签中的内容了
但是,这样的话,slot并不会生效,需要在配置一个参数,就是:
1 options: {2 multipleSlots:true3 },
这样的话,才能实现slot的功能
4、小程序中的externalClasses
这个是说的是小程序中的自定义组件如何来引入外部样式类,就是如何将page中的样式放入到自定义组件中,使其生效,小程序提供了externalClasses这个配置,可以应用这个设置来解决这个问题
小程序中国的官方的介绍文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#外部样式类
还是来看一下这个东西是如何在实战中应用的:
首先,看自定义组件中需要设置哪些地方:
1 // 首先是需要设置 js中配置 2 externalClasses:[ 3 ‘tag-class‘ 4 ], 5 6 // 其次是需要在页面中使用这个自定义class 7 <!-- tag标签组件 短评以及搜索中的标签组件 --> 8 <view class="container tag-class"> 9 <slot name="before"></slot>10 <text>{{text}}</text>11 <!-- 微信小程序中的 插槽 slot -->12 <slot name="after"></slot>13 </view>
这样的话,我们就可以在使用自定义标签的时候来传递样式进去了
1 // 这里面有一个样式的切换 第一个和第二个分别展示不同的背景色 2 <view class="sub-container"> 3 <text class="headline">短评</text> 4 <view class="comment-container"> 5 <block wx:for="{{comments}}" wx:key=""> 6 <v-tag tag-class="{{index==0?‘ex-tag1‘:‘‘ || index==1?‘ex-tag2‘:‘‘}}" text="{{item.content}}"> 7 <text class="num" slot="after">{{"+" + item.nums}}</text> 8 </v-tag> 9 </block>10 </view>
再来看一下ex-tag1与ex-tag2的样式设置
1 .ex-tag1 {2 background-color: #fffbdd !important;3 }4 5 .ex-tag2 {6 background-color: #eefbff !important;7 }
注意:!important 这里是提高该样式的重要性的,如果没有的话,虽然外部样式已经设置进去了,但是可能会被默认的样式覆盖的,这里为了强制覆盖默认样式而进行的设置
5、小程序中的wxs
这个文件的主要功能就是在wxml文件中可以调用外部的js代码,当然js代码是放在wxs文件中的,看看这个新的知识点是如何来实现的,官方文档:
https://developers.weixin.qq.com/miniprogram/dev/reference/wxs/01wxs-module.html
开始编写逻辑代码:
1 // filter.wxs文件中代码 注意model.exports导出 var关键字 正则替换写法 2 var format = function(text){ 3 if(!text){ 4 return 5 } 6 var reg = getRegExp(‘\\\\n‘,‘g‘); 7 return text.replace(reg,‘\n ‘); 8 9 }10 11 module.exports = {12 format:format13 }14 15 // 页面中的引入代码16 <wxs src="../../util/filter.wxs" module="util"/>17 // 标签中使用18 <text class="content" decode="{{true}}">{{util.format(book.summary)}}</text>