Create by jsliang on 2018-9-17 17:58:56
Recently revised in 2018-10-22 09:58:07javascript
Hello 小伙伴们,若是以为本文还不错,记得给个 star , 大家的 star 是我学习的动力!GitHub 地址css
2019-08-16 13:41:40 因为这篇文章可能比较久远,文章进行了微调,一些 bug 可能修复,或者其中一些连接坏了,请小伙伴们前往 GitHub 获取最新文章。html
首个微信小程序实践记录:
工做量: PSD 18 张 (导出的 JPG 30 张)
耗时:12 个工做日
总结1: 在页面制做商,须要 3 周工做日(工做 15 天)搞定,先后端对接口另计。实际上,12 个工做日能够搞定全部页面,可是应该往前铺 1.5D 熟悉框架,日后铺 1.5D 整理代码。固然每一个人的耗时可能不一样,可根据我的实际状况进行调整。
总结2:在 API 调用上,根据接口数量可能须要 7-12 个工做日进行 API 调用,难点表如今: 1. 接口不够丰富,数据量不足; 2. 接口数据不够正式真实,跟前面的假数据相差太大; 3. 接口可能无法正常调用 等缘由。故因根据小程序业务逻辑进行工做时长的报备。前端
这里有 jsliang 微信小程序开发中遇到的全部坑,以及在填坑过程当中的一些我的经验。jsliang 利用这篇教程存储一些经常使用的微信小程序开发技巧,方便查找。它可能教不了你什么,但至少能省下你百度的功夫。java
请结合 《目录》 和 《返回目录》 来进行跳转,得到更好的阅读体验。node
本文技术支持:Ansen江git
注1:因为更新频繁,有时候平台上的文章版本可能没有图片或者目录没法跳转,因此小伙伴须要获取最新资料的,请前往 GitHub:地址github
注2:若是小伙伴使用的是手机版打开,那么推荐小伙伴使用电脑打开,由于各平台的手机端大都不支持页内跳转,看起来比较费劲。web
目前已有 48 个坑。数据库
请各位按目录检索时注意:
3.一、3.二、3.3…… 等二级目录对应着一个章节。
3.1.一、3.1.二、3.1.3…… 等三级目录将该二级目录这个大章节详细拆分红诸多小坑,方便查看。
本文章原名【微信小程序 100 坑】
微信小程序的开发教程,或许写出来是很是受欢迎的。
可是:
因此,在这里, jsliang 结合 “平常躺坑” ,先为你解决小程序的 100 个坑!虽然如今可能还不够,可是第一天我就碰到 4/5 个了,我想我能够帮大家躺完 100 个的!!!
如今的微信开发者工具显示的开发版本是:"libVersion": "2.0.4"
若是你开发的版本已经解决了这个 bug ,或者你以为这个 bug 还有其余解决方法,或者你以为这个玩意还有其余 bug ,请告诉我,我会补充到这篇文档上,顺带记上您的大名,谢谢!
QQ: 1741020489
这里的坑:
但愿小伙伴在百度中或者无心看到这篇文章,请熟练使用浏览器的 Ctrl + F
,查找须要的问题答案。
本组件目前已有 5 个坑,有兴趣的小伙伴能够详看。
代码来源于该地址:微信组件 swiper 。
为方便小伙伴查看,下面贴出原版代码:
demo.wxml
<swiper indicator-dots="{{indicatorDots}}"
autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
<block wx:for="{{imgUrls}}">
<swiper-item>
<image src="{{item}}" class="slide-image" width="355" height="150"/>
</swiper-item>
</block>
</swiper>
<button bindtap="changeIndicatorDots"> indicator-dots </button>
<button bindtap="changeAutoplay"> autoplay </button>
<slider bindchange="intervalChange" show-value min="500" max="2000"/> interval
<slider bindchange="durationChange" show-value min="1000" max="10000"/> duration
复制代码
demo.js
Page({
data: {
imgUrls: [
'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg',
'http://img06.tooopen.com/images/20160818/tooopen_sy_175866434296.jpg',
'http://img06.tooopen.com/images/20160818/tooopen_sy_175833047715.jpg'
],
indicatorDots: false,
autoplay: false,
interval: 5000,
duration: 1000
},
changeIndicatorDots: function(e) {
this.setData({
indicatorDots: !this.data.indicatorDots
})
},
changeAutoplay: function(e) {
this.setData({
autoplay: !this.data.autoplay
})
},
intervalChange: function(e) {
this.setData({
interval: e.detail.value
})
},
durationChange: function(e) {
this.setData({
duration: e.detail.value
})
}
})
复制代码
好的,上面就是微信官方文档的演示代码,若是你跟着演示代码走了一遍碰到疑问的话,看看这里我挖的土是否是能填好你的坑:
demo.wxml
中出现的 <image src="{{item}}" class="slide-image" width="355" height="150"/>
这行, width
和 height
的行内属性是忽悠老百姓的,它并没卵用 !咱们须要在 slide-image
这个 class
类中修改 width
和 height
。简而言之,行内样式都是骗人的,乖,咱们仍是去 demo.wxss
写样式吧~
在任何出现图片的地方(包括但不限于轮播图),若是你发现不只行内写法无效以外,还发现单纯地给图片加 class
,去 *.wxss
写样式也无效的话。那么,我建议小伙伴最好采用样式加剧法,即 .image-wrap .image
这种写法格式,来确保图片样式能进行修改。详细用法可看下文。
swiper
属性值。官方文档说明:
虽然,它的属性名和属性值是这么说的。可是,用的时候,首先你须要在 demo.wxml
中的 swiper
绑定这个属性名,而后在 demo.js
中设置其属性值。值得注意的是,它的绑定值,稍微不一样于 Vue
, 须要设置 {{}}
形式。若是文字描述你看得不是很清楚,能够参照下面的代码进行理解。
关于轮播图的地址跳转,在微信小程序的官网是没用说起的,也是 jsliang 去百度查看了下,才知道怎么设置(多是我一开始就挑战的难度过高了么 -_-|| ),在下面 jsliang 贴出来代码~想知道怎么解决的能够去看看:首先,在 data
中设置 link
;而后,设置 navigator
导航遍历 item.link
。
关于 wx:key
, wx:key
的做用是:当数据改变触发渲染层从新渲染的时候,会校订带有 key 的组件,框架会确保他们被从新排序,而不是从新建立,以确保使组件保持自身的状态,而且提升列表渲染时的效率。可是,在其 swiper
中,小程序自己是没有写的,因此它会带有 warning
,这里也是个小坑, jsliang 也是百度了下也知道这件事:点我了解。
下面给出这 5 个坑的解决代码,若有不对,尽情指出:
index.wxml
<view class="carousel">
<swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}" indicator-color="#707071" indicator-active-color="#fff" circular="true">
<!-- wx:key : 提升列表渲染效率 -->
<block wx:for="{{imgUrls}}" wx:key="{{item.index}}">
<swiper-item>
<navigator url="{{item.link}}" hover-class="navigator-hover">
<image src="{{item.url}}" class="slide-image" />
</navigator>
</swiper-item>
</block>
</swiper>
</view>
复制代码
index.wxss
.carousel .slide-image {
width: 100%;
height: 420rpx;
}
复制代码
index.js
Page({
data: {
imgUrls: [
{
link: '../index/index',
url: 'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg',
},
{
link: '../demo/demo',
url: 'http://img06.tooopen.com/images/20160818/tooopen_sy_175866434296.jpg',
},
{
link: '../logs/logs',
url: 'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg'
}
],
indicatorDots: true,
autoplay: true,
interval: 5000,
duration: 1000
}
})
复制代码
本组件目前已有 3 个坑,有兴趣的小伙伴能够详看。
tabBar :底部菜单栏,须要在 app.json
中设置。使用方法:见下文。
navigator :导航切换。使用方法:地址
switchTab :控制 tabBar 的切换。使用方法:地址
在这里,咱们讲下 tabBar
的坑,若是你在 app.json
中设置了 tabBar
:
app.json
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "./public/index_tabBar1_nor.png",
"selectedIconPath": "./public/index_tabBar1.png"
}, {
"pagePath": "pages/demo/demo",
"text": "发现",
"iconPath": "./public/index_tabBar2_nor.png",
"selectedIconPath": "./public/index_tabBar2.png"
}, {
"pagePath": "pages/logs/logs",
"text": "个人",
"iconPath": "./public/index_tabBar3_nor.png",
"selectedIconPath": "./public/index_tabBar3.png"
}]
}
复制代码
那么,咱们就须要经过设置 switchTab
来控制底部导航的跳转,而不能经过 navigator
来跳转:
demo.wxml
<view>
<button bindtap="linkTo">Hello</button>
</view>
复制代码
demo.js
linkTo: function () {
wx.switchTab({
url: '../index/index'
});
},
复制代码
那么问题又来了,当咱们切换到子页面的时候,咱们发现 tabBar
这个底部导航栏不见了,而后问了下 Ansen江 ,他说以前是整个小程序都有的,有些页面还要千方百计去隐藏。
可是如今嘛……它没了!没了啊!!!在微信小程序的文档没看到有唤起底部导航条的方法,难道我要作一个导航条了么 -_-||
回答是:yes!
因此,下面给出该底部导航条 tabBar
的实现状况和代码片断:
注:图片宽高均为 54rpx
*.wxml
<view class="nav">
<view class="nav-home" bindtap="goHome">
<image src="../../public/index_productDetail_icon_home.png"></image>
<text>首页</text>
</view>
<view class="nav-service">
<image src="../../public/index_productDetail_icon_service.png"></image>
<text>在线咨询</text>
</view>
<view class="nav-phone" bindtap="callWaiter">
<image src="../../public/index_productDetail_icon_phone.png"></image>
<text>电话咨询</text>
</view>
<navigator url="../indexPurchaseProduct/indexPurchaseProduct">
<view class="nav-buy">
<text>当即订购</text>
</view>
</navigator>
</view>
复制代码
*.wxss
.nav {
display: flex;
justify-content: space-around;
font-size: 20rpx;
border: 1px solid #ccc;
position: fixed;
bottom: 0;
background: #fff;
}
.nav view {
display: flex;
flex-direction: column;
align-items: center;
}
.nav image {
width: 54rpx;
height: 54rpx;
}
.nav text {
margin-top: 7rpx;
}
.nav-home {
border-right: 1px solid #ccc;
width: 130rpx;
padding-top: 5rpx;
}
.nav-service {
border-right: 1px solid #ccc;
width: 130rpx;
padding-top: 5rpx;
}
.nav-phone {
width: 130rpx;
padding-top: 5rpx;
}
.nav-buy {
background: #eb333e;
color: #fff;
width: 360rpx;
height: 98rpx;
line-height: 80rpx;
font-size: 34rpx;
}
复制代码
*.js
callWaiter: function(res) {
wx.makePhoneCall({
phoneNumber: '13264862250',
success: function(res) {
console.log("拨打成功");
console.log(res);
},
fail: function(res) {
console.log("拨打失败");
console.log(res);
},
complete: function(res) {
console.log("拨打完成");
console.log(res);
}
})
},
goHome: function() {
wx.switchTab({
url: '../index/index'
})
},
复制代码
在最近的工做中,又发现一个小问题:
像这个导航条,它须要根据页面所在的模块,动态地展现不一样位置的状态为活跃,并且它是须要在多个页面重复出现的,若是每一个页面我都要复制粘贴一遍,到时候要修改起来的时候,麻烦不说,最重要的是,它可能影响我前端的性能了,能不能直接将其封装起来呢?
自定义组件:连接
是的,发如今小程序文档中是存在这个东西的。固然,至于过程当中我百度了几篇文章来辅助写出下面的代码,你猜?
子组件写法
navBar.wxml
<!-- 底部导航条 -->
<view class="navBar">
<view class="navBar-home" bindtap='goHome'>
<image wx:if="{{homeActive}}" src="../../public/index_tabBar1.png"></image>
<image wx:if="{{!homeActive}}" src="../../public/index_tabBar1_nor.png"></image>
<text>首页</text>
</view>
<view class="navBar-explore" bindtap='goExplore'>
<image wx:if="{{exploreActive}}" src="../../public/index_tabBar2.png"></image>
<image wx:if="{{!exploreActive}}" src="../../public/index_tabBar2_nor.png"></image>
<text>发现</text>
</view>
<view class="navBar-user" bindtap='goUser'>
<image wx:if="{{userActive}}" src="../../public/index_tabBar3.png"></image>
<image wx:if="{{!userActive}}" src="../../public/index_tabBar3_nor.png"></image>
<text>个人</text>
</view>
</view>
复制代码
navBar.wxss
/* 底部导航条 */
.navBar {
width: 100%;
padding: 18rpx 0;
border-top: 1rpx solid #cccccc;
display: flex;
justify-content: space-around;
position: fixed;
bottom: 0;
background: #fff;
}
.navBar image {
width: 55rpx;
height: 55rpx;
}
.navBar view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 20rpx;
color: #999999;
}
.navBar-user text {
color: #d0a763;
}
复制代码
navBar.js
// pages/componentNavBar/navBar.js
Component({
/**
* 组件的属性列表
*/
properties: {
homeActive: {
type: Boolean,
value: false
},
exploreActive: {
type: Boolean,
value: false
},
userActive: {
type: Boolean,
value: false
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
// 返回首页
goHome: function (e) {
wx.switchTab({
url: '../index/index',
})
},
// 返回发现页
goExplore: function (e) {
wx.switchTab({
url: '../explore/explore',
})
},
// 返回个人页面
goUser: function (e) {
wx.switchTab({
url: '../user/user',
})
},
showCode: function(e) {
console.log(e);
let that = this;
console.log(that.data);
}
}
})
复制代码
navBar.json
{
"component": true,
"usingComponents": {}
}
复制代码
而后,在父组件的使用,只须要:
*.wxml
<view>
<navBar userActive="{{userActive}}"></navBar>
</view>
复制代码
*.json
{
"usingComponents": {
"navBar": "../componentNavBar/navBar"
}
}
复制代码
*.js
data: {
userActive: true
},
复制代码
怎样?这就是自定义组件的写法,是否是以为特好用涅,一次写完,终身受用。
本节目前已有1个坑,有兴趣的小伙伴能够详看。
在微信中,它自带了一套属于本身的单位:rpx
, rpx
不一样于以前咱们认识的 px
、 rem
、 em
,若是你的设计稿是 750 px 的,那么很容易的, 1px = 1rpx ,可是,若是设计稿不是 750 px ,那么将形成一个 bug ,至于这个 bug 如何解决……
-_-|| 谁知道呢……要不先把UI设计师宰了?
知识补充:关于 rpx 。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
若是你在开发过程当中,发现只能写英文了,而中文没法输入了,千万别急,也别怪输入法出 bug 了,你只须要:重启开发工具。
若是你某时刻,忽然发现你无法滚动代码进行查看,而是须要拖动滚动条才行,请别怪你的鼠标,你能够去浏览器打开一篇文章看看,enm...你的鼠标仍是好的~因此,请:重启开发工具。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
首先,科普下 组件 与 API 是什么:
Function
或者后端接口,前端直接调用就好了。 可是,在微信小程序官方文档中,组件与API,拆分地有点不科学。
例如:轮播图与底部导航条
wxml
、 wxss
、 js
中要设置对应的参数,一个只须要在 app.json
中设置就行。 可能微信小程序考虑到底部导航条不该该有太大的变化(例如让你修改太多样式或者 js
),因此将导航条内嵌至源码中了。
可是,这可能致使什么重要影响吗?是的,若是底部导航条须要进行修改呢?例如:3.2.3 自定义组件。这样的状况下,咱们的开发时间就有所增长了。
若是小伙伴你常常有去看微信小程序官方文档的话,那么你必定会有一件事须要吐槽,那就是:
明明上次我见到过某个 API 实现了我须要作的功能,可是改天我回去查找的时候,它却提示我没有这个玩意,这是什么鬼?!
是的,跟咱们 3.13 黑科技:<modal> 这一章中讲到的 <modal>
这个黑科技同样,有时候官方文档也不是万能的,它总会有这样那样的毛病,致使咱们找不到须要的东西,只能去百度了 -_-||
本节目前已有 3 个坑,有兴趣的小伙伴能够详看。
Flex布局又称弹性布局,在小程序开发中比较适用。可是因为 jsliang 以前没怎么用过 Flex 布局,因此在这里我们特地去踩下坑,充实下本身。【小程序开发之页面布局】【阮一峰-Flex 布局教程】
在咱们布局页面的时候,最好看看 阮一峰 的教程,平时遇到布局的问题的时候,我都习惯去上面 阮一峰 的文章看看:
基础概念:地址
<!-- 设置 flex 布局 -->
display: flex;
<!--
一、决定主轴的方向
row - (默认)水平方向,起点在左端
row-reverse - 水平方向,起点在右端
column - 垂直方向,起点在上沿
column-reverse - 垂直方向,起点在下沿
-->
flex-direction: row | row-reverse | column | column-reverse;
<!--
二、一条轴线(一行)排不下时如何解决
nowrap - (默认)不换行
warp - 换行,第一行在上方
wrap-reverse - 换行,第一行在下方
-->
flex-wrap: nowrap | wrap | wrap-reverse;
<!--
三、flex-flow = flex-direction + flex-wrap。即 flex-flow 是这两个属性的合集
row nowrap - (默认)水平方向,起点在左端,不换行
-->
flex-flow: <flex-direction> || <flex-wrap>;
<!--
四、justify-content 定义项目在主轴上的对齐方式
flex-start - 左边对齐
flex-end - 右边对齐
center - 居中对齐
space-between - 两端对齐,空格在中间
space-around - 空格环绕
-->
justify-content: flex-start | flex-end | center | space-between | space-around;
<!--
五、align-items 定义项目在交叉轴上如何对齐
flex-start - 顶部对齐,即文字图片等顶部同一条线上
flex-end - 底部对其,即文字图片等底部在同一条线上
center - 中间对其,即文字图片无论多高,都拿它们的中间放在同一条线上
stretch - 将文字图片充满整个容器的高度,强制统一
baseline - 将每项的第一行文字作统一在一条线上对齐
-->
align-items: flex-start | flex-end | center | stretch | baseline;
<!--
六、align-content 定义多根轴线的对齐方式。若是只有一根轴线(只有一行),该属性不起做用
flex-start - 这几行顶部对齐
flex-end - 这几行底部对齐
center - 这几行居中对齐
stretch - 这几行进行扩展或者缩放,从而填满容器高
space-between - 这几行中间使用空格进行填充
space-around - 这几行两边及中间进行填充
-->
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
复制代码
实现效果以下:
如图,这是咱们要实现的左右布局效果。那么,在微信小程序要怎么作呢?
*.wxml
<view class="top-recommended-headlines">
<view class="top-recommended-headlines-left">
<text>热门推荐</text>
</view>
<view>
<image src="../../public/index_top_recommended_headlines.png"></image>
</view>
<view class="top-recommended-headlines-right">
<navigator url="../indexProduct/indexProduct">查看所有 ></navigator>
</view>
</view>
复制代码
*.wxss
.top-recommended-headlines {
display: flex;
align-items: flex-end;
height: 31rpx;
line-height: 31rpx;
margin-bottom: 10rpx;
}
.top-recommended-headlines-left text {
font-size: 32rpx;
font-weight: bold;
}
.top-recommended-headlines image {
width: 366rpx;
height: 31rpx;
margin-left: 10rpx;
}
.top-recommended-headlines-right navigator {
font-size: 26rpx;
color: #a9a9a9;
margin-left: 50rpx;
}
复制代码
实现效果以下:
如图,这是咱们要实现的左右布局效果。那么,在微信小程序要怎么作呢?
*.wxml
<view class="weui-tab__content-item3" wx:for="{{tabs3Content}}" wx:key="{{item.index}}">
<navigator url="../indexProductArticle/indexProductArticle">
<view class="weui-tab__content-item3-question">
<image src="../../public/index_productDetail_icon_question.png"></image>
<text>{{item.title}}</text>
</view>
<view class="weui-tab__content-item3-answer">
<image src="../../public/index_productDetail_icon_answer.png"></image>
<text>{{item.content}}</text>
</view>
<view class="weui-tab__content-item3-detail">
<text class="weui-tab__content-item3-detail-datatime">{{item.datatime}}</text>
<text class="weui-tab__content-item3-detail-reader">{{item.reader}}阅读</text>
<text class="weui-tab__content-item3-detail-label">#{{item.label}}#</text>
</view>
</navigator>
<view class="weui-tab__content-item3-gap">
<image src="../../public/index_productDetail_gap.png"></image>
</view>
</view>
复制代码
*.wxss
.weui-tab__content-item3 {
padding-left: 30rpx;
padding-right: 30rpx;
margin-top: -10rpx;
margin-bottom: 10rpx;
}
.weui-tab__content-item3:first-child {
padding: 40rpx 30rpx 0;
}
.weui-tab__content-item3-question image {
width: 30rpx;
height: 30rpx;
}
.weui-tab__content-item3-question text {
font-size: 30rpx;
line-height: 46rpx;
font-weight: bold;
color: #333;
margin-left: 10rpx;
}
.weui-tab__content-item3-answer image {
width: 30rpx;
height: 30rpx;
}
.weui-tab__content-item3-answer text {
font-size: 26rpx;
line-height: 42rpx;
color: #a9a9a9;
margin-left: 10rpx;
}
.weui-tab__content-item3-detail {
display: flex;
justify-content: space-between;
font-size: 26rpx;
color: #a9a9a9;
}
.weui-tab__content-item3-detail-label {
color: #d0a763;
}
.weui-tab__content-item3-gap image {
width: 100%;
height: 30rpx;
}
复制代码
*.js
tabs3Content: [
{
title: '员工发明创造是否属于职务发明的认证标准?',
content: '随着企业对知识产权在企业发展中核心竞争力的认识力提升,企业保护自身知识产权的意识不断加强,使其技术得......',
datatime: '2018-03-05',
reader: '2081',
label: '知识产权'
}
]
复制代码
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
在小程序中,若是你使用 wxss,你是能够发现有 background-image
的提示的。可是,若是你设置它的背景图是本地图片,你会发现,它是不生效的。
解决方案:
https://xxxx/xxx.jpg
;image
组件 + position
定位而不是使用 background-image
。本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
二者的区别是,<view>
是一个组件,会在页面上作渲染;<block>
不是一个组件,它仅仅是一个包装元素,只接受控制属性,不会在页面中作任何渲染。
因此,若是你仅仅是须要包裹,而不是渲染一个层,可使用 <block>
提高性能。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
首先,咱们要实现的效果是:
而后, jsliang 的想法是:
*.wxml
<view class="search">
<input class="search-product-input" bindinput="bindKeyInput" auto-focus maxlength='10'></input>
<label class="search-placeholder">
<image class="search-placeholder-icon" src="../../public/index_indexProduct_icon_search.png"></image>
<text class="search-placeholder-text">搜索产品</text>
</label>
<view></view>
</view>
复制代码
*.wxss
.search {
height: 100rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
position: relative;
}
.search-product-input {
background: #f5f5f5;
width: 650rpx;
height: 65rpx;
border-radius: 30rpx;
font-size: 30rpx;
padding-left: 20rpx;
}
.search-placeholder {
font-size: 26rpx;
text-align: center;
margin-top: -65rpx;
z-index: 2;
}
.search-placeholder-icon {
width: 24rpx;
height: 24rpx;
}
.search-placeholder-text {
margin-left: 10rpx;
}
复制代码
你注意到了吗?在 *.wxml
中, jsliang 设置了个空的 <view>
,若是你把这个 <view>
去掉,你会惊奇地发现,它……下来了……
好吧,可能有其余的实现方式,可是若是你下次使用这种方式,注意上面这个坑~
回头看了下 WeUI
的实现方式,发现跟个人思路是挺像的,关于 input
的实现方式,如今依据 WeUI
,成功实现了输入框:
源码奉上:
*.wxml
<!-- 搜索框 -->
<view class="search">
<view class="weui-search-bar">
<view class="weui-search-bar__form {{inputShowed ? 'form-border' : ''}}">
<view class="weui-search-bar__box">
<icon class="weui-icon-search_in-box" type="search" size="14"></icon>
<input type="text" class="weui-search-bar__input" placeholder="搜索" value="{{inputVal}}" focus="{{inputShowed}}" bindinput="inputTyping" />
<view class="weui-icon-clear" wx:if="{{inputVal.length > 0}}" bindtap="clearInput">
<icon type="clear" size="14"></icon>
</view>
</view>
<label class="weui-search-bar__label" hidden="{{inputShowed}}" bindtap="showInput">
<icon class="weui-icon-search" type="search" size="14"></icon>
<view class="weui-search-bar__text">搜索</view>
</label>
</view>
<view wx:if="{{inputVal.length <= 0}}" class="weui-search-bar__cancel-btn" hidden="{{!inputShowed}}" bindtap="hideInput">取消</view>
<view wx:if="{{inputVal.length > 0}}" class="weui-search-bar__submit-btn" hidden="{{!inputShowed}}" bindtap="searchInput">搜索</view>
</view>
</view>
复制代码
*.js
Page({
data: {
inputShowed: false,
inputVal: ""
},
showInput: function () {
this.setData({
inputShowed: true
});
},
hideInput: function () {
this.setData({
inputVal: "",
inputShowed: false
});
},
clearInput: function () {
this.setData({
inputVal: ""
});
},
inputTyping: function (e) {
this.setData({
inputVal: e.detail.value
});
}
})
复制代码
*.wxss
.search {
height: 100rpx;
padding: 18rpx 30rpx;
}
.weui-search-bar {
padding: 0;
background-color: #fff;
border-top: none;
border-bottom: none;
height: 64rpx;
}
.weui-search-bar__form {
border: none;
}
.form-border {
border: 1rpx solid #f5f5f5;
background: #f5f5f5;
}
.weui-search-bar__label {
background: #f5f5f5;
border-radius: 30rpx;
}
.weui-search-bar__cancel-btn {
font-size: 26rpx;
background: rgb(8, 202, 186);
color: #fff;
padding: 2rpx 20rpx 0 20rpx;
border-radius: 10rpx;
}
.weui-search-bar__submit-btn {
font-size: 26rpx;
background: rgb(8, 200, 248);
color: #fff;
padding: 10rpx 20rpx 0 20rpx;
border-radius: 10rpx;
}
复制代码
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
官方文档:地址
在这里,提醒广大小伙伴注意了,注意了,注意了!重要的事说三遍。
当你新建 page
的时候,微信 web 开发者工具会自动帮你添加分享事件:
/**
* 用户点击右上角分享
*/
onShareAppMessage: function (res) {
// 实现分享功能
return {
title: this.data.productName,
path: '/pages/indexProductDetail.js?productId=' + this.data.productId,
imageUrl: this.data.videoImageSrc,
success: (res) => {
console.log("分享成功~");
},
fail: (res) => {
console.log("分享失败~");
}
}
}
复制代码
因此,若是你在前面定义了,它会在最下面偷偷帮你清空,而后你就以为没法自定义分享事件……
是的,jsliang 打死都不认可这是我本身的锅,新手注意!新手注意!!新手注意!!!
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
熟知盒模型的小伙伴应该知道,盒模型有两种计算方式:
在 border-box
中,整个 view
的宽、高,包括 margin
、padding
、border
。
而在 content-box
中,整个 view
的宽、高,则不包括上面元素。
如上图,若是一个 view
,你的代码以下:
view {
box-sizing: border-box;
margin: 10rpx;
width: 100rpx;
height: 100rpx;
padding: 10rpx;
}
复制代码
那么,你的整个宽高仍是 100rpx
。
可是,若是你的代码以下:
view {
box-sizing: content-box;
margin: 10rpx;
width: 100rpx;
height: 100rpx;
padding: 10rpx;
}
复制代码
那么,你的整个盒子宽高是 120rpx
。
若是你在设计页面中,发现内容区被撑爆了,那么,请检查下如今的 border-box
是什么。
本节目前已有 6 个坑,有兴趣的小伙伴能够详看。
使用 WeUI 的导航条,首先须要引用 WeUI 的 CSS 样式:地址
下载 weui.wxss
并在 app.wxss
中引用便可
app.wxss
/* 引用WeUI */
@import 'weui.wxss';
复制代码
而后,咱们直接往页面加入它的选项卡并根据项目需求修改其样式:
*.wxml
<view class="tab">
<view class="weui-tab">
<view class="weui-navbar">
<block wx:for="{{tabs}}" wx:key="*this">
<view id="{{index}}" class="weui-navbar__item {{activeIndex == index ? 'weui-bar__item_on' : ''}}" bindtap="tabClick">
<view class="weui-navbar__title">{{item}}</view>
</view>
</block>
<view class="weui-navbar__slider" style="left: {{sliderLeft}}px; transform: translateX({{sliderOffset}}px); -webkit-transform: translateX({{sliderOffset}}px);"></view>
</view>
<view class="weui-tab__panel">
<!-- 所有 -->
<view class="weui-tab__content" hidden="{{activeIndex != 0}}">
<view class="weui-tab__content-item1">
<text>所有</text>
</view>
</view>
<!-- 已付款 -->
<view class="weui-tab__content" hidden="{{activeIndex != 1}}">
<view class="weui-tab__content-item2">
<text>已付款</text>
</view>
</view>
<!-- 待付款 -->
<view class="weui-tab__content" hidden="{{activeIndex != 2}}">
<view class="weui-tab__content-item3">
<text>待付款</text>
</view>
</view>
</view>
</view>
</view>
复制代码
*.wxss
.tab {
font-size: 26rpx;
}
.tab image {
width: 173rpx;
height: 29rpx;
}
.weui-navbar {
border-top: 1rpx solid #efefef;
border-bottom: 1rpx solid #efefef;
}
.weui-navbar__slider {
background: #d0a763;
width: 4em;
}
.weui-navbar__item.weui-bar__item_on {
color: #d0a763;
}
.weui-tab__content {
margin-bottom: 100rpx;
}
复制代码
*.js
var sliderWidth = 52; // 须要设置slider的宽度,用于计算中间位置
Page({
/**
* 页面的初始数据
*/
data: {
// 选项卡导航
tabs: ["所有", "已付款", "待付款"],
activeIndex: 1,
sliderOffset: 0,
sliderLeft: 0,
},
// 选项卡切换
tabClick: function (e) {
this.setData({
sliderOffset: e.currentTarget.offsetLeft,
activeIndex: e.currentTarget.id
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 计算搜索框活跃条
var that = this;
wx.getSystemInfo({
success: function (res) {
that.setData({
sliderLeft: (res.windowWidth / that.data.tabs.length - sliderWidth) / 2,
sliderOffset: res.windowWidth / that.data.tabs.length * that.data.activeIndex
});
}
});
}
})
复制代码
自定义选项卡的代码实现:
实现效果图以下:
实现代码以下:
*.wxml
<view>
<view class="weui-tab__nav">
<text wx:for="{{tabs2Nav}}" wx:key="item.index" class="{{item.state == 1 ? 'weui-tab__nav-active' : ''}}" bindtap="tabs2NavClick" data-labelId="{{item.id}}">{{item.label}}</text>
</view>
<view class="weui-tab__content-item2" wx:for="{{tabs2Content}}" wx:key="{{item.index}}">
<view class="weui-tab__content-item-descritpion">
<view class="{{item.type == 1 ? 'weui-tab__content-item-icon-type' : 'hide'}}">
<image src="../../public/index_productDetail_icon_word.png"></image>
</view>
<view class="{{item.type == 2 ? 'weui-tab__content-item-icon-type' : 'hide'}}">
<image src="../../public/index_productDetail_icon_excel.png"></image>
</view>
<view class="{{item.type == 3 ? 'weui-tab__content-item-icon-type' : 'hide'}}">
<image src="../../public/index_productDetail_icon_ppt.png"></image>
</view>
<view class="{{item.type == 4 ? 'weui-tab__content-item-icon-type' : 'hide'}}">
<image src="../../public/index_productDetail_icon_pdf.png"></image>
</view>
<view class="weui-tab__content-item-descritpion-content">
<text class="weui-tab__content-item-descritpion-content-title">{{item.title}}</text>
<view class="weui-tab__content-item-descritpion-content-datatime">
<text class="weui-tab__content-item-descritpion-content-datatime1">发布时间:{{item.datatime}}</text>
<text class="{{item.effectiveTime ? 'weui-tab__content-item-descritpion-content-datatime2' : 'hide'}}">生效时间:{{item.effectiveTime}}</text>
</view>
</view>
</view>
<view class="weui-tab__content-item-download-state" bindtap='downloadFile' data-url="{{item.url}}">
<image src="../../public/index_productDetail_icon_undown.png"></image>
</view>
</view>
</view>
复制代码
*.wxss
.weui-tab__nav {
background: #f5f5f5;
border: 1rpx 0rpx solid #e6e6e6;
height: 90rpx;
padding: 17rpx 41rpx;
display: flex;
justify-content: space-between;
}
.weui-tab__nav text {
border-radius: 56rpx;
height: 56rpx;
line-height: 56rpx;
padding: 15rpx 23rpx;
font-size: 26rpx;
font-weight: bold;
}
.weui-tab__nav-active {
color: #fefefe;
background: #d0a763;
}
.weui-tab__content-item2 {
display: flex;
justify-content: space-between;
padding: 25rpx 30rpx;
}
.weui-tab__content-item-descritpion {
display: flex;
justify-content: space-between;
}
.weui-tab__content-item-descritpion image {
width: 60rpx;
height: 70rpx;
}
.hide {
display: none;
}
.weui-tab__content-item-descritpion-content {
margin-left: 26rpx;
}
.weui-tab__content-item-descritpion-content-title {
font-size: 28rpx;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.weui-tab__content-item-descritpion-content-datatime {
font-size: 22rpx;
color: #bbb;
}
.weui-tab__content-item-descritpion-content-datatime1 {
margin-right: 35rpx;
}
.weui-tab__content-item-download-state image {
width: 64rpx;
height: 64rpx;
}
复制代码
*.js
data: {
tabs2Nav: [
{
id: '1',
label: '法律大全',
state: 1
},
{
id: '2',
label: '合同模板',
state: 0
},
{
id: '3',
label: '民事',
state: 0
},
{
id: '4',
label: '行政',
state: 0
},
{
id: '5',
label: '执行',
state: 0
}
],
tabs2Content: [
{
title: '中华人名共和国民用航空法(2015年...).doc',
url: 'https://wxmcard.imusic.cn/testfordocdownload.doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
},
{
title: '原生申诉表格.xls',
url: 'https://wxmcard.imusic.cn/testfordocdownload.doc',
datatime: '2018-01-26',
type: '2'
},
{
title: '法律常识大聚集及范例.ppt',
url: 'https://wxmcard.imusic.cn/testforpptdownload.pptx',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '3'
},
{
title: '事业单位法律基础知识总结.pdf',
url: 'https://wxmcard.imusic.cn/testforpdfdownload.pdf',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '4'
}
],
// 选项卡第二屏分组
tabs2Content1: [
{
title: '中华人名共和国民用航空法(2015年...).doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
},
{
title: '原生申诉表格.xls',
datatime: '2018-01-26',
type: '2'
},
{
title: '法律常识大聚集及范例.ppt',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '3'
},
{
title: '事业单位法律基础知识总结.pdf',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '4'
}
],
tabs2Content2: [
{
title: '合同模板.doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
}
],
tabs2Content3: [
{
title: '民事合同模板.doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
}
],
tabs2Content4: [
{
title: '行政合同模板.doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
}
],
tabs2Content5: [
{
title: '执行合同模板.doc',
datatime: '2018-01-26',
effectiveTime: '2018-01-26',
type: '1'
}
]
},
// 选项卡2切换
tabs2NavClick: function(e) {
var that = this;
console.log("完整的数据是:");
console.log(that.data.tabs2Nav);
console.log("点击的标签是:");
console.log(e.currentTarget.dataset.labelid);
var newTabs2Content;
console.log("正在经历的标签是:");
for(var i=0; i<that.data.tabs2Nav.length; i++) {
console.log(that.data.tabs2Nav[i].id);
that.data.tabs2Nav[i].state = 0;
if (that.data.tabs2Nav[i].id == e.currentTarget.dataset.labelid) {
that.data.tabs2Nav[i].state = 1;
console.log("将改变的标签是:");
console.log(that.data.tabs2Nav[i]);
// 改变内容
var changeContentName = "tabs2Content" + (i + 1);
if (changeContentName == "tabs2Content1") {
console.log(that.data.tabs2Content1);
newTabs2Content = that.data.tabs2Content1;
} else if (changeContentName == "tabs2Content2") {
newTabs2Content = that.data.tabs2Content2;
} else if (changeContentName == "tabs2Content3") {
newTabs2Content = that.data.tabs2Content3;
} else if (changeContentName == "tabs2Content4") {
newTabs2Content = that.data.tabs2Content4;
} else {
newTabs2Content = that.data.tabs2Content5;
}
console.log("但愿转换内容到:");
console.log(changeContentName);
}
}
this.setData({
tabs2Nav: that.data.tabs2Nav,
tabs2Content: newTabs2Content
})
},
复制代码
绑定事件如何传递数据:
若是学过 Vue
的同窗,应该知道 Vue
的数据传递形式是: @click='tabs2NavClick(item.id)'
那么,在微信小程序中,你千万记得,绑定时间的传递参数的方式不是这样子的,而是:
<text wx:for="{{tabs2Nav}}" wx:key="item.index" bindtap="tabs2NavClick" data-labelId="{{item.id}}">{{item.label}}</text>
复制代码
经过 data-*="{{item}}"
的形式传递的~而后你须要在 js
中,经过 e.currentTarget.dataset.labelid
来获取。
而后,注意了,这里还有个小 bug。在代码中,咱们使用的是 data-labelId="{{item.id}}"
,而获取数据的时候,咱们获取的是 labelid
,是的,驼峰不见了~
参考连接:连接
如何在方法中获取 data
中定义的数据:
若是我想在选项卡切换的方法 tabs2NavClick
中获取 data
里面的数据,那么我应该怎么作呢?
是的,经过:
tabs2NavClick: function(e) {
var that = this;
console.log(that.data.tabs2Nav);
}
复制代码
这种形式,咱们就能够获取到 data
中的数据。
参考连接:连接
如何实现文字省略:
加入你有一段文本,你想让页面根据自身宽度,自动省略多余长度,那么,咱们能够设置下面的 css
代码,从而实现文字省略效果(不使用 js 的缘由,是由于 js 没有 css 那么灵活)
text {
overflow:hidden;
text-overflow:ellipsis;
display:-webkit-box;
-webkit-line-clamp:1;
-webkit-box-orient:vertical;
}
复制代码
参考连接:连接
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
一个坑就是一个故事。
故事都有四元素:时间,地点,人物,事情。
前三个自没必要说,咱们直接讲事情通过:咱们项目的负责人须要一个留言弹窗,而后里面有个文本框能够填信息,最后点击【留言】按钮将数据传到后端,点击【取消】按钮关闭弹窗。
需求是否是很简单~既然微信小程序有本身的官方文档。那么,怎么方便怎么来吧,因而 jsliang 在微信小程序中搜索关键字 弹窗
:
看了下搜索记录,最匹配的就是上面这个了。enm...好像没看到放文本框的?先试试:
额(⊙o⊙)…
很差意思打扰了,我去百度看看:连接
咦~ 它这里好像有个 <modal>
标签?Ctrl+C、Ctrl+V 试试先~
Duang~~~这不就是我要的效果么,挖槽,黑科技?因而 jsliang 去小程序那里搜了下 modal
……enm...蜜汁尴尬,好像只有上面的 wx.showModal()
方法……因而 jsliang 满头黑线……好嘛,黑科技黑科技!!!
下面贴出实现代码:
*.wxml
<text class="article-message-board-head-addMessage" bindtap="modalinput">写留言</text>
复制代码
*.js
Page({
data: {
// 弹窗
hiddenmodalput: true, //能够经过hidden是否掩藏弹出框的属性,来指定那个弹出框
},
//点击按钮指定的hiddenmodalput弹出框
modalinput: function () {
this.setData({
hiddenmodalput: !this.data.hiddenmodalput
})
},
//取消按钮
cancel: function () {
this.setData({
hiddenmodalput: true
});
},
//确认
confirm: function () {
wx.showToast({
title: '留言成功!',
})
this.setData({
hiddenmodalput: true
})
}
})
复制代码
好的,上面就实现了一个简单的可填写文本的弹窗了。
做为一枚职业填坑人,怎么能知足于上面的两种弹窗形式呢!因而,使用百度大法又找到了一篇填坑文:连接
因此,总结下就有了四种弹窗写法:
类型 | 说明 | 地址 |
---|---|---|
模态弹窗 | wx.showModal(Object) - 模态弹窗就是上面的第一种弹窗,它能够给你选择【取消】或者【肯定】 | 连接 |
<modal> | <modal>是上面的第二种弹窗,能够提供用户填写 | 连接 |
消息弹窗 | wx.showToast(Object) - 消息弹窗就是操做成功或者操做失败的那一刻,系统的提示弹窗,无需用户操做,可设定几秒自动关闭 | 连接 |
操做菜单 | wx.showActionSheet(Object) - 操做菜单相似于弹出的下拉菜单,提供你选择其中某项或者【取消】 | 连接 |
在这里,就讲完了微信小程序的四种弹窗形式了。若是你改样式改的烦啊烦的,可能你须要封装一个属于本身的弹窗?嘿嘿,说不定你的产品经理会有兴趣让你开发一个 beautiful
弹窗的~
这坑我不填,我没碰到~碰到了再说!在这里预留下这个坑,哈哈。
本节目前已有 6 个坑,有兴趣的小伙伴能够详看。
在小程序的文章处理中,文章的主体内容,通常来讲,后端会采用富文本的形式存储数据到数据库。就是说,你要在 view
中展现 html
变迁。可是,你知道的,小程序不采用浏览器的那一套,因此,你可能须要兜圈子了:连接
在上面这篇文章中,讲述了三种解析富文本的方法:
在百度大法的渲染下,jsliang 采用了 wxParse。
Github 的 wxParse 地址:连接
使用方法很简单,照着它 GitHub 地址去撸就是了。然而,坑不是那么容易填的 o(╥﹏╥)o
( bug1 )wxParse 在其神秘源码中,会将你的 html+css 样式弄乱,例如:px
要转成 rpx
,才能在小程序中正常显示,若是你不处理……enm...你试试~
( bug2 )而后,若是你忽然发现,内容没法显示,那么,恭喜你又触发了 bug,这个是 wxParse 代码的一个 bug,在一些特殊的手机里面,在 wxparse/html2json.js 中的第 112 和 119行,都有一个 console.dir() 这个函数的使用,它使你的内容不能正常显示了。把这个函数注释掉,内容就能够正常显示出来了。
if (name == 'class') {
// console.dir(value); // 112 行
// value = value.join("")
node.classStr = value;
}
// has multi attibutes
// make it array of attribute
if (name == 'style') {
// console.dir(value); // 119行
// value = value.join("")
node.styleStr = value;
}
复制代码
( bug3 )若是你须要引用图片,那么,你会发现引用不成功。这是由于,咱们在网页后台编辑器里面上传的图片,是采用相对路径的,上传成绝对网络地址路径以后,换成域名,就无法很好的展现了。因此最好的方法,就是修改 html2json.js 这个文件,让 wxParse 自动添加域名前缀:地址
( bug4 )空格无法被正确替换,须要修改 wxDiscode.js
中的 strcharacterDiscode
:
// 将原语句注释掉,替换为下面的语句
// str = str.replace(/ /g, ' ');
str = str.replace(/ /g, '\xa0');
复制代码
( bug5 )如何干掉控制台 console 中 wxParse 的一大串输出:
wxParse.js
// 36行注释掉
console.log(JSON.stringify(transData, ' ', ' '));
// 41行注释掉
console.log(JSON.stringify(transData, ' ', ' '));
复制代码
综上,jsliang 气得差口吐白沫了……换换换!有空要换成其余两种方式才行!!!
jsliang 还未使用过 rich-text,这里先预留个坑。若是小伙伴们在开发 rich-text 过程当中碰到过各类坑,能够跟 jsliang 提一下,我会写进这章节,顺带在章节尾写上你的大名,辛苦了~
jsliang 还未使用过 web-view,这里先预留个坑。若是小伙伴们在开发 web-view 过程当中碰到过各类坑,能够跟 jsliang 提一下,我会写进这章节,顺带在章节尾写上你的大名,辛苦了~
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
在小程序中,它有一些自定义的方法,例如 open-type
,是须要 <button>
来承接的。
因此,若是你写好了一个 view
,里面有很好看的样式了,你原本打算用 bindtap
来搞事情的。可是,忽然接到信息,须要外套一层 <button open-type="***">
,而后发现,样式须要从新跳过……
enm...加油不哭,从新写过样式吧~
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
就像上一章所说的,有时候,无可奈何,咱们必须在 <button>
中内嵌个 <image>
或者 <text>
之类的,那么,通常怎么作呢?
如今,假设我有一个 42*40
的图片,我来试试调下它的样式:
*.wxml
<button open-type='share'>
<image src="../../public/explore_activityDetail_icon_share.png"></image>
</button>
复制代码
*.wxss
.activity-user-action button {
width: 42rpx;
height: 80rpx;
margin: 0;
padding: 0;
margin-top: -21rpx;
background: #fff;
}
.activity-user-action button::after {
border: none;
}
.activity-user-action image:last-child {
width: 42rpx;
height: 40rpx;
}
复制代码
如上,咱们须要设置这个按钮的高度是图片高度的 2 倍,而后还须要设置 margin-top
的高度为图片高度的 1/2(注意 margin 与 margin-top 的顺序,若是你不知道顺序的重要性,推荐你使用 margin: -21rpx 0 0 0
),同时 margin
、padding
、background
、border
须要清空。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
在浏览器中,有 F5 刷新,有鼠标滚轮滑动加载。
那么,换到微信小程序,又是怎样子的呢?
是的,这就要说说用户下拉动做和上拉触底了:
下拉事件在小程序文档的解释:连接
/**
* 页面相关事件处理函数--监听用户下拉动做
*/
onPullDownRefresh: function () {
},
复制代码
上拉触底在小程序文档的解释:连接
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
复制代码
这两个事件,都是在你新建 page
的时候,会自动添加的,小伙伴们注意下,省得前面写了,被后面的覆盖了哦~
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
<input class="phone-number"></input>
<button>获取电话号码</button
如今,假设咱们在小程序中输入了一个 class
为 phone-number
的input
框,而且有一个 <button>
。那么,咱们在微信小程序中,须要如何获取该 phone-number
的值呢?
<input>
中设置 name
为 phoneNumber
data
中设置 phoneNumber: ''
bindinput="phoneCodeInput'
phoneCodeInput
方法来修改 this.data.phoneNumber
<button>
的绑定事件中获取 phoneNumber
步骤繁杂,下面贴出实现代码:
*.wxml
<input maxlength='11' placeholder='请输入手机号码' placeholder-class="phone-number" name="phoneNumber" bindinput='phoneNumberInput'></input>
<button class="get-phone-number" bindtap="getPhoneNumber">获取验证码</button>
复制代码
*.js
Page({
data: {
// 输入的手机号码
phonenumber: '',
},
// 获取手机号码
phoneNumberInput: function(e) {
this.setData({
phonenumber: e.detail.value
})
},
// 点击获取验证码
getPhoneNumber: function (e) {
var phoneNumber = this.data.phonenumber
console.log(phoneNumber);
}
})
复制代码
看到这里,若是有习惯 jQuery
,习惯操做 dom
节点的小伙伴可能会抱有很大疑惑:“它就不能像 jQuery
同样直接获取 dom
的内容么?”
答案是,有的:地址。可是,在这里, jsliang 并不推荐小伙伴这么作,想要学好一门新的技术,就不能由于旧的技术而限制了本身的观念。时代在进步,科技在发展,咱们不学更多的知识,只能被淘汰在前端的潮流中。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
此次的需求是:判断用户是否登陆,若是登陆了就跳转到首页,若是没登陆就跳转到登陆页。
咱们都知道,在微信小程序中,有个 onLaunch
方法,微信小程序官方文档对其描述就是:每一个页面进来须要先加载 onLaunch
方法,再去执行其余方法。而后,在 jsliang 尝试设置在 onLaunch
中调用 wx.login()
,却发现,index.js
的 onLoad
方法是先于 onLaunch
执行的,这致使咱们无法预先获取到须要的信息:
index.js
的 onload
app.js
中的 onLaunch
迫于无奈,jsliang 对其进行了百度:地址 。经过百度这篇文章发现,有两个解决方案:
Promise
来进行进程管理 可是,因为 jsliang 对于 Promise
的作法,以为其太过复杂,故新增了一个 page/login
。
那么,如何在用户进入首页阅读文章、查看产品以前,先对用户进行微信受权、帐号登陆呢?
onLogin
的 Storage
,在 index.js
中的 onload
进行判断,若是用户未进行登陆,则使用 wx.redirectTo()
跳转到登陆页面:index.js
onLoad: function (options) {
if (!wx.getStorageSync('isLogin')) {
wx.redirectTo({
url: '../login/login',
})
}
}
复制代码
onLogin
为 true
。login.js
loginSubmit: function(e) {
wx.setStorageSync('isLogin', true);
}
复制代码
这样,咱们就作到了路由守卫,即你不登陆,不给跳转到首页。
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
在 Ansen江 的推荐下,参照 Ansen江 的 api.js
,对我这边的小程序接口 request
进行了 promise
封装,并作了 api.js
的分离。
api.js
/*
* @Author: jsliang
* @Date: 2018-10-11 09:11:26
* @LastEditors: jsliang
* @LastEditTime: 2018-10-11 09:11:29
* @Description: 接口文件
*/
// 引入请求头文件
import header from './header.js';
// 加载中
const Loading = {
show() {
wx.showLoading({
title: '加载中'
});
},
hide() {
wx.hideLoading()
}
};
// 加载中白名单
const loadingWhite = [
'index/index'
]
// 将请求进行 Promise 封装
const fetch = ({url, data, header}) => {
// 白名单地址会显示加载中状态
if(loadingWhite.includes(url)) {
Loading.show();
}
// 打印接口请求的信息
console.log(`【step1】API接口:${url}`);
console.log("【step2】header请求头:");
console.log(header);
console.log("【step3】data传参:");
console.log(data);
// 返回 Promise
return new Promise((resolve, reject) => {
wx.request({
url: getApp().globalData.api + url,
header: header,
data: data,
success: res => {
Loading.hide();
// 成功时的处理
if (res.data.code == 0) {
console.log("【step4】请求成功:");
console.log(res.data);
return resolve(res.data);
} else {
wx.showModal({
title: '请求失败',
content: res.data.message,
showCancel: false
});
}
},
fail: err => {
Loading.hide();
// 失败时的处理
console.log(err);
return reject(err);
}
})
})
}
/**
* code 换取 openId
* @data {
* jsCode - wx.login() 返回的 code
* }
*/
export const wxLogin = data => {
return fetch({
url: "tbcUser/getWechatOpenId",
header: header.newHeader(),
data: data
})
}
复制代码
在上面进行了分离 api.js
后,接着在其余地方调用 api.js
:
login.js
import {
wxLogin
} from "../../utils/api.js"
// 登陆
wx.login({
success: res => {
// 发送 code ,获取 openId
console.log("\n【API:获取 openId】");
wxLogin({
jsCode: res.code
}).then(
res => {
console.log("【step5】返回成功处理:");
console.log(res);
},
err => {
console.log("【step5】返回失败处理:");
console.log(err);
}
)
}
})
复制代码
输出结果:
这样,咱们就成功作了 request
的封装,并经过调用 api.js
的形式,分离了代码,从而更方便地进行编程。
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
在页面布局中,咱们常用列表展现,而后在列表展现上,有时候该列表的最后一条数据下面是没有下划线或者虚线的。
那么,当数据读取到最后一条的时候,如何判断已经到了最后一条,再也不展现下划线或者虚线呢?
*.wxml
<view class="content">
<view wx:for="{{topRecommended}}" wx:key="{{item.recommendId}}" wx:for-index="index">
<navigator url="../indexProduct/indexProduct">
<view class="content-item">
<image src="{{item.coverImage}}"></image>
<view class="content-item-text">
<text class="content-item-text-title">{{item.recommendTitle}}</text>
<text class="content-item-text-content">{{item.recommendDescription}}</text>
<view class="content-item-text-row">
<text class="content-item-text-user">{{item.userCount}}人在用</text>
<text class="content-item-text-price">¥{{item.productPrice}}</text>
</view>
</view>
</view>
</navigator>
<view class="{{(index+1) == topRecommended.length ? 'content-item-gap-hide' : 'content-item-gap'}}">
<image src="../../public/index_top_recommended_content_item_gap.png"></image>
</view>
</view>
</view>
复制代码
在这里,咱们对要循环的对象使用 wx:for-index="index
,即:
<view wx:for="{{topRecommended}}" wx:key="{{item.recommendId}}" wx:for-index="index">
复制代码
而后,咱们在下划线/点虚线位置进行 class
判断,若是 index+1 == topRecommended.length
,那么咱们就换一个样式:class="{{(index+1) == topRecommended.length ? 'class1' : 'class2'}}"
,即:
<view class="{{(index+1) == topRecommended.length ? 'content-item-gap-hide' : 'content-item-gap'}}">
复制代码
这样,咱们就作到了判断是否处于最后一条数据,从而经过 class
来隐藏下划线或者虚线。
经过 CSS
的 last-child { ... }
,能够直接修改最后的 view
样式:
*.wxss
.content-item-gap:last-child {
display: none;
}
复制代码
这样,就不须要复杂的 JS
逻辑,从而实现最后一个下划线的显示影藏。
若是你想了解下 last-child
为什么物:连接
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
在微信小程序中,客服系统也是个至关诡异的玩意。
目前实现:
*.wxml
<button open-type="contact" send-message-title='{{productName}}' send-message-img='{{videoImageSrc}}' send-message-path='../indexProductDetail/indexProductDetail?productId={{productId}}' show-message-card='true'>
<view class="nav-service">
<image src="../../public/index_productDetail_icon_service.png"></image>
<text>在线咨询</text>
</view>
</button>
复制代码
可是,这不能知足个人要求:
这些,都须要咱们的小程序发布后进行验证。
本节目前已有 2 个坑,有兴趣的小伙伴能够详看。
话很少说,先丢出实现代码:
*.wxml
<view class="container" bindtap='downloadFile' data-url="{{downloadUrl}}">
<button>点我下载</button>
</view>
复制代码
*.js
Page({
data: {
downloadUrl: '网上随便找一个下载地址'
},
downloadFile: function(e) {
// 获取传递过来的下载地址
var url = e.currentTarget.dataset.url;
// 调用下载 API
wx.downloadFile({
url: url,
success: function (res) {
console.log("下载文件成功");
console.log(res);
var tempFilePath = res.tempFilePath;
// 在线预览文档
wx.openDocument({
filePath: tempFilePath,
success: res => {
console.log("打开成功");
},
fail: res => {
console.log(res);
},
complete: res => {
console.log(res);
}
})
},
fail: function () {
console.log("下载失败");
}
})
}
})
复制代码
而后,在下载的时候,后端小伙伴偷懒,上传的是没有数据的 Word、PPT,这时候,小程序会报:openDocument:fail filetype not supported
的 error,因此小伙伴们要注意下。
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
因为 jsliang 平时都是使用 ES5 ,一直口嚷嚷说要步入 ES6 大堂,可是一直就是没入门,因此乘着有功夫,尽可能使用 ES6 ,用熟了才能进步。
绑定方法写法修改:
getUserPhone: function(e) {
console.log(e);
}
复制代码
getUserPhone(e) {
console.log(e);
}
复制代码
循环写法:
for(var i = 0; i <= array.length; i++) {
console.log(array[i]);
}
复制代码
for (let i of array) {
console.log(i);
}
复制代码
本节目前已有 1 个坑,有兴趣的小伙伴能够详看。
官方地址:地址
官方实现效果:
项目实现效果:
从图中能够看出,咱们大概要实现3个步骤:
调整 CSS,使 Video 占满 100% 的宽,而且居顶。
调整 HTML+CSS,使图片覆盖住视频。
编写 JS,使图片点击时,隐藏图片,播放视频。
*.wxml
<view class="video">
<!--
1. 绑定接口视频路径
2. controls - 可控制播放暂停
3. show-fullscreen-btn - 显示全屏按钮
-->
<video id="video" src="{{videoSrc}}" controls show-fullscreen-btn></video>
<!-- 经过 playVideo 这个方法来控制 showVideo 这个属性,从而控制遮罩的产品图片是否隐藏 -->
<cover-view class="{{showVideo ? 'video-mask' : 'hide-video-mask'}}" bindtap='playVideo'>
<cover-image class="video-image" src="{{videoImageSrc}}"></cover-image>
</cover-view>
<!-- 经过 playVideo 这个方法来控制 showVideo 这个属性,从而控制遮罩的播放按钮是否隐藏 -->
<cover-view class="{{showVideo ? 'video-mask' : 'hide-video-mask'}}" bindtap='playVideo'>
<cover-image class="video-button" src="../../public/index_productDetail_video_button.png"></cover-image>
</cover-view>
</view>
复制代码
*.wxss
/* 设置视频宽高 */
video {
width: 100%;
height: 420rpx;
}
/* 隐藏遮罩层 */
.hide-video-mask {
display: none;
}
/* 遮罩层 */
.video-mask {
width: 100%;
height: 420rpx;
position: absolute;
top: 0;
left: 0;
z-index: 8;
}
/* 遮罩层图片 */
.video-image {
width: 100%;
height: 420rpx;
position: absolute;
top: 0;
left: 0;
z-index: 9;
}
/* 遮罩层播放按钮 */
.video-button {
width: 98rpx;
height: 98rpx;
position: absolute;
top: 50%;
left: 50%;
margin-top: -49rpx;
margin-left: -49rpx;
z-index: 99;
}
复制代码
*.js
Page({
data: {
// 视频字段
// videoSrc: 'http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400',
videoSrc: '',
videoImageSrc: '',
showVideo: true,
},
/**
* 播放视频
*/
playVideo: function () {
var that = this;
that.setData({
showVideo: false
});
// videoContext 的定义在 onReady 上
this.videoContext.play();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
this.videoContext = wx.createVideoContext("video")
},
})
复制代码
如上,实现了视频播放。
这章主要讲解小伙伴们发现的 bug 及其解决思路,有兴趣的小伙伴能够私我QQ:1741020489,我将把你提出的 bug 写进这篇文章并附上你的大名(看我的意愿),谢谢小伙伴们的支持~
注:小伙伴们提出的 bug,若是包含详细的问题描述和解决方案,我会统计进该文章的 bug 清单。
本组件目前已有 ? 个坑,有经验的小伙伴能够进行补充。
问题提出者:掘金 史前图腾。
问题反馈:wx.getFileSystemManager().readdir
文件夹读取 api
报错,但仍会返回结果。
问题解决:这个 api 做用是读取某个目录下的文件名,正常会返回文件名数组,可是如今 IDE 在返回数据以前会报 'indexOf' 未定义的错误,并不影响api 使用。我看官方论坛上月就有人反应了,至今未修复。
本组件目前已有 ? 个坑,有经验的小伙伴能够进行补充。
问题提出者:掘金 jilaokang。
问题反馈:textare问题很是多,补一个。
问题解决:已联系大佬,等待回复。
jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/LiangJunron…上的做品创做。
本许可协议受权以外的使用权限能够从 creativecommons.org/licenses/by… 处得到。