前言
《微信小程序开发快速入门-小程序之开发前准备(1/5)》
《微信小程序开发快速入门-第一个页面编写(2/5)》
《微信小程序开发快速入门-列表页编写(3/5)》
《微信小程序开发快速入门-添加备忘录页面(4/5)》
在前面的 4 个章节的学习中我们已经完成了整个本地版本码仔备忘录的开发,今天这章节主要讲解对于前面的代码的优化。
1. 首页优化
两种按下效果的不同优化
绑定事件替换路径
第一种,通过动态参数替换:
思路分析:通过监听用户的触摸开始函数和触摸结束函数,在触摸开始就用按下按钮图片,触摸结束就用默认按钮图片。
涉及到的知识点:
监听事件(bindtouchstart、bindtouchend)、数据绑定
具体实现:
- 先绑定事件及数据
<view class="content">
<image class="logo" src="./../../image/index_logo.png"></image>
<navigator url="./../home/home" open-type="redirectTo" hover-class="none">
<image class="btn" src="{{imgUrl}}" bindtouchstart="onImagePress" bindtouchend="onImageNormal" ></image>
</navigator>
</view>
- 通过不同的事件触发,然后进行改变绑定数据
Page({
/**
* 页面的初始数据
*/
data: {
// 1. 初始化默认图片路径数据
img:{
press:"./../../image/index_btn.png",
normal:"./../../image/logo_btn_normal.png",
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 2. 设置默认图片
this.setData({
imgUrl:this.data.img.normal
})
},
// 触摸后调用的函数
onImagePress(){
console.log("------onImagePress")
this.setData({
imgUrl:this.data.img.press
})
},
// 触摸结束后调用的函数
onImageNormal(){
console.log("------onImageNormal")
this.setData({
imgUrl:this.data.img.normal
})
}
})
在这里我们要注意的是虽然之前在分享中,只提到点击事件和长按事件,但是除了这两个事件还有其他很多事件,我们要结合自己的需求要进行使用。
事件分类
扩展:事件冒泡
说到事件就不得不说冒泡事件了,那什么是冒泡事件呢?
事件分为冒泡事件和非冒泡事件:
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
举个例子:
我们分别在首页的开启按钮和首页容器都绑定个点击事件,然后在事件中打印log。从结构上来说容器包含了按钮,按钮和容器分别绑定了一个点击事件,如果现在用户点击按钮了大家猜一下哪些方法会被调用?
那我们点击一下,看下具体的log输出:
结果是先调用了图片的点击事件而后调用了容器的点击事件。这就是事件冒泡,当一个组件上的事件被触发后,该事件会向父节点传递。
其实大部分情况下,我们不需要再去调用父节点事件。比如:一个列表项上有一个删除按钮,点击整个列表项是查看列表项详情,如果我们点击删除按钮出发了查看详情,那这样的情况肯定不是我们想要的。
那么如何解决?这个时候我们就要阻止冒泡,把原来的bind改成catch。看下效果:
这样修改完成之后就不会事件冒泡就被阻止了。
设置样式 hover-class
第二种,设置样式。
- 首先我们原来是用image组件,这个组件没有按下样式,所以要替换组件。
- 替换成view组件,在view组件属性里面我们可以看到有个hover-class (按下样式)。
具体实现:
<!--pages/index/index.wxml-->
<view class="container">
<image src="/images/home_logo.png" class="home-logo"></image>
<navigator url="/pages/list/list" open-type="redirect" hover-class="none">
<!-- <image src="/images/home_btn_default.png" class="home-btn" hover-class="hover-btn"></image> -->
<view class="home-btn" hover-class="hover-btn"></view>
</navigator>
</view>
.home-btn {
width: 550rpx;
height: 150rpx;
margin-top: 168rpx;
opacity: 0.8;
background-image: url(https://6d69-mini-cloud-7g18d9qfb4322b84-1306493822.tcb.qcloud.la/home_btn_default.png?sign=70c835d5c457a85817027ceaf896bf4e&t=1626100746);
background-size: 100% 100%;
}
.hover-btn{
opacity: 1;
}
在这里使用了一个小技巧,利用 css 中的 opacity 属性来做的按下效果。
opacity:设置元素的不透明级别。
value:从 0.0 (完全透明)到 1.0(完全不透明)。
思路分析:按钮默认是80%透明度,按下变成不透明,这样做出一种按下效果,还省了一张图片。
扩展:布局优化
- 去掉边距,用flex布局来居中
既然我们说到了样式,然后我们再继续优化下之前写的启动页样式。之前在整体布局时候使用了flex布局,我们进行了水平居中,然后通过 margin-top 进行了向上边距,让布局看上去居中了。
效果如下:
那么既然我们深度的学习了flex布局,有没有更好的方式呢?
那当然有,我们出了水平居中,其实还可以垂直居中。我们设置了align-items实现了纵轴居中,然后我再使用下justify-content实现横轴居中。
align-items 属性定义flex子项在flex容器的当前行的侧轴方向上的对齐方式。
justify-content 用于设置或检索弹性盒子元素在主轴方向上的对齐方式。
效果如下:
设置之后我们会发现居然没有居中!什么问题?我们来看下我选中容器布局,它的高度就只是包裹了logo和按钮。
居中属性其实已经其效果了,不过因为高度问题所以导致的没有效果。解决方案就是给容器设置一个高度,设置 height:100vh。
效果如下:
这个时候又有一个小知识点,之前单位我们只提到过px,rpx。
那么这个vh是什么?
vh(viewport height)是一个百分比单位,1vh是当前屏幕可见高度的1%。除了vh还有vw,1vw是当前屏幕可见宽度度的1%。
理解 flex 的居中属性
刚才有提到两个居中属性:align-items 、justify-content。这两个属性在flex布局中使用频率特别高。
align-items 属性定义flex子项在flex容器的当前行的侧轴方向上的对齐方式。
justify-content 用于设置或检索弹性盒子元素在主轴方向上的对齐方式。
看下解释,里面有提到两个词:主轴、侧轴 这两个词怎么理解呢?
这个主轴和侧轴其实是动态变化的,主要依据是 flex-direction。
flex-direction 属性指定了内部元素是如何在 flex 容器中布局的,定义了主轴的方向
当 flex-direction:column 时,纵向排列,那么主轴就是纵轴。
如下图所示:
这个时候align-items居中就是左右居中,justify-content 居中是上下居中。
当 flex-direction:row 时,横向排列,那么主轴就是横轴。
这个时候align-items居中就是上下居中,justify-content 居中是左右居中。
总结
通过两个按下效果的分析,我们在这个小节不仅学习到了js的方式还学习到了css的方式来实现同样的效果。
那个效果更好呢?
如果只是从按下效果这个需求来看,通过样式能解决的会更好一些。
如果是多个样式的切换,并且每个样式的切换还涉及到数据上的处理,那么就需要用到js切换的方式,所以我们两种方式都要学会,针对不同的需求选择适合的方案。
扩展知识:
- 如何将冒泡事件变成非冒泡事件?用 catch 方法来捕捉事件冒泡,阻止冒泡。
- flex布局中,主轴是由谁来决定的?由 flex-direction 的属性决定主轴的方向。
2. 列表页面
文本超出效果
在原来的布局样式中列表项会有个显示问题,那就是当备忘录内容过多的时候会导致布局显示异常。
现实实现效果 VS 效果图效果
我们希望是像效果图一样,超过两行就用省略符号。因为我们不能控制用户输入内容的长度只有两行,不符合用户实际需求,所以我们在这里要通过样式来控制。
首先分析一下目前的效果和理想效果的差距:
- 文本内容溢出,超过列表项长度
- 溢出部分要变成省略符号来代替
- 目前为一行,需要显示两行文本
对应的样式有:
list-content {
margin-top: 50rpx;
font-size: 26rpx;
color: #CCCCCC;
/* 隐藏溢出内容 */
overflow: hidden;
/* 溢出内容用省略号 */
text-overflow: ellipsis;
/* 文字显示两行,需要组合下面四个属性*/
/* 将对象作为弹性伸缩盒子模型显示 */
display: -webkit-box;
/* 设置缩盒对象的子元素的排列方式 */
-webkit-box-orient: vertical;
/* 显示的文本的行数 */
-webkit-line-clamp: 2;
/* 允许换行到下一行 */
word-break: break-all;
}
显示效果:
3. 编辑页面
整体布局优化
在上次分享中采用的是 position: absolute; 绝对定位的方式,还有一种方式就是flex布局的方式,通过设置 justify-content: space-between; 让两个布局进行上下分开。
space-between:均匀排列每个元素首个元素放置于起点,末尾元素放置于终点
首先从结构上要分开:
<!--pages/edit/edit.wxml-->
<view class="container">
<!-- 用户输出部分 -->
<view class="content-container">
<!-- 用户输入部分,省略无关代码 -->
</view>
<!-- 按钮部分 -->
<text class="save" bindtap="save">保存</text>
</view>
然后通过 container 样式使用flex布局:
.container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
}
公共方法抽取
在编辑页面使用到了时间格式化的方法,这种方法属于通用方法。我们除了在当前页面会用到还有可能会在其他页面也用到,这个时候我们就可以单独建立一个工具类来管理便于多个页面进行复用。
可以在pages同级目录新建个utils文件夹,然后在这个文件夹下面建立一个utils.js来写通用方法。
然后把原来的方法复制到这个工具类里面,然后使用 export 加上方法导出声明。
// 获取当前日期
function getNowDate() {
let dateTime
let YYYY = new Date().getFullYear()
let MM = new Date().getMonth() + 1
let DD = new Date().getDate()
dateTime = YYYY + '-' + MM + '-' + DD
return dateTime
}
// 获取当前时间
function getNowTime() {
let dateTime
let HH = new Date().getHours()
let mm = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() :
new Date().getMinutes()
dateTime = HH + ':' + mm
return dateTime
}
// 导出声明
export {
getNowDate,
getNowTime
}
那么页面对象如果引用呢?使用 import 引用
// 在js头部引用,指定引入方法和工具文件路径即可
import {
getNowDate,
getNowTime
} from '../../utils/utils.js'
如何使用?
this.setData({
nowDate: getNowDate(),
nowTime: getNowTime()
})
这个时候我们就无需写重复代码了,当别的页面有需求的时候直接引入使用即可。
同样的我们可以举一反三,如:判空函数,所有的数据处理都可以单独抽取到一个JS类,一样的做法。这样的做法除了可以减少复用代码,还便于维护。
4. 整体优化
app.wxss
我们会发现在这三个页面中都用到了同样的样式,那么其实之前有提到过,公用样式可以放在app.wxss里面这样就可以直接复用。
如:flex布局的纵向排列,定义在app.wxss里面
.flex-col{
display: flex;
flex-direction: column;
}
然后其他页面可以直接使用组合样式:
通用的写在app.wxss里面,个性化的在具体页面编写。
以上是样式的复用,还有一种是样式中常用的具体属性值设置成变量,便于复用。
如:列表页面和编辑页面的按钮都是主色调,那么我们就把主色调自定义成 css 属性然后列表页面和编辑页进行调用即可。
使用CSS自定义属性(变量)
声明一个自定义属性,属性名需要以两个减号(–)开始,属性值则可以是任何有效的CSS值。
page {
--color:#F8D300
}
注意:需要在app.wxss定义,这样所有页面的wxss才能使用。
使用一个局部变量时用 var() 函数包裹以表示一个合法的属性值:
.content-btn {
background: var(--color);
}
同样的除了颜色,还有一些统一的边距、大小、等等属性都可以。
app.js
我们之前学习过页面的生命周期函数,其实app.js也有生命周期。区别在于页面生命周期只是和当前页面相关,而app.js的生命周期和整个小程序应用相关,它和页面的有所不同。
App({
onLaunch (options) {
// 小程序初始化完成时触发,全局只触发一次。
},
onShow (options) {
// 小程序启动,或从后台进入前台显示时触发。
},
onHide () {
// 小程序从前台进入后台时触发。
},
onError (msg) {
// 小程序发生脚本错误或 API 调用报错时触发。
},
globalData: 'I am global data' // 全局参数
})
声明周期函数,我就不做过多讲解。因为之前讲解页面的基本都覆盖到了,只不过范围不一样,理解起来较简单。
里面要特别讲一讲这个 globalData 参数的使用,如果我们有所有页面都要用到的全局变量就可以存储在app的 globalData 里面,不仅支持全局参数还支持全局方法也可以。
先假设一个需求:
码仔觉得问候语挺有意思,希望问候语在每个页面都显示,这个时候需要用到全局变量。在app里面设置为全局变量:
// app.js
App({
globalData: {
msg:["你好吗?","加油鸭!","早点睡!","奥利给!","别熬夜!"]
}
})
然后在具体页面通过getApp()方法获取:
// 获取app对象
const app = getApp()
Page({
onLoad: function (options) {
// 使用app的globalData全局变量
console.log(app.globalData)
},
})
但是需求不能获取列表,而是获取到随机的问候语句。这个时候就需要把随机方法也提取出来。
// app.js
App({
randomMsg(){
let msgs = this.globalData.msgs
let msg = msgs[Math.floor(Math.random() * msgs.length)];
return msg
},
globalData: {
msgs:["你好吗?","加油鸭!","早点睡!","奥利给!","别熬夜!"]
}
})
页面使用全局方法:
const app = getApp()
Page({
onLoad: function (options) {
console.log(app.randomMsg())
},
})
适用场景:在小程序里面共享都是一次应用生命周期中会有多个页面使用到的数据,小程序重启后将全局变量会重新初始化。
使用第三方包
在上个小节中,我们提取来时间格式化的方法到utils里面达到了便于复用。有时候我们维护常用时间工具类成本很高,而且我们要去深入去了解里面的API,这个时候去其实我们用别人维护的时间工具类。
这个时候我们就会去github查找相关的开源库,找到合适的就需要进行使用。使用通常有两种方式:
- 直接复制原来到自己的项目中
- 使用 npm 包进行远程引用《微信小程序如何引入npm包?》
总结
- 无论是 css 样式还是 js 方法都要尽可能的抽象复用,这样才能提升整体效率。
- 在优化的过程中先局部再整体,没有最好只有更好,基于业务场景来做优化。
- 常用的工具类就不需要重复发明轮子,学会使用已有第三方开源库可提升效率。
最后
微信小程序开发快速入门之码仔备忘录以及全部写完,希望能够帮助更多人入门微信小程序开发,在这 5 篇文章中我希望传达是一种学习编程的思维方式,利用这些方法你们可以自学其他相关性的开发知识,用项目的方式去学习新的技术。
更多教程可以看 我的主页,我会持续更新小程序相关内容。
感谢用心制作的教程,视频版和图文版都学习了一遍,消除了好多知识盲区。
学习了,非常感谢输出。弱问一下,源码有地址不?
非常好,希望出更多的好教程文章。