小程序基礎
數組的遍歷wx:for
類似vue,但不同於vue
用法:
people = [
{name:'xiaohong'},
{name:'xiaowang'},
{name:'xiaozi'}
]
<view wx:for="{{people}}">{{item.name}}</view>
監聽事件點擊bindtab
<button bindtap='clickMe'></button>
data:{
count:0;
},
clickMe(){
//錯誤的做法,頁面並不會發生改變
//this.count++;
this.setData({
count:this.data.count +1
})
}
MVVM架構
小程序的MVVM與Vue的MVVM一樣。
MVVM的好處
- ViewModel層可以將DOM的監聽綁定到Model層
- ViewModel層可以將數據的變量,相適應的反應到View層
project.config和sitemap的作用
project.config是為了保存當前參數,為了防止在多個電腦開發參數不一致。
sitemap是一個搜索選項,一般為通配符*。意味着小程序所有頁面都能被用戶搜索到。
全局配置
在小程序中有很多配置,這里只列舉三個最重要的
pages
可以直接新建page,自動在app.json中自動注冊。
也可以直接在app.json中直接注冊,系統會自動幫我們創建文件
window
window一般默認的配置就可以,其余查看官方文檔
tabBar
直接在app.json中輸入tabBar敲Tab即可
注意:selectedColor和list同級
局部配置
直接在單頁面json中設置屬性即可,無需加window
頁面渲染的整體流程
- 在渲染層,宿主環境會把WXML轉化成對應的JS對象;
- 將JS對象再次轉成真是DOM樹,交由渲染層線程渲染;
- 數據變化時,邏輯層提供最新的變化數據,JS對象發生變化啊比較diff算法進行對比;
- 將最新變化的內容反應到最真實的DOM樹中,更新UI;
app生命周期函數
-
onLaunch() //一般用來獲取用戶信息
-
onShow() //用來判斷小程序進入場景
里面可以傳入參數options,通過options.scene來判斷用戶場景,來做對應的事,如獲取個人信息保存到服務器
這里只記錄兩個重要的。其他創建app時自動會顯示
保存全變量
我們除了在app中使用生命周期,還可以保存全局屬性
globalData:{
name:'vicer',
age:18
}
使用方法
const app = getApp();
console.log(app.globalData.name);
獲取個人信息的方式
- 在生命周期函數中獲取
onLaunch只獲取一次。onShow()有可能會獲取多次。
這種獲取方式有可能會被廢棄
wx.getUserInfo({
success:function(res){
console.log(res)
}
})
-
使用button的方式授權獲取
<button open-type="getUserInfo" bindgetuserinfo="getUser">獲取授權</button>
getUser(event){ console.log(event) }
-
使用open-data來展示用戶信息
這里只能展示,不能獲取。具體的type類型可以去看官方文檔<open-data type='userNickName'></open-data>
page生命周期函數
onLoad() //頁面加載時調用
onShow() //頁面顯示時
onReady() //頁面初次渲染完成時
onHide() //頁面隱藏時
onUnload() //頁面跳轉時
一般在page頁面需要做什么
- 發送網絡請求,從服務器獲取數據
這里注意,請求服務器數據成功回調時,要使用箭頭函數的this,就近指向當前的page - 初始化數據
- 監聽wxml中的事件,綁定對應的事件函數
- 其他一些監聽,如頁面滾動,上拉刷新,下拉加載等
text的用法
行級元素
屬性
- selectable 文本是否可選 直接把屬性添加進去即可
- space 空格大小 選項ensp/emsp/nbsp
- decode 文本解碼 直接把屬性添加進去即可
button的用法
塊級元素,如果設置size='mini'則是行內塊元素
屬性
size, //大小
type, //類型
plain, //鏤空
disabled, //禁用
loading, //加載
hover-class,//按下去樣式
open-type //微信開放功能,如獲取個人信息
屬性太多就不一一列舉
button用法
view的用法
塊級元素,並且是容器組件
hover-class //按下去的樣式 設置class
hover-stop-propagation //阻止事件冒泡 布爾值,默認不阻止
hover-start-time //按住后多久出現點擊態 number
hover-stay-time number //手指松開后點擊態保留時間 number
image的用法
- scr
可以使用相對路徑/絕對路徑/網絡路徑。注:使用絕對路徑前面加個/ - lazy-load
圖片懶加載 - show-menu-by-longpress
圖片小程序識別 - bindload
圖片加載完成調用 - mode
圖片的縮放模式- widthFix 最常用的縮放模式,寬度不變,高度自動變化,保持原圖寬高比不變
- 其他縮放模式請查看官網
input的用法
- value //input的默認值
- type //鍵盤類型,如數字鍵盤number/文本鍵盤text
- password //暗文
- placeholder //占位符
- confirm-type //鍵盤右下角,顯示的文字,如發送/確認
input的事件
- bindinput //鍵盤輸入時觸發
- bindfocus //獲取焦點觸發
- bindblur //失去焦點觸發
scroll-view的用法
在組件scroll-view中添加scroll-x可橫向滾動,scroll-y可縱向滾動
監聽滾動距離 bindscroll
<scroll-view class="scroll" scroll-y bindscroll="topHeight" scroll-top="{{scrollTop}}">
ul>li{$}*100 //此處是100個li。按Tab鍵即可生成
</scroll-view>
<button bind:tap="">返回頂部</button>
.scroll{
height: calc(100% - 120rpx)
}
data:{
scrollTop:0
},
topHeight(e){
let top = e.detail.scrollTop;
},
backtop(){
this.setData({
scrollTop: 0
})
}
WXSS&WXML&WXS
wxss的尺寸:rpx
rpx:指可以根據屏幕寬度進行自適應
設備 | rpx換算px(屏幕寬度/750) |
---|---|
iPhone5 | 1rpx = 0.42px |
iPhone6 | 1rpx = 0.5px |
iPhone6 Plus | 1rpx = 0.552px |
建議:使用iPhone6作為設計稿
導入樣式文件
如果有多個頁面需要用到樣式,我們可以把樣式封裝到一個文件中,在需要的地方導入
@import '路徑';
官方樣式庫
可以引用官方樣式庫進行修改:github下載
動態改變樣式
這里只能通過三元運算符來修改樣式,和vue不一樣
通過動態改變isActvice的布爾值來決定class
<view class='{{isActive ? "active" : ""}}'>顏色</view>
wx:if/wx:elif/wx:else的使用
這里使用方法和vue一樣。但要注意的是這里是elif,不是elseif
wx:if和hidden的區別
區別:
wx:if:如果設為隱藏,將組件進行銷毀
hidden:如果設為隱藏,將display:none
選擇:
隱藏與銷毀的頻率非常低,選擇wx:if
隱藏與銷毀的頻率非常高,選擇hidden
block標簽的使用
block標簽和view一樣,都可以對一組標簽進行包裹。
但view是一個組件,每次還需要渲染,會降低性能
如果我們只需要進行包裹的話,直接使用block即可。
注意:
- block是一個標簽,不是組件
- 內部只接收控制屬性,如wx:for等就是控制屬性。不接受class
- 顯示內容使用view,包裹標簽使用block
wx:for中item與index的別名
當出現多層遍歷時,item名字有可能會重復,為了區別我們可以起一個別名
wx:for-item='nowItem'
wx:for-index='nowIndex'
<block wx:for = "{{list}}" wx:for-item="nowItem">
<view wx:for="{{nowItem}}">
{{item}}
</view>
</block>
wx:key的作用
沒有key的時候,數組如果添加value是依次變化,然后再添加進去。
當有key的時候,diff算法就可以正確找到key的位置,直接把元素進行插入即可
wx:key="*this"
template的使用
- 在小程序中如果我們想要復用某一段代碼,可以使用template。
- template中包裹的內容在沒有使用之前,不會進行渲染
- 如果template中不添加元素可以使用單標簽
- 設置模板並設置name
<template name='item'>
<view>{{currentText}}</view>
<button>{{btn}}</button>
</template>
- 使用is導入模板,並傳入data
<template is='item' data="{{currentText:'我是模板',btn:'按鈕'}}"/>
wxml的兩種導入方式
template只能在當前頁面導入。如果我們想要復用的話可以封裝進一個單獨wxml中,然后導入使用頁面
import導入:
- 主要是用來導入template,可以用來傳遞data
- 特點:不能進行遞歸導入
<import src='/wxml/template.wxml'/>
include引入:
- 將公共的組件抽取到一個文件中
- 特點:可以進行遞歸導入。但不能導入template/wxs
<include src='/wxml/item.wxml'/>
WXS模塊
- WXS的運行環境與JS代碼隔離。不能讓你和調用JS文件的函數,也不能調用小程序的API
- WXS函數不能為組件的事件回調
- WXS一般用來做WXML中的數據處理,類似vue的過濾器
- WXS官方說和JS不一致,但其實就是JS加commonjs導出的用法
- 創建一個WXS文件並寫入代碼
var name = 'vicer';
module.exports = {
name: name
}
- 在需要的地方導入。注意:這里導入必須要使用相對路徑,不能用絕對路徑
<wxs src="../../wxs/info.wxs" module="info" />
- 在WXML中使用
<view>{{info.name}}</view>
小程序時間戳js代碼
// parseInt(timestamp) * 1000中傳入的是秒s,如果是毫秒ms則去掉1000
var dateFormat = function (timestamp, format) { if (!format) { format = "yyyy-MM-dd hh:mm:ss"; }
var timestamp = parseInt(timestamp) * 1000;
var realDate = getDate(timestamp);
function timeFormat(num) {
return num < 10 ? '0' + num : num;
}
var date = [
["M+", timeFormat(realDate.getMonth() + 1)],
["d+", timeFormat(realDate.getDate())],
["h+", timeFormat(realDate.getHours())],
["m+", timeFormat(realDate.getMinutes())],
["s+", timeFormat(realDate.getSeconds())],
["q+", Math.floor((realDate.getMonth() + 3) / 3)],
["S+", realDate.getMilliseconds()],
];
var regYear = getRegExp("(y+)", "i");
var reg1 = regYear.exec(format);
// console.log(reg1[0]);
if (reg1) {
format = format.replace(reg1[1], (realDate.getFullYear() + '').substring(4 - reg1[1].length));
}
for (var i = 0; i < date.length; i++) {
var k = date[i][0];
var v = date[i][1];
var reg2 = getRegExp("(" + k + ")").exec(format);
if (reg2) {
format = format.replace(reg2[1], reg2[1].length == 1
? v : ("00" + v).substring(("" + v).length));
}
}
return format;
}
//這里應用到小程序中,如果不需要則刪除
module.exports = {
dateFormat: dateFormat
};
<wxs module="dateUtil" src="../../../utils/dataTime.wxs"></wxs>
<view class='textCenter'>{{dateUtil.dateFormat(currentTime,'YYYY-MM-dd hh:mm:ss')}}</view>
常用的事件點擊
bindtap等事件在使用時可以寫成bind:tap
- touchstart 手指觸摸動作開始
- touchmove 手指觸摸后移動
- touchend 手指觸摸動作結束
- tap 手指觸摸后馬上離開
- longpress 手指觸摸后,超過350ms再離開,如果指定了事件回調函數並觸發了這個事件,tap事件將不被觸發
event的事件對象
當發生點擊事件時,函數收到的事件對象列表
type //事件的點擊類型
timeStamp //事件生成時的時間戳
target //觸發事件的組件的一些屬性值集合
currentTarget //當前組件的一些屬性值集合
touches //觸摸事件,當前停留在屏幕中的觸摸點信息的數組
changedTouches //觸摸事件,當前變化的觸摸點信息的數組
target的區別
當發生事件冒泡時,內部觸發外部,target記錄的是觸發對象,currentTarget記錄的是當前對象
touches的區別
當手指點擊屏幕時,touches數組中記錄的是當前的手指數量,changedTouches記錄的是點擊數量改變。
如之前一個手指點擊,現在變成2個點擊。touches數組length為2,changedTouches數組length為1
參數的傳遞data-
當我們需要傳遞參數時,只需要在標簽內添加屬性
data-被傳遞的屬性
即可在函數中通過event拿到所需要的屬性
<block wx:for="abc" wx:key="*this">
<view bind:tap="handleBtn"
data-index="{{index}}">
{{item}}
</view>
</block>
handleBtn(e){
const data = e.currentTarget.dataset;
console.log(data.index)
}
事件冒泡與事件捕獲
bind 可以進行事件冒泡。
capture-bind 可以進行事件捕獲。
catch 阻止事件冒泡或捕獲
<view capture-bind:tap='captureView'
bind:tap='bindView'
catch:tap='catchView'>
</view>
小程序組件化開發
組件的使用
創建component,並在需要顯示的json中注冊。
{
"usingComponents": {
"my-cpn":"/component/my-cpn/my-cpn"
}
}
在wxml中使用即可
<my-cpn/>
組件的注意事項
- 只能用小寫字母,中划線,下划線命名,並不要加'wx-'的前綴
- 組件內可以嵌套組件
- 如果在多個頁面中使用,可以在app.json中注冊全局組件
- 組件和引用組件的頁面不能使用 id選擇器/屬性選擇器/標簽名選擇器,請改用class選擇器,因為只有class樣式不會互相影響,相互隔離。
組件樣式的互相影響
默認組件與引用頁面是不互相影響的,如果我們希望互相影響,可以更改styleIsolation的取值
- isolated 啟用樣式隔離(默認值)
- apply-shared 引用頁面將影響自定義組件,但組件不影響引用頁面
- shared 引用樣式和組件互相影響
Component({
options: {
styleIsolation: 'apply-shared'
}
})
注意:希望其互相影響最好也使用class樣式
父傳子
父頁面向組件傳遞數據
- 在父頁面中傳遞數據
<my-cpn title="我被傳遞"/>
- 在組件中通過properties設置傳遞數據的類型,默認值,及觀察新舊數據
properties: {
title:{
type:String,
value:'我是默認標題',
observer(newVal,oldVal){
console.log(newVal,oldVal)
}
}
}
- 在組件中接收數據
<view>{{title}}</view>
小技巧:
在js中除了使用data-獲取父傳子的數據外,還可以使用
this.properties.數據名
來獲取
父頁面向組件傳遞樣式
- 在wxss中設置樣式,並通過父頁面傳遞
<my-cpn titleclass="redstyle"/>
- 在組件中通過externalClasses設置被傳遞樣式(數組形式)
externalClasses:["titleclass"]
- 在組件中設置接收樣式名 (注意不能用駝峰)
<view class="titleclass">{{title}}</view>
父頁面向組件發送事件
在父頁面中想要操作組件,可以向組件發送事件,但不建議在父頁面直接修改組件中的data,有違反設計原則,組件中的數據最好在組件中修改。
- 使用selectComponent獲取組件的class/id(建議獲取id)
var my_cpn = this.selectComponent("#mycpn");
- 調用組件的方法
// my_cpn.setData({
// current:my_cpn.data.current+10
// })
my_cpn.changeData(10)
- 通過組件的方法來改變組件的data。而不是直接改變
methods: {
changeData(num){
this.setData({
current:this.data.current +num
})
}
}
子傳父
組件向父頁面發送數據
- 在組件中點擊事件調用methods中的函數
<button bind:tap="btnClick">按鈕</button>
- 在函數中使用triggerEvent觸發事件,並指定事件名、發送數據和事件選項(事件選項一般用不到)
methods:{
btnClick(){
var myinfo = {name:'vicer',age:18};
this.triggerEvent('myevent',myinfo,{})
}
}
- 在父頁面中使用bind監聽事件名,傳遞event並通過event.detail來獲取數據
<my-cpn bind:myevent="onMyEvent"/>
onMyEvent(event){
console.log(event.detail)
}
插槽的使用
普通插槽和vue的插槽使用方法一樣,在組件中添加slot標簽
如果需要使用多個插槽,除了給插槽命名,還需要增加一個options
多個插槽的使用
- 在組件中給插槽命名
<view>
<slot name="slot1"/>
<slot name="slot2"/>
</view>
- 在組件的js文件中配置options
Component({
options:{
multipleSlots:true
}
})
- 使用時給標簽添加插槽名
<my-slot>
<button slot="slot1">按鈕</button>
<view slot="slot2">哈哈哈</view>
</my-slot>
監聽事件的改變
observers (等同於vue中的watch)
監聽properties/data的改變
observers:{
//不同於observer,這里只能監聽newVal
corrent:function(newVal){
console.log(newVal)
}
}
組件中監聽生命周期函數
監聽所在頁面的生命周期
- show 組件所在的頁面被展示時執行
- hide 組件所在的頁面被隱藏時執行
- resize 組件所在的頁面尺寸變化時執行
pageLifetimes: {
show: function() {
// 頁面被展示
},
hide: function() {
// 頁面被隱藏
},
resize: function(size) {
// 頁面尺寸變化
}
}
監聽組件本身的生命周期
- created 在組件實例剛剛被創建時執行
- attached 在組件實例進入頁面節點樹時執行
- ready 在組件在視圖層布局完成后執行
- moved 在組件實例被移動到節點樹另一個位置時執行
- detached 在組件實例被從頁面節點樹移除時執行
- error 每當組件方法拋出錯誤時執行
lifetimes: {
attached: function() {
// 在組件實例進入頁面節點樹時執行
},
detached: function() {
// 在組件實例被從頁面節點樹移除時執行
}
}
小程序網絡請求
網絡請求基本使用
wx.request(Object)
關鍵屬性解析:
- url 請求地址
- data 請求參數
- method 請求方式
- success 成功回調
- fail 失敗回調
使用promiese進行網絡封裝
原因:
- 微信的封裝方法太老舊
- 防止回調地獄
- 防止微信更換網絡請求方法
網絡封裝
export default function(options){
return new Promise((reslove,reject)=>{
wx.request({
url: options.url,
timeout: options.timeout || 5000,
method: options.method || 'get',
data: options.data || {},
success:reslove,
fail:reject
})
})
}
使用方法
import request from '../../server/request';
onLoad(){
request({
url:'接口地址',
method:'get'
}).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
}
小程序交互
Toast彈窗
微信小程序彈窗wx.showToast
使用方法,直接在函數中調用即可
btnClick(){
wx.showToast({
title:"我是彈窗",
duration:2000
})
}
showModal對話框
微信小程序對話框wx.showModal
點擊確定或取消,來進行事件操作
wx.showModal({
title: '提示',
content: '這是一個模態彈窗',
success (res) {
if (res.confirm) {
console.log('用戶點擊確定')
} else if (res.cancel) {
console.log('用戶點擊取消')
}
}
})
showLoading加載中彈框
類似於showToast。需主動調用 wx.hideLoading 才能關閉提示框
所以showLoading更多用於網絡請求
wx.showLoading({
title: '加載中',
})
setTimeout(function () {
wx.hideLoading()
}, 2000)
showActionSheet下拉操作菜單
一般用於上傳相冊或文件等其他操作
tapIndex屬性用於顯示序列號,用於獲取用戶點擊按鈕的下標。
可以用switch來判斷並執行操作
wx.showActionSheet({
itemList: ['A', 'B', 'C'],
success (res) {
console.log(res.tapIndex)
},
fail (res) {
console.log(res.errMsg)
}
})
分享小程序
當我們需要分享小程序時,在page中添加onShareAppMessage函數
title //設置分享頁標題
path //分享后打開的頁面
imgUrl //分享時展示的圖片
除了點擊右上角分享,我們還可以通過button設置open-type='share'來調用此函數
<button open-type="share">轉發</button>
onShareAppMessage(res){
console.log(res)
return {
title:'我是標題',
path: "pages/category/category"
}
}
小程序登入
登入流程圖
我們前端需要做的操作
- 調用wx.login獲取code
- 調用wx.request發送code到我們自己的服務器(我們的服務器會返回一個登入態標識,比如token)
- 將登錄態標識token進行儲存,以便下次使用
- 請求登錄態標識的接口時,攜帶token
下面代碼中用到的接口
請求token接口
接口地址:/login
請求方式:post
參數列表:code
返回值:token
驗證token接口
接口:/auth
請求:post
參數:header:{token} 本地保存的token
返回值:
錯誤碼:
1001 沒有傳入token
1002 傳入錯誤的token
1003 token過期
const TOKEN = 'token';
App({
globalData:{
token:''
},
onLaunch: function () {
//3. 獲取token
const token = wx.getStorageSync(TOKEN);
//4. 驗證是否得到token
if(token && token.length !==0){
//5. 驗證是否過期
this.checkToken(token)
}else{
this.login()
}
},
checkToken(token){
wx.request({
url: '接口/auth',
method:'post',
header:{
token
},
success:(res)=>{
//6. 驗證token過期代碼,過期則重新登入,不過期則保存到全局變量
if(!res.data.errorCode){
console.log('得到token並儲存到全局變量')
this.globalData.token = token;
}else{
console.log('token過期,執行登入')
this.login()
}
}
})
},
login(){
//1. 登入小程序
console.log('執行登入操作');
wx.login({
success: (res) => {
const code = res.code
wx.request({
url: '接口/login',
method: 'post',
data: {
code
},
success: (res) => {
//2. 進行保存token
const token = res.data.token
this.globalData.token = token;
wx.setStorageSync(TOKEN, token);
},
fail: (err) => {
console.log(err)
}
})
}
})
}
})
頁面跳轉navigator
navigator跳轉標簽
屬性:
- url //要跳轉的鏈接
- open-type //跳轉方式
- delta //退回的層數
open-type屬性:
- navigate //默認跳轉。保留當前頁面跳轉,不能跳轉tab頁面
- redirect //關閉當前頁面跳轉,不能跳轉tab頁面
- switchTab //跳轉到tab頁面
- reLaunch //關閉所有頁面重定向
- navigateBack //返回上一個頁面,可以配合delta使用
<navigator url="/pages/detail/detail">保留當前頁面跳轉</navigator>
頁面跳轉的數據傳遞
跳轉時數據傳遞
- 在navigator的url中插入查詢參數
- 直接輸入常量
?name='vicer'&age=18
- 也可以使用保存在data中的數據
?name={{name}}&age={{age}}
- 使用代碼跳轉的方式,傳入參數
'/pages/detail/detail?iid=' + iid
- 直接輸入常量
<navigator url="/pages/detail/detail?name='vicer'&age=18">保留當前頁面跳轉</navigator>
- 在跳轉頁面中通過onLoad生命周期函數的參數來獲取值
onLoad: function (options) {
console.log(options)
}
返回時數據傳遞
返回時不能通過navigator來傳遞數據,頁面返回時會調用onUnload,
所以要通過onUnload生命周期函數來獲取getCurrentPages的數據來進行改變
onUnload(){
const pages = getCurrentPages()
const home = pages[pages.length - pages.length];
home.setData({
name:'tace'
})
}
通過代碼進行跳轉
當我們不希望有navigator時,可以使用代碼進行跳轉
open-type屬性對應的代碼:
- navigate 對應 wx.navigateTo 的功能
- redirect 對應 wx.redirectTo 的功能
- switchTab 對應 wx.switchTab 的功能
- reLaunch 對應 wx.reLaunch 的功能
- navigateBack 對應 wx.navigateBack 的功能
通過綁定監聽事件,再函數中運行跳轉功能,如
btnClick(){
wx.navigateTo({
url:'/pages/detail/detail'
})
}
小程序高級技巧
setData的使用技巧
在修改data中的參數時,data中保存的不一定直接就是單一的屬性,還有可能保存的對象或者數組,那么我們該如何在setData中修改對象和數組呢?
data:{
name:'vicer', //屬性
info:{ //對象
name:'viceroy'
},
arr:[1,2,3,4], //數組
obj:{
people:{
name:'xiaowang'
}
}
}
//let objname = this.data.obj.people.name 錯誤用法
let objname = `obj.people.name`
this.setData({
name:'tace',
'info.name':'VICEROY', //對象的修改方法
arr:[10 ,20 ,30 ,40 ], //數組的修改方法1
'arr[3]': 999, //數組的修改方法2
[objname]:'xiaohong' //對象&數組的修改方法
})
解釋:
-
""的用法: 對象和數組可以加引號,來進行修改
-
[ ]的用法: 意為着這里直接把方括號中對應的值進行修改
例如//let objname = this.data.obj.people.name 錯誤用法 let objname = `obj.people.name` //使用模板字符串拼接 this.setData({ [objname]:'xiaohong' })
這里修改的不是objname,而是obj.people.name對應的值。
在setData中還有回調函數,但回調函數經過測試並能不修改參數,也可自己進行測試或,進行一些其他操作
this.setData({
text: 'Set some data for updating view.'
}, function() {
// this is setData callback
console.log(this)
})