小程序基礎(chǔ)-分包加載&&自定義組件
小程序分包加載
小程序分包加載-為什么要分包加載
- 微信平臺對小程序單個包的代碼體積限制為 2M,超過 2M 的情況下可以采用分包來解決
- 即使小程序代碼體積沒有超過 2M 時也可以拆分成多個包來實現(xiàn)按需加載
- 配置文件能忽略的只有靜態(tài)資源,代碼無法被忽略
配置忽略文件
project.config.json
{
"description": "項目配置文件",
"packOptions": {
"ignore": [
{
"value": "static/uploads",
"type": "folder"
}
],
"include": []
},
type: 表示要忽略的資源類型
value: 表示具體要忽略的
小程序分包加載-使用分包配置
分類:
- 主包:
-
每個小程序必定含有一個主包。
-
默認(rèn)啟動頁面、TabBar 頁面,以及公共資源/JS 腳本必須放在主包;
2.分包 https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html
-
通過subPackages來配置
-
所有包的大小之和不超過20M
app.json
{
// 省略其他的...
"subPackages": [
{
"root": "subpkg_user", // 分包代碼的目錄,其實就是一個獨立的文件夾
"pages": [
"pages/profile/profile"
]
},
{
"root": "subpkg_order", // 文件夾
"pages": [
"pages/order_list/index",
"pages/order_list/index"
]
}
]
}
注意: 寫完分包之后,如果對應(yīng)的文件夾和頁面不存在,它會自動創(chuàng)建文件夾和頁面。
小程序分包—預(yù)加載
https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html
在打開小程序啟動的時候只下載主包代碼,分包并不會下載,因此能夠提升小程序啟動時的打開速度,但是分包的代碼只有在訪問到分包的頁面時才去下載,這樣用戶就需要有一定時間的等待(一般不太影響),通過分包預(yù)加載技術(shù)可以實現(xiàn)提前去下載分包的代碼,這樣分包頁面的訪問速度也會得到提升。
小程序通過 preloadRule 配置需要預(yù)加載的分包。
app.json
{ ......
"preloadRule": {
"頁面地址,進入這個頁面就需要預(yù)加載分包": { // pages/index/index
"network": "網(wǎng)絡(luò)環(huán)境", // "wifi"
"packages": ["要預(yù)加載的包名"] //["goods_pkg"]
}
}, ......
} //當(dāng)用戶訪問到 pages/index/index 時,在 wifi 網(wǎng)絡(luò)前提下預(yù)先下載 goods_pkg 分包的代碼。
- 指定某個頁面路徑做為 key,含義是當(dāng)訪問這個頁面時會去預(yù)加載一個分包
- network 預(yù)加載分包的網(wǎng)絡(luò)條件,可選值為 all、wifi,默認(rèn)為 wifi
- packages 指定要預(yù)下載的分包名或根路徑
配置完成之后,訪問指定頁面時,就會在控制臺輸出提示。
自定義組件—基本使用
創(chuàng)建組件
通常將項目中的組件都放在一個獨立的目錄下,并且一般就給這個文件夾取名為:components 。這個目錄需要我們手動進行創(chuàng)建。
-
新建一個目錄:components
-
在components上點擊鼠標(biāo)右鍵,選擇「新建Component」
-
填入組件的名字。它會自動創(chuàng)建4個同名的文件。(這一點和創(chuàng)建頁面是一樣的)
組件和頁面的結(jié)構(gòu)區(qū)別:
- 組件的配置文件(.json文件)中,有一個配置項:component: true
- 組件的 .js 文件中調(diào)用 Component 函數(shù),頁面的.js文件中調(diào)用Page函數(shù)
注冊組件
-
頁面注冊是在使用組件的(xxxx.json)中通過 usingComponents 進行注冊,只能在當(dāng)前頁面中組件
-
全局注冊是在 app.json 文件中通過 usingComponents 對自定義組件進行注冊,可以在任意頁面中使用
"usingComponents": {
"my-test": "/components/MyTest/index"
}
使用組件
在wxml中,直接通過標(biāo)簽的方式使用即可。
自定義組件—組件樣式
- 組件中的樣式不要使用標(biāo)簽選擇器
- 組件中,樣式默認(rèn)是隔離的: 自定義組件的樣式只受到自定義組件 wxss 的影響
- 通過對組件的配置,可以取消這個隔離的狀態(tài)。
樣式隔離注意點
-
app.wxss中的全局樣式對組件無效
-
只有class選擇器具有樣式隔離效果,id選擇器、屬性選擇器、標(biāo)簽選擇器不受樣式隔離的影響
建議:在組件和引用組件的頁面中建議使用class選擇器,不要使用id、屬性、標(biāo)簽選擇器
修改組件樣式的隔離選項
默認(rèn)情況下,自定義組件的樣式隔離特性能夠防止組件內(nèi)外樣式互相干擾的問題。但有時,我們希望外界能夠控制組件內(nèi)部的樣式,此時,可以通過在組件的.js文件中設(shè)置: options → addGlobalClass 為true
XX.js
Component({
options: {
addGlobalClass: true
}
})
在頁面中設(shè)置的同類名的選擇器就能作用于子組件內(nèi)部的元素。但是,組件內(nèi)的class選擇器,不能影響頁面的元素。
自定義組件—組件樣式-外部樣式類
組件希望接受外部傳入的樣式類。此時可以在 Component 中用 externalClasses 定義若干個外部樣式類。
在開發(fā)組件時,主動暴露給組件使用者,修改組件內(nèi)部樣式
組件 custom-component.js
/* 組件 custom-component.js */
Component({
externalClasses: ['my-class']
});
組件 custom-component.wxml
<!-- 組件的wxml -->
<!-- 這里的my-class相當(dāng)于一個占位符 -->
<view class="my-class">components/MyTest/index.wxml</view>
頁面的 WXML
<!-- 頁面的 WXML -->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />
頁面的wxss
.red-text{ color: red; }
.large-text {font-size: 50px; }
外部樣式類相當(dāng)于用一個類名去當(dāng)占位符,以便于在后期使用時替換成真實的類名,方便添加額外的樣式。
參考:https://vant-contrib.gitee.io/vant-weapp/#/button#wai-bu-yang-shi-lei
自定義組件—數(shù)據(jù)方法
組件的典型結(jié)構(gòu)
// borderImage.js
Component({
/**
* 組件的屬性列表
*/
properties: {
},
/**
* 組件的初始數(shù)據(jù)
*/
data: {
},
/**
* 組件的方法列表
*/
methods: {
}
})
定義數(shù)據(jù)
在小程序中,用于組件模板渲染的私有數(shù)據(jù),需要定義到data中
methods方法
在小程序的組件中,事件處理函數(shù)和自定義方法需要定義到methods中
自定義組件—組件插槽
單個插槽
在小程序中,默認(rèn)情況下每個自定義組件中只允許使用一個插槽進行占位。
<!--components/MyTest2/index.wxml-->
<view>
<text>components/MyTest2/index.wxml</text>
<!-- 對于不確定的內(nèi)容,可以使用slot進行占位,具體內(nèi)容交給使用者確定 -->
<slot></slot>
</view>
使用組件
<my-test2>
<!-- 這里的內(nèi)容將被放到組件中<slot>的位置 -->
<view>
這里是slot里的內(nèi)容
</view>
</my-test2>
多插槽(具名插槽)
組價.js
Component({
options: {
multipleSlots: true // 在組件定義時的選項中啟用多 slot 支持
},
// ... 省略其他
})
此時,可以在這個組件的 wxml 中使用多個 slot ,以不同的 name 來區(qū)分。
定義插槽
<view>
<text>components/MyTest2/index.wxml</text>
<!-- 對于不確定的內(nèi)容,可以使用slot進行占位,具體內(nèi)容交給使用者確定 -->
<!-- <slot></slot> -->
<slot name="before"></slot>
<view>
---------這里是分割線--------
</view>
<slot name="after"></slot>
</view>
使用組件
<my-test2>
<!-- 這里的內(nèi)容將被放到組件中<slot>的位置 -->
<!-- <view>
這里是slot里的內(nèi)容
</view> -->
<view slot="before">
這里是before slot里的內(nèi)容
</view>
<view slot="after">
這里是after slot里的內(nèi)容
</view>
</my-test2>
自定義組件—生命周期
組件生命周期-lifetimes
生命周期 | 參數(shù) | 描述 |
---|---|---|
created | 無 | 在組件實例剛剛被創(chuàng)建時執(zhí)行,此時還不能調(diào)用 setData,一般用于給組件的this添加一些自定義的屬性字段 |
attached | 無 | 在組件實例進入頁面節(jié)點樹時執(zhí)行,絕大多數(shù)初始化工作可以在這個時機進行,例如發(fā)請求獲取初始數(shù)據(jù) |
ready | 無 | 在組件在視圖層布局完成后執(zhí)行 |
moved | 無 | 在組件實例被移動到節(jié)點樹另一個位置時執(zhí)行 |
detached | 無 | 在組件實例被從頁面節(jié)點樹移除時執(zhí)行,適合做一些清理工作 |
error | Object Error | 每當(dāng)組件方法拋出錯誤時執(zhí)行 |
生命周期函數(shù)要寫在lifetimes里邊
lifetimes: {
created() {
console.log('組件被created') // 這里使用setData不會引起視圖的更新
this.setData({ msg: 'abc!' })
},
attached() {
this.setData({ msg: 'abcd' })
}
}
自定義組件-屬性(父傳子)
在小程序中,properties是組件的對外屬性,用于接收外界傳遞到組件中的數(shù)據(jù)
父組件傳入屬性值
<my-test isOpen max="9" min="1" />
子組件.js中接收
Component({
properties: {
isOpen: Boolean,
min: Number, // 直接寫類型
max: { // 寫類型 + 初始值
type: Number,
value: 10 // value用于指定默認(rèn)值
}
}
})
自定義組件-組件通訊-自定義事件triggerEvent(子傳父)
Vant組件庫
官方文檔:https://vant-contrib.gitee.io/vant-weapp/#/quickstart
步驟一 通過 npm 安裝
npm i @vant/weapp -S --production
步驟二 修改 app.json
將 app.json 中的 "style": "v2"
去除
步驟三 修改 project.config.json
開發(fā)者工具創(chuàng)建的項目,miniprogramRoot
默認(rèn)為 miniprogram
,package.json
在其外部,npm 構(gòu)建無法正常工作。
需要手動在 project.config.json
內(nèi)添加如下配置,使開發(fā)者工具可以正確索引到 npm 依賴的位置。
{
...
"setting": {
...
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}
步驟四 構(gòu)建 npm 包(重點)
開發(fā)者工具上 > “工具” > “構(gòu)建npm”
使用
去app.json(全局注冊)或頁面.json(局部注冊)中注冊
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
在頁面中使用
<van-button type="primary">按鈕</van-button>
小程序開發(fā)環(huán)境-優(yōu)化目錄結(jié)構(gòu)
項目的根目錄
├── miniprogram // 項目相關(guān)的代碼夾
├── node_modules // npm包目錄
├── package.json
├── project.config.json
├── package-lock.json
└── sitemap.json
修改 project.config.json
中的配置項
{
// 省略其他......
"setting": {
// 省略其他......
"packNpmManually": true,
"packNpmRelationList": [
{
"miniprogramNpmDistDir": "./miniprogram",
"packageJsonPath": "package.json"
}
]
},
"miniprogramRoot": "miniprogram/"
}
啟用 less/sass
通過 less/sass 可以更好的管理 css 樣式,通過 project.config.json
可以啟用對 less/sass 的支持。
{
"setting": {
"useCompilerPlugins": ["sass"]
}
}
然后將 .wxss
文件后綴改換成 .scss
即可。
啟動項目
-
拉取代碼
-
導(dǎo)入項目
使用小程序開發(fā)者工具導(dǎo)入【項目】的代碼
3. 使用小程序開發(fā)者工具構(gòu)建 npm
- 安裝包:npm install
- 手動構(gòu)建: 【工具】→【構(gòu)建npm】
project.config.json的幾個配置
{
"miniprogramRoot": "miniprogram/",
"setting": {
"useCompilerPlugins": ["sass"],
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram"
}
],
}
}
-
miniprogramRoot
項目的根目錄為miniprogram
-
setting.useCompilerPlugins
啟用了sass
支持 -
packNpmRelationList
指定了 npm 構(gòu)建時所需的package.json
的位置以及構(gòu)建后代碼的生成位置
4. 改成自己的appid
這個項目中的appid是別人的,如果我們需要改成自己的。
基礎(chǔ)封裝-消息反饋
將所有通用的工具方法封裝到 utils/utils.js 中
/**
* 用戶消息反饋
* @param {string} title 文字提示的內(nèi)容
*/
export const toast = (title = '數(shù)據(jù)加載失敗...') => {
wx.showToast({
title,
mask: true,
icon: 'none',
})
}
// 掛載到全局對象 wx
wx.$toast = toast
app.js
// 在入口中執(zhí)行 utils.js
import './utils/utils.js'
App({
// ...
})
使用
wx.$toast('//提示文字', "icon圖標(biāo)")
基礎(chǔ)封裝-網(wǎng)絡(luò)請求
安裝第三方的包-構(gòu)建
- npm install wechat-http
- 安裝完成后還必須要構(gòu)建 npm后才可以使用
wechat-http用法與 axios
類似:
-
http.baseURL
配置接口基礎(chǔ)路徑 -
http.get
以GET
方法發(fā)起請求 -
http.post
以POST
方法發(fā)起請求 -
http.put
以PUT
方法發(fā)起請求 -
http.delete
以DELETE
方法發(fā)起請求 -
http.intercept
配置請求和響應(yīng)攔截器 -
http
本身做為函數(shù)調(diào)用也能用于發(fā)起網(wǎng)絡(luò)請求
二次封裝
新建 utils/http.js
文件
// 導(dǎo)入 http 模塊
import http from 'wechat-http'
// 基礎(chǔ)路徑
http.baseURL = 'https://live-api.itheima.net'
// 掛載到全局對象
wx.http = http
// 普通的模塊導(dǎo)出
export default http
以全局對象方式調(diào)用時需要在入口中執(zhí)行 utils/http.js
// 執(zhí)行 uitls/http.js
import './utils/http.js'
App({
// ...
})
配置響應(yīng)攔截器
// 配置響應(yīng)攔截器
http.intercept.response = function ({ data, config }) {
// 檢測接口是否正常返回結(jié)果
if (data.code !== 10000) {
wx.$toast()
return Promise.reject(data)
}
// 只保留data數(shù)據(jù),其它的都過濾掉
return data.data
}
跳轉(zhuǎn)傳參
點擊公告列表后將公告的ID通過地址參數(shù)傳遞到公告詳情頁面,在公告詳情頁 onLoad 生命周期中讀取到公告 ID,然后調(diào)用接口獲取公告詳情的數(shù)據(jù)。
van-count-down 組件(倒計時)的應(yīng)用
<van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
<text>{{timeData.seconds}}秒后重新獲取</text>
</van-count-down>
time: 指定了倒計時多少毫秒
bind:change每隔一秒的回調(diào),它會傳出來當(dāng)前的倒計時信息
countDownChange(ev) {
console.log(ev.detail)
this.setData({
timeData: ev.detail,
getCodeBtnVisible: ev.detail.minutes === 0 && ev.detail.seconds === 0,
})
},
表單驗證插件使用
先在data中設(shè)置mobile,再在模板中進行雙向綁定
model:value=“{{mobile}}”
-
安裝 并 構(gòu)建 表單驗證碼插件
wechat-validate
npm install wechat-validate
- 將插件導(dǎo)入到項目中
-
behaviors
將插件注入到頁面中 -
rules
由插件提供的屬性,用來定義數(shù)據(jù)驗證的規(guī)則(類似于 Element UI) -
validate
由插件提供的方法,根據(jù)rules
的規(guī)則來對數(shù)據(jù)進行驗證
// 導(dǎo)入表單驗證插件
import validate from 'wechat-validate'
Page({
data: {
mobile: '' // 省略其他
},
behaviors: [validate], // 將插件注入到頁面實例中
rules: {
mobile: [
{required: true, message: '請?zhí)顚懯謾C號碼!'},
{pattern: /^1[3-8]\d{9}$/, message: '請?zhí)顚懻_的手機號碼!'}
]
},
getSMSCode() {
// 獲取驗證結(jié)果
const { valid, message } = this.validate('mobile')
// 如果驗證不合法則不再執(zhí)行后面的邏輯
if (!valid) return wx.$toast(message)
console.log('getCode')
this.setData({ getCodeBtnVisible: false })
},
})
保存token-跳轉(zhuǎn)
- 在app.js中設(shè)置setToken方法,保存到本地存儲
App({
// ...
setToken(key, token) {
// 將 token 記錄在應(yīng)用實例中
this[key] = token
// 將 token 存入本地
wx.setStorageSync(key, token)
}
})
2.點擊登錄 | 注冊發(fā)送請求成功之后
const app = getApp() //小程序中獲取全局的實例對象
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
// 跳轉(zhuǎn)
const url = '/pages/profile/index'
wx.redirectTo({ url })
登錄檢測-鑒權(quán)組件
1.在根目錄中創(chuàng)建 components
文件夾用來存放全局的組件,然后通過小程序開發(fā)者工具創(chuàng)建一個名為 authorization
的組件
2.接下來全局來注冊這個組件,保證任何頁面中都可以直接應(yīng)用 authorization
組件
{
"usingComponents": {
"authorization": "/components/authorization/index"
},
}
3.到用戶信息頁面中應(yīng)用 authorization
使用做為頁面根節(jié)點
<authorization>
...
</authorization>
4.在authorization中補充插槽和狀態(tài)
<!--components/authorization/index.wxml-->
<slot wx:if="{{isLogin}}"></slot>
data: {
isLogin: false
},
讀取本地存儲token
讀取本地存儲的 token
數(shù)據(jù),用于判斷是否曾登錄過
// app.js
App({
......
getToken() {
// 將 token 數(shù)據(jù)記到應(yīng)用實例中
// return this.token = wx.getStorageSync('token')
return this.token
}
})
在組件內(nèi)讀token并處理
data: {
isLogin: false
},
lifetimes: {
attached() {
const isLogin = !!getApp().getToken()
//const app = getApp() const isLogin = !!app.getToken()
this.setData({ isLogin })
if (!isLogin) {
wx.redirectTo({ url: '/pages/login/index' })
}
}
},
地址重定向,登錄成功后跳回到原來的頁面
在 authoirzation
組件檢測登錄時獲取當(dāng)前頁面棧實例,并在跳轉(zhuǎn)到登錄頁面時在 URL 地址上拼湊參數(shù):
// /components/authorization/index.js
Component({
// ...
lifetimes: {
attached() {
// 獲取登錄狀態(tài)
const isLogin = !!getApp().token
// 變更登錄狀態(tài)
this.setData({ isLogin })
// 獲取頁面棧
const pageStack = getCurrentPages()
// 獲取頁面路徑
const currentPage = pageStack.pop()
// 未登錄的情況下跳轉(zhuǎn)到登錄頁面
if (!isLogin) {
wx.redirectTo({
url: '/pages/login/index?redirectURL=/' + currentPage.route,
})
}
},
},
})
用戶管理-顯示默認(rèn)值
app.js中添加初始值
App({
globalData: {},
userInfo: { avatar: '', nickName: '微信用戶1' }
}
在onLoad中加載值
data: {
avatar: '',
nickName: ''
},
onLoad() {
const app = getApp()
console.log(app.userInfo)
const { avatar, nickName } = app.userInfo
this.setData({ avatar, nickName })
// 用戶未登錄時不必請求
app.token && this.getUserProfile()
},
配置請求攔截器
將用戶的登錄狀態(tài)通過自定義的頭信息 Authorization
隨接口調(diào)用時一起發(fā)送到服務(wù)端。
// 導(dǎo)入 wechat-http 模塊
import http from 'wechat-http'
// 配置接口基礎(chǔ)路徑
http.baseURL = 'https://live-api.itheima.net'
// 配置請求攔截器
http.intercept.request = function (options) {
console.log('請求攔截器', options.header)
// 擴展頭信息
const defaultHeader = {}
// 身份認(rèn)證
const token = getApp().getToken()
if (token) {
defaultHeader.Authorization = 'Bearer ' + getApp().getToken()
}
// 與默認(rèn)頭信息合并
options.header = Object.assign({}, defaultHeader, options.header)
// 處理后的請求參數(shù)
return options
}
注:傳遞 token
時需要拼湊字符串前綴 "Bearer "
文件上傳(例:更新用戶頭像)
獲取用戶選擇的頭像地址,通過 wx.uploadFile
將圖片上傳到服務(wù)端。
wx.uploadFile
的基本語法:
-
url
上傳接口地址 -
filePath
待上傳文件的臨時路徑(該路徑只能用于小程序內(nèi)部) -
name
接口接收上傳文件的數(shù)據(jù)名稱(由后端指定) -
formData
除上傳文件外的其它數(shù)據(jù) -
header
自定義頭信息 -
success
上傳成功的回調(diào)函數(shù) -
fail
上傳失敗后的回調(diào)函數(shù) -
complete
上傳完成時的回調(diào)(無論成功或失?。?/p>
注:該 API 不支持返回 Promise,調(diào)用該 API 時,需要提前在小程序管理后臺添加服務(wù)器域名。
<!-- pages/profile/index.wxml -->
<authorization>
<view class="profile">
<van-cell center title="頭像">
<van-icon slot="right-icon" name="arrow" size="16" color="#c3c3c5" />
<button
class="button"
size="mini"
hover-class="none"
bind:chooseavatar="updateUserAvatar" //事件,事件名全部小寫,A千萬不要大寫,不會觸發(fā)
open-type="chooseAvatar"> //與上邊事件對應(yīng)
<image class="avatar" src="{{avatar}}"></image>
</button>
</van-cell>
...
</view>
</authorization>
// pages/profile/index.js
const pageStack = getCurrentPages()
Page({
...
// 更新用戶頭像
updateUserAvatar(ev.detail.avatarUrl) {
// 調(diào)用 API 上傳文件
wx.uploadFile({
// 接口地址
url: wx.$http.baseURL + '/upload',
// 待上傳的文件路徑
filePath: avatar,
name: 'file',// wx.uploadFile 要求必傳。
header: {
Authorization: 'Bearer ' + getApp().getToken() // 用戶登錄狀態(tài)
},
formData: { // 是我們自己的接口文檔的要求??梢圆粋?,默認(rèn)就是avatar
type: 'avatar'
},
success: (result) => {
console.log(JSON.parse(result.data))
const res = JSON.parse(result.data)
// console.log(res.data.url)
const avatar = res.data.url
// 1. 在頁面上顯示
this.setData({ avatar })
// 2. 更新全局?jǐn)?shù)據(jù)
const app = getApp()
app.userInfo.avatar = avatar
// 3. 通過頁面棧找到my/index頁面,更新它的avatar信息
const pages = getCurrentPages()
// pages[0].data.nickName = nickName 直接修改數(shù)據(jù)不會讓視圖更新
// 調(diào)用setData更新
pages[0].setData({ avatar })
}
})
}
})
上述代碼中通過 wx.http.baseURL
獲取接口服務(wù)器地址,通過應(yīng)用實例獲取 token
。
refresh_token使用
-
token:
-
- 作用:在訪問一些接口時,需要傳入token,就是它。
- 有效期:2小時(安全)。
-
refresh_token
-
- 作用: 當(dāng)token的有效期過了之后,可以使用它去請求一個特殊接口(這個接口也是后端指定的,明確需要傳入refresh_token),并返回一個新的token回來(有效期還是2小時),以替換過期的那個token。
- 有效期:14天。(最理想的情況下,一次登陸可以持續(xù)14天。)
1.用戶在首次完成登錄時會分別得到 token 和 refresh_token
2.當(dāng) token 失效后(例如2小時之后),調(diào)用接口A會返回 401 狀態(tài)碼(這是與后端約定好的規(guī)則)
3.檢測狀態(tài)碼是否為 401**,如果是,則攜帶refreshToken去調(diào)用刷新token的接口
4.刷新 token 的接口后會返回新的 token 和 refreshToken
5.把401的接口A重新發(fā)送一遍
注意:
refresh_token也是有過期時間的,只不過一般會比token過期時間更長一些。這就是為啥如果某個應(yīng)用我們天天打開,則不會提示我們登錄,如果是有幾周或更長時間去打開時,會再次要求我們登錄。
refresh_token一個更常見的名字叫token無感刷新。
refreshToken功能-基本實現(xiàn)
// 響應(yīng)攔截器
http.intercept.response = async ({ statusCode, data, config }) => {
console.log(statusCode, data, config)
// console.log(statusCode) // http 響應(yīng)狀態(tài)碼
// console.log(config) // 發(fā)起請求時的參數(shù)
if (data.code === 401) {
const app = getApp()
// 調(diào)用接口獲取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
// 獲得新的token后需要重新發(fā)送剛剛未完成的請求
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新發(fā)請求
return http(config)
}
// 攔截器處理后的響應(yīng)結(jié)果
if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '請求失敗')
return Promise.reject(data.message)
}
}
refreshToken也過期的特殊處理
完整版響應(yīng)攔截器
// 響應(yīng)攔截器
http.intercept.response = async ({ statusCode, data, config }) => {
console.log(statusCode, data, config)
// console.log(statusCode) // http 響應(yīng)狀態(tài)碼
// console.log(config) // 發(fā)起請求時的參數(shù)
if (data.code === 401) {
++ if (config.url.includes('/refreshToken')) {
++ console.log('/refreshToken過期了')
++ // 獲取當(dāng)前頁面的路徑,保證登錄成功后能跳回到原來頁面
++ const pageStack = getCurrentPages()
++ const currentPage = pageStack.pop()
++ const redirectURL = currentPage.route
++ // 跳由跳轉(zhuǎn)(登錄頁面)
++ wx.redirectTo({
++ url: '/pages/login/index?redirectURL=/' + redirectURL,
++ })
++ return Promise.reject('refreshToken也過期了,就只能重新登錄了')
++ }
const app = getApp()
// 調(diào)用接口獲取新的 token
const res = await http({
url: '/refreshToken',
method: 'POST',
header: {
Authorization: 'Bearer ' + app.getToken('refreshToken'),
}
})
app.setToken('token', res.token)
app.setToken('refreshToken', res.refreshToken)
config = Object.assign(config, {
header: {
// 更新后的 token
Authorization: 'Bearer ' + res.token,
},
})
// 重新發(fā)請求
return http(config)
}
// 攔截器處理后的響應(yīng)結(jié)果
else if (data.code === 10000) {
return data.data
} else {
wx.$toast(data.message || '請求失敗')
return Promise.reject(data.message)
}
}
騰訊位置服務(wù)-需要提前注冊
文檔地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview
使用步驟(共4步)
- 申請開發(fā)者密鑰(key):申請密鑰(地址:https://lbs.qq.com/dev/console/application/mine)
- 開通webserviceAPI服務(wù):控制臺 ->應(yīng)用管理 -> 我的應(yīng)用->添加key-> 勾選WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服務(wù),所以使用該功能的KEY需要具備相應(yīng)的權(quán)限)
3.下載微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.1.zip) JavaScriptSDK v1.2(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.2.zip) js文件
4.安全域名設(shè)置,在小程序管理后臺-> 開發(fā) -> 開發(fā)管理 -> 開發(fā)設(shè)置 -> “服務(wù)器域名” 中設(shè)置request合法域名,添加https://apis.map.qq.com
地理定位-wx.getLocation
獲取用戶所在位置的經(jīng)緯度。在小程序中調(diào)用這個接口時必須先在 app.json 中申請調(diào)用權(quán)限(開發(fā)環(huán)境可以省略)。
//app.json
{
"requiredPrivateInfos": [
++ "getLocation"
],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息將用于小程序位置接口的效果展示"
}
},
}
Page({
onLoad() {
this.getLocation()
},
async getLocation() {
const res = await wx.getLocation() // 要提前申請權(quán)限
console.log(res)
},
})
wx.getLocation返回的結(jié)果格式大致如下:
accuracy: 65
errMsg: "getLocation:ok"
horizontalAccuracy: 65
latitude: 30.88131
longitude: 114.37509
speed: -1
verticalAccuracy: 65
wx.getLocation 只能得到經(jīng)緯度信息
逆地址解析-reverseGeocoder
由坐標(biāo) → 坐標(biāo)所在位置的文字描述的轉(zhuǎn)換,輸入坐標(biāo)返回地理位置信息和附近poi列表
文檔地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodReverseGeocoder
1. 導(dǎo)入 QQMapWX 并設(shè)置好 key
2.在代碼中補充getPoint方法:
- 調(diào)用接口把經(jīng)緯度轉(zhuǎn)換成對應(yīng)位置的文字
- 保存文字到address
// 導(dǎo)入位置服務(wù)實例
import QQMap from '../../../utils/qqmap'
Page({ ......
onLoad() {
this.getLocation()
},
async getLocation() {
// 調(diào)用小程序API獲取經(jīng)緯度等信息
const { latitude, longitude } = await wx.getLocation() //獲取用戶經(jīng)緯度
this.getPoint(latitude, longitude)
},
getPoint(latitude, longitude) {
// 逆地址解析(根據(jù)經(jīng)緯度來獲取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: (result) => {
const address = res.address
this.setData({ address })
},
})
}
})
3.渲染頁面
<van-cell-group border="{{false}}" title="當(dāng)前地點">
<van-cell title="{{address}}" border="{{false}}"> //border="{{false}}"設(shè)置無邊框樣式
<text bind:tap="chooseLocation" class="enjoy-icon icon-locate">重新定位</text>
</van-cell>
</van-cell-group>
QQMap地點搜索—search
根據(jù)當(dāng)前的定位,調(diào)用 QQMap.search() 找到周邊的信息。
搜索周邊poi(Point of Interest),比如:“酒店” “餐飲” “娛樂” “學(xué)?!?等等
文檔地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch
在小程序中調(diào)用這個接口時必須要在 app.json 中申請調(diào)用權(quán)限
//app.json
{
"requiredPrivateInfos": [
++ "chooseLocation"
]
}
// 選擇新的位置
async chooseLocation() {
// 調(diào)用小程序 API 獲取新的位置
const { latitude, longitude } = await wx.chooseLocation()
this.getPoint(latitude, longitude) // 獲取新的位置經(jīng)緯度
},
getPoint(latitude, longitude) {
wx.showLoading({
title: '正在加載...', // 顯示loading提示
})
// 逆地址解析(根據(jù)經(jīng)緯度來獲取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: ({ result: { address } }) => {
this.setData({ address })
},
})
QQMap.search({
keyword: '住宅小區(qū)', //搜索關(guān)鍵詞
location: [latitude, longitude].join(','), //設(shè)置周邊搜索中心點
page_size: 5, //只顯示5條信息,不設(shè)置此項默認(rèn)為10
success: (result) => { //success 是一個回調(diào)函數(shù),表示搜索成功后的處理邏輯。
const points = result.data
this.setData({ points }) // 渲染數(shù)據(jù)
},
fail: (err) => { //fail 是一個回調(diào)函數(shù),表示搜索失敗后的處理邏輯。
console.log(err.message)
},
complete: () => {//complete 是一個回調(diào)函數(shù),表示搜索結(jié)束后的處理邏輯(無論搜索成功還是失?。?/span>
wx.hideLoading() // 隱藏loading提示
},
})
},
重新定位-wx.chooseLocation
申請權(quán)限
獲取用戶指定位置的經(jīng)緯度。在小程序中調(diào)用這個接口時必須要在 app.json 中申請調(diào)用權(quán)限
{
"requiredPrivateInfos": [
"chooseLocation"
]
}
示例代碼:
// 選擇新的位置
async chooseLocation() {
// 調(diào)用小程序 API 獲取新的位置
const { latitude, longitude } = await wx.chooseLocation()
// 獲取新的位置附近的小區(qū)
this.getPoint(latitude, longitude)
console.log('起點位置:', latitude, longitude)
},
getPoint(latitude, longitude) {
// 顯示loading提示
wx.showLoading({
title: '正在加載...',
})
// 逆地址解析(根據(jù)經(jīng)緯度來獲取地址)
QQMap.reverseGeocoder({
location: [latitude, longitude].join(','),
success: ({ result: { address } }) => {
// console.log(address)
// 數(shù)據(jù)數(shù)據(jù)
this.setData({ address })
},
})
QQMap.search({
keyword: '住宅小區(qū)', //搜索關(guān)鍵詞
location: [latitude, longitude].join(','), //設(shè)置周邊搜索中心點
page_size: 5,
success: (result) => {
// console.log(result)
// 過濾掉多余的數(shù)據(jù)
const points = result.data.map(({ id, title, _distance }) => {
return { id, title, _distance }
})
// console.log(points)
// 渲染數(shù)據(jù)
this.setData({ points })
},
fail: (err) => {
console.log(err.message)
},
complete: () => {
// 隱藏loading提示
wx.hideLoading()
},
})
},
圖片收集(收集身份證信息)
小程序沒有input type="file"用于選擇文件,要實現(xiàn)類似功能,用以下api:
wx.chooseMedia**:**拍攝或從手機相冊中選擇圖片或視頻。
低版本請用wx.chooseImage。
1.綁定事件選擇身份證圖片上傳。
2.發(fā)送請求上傳圖片,拿到上傳后的圖片地址。
Page({
...
async uploadPicture(ev) {
// 獲取圖片臨時地址
const res = await wx.chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['compressed'],
})
const tempPath = res.tempFiles[0].tempFilePath
const type = ev.mark.type
// 上傳圖片到服務(wù)器
wx.uploadFile({
url: wx.$http.baseURL + '/upload',
filePath: tempPath,
name: 'file',
header: {
Authorization: 'Bearer ' + getApp().getToken(),
},
success: (res) => {
const res = JSON.parse(result.data)
console.log(res.data.url) // 上傳成功的回調(diào)
this.setData({ [type]: res.data.url })
},
})
},
})
校驗表單信息
獲取了全部的表單數(shù)據(jù)后再對數(shù)據(jù)進行驗證,說明如下:
- 房屋的信息是通過url地址獲取的不需要驗證
- 性別可以指定默認(rèn)值也不需要驗證
- 剩下的數(shù)據(jù)通過
wechat-validate
插件進行驗證:
// house_pkg/pages/form/index.js
// 導(dǎo)入表單驗證插件
import wxValidate from 'wechat-validate'
Page({
behaviors: [wxValidate],
data: {
point: '',
building: '',
room: '',
name: '',
gender: 1,
mobile: '',
idcardFrontUrl: '',
idcardBackUrl: '',
},
rules: {
name: [
{ required: true, message: '業(yè)主姓名不能為空!' },
{ pattern: /^[\u4e00-\u9fa5]{2,5}$/, message: '業(yè)主姓名只能為中文!' },
],
mobile: [
{ required: true, message: '業(yè)主手機號不能為空!' },
{ pattern: /^1[3-8]\d{9}$/, message: '請?zhí)顚懻_的手機號!' },
],
idcardFrontUrl: [
{ required: true, message: '請上傳身份證國徽面!' }
],
idcardBackUrl: [
{ required: true, message: '請上傳身份證照片面!' }
],
},
})
表單收集—收集預(yù)約日期
1.時間選擇控件:van-datetime-pickerhttps://vant-contrib.gitee.io/vant-weapp/#/datetime-picker
2.彈出層控件:van-popuphttps://vant-contrib.gitee.io/vant-weapp/#/popup
- 給選擇日期單元綁定事件——顯示選擇日期彈層(van-popup)
2.給日期控件綁定確認(rèn)事件confirm
3.在確認(rèn)回調(diào)中獲取時間戳
4.將時間戳格式化并顯示
selectDate(ev) {
// console.log(ev)
this.setData({
currentDate: ev.detail,
appointment: wx.$utils.formatDate(ev.detail), // 格式化日期
})
this.closeDateLayer()
},
補充格式化日期的函數(shù)
formatDate(time) {
const d = new Date(time)
const year = d.getFullYear()
let month = d.getMonth() + 1 // 獲取月份,月份從0開始,所以加1
let day = d.getDate()
month = month < 10 ? '0' + month : month
day = day < 10 ? '0' + day : day
return `${year}-${month}-${day}`
},
wxml
<van-cell title-width="100" title="預(yù)約日期" value-class="{{appointment && 'active-cell'}}" bind:click="openDateLayer"
is-link value="{{appointment || '請選擇上門維修日期'}}" />
...省略
<van-popup bind:close="closeDateLayer" round show="{{ dateLayerVisible }}" position="bottom">
<van-datetime-picker bind:cancel="closeDateLayer" bind:confirm="selectDate" type="date" value="{{ currentDate }}"
min-date="{{ 1664582400000 }}" />
</van-popup>
加載更多
在scroll-view上添加 bindscrolltolower=“l(fā)oadMore”
<scroll-view
bindscrolltolower="loadMore"
show-scrollbar="{{false}}" enhanced scroll-y>
<view class="repairs">
<view class="repairs-title">我的報修</view>
loadMore() {
// if(是否有更多的數(shù)據(jù))
if (this.data.total <= this.data.list.length)
return
console.log('更多的數(shù)據(jù)')
// 把頁碼+1,發(fā)請求,請求回來的數(shù)據(jù)要 追加 到原數(shù)組
// [5] → [10]
this.data.page++
this.getList()
},
async getList() {
// 發(fā)請求
const { pageTotal, total, rows: list } = await wx.$http.get('/repair', { current: this.data.page, pageSize: this.data.pageSize })
console.log(list, pageTotal, total)
// 渲染數(shù)據(jù)
// 在原來的基礎(chǔ)上添加數(shù)據(jù)
this.setData({ total, list: [...this.data.list, ...list] })
},
路線規(guī)劃
路線規(guī)劃是常見的一個功能,它用來在地圖上展示兩點間路線,要使用這個功能需要用到兩部分的知識:
- 小程序提供的 map 組件用來在頁面渲染地圖
map | 微信開放文檔https://developers.weixin.qq.com/miniprogram/dev/component/map.html)
- 騰訊位置服務(wù)計算兩點路線的所有坐標(biāo)點(經(jīng)緯度)
首先來看小程序提供的地圖組件 map
- latitude 指定地圖中心點的緯度
- longitude 指定地圖中心點的經(jīng)功
- scale 指定地圖初始的縮放比例,取值范圍 3 - 20
- markers 地圖上的標(biāo)記點
- polyline 地圖上的路線
latitude、longitude、scale 相對容易理解,重點來看一下 markers 的使用:
repqir_pkg/pages/detail/index.js
Page({
data: {
markers: [
{
id: 1,
latitude: 40.22077,
longitude: 116.23128,
width: 24,
height: 30,
},
{
id: 2,
latitude: 40.225857999999995,
longitude: 116.23246699999999,
iconPath: '/static/images/marker.png',
width: 40,
height: 40,
},
],
}
})
在定義標(biāo)記點時每個標(biāo)記點必須要指定 ID 屬性,否則會有錯誤產(chǎn)生,通過 iconPath 可以自定義標(biāo)記點的圖片,width/height 定義標(biāo)記點的大小尺寸。
polyline
polyline 用來在在圖上展示兩點間的行進路線,需要傳遞給它路線對應(yīng)的坐標(biāo)點(很多個點組成的線),獲取這些坐標(biāo)點需要通過位置服務(wù)計算得到。
計算兩點間路線的坐標(biāo)點需要用到位置服務(wù)的[路線規(guī)劃]https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodDirection方法
repqir_pkg/pages/detail/index.js
// repqir_pkg/pages/detail/index.js
Page({
data: {},
onLoad() {
// 生成路線
this.getPolyline()
},
// 調(diào)用位置服務(wù)(路線規(guī)劃)
getPolyline() {
qqMap.direction({
mode: 'bicycling',
from: '40.227978,116.22998',
to: '40.22077,116.23128',
success: ({ result }) => {
const coors = result.routes[0].polyline
const points = []
//坐標(biāo)解壓(返回的點串坐標(biāo),通過前向差分進行壓縮)
for (let i = 2; i < coors.length; i++) {
coors[i] = Number(coors[i - 2]) + Number(coors[i]) / 1000000
}
// 獲取經(jīng)緯度
for (let i = 0; i < coors.length; i += 2) {
points.push({ latitude: coors[i], longitude: coors[i + 1] })
}
// 渲染數(shù)據(jù)
this.setData({
latitude: points[30].latitude,
longitude: points[30].longitude,
polyline: [
{points, color: '#5591af', width: 4},
],
})
},
})
},
})
計算出來的坐標(biāo)點是經(jīng)過壓縮的,需要按著官方指定的方式對數(shù)據(jù)進行解壓才可以獲取真正的坐標(biāo)點,并且為了適應(yīng)小程序地圖組件的用法,還需要對數(shù)據(jù)進行二次的加工。
關(guān)于數(shù)據(jù)的處理大家只需要參考文檔來實現(xiàn)就可以,可以說之所這么操作是騰訊位置服務(wù)規(guī)訂好的,做為開發(fā)者按著官方提從的方法來應(yīng)用即可。
自定義分享
[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object
監(jiān)聽用戶點擊頁面內(nèi)轉(zhuǎn)發(fā)按鈕(button 組件 open-type=“share”)或右上角菜單“轉(zhuǎn)發(fā)”按鈕的行為,并自定義轉(zhuǎn)發(fā)內(nèi)容。
注意:只有定義了此事件處理函數(shù),右上角菜單才會顯示“轉(zhuǎn)發(fā)”按鈕
Page({
onLoad({ id, encryptedData }) {
this.getPassport(id)
this.getPassportShare(encryptedData)
},
// 獲取訪客詳情(通行證)
async getPassport(id) {
// 檢測是否存在 id
if (!id) return
// 調(diào)用接口
const passport = await wx.$http.get('/visitor/' + id)
// 渲染數(shù)據(jù)
this.setData({ ...passport })
}
async getPassportShare(encryptedData) {
// 檢測是否存在 id
if (!encryptedData) return
// 調(diào)用接口
const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
// 渲染數(shù)據(jù)
this.setData({ passport })
},
onShareAppMessage() {
return {
title: '查看通行證',
path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
}
},
})
保存到本地api介紹
[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
保存圖片到系統(tǒng)相冊。
[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html
獲取圖片信息。網(wǎng)絡(luò)圖片需先配置 download 域名才能生效。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UyIG4FD7-1687267141879)(C:\Users\ZhengKaiYa\Desktop\個人筆記\3\21.png)]
保存到本地實現(xiàn)
1.給按鈕綁定事件,調(diào)用getImageInfo獲取圖片臨時路徑文章來源:http://www.zghlxwxcb.cn/news/detail-719316.html
2.調(diào)用saveImageToPhotosAlbum傳入臨時路徑完成保存功能文章來源地址http://www.zghlxwxcb.cn/news/detail-719316.html
// 保存圖片
async saveQRCode() {
try {
// 讀取圖片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存圖片到相冊
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存圖片失敗,稍后重試!')
}
},
gram/dev/component/button.html) 組件 open-type=“share”)或右上角菜單“轉(zhuǎn)發(fā)”按鈕的行為,并自定義轉(zhuǎn)發(fā)內(nèi)容。
注意:只有定義了此事件處理函數(shù),右上角菜單才會顯示“轉(zhuǎn)發(fā)”按鈕
Page({
onLoad({ id, encryptedData }) {
this.getPassport(id)
this.getPassportShare(encryptedData)
},
// 獲取訪客詳情(通行證)
async getPassport(id) {
// 檢測是否存在 id
if (!id) return
// 調(diào)用接口
const passport = await wx.$http.get('/visitor/' + id)
// 渲染數(shù)據(jù)
this.setData({ ...passport })
}
async getPassportShare(encryptedData) {
// 檢測是否存在 id
if (!encryptedData) return
// 調(diào)用接口
const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
// 渲染數(shù)據(jù)
this.setData({ passport })
},
onShareAppMessage() {
return {
title: '查看通行證',
path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
}
},
})
保存到本地api介紹
[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html
保存圖片到系統(tǒng)相冊。
[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html
獲取圖片信息。網(wǎng)絡(luò)圖片需先配置 download 域名才能生效。
保存到本地實現(xiàn)
1.給按鈕綁定事件,調(diào)用getImageInfo獲取圖片臨時路徑
2.調(diào)用saveImageToPhotosAlbum傳入臨時路徑完成保存功能
// 保存圖片
async saveQRCode() {
try {
// 讀取圖片信息
const { path } = await wx.getImageInfo({
src: this.data.url,
})
// 保存圖片到相冊
wx.saveImageToPhotosAlbum({ filePath: path })
} catch (err) {
wx.$toast('保存圖片失敗,稍后重試!')
}
},
到了這里,關(guān)于原生微信小程序基礎(chǔ)-分包加載&&自定義組件&&&項目全流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!