微信小程序之组件的集合(五)
这个是学习复杂的组件的封装的,在课程中,主要实现的是书单上方的搜索功能组件的开发,这个应该是较之前的组件是有一定难度的,但是现在学到现在,感觉前端的内容和后端的内容比较起来,还是比较容易的,而且好多内容,其实在后端的开发中是很成熟的,所以学起来并不是很难以理解,这也是我们的一个优势吧,毕竟选择后端的同学应该是不错的啊,哈哈哈!这种组件其实是有一个特别的名字的,那就是高阶组件,来,把这个东西尽快学习掌握!
一、新建search组件
首先还是新建search组件的各个文件,这里微信开发者工具的功能很好用,直接就新建一个search目录,然后在目录中新建一个名字为index的component组件,这样就建好了四个与组件相关的文件

二、search组件开发
1、基础的结构搭建
(1)样式文件的代码index.wxml文件
<view class="container">
<view class="header">
<view class="search-container">
<image class="icon" src="data:images/search.png"></image>
<input placeholder-class="in-bar" placeholder="书籍名" class="bar" auto-focus="true"></input>
<image class="cancel-img" src="data:images/cancel.png"></image>
</view>
<view class="cancel" bindtap="onCancel">取消</view>
</view>
<view>
<view class="history">
<view class="title">
<view class="chunk"></view>
<text>历史搜索</text>
</view>
</view>
<view class="history hot-search">
<view class="title">
<view class="chunk"></view>
<text>热门搜索</text>
</view>
</view>
</view>
</view>
(2)样式文件 index.wxss代码
 .container {
   display: flex;
   flex-direction: column;
   align-items: center;
   width: 100%;
   /* padding-left:15px; *//* padding-right:15px; */
 }
 .history {
   width: 690rpx;
   margin: 40rpx 0 20rpx 0;
   display: flex;
   font-size: 14px;
   margin-top:160rpx;
   flex-direction: column;
 }
 .hot-search{
   margin-top:70rpx;
 }
 .title {
   line-height: 15px;
   display: flex;
   flex-direction: row;
   align-items: center;
   /* margin-left:100px; */
 }
 .search-container {
   display: inline-flex;
   flex-direction: row;
   align-items: center;
   background-color: #f5f5f5;
   border-radius: 50px;
   margin-left: 20rpx;
   /* margin-left: */
 }
 .books-container book-cmp {
   margin-bottom: 25rpx;
 }
 .cancel-img {
   width: 14px;
   height: 14px;
   margin-right: 10px;
 }
 .books-container {
   width: 570rpx;
   margin-top:100rpx;
   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
   padding: 0 90rpx 0 90rpx;
   justify-content: space-between;
 }
 .loading {
   margin: 50rpx 0 50rpx 0;
 }
 .loading-center {
   position: absolute;
   top: 50%;
   left: 50%;
 }
 .empty-tip {
   display: inline-block;
   width: 100%;
   text-align: center;
   position: absolute;
   top: 50%;
   /* left: 275rpx; */
 }
 .icon {
   width: 14px;
   height: 14px;
   margin-left: 12px;
   margin-right: 8px;
 }
 .in-bar {
   color: #999;
 }
 .cancel {
   line-height: 34px;
   width: 60px;
   /* margin-left:10px; */
   text-align: center;
   display: inline-block;
   border: none;
 }
 .chunk {
   height: 15px;
   width: 5px;
   background-color: #000;
   display: inline-block;
   margin-right: 10px;
 }
 .tags {
   /* padding-left:15px; */
   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
   /* justify-content: flex-start;  */
   margin-top: 24rpx;
   padding-left: 15px;
   width: 630rpx;
 }
 .tags tag-cmp {
   margin-right: 10px;
   margin-bottom: 10px;
   /* padding-bottom: 10px; *//* margin-right:6px; */
 }
 .header {
   background-color: #ffffff;
   position:fixed;
   height: 100rpx;
   border-top: 1px solid #f5f5f5;
   border-bottom: 1px solid #f5f5f5;
   display: flex;
   flex-direction: row;
   width: 750rpx;
   align-items: center;
   z-index:;
   /* padding-left:15px;  *//* padding-right:5px; */
 }
 .bar {
   border-top-right-radius: 15px;
   border-bottom-right-radius: 15px;
   display: inline-block;
   height: 34px;
   /* width:100%; */
   width: 500rpx;
   font-size: 14px;
 }
 .test {
   background-color: #000;
 }
(3)基础的业务逻辑处理 index.js
这个主要是将取消操作交给page中页面进行处理,将组件中的取消事件传递给page页面中
/**
* 组件的方法列表
*/
methods: {
// 搜索取消事件
onCancel(event){
this.triggerEvent('cancel',{},{});
}
}
page中book页面进行组件的展示以及业务逻辑处理的代码:
book.wxml文件中添加事件以及显示代码
 <view class="container" wx:if="{{!searching}}">
   <view class="header">
     <view class="box" bindtap="onSearching">
       <image src="/images/icon/search.png"></image>
       <text>搜索书籍</text>
     </view>
   </view>
   <view class="sub-container">
     <image src="/images/book/quality.png" class="head-img"></image>
     <view class="books-container">
       <block wx:key="id" wx:for="{{books}}">
         <v-book book="{{item}}" />
       </block>
     </view>
   </view>
 </view>
 <!-- search组件的使用 -->
 <v-search bind:cancel="onCancel" wx:if="{{searching}}"></v-search>
book.js中添加部分处理方法
// 添加searching属性
data: {
// 服务器请求的数据 book的集合
books:[],
searching:false
}, // 搜索框的点击事件
onSearching(event){
this.setData({
searching:true
})
}, // 搜索框取消事件
onCancel(event){
this.setData({
searching:false
})
},
2、组件中的业务代码
现在不知道从哪里开始写起了,昨天自己动手写了一下,简单的实现了搜索的功能,把前两天学习的内容简单的记录一下。
(1)搜索记录的标签显示
这个是分为两种搜索标签的,一种是历史搜索,一种是热门搜索,这两种实现方式是不同的,历史搜索是从缓存中加载保存的用户搜索记录,这个是有总数限制的,很值得学习一下这种方案的处理思路,热门搜索就是从服务器加载热门搜索记录,这个就有灰色空间了,如果数据量非常大的时候,这个时候会用到排序算法了,之前学习过,具体怎么实现,现在也是没有记住,总体的思路还是在大脑中有点的,看看这两种的实现:
   <view wx:if="{{!searching}}">
     <view class="history">
       <view class="title">
         <view class="chunk"></view>
         <text>历史搜索</text>
       </view>
       <view class="tags">
         <block wx:for="{{historyWords}}" wx:key="">
           <v-tag text="{{item}}" bind:tapping="onConfirm" />
         </block>
       </view>
     </view>
     <view class="history hot-search">
       <view class="title">
         <view class="chunk"></view>
         <text>热门搜索</text>
       </view>
       <view class="tags">
         <block wx:for="{{hotWords}}" wx:key="">
           <v-tag text="{{item}}" bind:tapping="onConfirm" />
         </block>
       </view>
     </view>
   </view>
上面是页面展示的实现,下面看一下具体的逻辑实现:
这个是新建的keyword.js文件,在models文件夹下面,主要是有几个相关的方法,重点是关注一下addToHistroy方法的
 import {HTTP} from '../util/http-p.js'
 class KeywordModel extends HTTP{
   key = "q"; // 缓存中的key
   maxLength = 10; // 历史搜索展示的条数
   // 获取历史搜索方法
   getHistory(){
     const words =  wx.getStorageSync(this.key);
     if(!words){
       return [];
     }
     return words;
   }
   // 获取热门的方法
   getHot(){
     return this.request({
       url:'/book/hot_keyword'
     })
   }
   // 将搜索关键字写入缓存中
   addToHistory(keyword){
     // 注意缓存中是一组数据
     let words = this.getHistory(this.key);
     const has = words.includes(keyword);
     if(!has){
       const length = words.length;
       // 删除末尾的Word
       if(length >= this.maxLength){
         words.pop();
       }
       words.unshift(keyword);
       wx.setStorageSync(this.key, words);
     }
   }
 }
 export { KeywordModel }
下面是search组件中的index.js文件中的具体逻辑实现,主要就是在search组件加载的时候,初始化这个历史搜索与热门搜索的标签,这个是在attached函数中,这个attached方法是小程序中的默认的组件加载时执行的方法
/**
* 组件的初始数据
*/
data: {
historyWords:[],
hotWords:[],
dataArray:[],
searching:false,
q:""
}, // 组件初始化时候调用的方法
attached(){
this.setData({
historyWords: keywordModel.getHistory()
}) keywordModel.getHot().then(res => {
this.setData({
hotWords:res.hot
})
})
},
(2)书籍信息的显示
这个现在只是实现了书籍信息的简单展示,没有实现分页的操作,后续的会实现这个功能
<!-- 书籍展示 -->
<view class="books-container" wx:if="{{searching}}">
<block wx:for="{{dataArray}}" wx:key="{{item.id}}">
<v-book book="{{item}}" class="book"></v-book>
</block>
</view>
这个页面展示的代码就比较简单了,我们只是复用了一下book组件,所以这里实现起来就比较简单了,下面是逻辑代码,主要就是调用接口加载数据,还有就是对数据的一些处理,以及一些具体细节的处理,这个细节的处理很容易被忽视的,但是这些东西才是体现一个项目的好坏,一个开发者好坏的真正的东西
// 用户搜索的方法
onConfirm(event){
this.setData({
searching:true
})
const word = event.detail.value || event.detail.text;
bookModel.search(0, word).then(res => {
console.log(res);
this.setData({
dataArray:res.books,
q: word
})
keywordModel.addToHistory(word);
})
},
(3)搜索结果的分页加载
这个业务场景是当用户搜索结果展示出来的时候,之前只是展示若干条数据,无法全部显示搜索结果,这种做法当然无可厚非,但是我们要进一步完善这个功能,那就有必要来实现分页功能了,当用户下滑到底部的时候,如果还有数据,那么我们需要加载出来,那么这个如何实现,哈哈
具体思路:
(1)在page中小程序是有事件来实现这个下拉触发动作的,那就是onReachBottom事件,如何将这个动作的通知传递到组件中,让组件接收到这个通知,实现具体的逻辑
(2)可以通过组件的properties属性来传递这个通知,属性中监听函数observer来实现处理逻辑,这里observer监听 函数只有当属性值改变的时候才会触发,所以,我们的解决办法是每次传递一个随机数给属性,让每一次通知都能被组件接收
(3)剩下的就是具体的逻辑处理了,这里面有好多细节需要处理的,具体看代码
首先,看一下page中 book.wxml以及book.js中的代码
// wxml中代码 简写省略其他
<!-- search组件的使用 -->
<v-search more="{{more}}" bind:cancel="onCancel" wx:if="{{searching}}"></v-search> // js中代码
data: {
// 服务器请求的数据 book的集合
books:[],
searching:false,
more:'' // 是否加载更多数据
}, onReachBottom: function() {
// console.log("aaaa");
// 加载更多数据
this.setData({
more:random(16)
})
},
这里有一个产生随机数的方法,random(16) 产生16位的随机数,很简单,不贴代码了
看一下search组件中的相关代码,主要是增加了一个属性,增加了loading ,这个充当的是锁的角色,这个方法还有待优化,
说一下这里面的细节处理:
loading这个锁的引入,防止用户下拉触发事件过于频繁,向服务器发送过多请求,导致的信息加载出现重叠的问题,影响服务器的性能,引入loading锁之后,只有一个请求发送完毕之后,接下来的请求才能继续发送,这个锁的概念在多线程中应用的很广泛,作为后端开发,这个问题很容易理解!
   properties: {
     more:{
       type:String,
       observer:'_load_more'
     }
   },
   data: {
     loading:false
   },
     // 加载更多数据
     _load_more(){
       console.log(123123);
       if(!this.data.q){
         return;
       }
       // loading在这里扮演的是锁的角色
       if(this.data.loading){
         return;
       }
       const length = this.data.dataArray.length;
       this.data.loading = true
       bookModel.search(length,this.data.q).then(res => {
         const tempArray = this.data.dataArray.concat(res.books);
         this.setData({
           dataArray:tempArray
         })
         this.data.loading = false
       })
     },
(4)搜索代码的优化
这个优化主要是设计到两方面,一方面是代码的抽离,一方面是代码的可读性
先看看代码的抽离如何来优化,主要是将分页的相关的代码抽离成behavior行为,然后直接在组件中引用behavior中的方法,新建一个behaviors文件夹,创建一个pagination.js文件
看一下pagination.js中的代码:
 const paginationBev = Behavior({
   data: {
     dataArray: [], // 分页数据
     total: null
   },
   methods: {
     setMoreData(dataArray) {
       const tempArray = this.data.dataArray.concat(dataArray);
       this.setData({
         dataArray: tempArray
       })
     },
     // 获取当前开始的index值
     getCurrentStart() {
       return this.data.dataArray.length;
     },
     setTotal(total) {
       this.data.total = total;
     },
     // 是否还有数据需要加载
     hasMore() {
       if (this.data.dataArray.length >= this.data.total) {
         return false;
       } else {
         return true;
       }
     },
     initialize(){
       this.data.dataArray = [];
       this.data.total = null;
     }
   }
 })
 export {
   paginationBev
 }
看一下在组件中如何使用:
 import {
   KeywordModel
 } from '../../models/keyword.js'
 import {
   BookModel
 } from '../../models/book.js'
 import {
   paginationBev
 } from '../behaviors/pagination.js'
 Component({
   // 引入 组件中behaviors属性
   behaviors: [paginationBev],
   /**
    * 组件的属性列表
    */
   properties: {
     more: {
       type: String,
       observer: 'loadMore'
     }
   },
   /**
    * 组件的方法列表
    */
   methods: {
     // 加载更多数据
     loadMore() {
       if (!this.data.q) {
         return;
       }
       // loading在这里扮演的是锁的角色
       if (this._isLocked()) {
         return;
       }
       if (this.hasMore()){
         this._locked();
         bookModel.search(this.getCurrentStart(), this.data.q).then(res => {
           this.setMoreData(res.books);
           this._unLocked();
         },()=>{
           // 避免死锁 在请求失败的时候也需要释放锁
           this._unLocked();
         })
       }
     },
     // 搜索取消事件
     onCancel(event) {
       this.triggerEvent('cancel', {}, {});
     },
     // 用户搜索的方法
     onConfirm(event) {
       // 控制搜索结果的显示
       this._showResult();
       // 初始化behavior中的数据
       this.initialize();
       const word = event.detail.value || event.detail.text;
       bookModel.search(0, word).then(res => {
         this.setMoreData(res.books);
         this.setTotal(res.total);
         this.setData({
           q: word
         })
         keywordModel.addToHistory(word);
       })
     },
     // X的图标取消事件
     onDelete(event) {
       this._closeResult();
     },
     // 显示搜索结果
     _showResult(){
       this.setData({
         searching: true
       })
     },
     // 隐藏搜索结果
     _closeResult(){
       this.setData({
         searching: false
       })
     },
     // 判断是否有锁
     _isLocked(){
       this.data.loading?true:false;
     },
     // 加锁
     _locked(){
       this.data.loading = true;
     },
     // 释放锁
     _unLocked(){
       this.data.loading = false;
     }
   },
 })
注意:带有下划线的方法是理论上的私有方法,姑且这么说吧,其实本质上和其他方法是一致的,这些方法的优化是增加代码的可读性,使得代码更加容易让人理解,这里其实由很多细节需要注意的,包括锁,为了避免死锁,需要在请求失败的时候同时将锁释放,以及confirm方法中主要将之前的数据清空,否则会造成dataArray中数据是重复数据,还有就是在加锁的时候需要在判断是否还有更多数据之后进行,如果在这之前进行,那么会造成数据不会加载的情况,等等,之后会完善一下加载图标,哈哈,感觉越来越完美
(5)loading组件的开发与应用
这个就直接从网上找一个loading图标的样式就行,看看loading组件的代码
// index.wxml代码
<view class="spinner">
<view class="double-bounce1"></view>
<view class="double-bounce2"></view>
</view> // 样式代码 index.wxss
.spinner {
width: 40rpx;
height: 40rpx;
position: relative;
/* margin: 100px auto; */
} .double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #3063b2;
opacity: 0.6;
position: absolute;
top: 0;
left: 0; -webkit-animation: bounce 2.0s infinite ease-in-out;
animation: bounce 2.0s infinite ease-in-out;
} .double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
} @-webkit-keyframes bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
} @keyframes bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
看一下loading组件的应用:
主要是在搜索结果展示之前,以及加载更多的时候进行loading组件的显示,在其他时候是无需显示的
<!-- loading图标显示 -->
<v-loading class="loading-center" wx:if="{{loadingCenter}}" />
<v-loading class="loading" wx:if="{{loading}}" />
看一下如何控制显示隐藏的
loadingCenter主要是在onConfirm方法中进行控制的,这个不多说,看一下loading的控制,就是在加锁和释放锁的时候进行控制就行了
// 加锁
locked() {
// this.data.loading = true;
this.setData({
loading: true
})
},
// 释放锁
unLocked() {
// this.data.loading = false;
this.setData({
loading: false
})
}
这里还有代码的优化,以及在没有搜索结果的时候进行友好的提示,以及在没有更多的数据的时候进行友好的提示,以及在取消的时候进行数据的初始化操作,很多细节的东西,这里就不想写了,很琐碎的东西,但是在我看来是很值得付出时间去完善的一部分,细节决定成败,大家同样是一个功能,最能看出一个人水平的是谁能把细节注意到,并且能够做好,在以后的工作中,这是自己需要提升的一个领域,专注细节,布局整个系统
微信小程序之组件的集合(五)的更多相关文章
- 微信小程序之组件的集合(四)
		
这个主要是来开发book的这个大模块的,看看如何优雅的开发出booked模块! 一.book模块的创建 这个就很简单了,创建一个大的框架是很简单的 二.组件的编写 (1)wxml组件页面的编码 首先是 ...
 - 微信小程序之组件的集合(六)
		
这个将是最后一篇关于小程序的记录了,课程接近尾声,最后一个是关于用户的page页面,看看这个页面中有哪些值得学习的地方! 一.page中my开发 这个主要是展示用户喜欢的杂志,以及用户的信息,需要创建 ...
 - 微信小程序之组件的集合(三)
		
看看音乐播放组件是如何实现完成的音乐的播放的!!! 一.音乐music组件的开发 1.页面以及页面样式的开发 // music组件页面开发 <view hidden="{{hidden ...
 - 微信小程序之组件的集合(二)
		
继续微信小程序开发的学习,继续跟着老师的讲课思路来学习,继续开发项目中所用到的组件 一.导航栏navi组件的开发 1.新建组件的文件结构 这个就是先新建目录navi.然后在navi文件夹中新建comp ...
 - 微信小程序之组件的集合(一)
		
小程序中是很强调组件中开发的,我们看到的页面是由许多组件组成的,但是这些组件是如何组合在一起的呢?来学习一下! 一.组件中数据的获取 接着上一篇文章,继续来优化代码,如何把从服务器上获取的数据显示到 ...
 - 微信小程序把玩(二十五)loading组件
		
原文:微信小程序把玩(二十五)loading组件 loading通常使用在请求网络数据时的一种方式,通过hidden属性设置显示与否 主要属性: wxml <!----> <butt ...
 - 微信小程序把玩(三十五)Video API
		
原文:微信小程序把玩(三十五)Video API 电脑端不能测试拍摄功能只能测试选择视频功能,好像只支持mp4格式,值得注意的是成功之后返回的临时文件路径是个列表tempFilePaths而不是tem ...
 - 微信小程序_(组件)scroll-view可滚动视图
		
微信小程序scroll-view组件官方文档 传送门 提前准备:使用<view>组件制作五条撑满的横向区域 <!--index.wxml--> Cynical丶Gary < ...
 - 微信小程序image组件binderror使用例子(对应html、js中的onerror)
		
官方文档 binderror HandleEvent 当错误发生时,发布到 AppService 的事件名,事件对象event.detail = {errMsg: 'something wrong' ...
 
随机推荐
- 计算几何——poj1410,线段不规范交
			
直接用kuangbin的板子,能判不规范,规范和不交 另外线段在矩形内也可以,判断方式是比较线段的端点和矩形四个角 #include <cstdio> #include <cmath ...
 - 解决eclipse启动时出现“failed to load the jni shared library”
			
如何解决启动eclipse出现failed to load the jni shared library的问题 问题描述:启动eclipse时,出现以下弹出框 此时,即表示eclispe和jdk位数不 ...
 - LUOGU P1039 侦探推理 (字符串+模拟)
			
传送门 解题思路 一道%你神题,\(string\)好强大啊..首先枚举一个周几,再枚举一个罪犯是谁,然后判断的时候就是枚举所有人说的话.定义\(fAKe[i]\)表示第\(i\)个人说的是真话还是假 ...
 - 自定义Collection View布局
			
转自answer-huang的博客 原文出自:Custom Collection View Layouts UICollectionView在iOS6中第一次被介绍,也是UIKit视图类中的一颗 ...
 - sql 查询问题
			
在做数据导出时候,当某个表某字段含有单引号时候老是报错,所以要排除这种情况: sql查询某表某字段值带单引号情况 select 主键码 from 馆藏书目库 where 题名 like '%''%' ...
 - linux内核参数sysctl.conf,TCP握手ack,洪水攻击syn,超时关闭wait(转)
			
http://www.xshell.net/linux/Linux_sysctl_conf.html 优化Linux内核sysctl.conf参数来提高服务器并发处理能力 Posted by 破冰 o ...
 - <jquery>基本的模态框
			
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
 - NEO4J全文检索架构
			
NEO4J全文检索架构 一.有大量存量数据(亿级以上)(并长期有增量数据进入) 二.无大量存量数据或者少量存量数据(或全部为增量数据) 三.架构方案选择优先级 以下方案,是根据实践总结的基于NEO4J ...
 - LeetCode 28.实现strStr()(Python3)
			
题目: 实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存 ...
 - UBOOT的的 C 语言代码部分
			
调用一系列的初始化函数 1. 指定初始函数表: init_fnc_t *init_sequence[] = { cpu_init, /* cpu 的基本设置 */ ...