uni-app開發(fā)小程序:項目架構(gòu)以及經(jīng)驗分享
2022
年的時候,公司為了快速完成產(chǎn)品并上線,所以選用微信小程序為載體;由于后期還是打算開發(fā)App
;雖然公司有ios
和Android
踪少,但是如果能一套代碼打包多端,一定程度上可以解決成本糠涛。前端技術(shù)棧也是vue
援奢,在考察選擇了uni-app
。后來多個小程序項目都采用了uni-app
開發(fā)忍捡,積累了一定的經(jīng)驗以及封裝了較多業(yè)務(wù)組件集漾,這里就分享一下uni-app
項目的整體架構(gòu)、方法封裝組件庫選擇以及注意事項砸脊。全文代碼都會放到github
具篇,先贊后看,月入百萬凌埂!
創(chuàng)建項目
uni-app
提供了兩種創(chuàng)建項目的方式:
??需要注意的是驱显,一定要根據(jù)項目需求來選擇項目的創(chuàng)建方式;如果只是單獨的開發(fā)
小程序
或App
,且開發(fā)環(huán)境單一埃疫,可以使用HBuilderX
可視化工具創(chuàng)建伏恐。如果多端開發(fā),以及同一套代碼可能會打包生成多個
小程序建議使用vue-cli
進行創(chuàng)建熔恢,不然后期想搞自動化構(gòu)建以及按指定條件進行編譯比較痛苦脐湾。關(guān)于按條件編譯臭笆,文章后面會有詳細說明叙淌。
使用vue-cli
安裝和運行:
1.全局安裝 vue-cli
npm install -g @vue/cli
2.創(chuàng)建uni-app
vue create -p dcloudio/uni-preset-vue 項目名稱
3.進入項目文件夾
cd 項目名稱
4.運行項目,如果是已微信小程序為主愁铺,可以在package.json
中的命令改為:
"scripts": {
"serve": "npm run dev:mp-weixin"
}
然后執(zhí)行
npm run serve
使用cli
創(chuàng)建項目默認不帶css
預(yù)編譯鹰霍,需要手動安裝一下,這里已sass
為例:
npm i sass --save-dev
npm i sass-loader --save-dev
整體項目架構(gòu)
通過HBuilderX
或者vue-cli
創(chuàng)建的項目茵乱,目錄結(jié)構(gòu)有稍許不同茂洒,但基本沒什么差異,這里就按vue-cli
創(chuàng)建的項目為例瓶竭,整體架構(gòu)配置如下:
├──dist 編譯后的文件路徑
├──package.json 配置項
├──src 核心內(nèi)容
├──api 項目接口
├──components 全局公共組件
├──config 項目配置文件
├──pages 主包
├──static 全局靜態(tài)資源
├──store vuex
├──mixins 全局混入
├──utils 公共方法
├──App.vue 應(yīng)用配置督勺,配置App全局樣式以及監(jiān)聽
├──main.js Vue初始化入口文件
├──manifest.json 配置應(yīng)用名稱、appid等打包信息
├──pages.json 配置頁面路由斤贰、導(dǎo)航條智哀、選項卡等頁面類信息
└──uni.scss 全局樣式
封裝方法
工欲善其事,必先利其器荧恍。在開發(fā)之前瓷叫,我們可以把一些全局通用的方法進行封裝,以及把uni-app
提供的api
進行二次封裝送巡,方便使用摹菠。全局的公共方法我們都會放到/src/utils
文件夾下。
封裝常用方法
下面這些方法都放在/src/utils/utils.js
中骗爆,文章末尾會提供github
鏈接方便查看次氨。如果項目較大,建議把方法根據(jù)功能定義不同的js
文件摘投。
小程序Toast
提示
/**
* 提示方法
* @param {String} title 提示文字
* @param {String} icon icon圖片
* @param {Number} duration 提示時間
*/
export function toast(title, icon = 'none', duration = 1500) {
if(title) {
uni.showToast({
title,
icon,
duration
})
}
}
緩存操作(設(shè)置/獲取/刪除/清空)
/**
* 緩存操作
* @param {String} val
*/
export function setStorageSync(key, data) {
uni.setStorageSync(key, data)
}
export function getStorageSync(key) {
return uni.getStorageSync(key)
}
export function removeStorageSync(key) {
return uni.removeStorageSync(key)
}
export function clearStorageSync() {
return uni.clearStorageSync()
}
頁面跳轉(zhuǎn)
/**
* 頁面跳轉(zhuǎn)
* @param {'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab' | 'navigateBack' | number } url 轉(zhuǎn)跳路徑
* @param {String} params 跳轉(zhuǎn)時攜帶的參數(shù)
* @param {String} type 轉(zhuǎn)跳方式
**/
export function useRouter(url, params = {}, type = 'navigateTo') {
try {
if (Object.keys(params).length) url = `${url}?data=${encodeURIComponent(JSON.stringify(params))}`
if (type === 'navigateBack') {
uni[type]({ delta: url })
} else {
uni[type]({ url })
}
} catch (error) {
console.error(error)
}
}
圖片預(yù)覽
/**
* 預(yù)覽圖片
* @param {Array} urls 圖片鏈接
*/
export function previewImage(urls, itemList = ['發(fā)送給朋友', '保存圖片', '收藏']) {
uni.previewImage({
urls,
longPressActions: {
itemList,
fail: function (error) {
console.error(error,'===previewImage')
}
}
})
}
圖片下載
/**
* 保存圖片到本地
* @param {String} filePath 圖片臨時路徑
**/
export function saveImage(filePath) {
if (!filePath) return false
uni.saveImageToPhotosAlbum({
filePath,
success: (res) => {
toast('圖片保存成功', 'success')
},
fail: (err) => {
if (err.errMsg === 'saveImageToPhotosAlbum:fail:auth denied' || err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
uni.showModal({
title: '提示',
content: '需要您授權(quán)保存相冊',
showCancel: false,
success: (modalSuccess) => {
uni.openSetting({
success(settingdata) {
if (settingdata.authSetting['scope.writePhotosAlbum']) {
uni.showModal({
title: '提示',
content: '獲取權(quán)限成功,再次點擊圖片即可保存',
showCancel: false
})
} else {
uni.showModal({
title: '提示',
content: '獲取權(quán)限失敗糟需,將無法保存到相冊哦~',
showCancel: false
})
}
},
fail(failData) {
console.log('failData', failData)
}
})
}
})
}
}
})
}
更多函數(shù)就不在文章中展示了,已經(jīng)放到/src/utils/utils,js
里面谷朝,具體可以到github查看洲押。
請求封裝
為了減少在頁面中的請求代碼,所以我們要對uni-app
提供的請求方式進行二次封裝圆凰,在/src/utils
文件夾下建立request.js
杈帐,具體代碼如下:
import {toast, clearStorageSync, getStorageSync, useRouter} from './utils'
import {BASE_URL} from '@/config/index'
const baseRequest = async (url, method, data, loading = true) =>{
header.token = getStorageSync('token') || ''
return new Promise((reslove, reject) => {
loading && uni.showLoading({title: 'loading'})
uni.request({
url: BASE_URL + url,
method: method || 'GET',
header: header,
timeout: 10000,
data: data || {},
success: (successData) => {
const res = successData.data
uni.hideLoading()
if(successData.statusCode == 200){
// 這里根據(jù)自己的業(yè)務(wù)邏輯去調(diào)整
if(res.resultCode == 'PA-G998'){
clearStorageSync()
useRouter('/pages/login/index', 'reLaunch')
}else{
reslove(res.data)
}
}else{
toast('網(wǎng)絡(luò)連接失敗,請稍后重試')
reject(res)
}
},
fail: (msg) => {
uni.hideLoading()
toast('網(wǎng)絡(luò)連接失敗,請稍后重試')
reject(msg)
}
})
})
}
const request = {};
['options', 'get', 'post', 'put', 'head', 'delete', 'trace', 'connect'].forEach((method) => {
request[method] = (api, data, loading) => baseRequest(api, method, data, loading)
})
export default request
請求封裝好以后挑童,我們在/src/api
文件夾下按業(yè)務(wù)模塊建立對應(yīng)的api
文件累铅,拿獲取用戶信息接口舉例子:
在/src/api
文件夾下建立user.js
,然后引入request.js
import request from '@/utils/request'
//個人信息
export const info = data => request.post('/v1/api/info', data)
在頁面中直接使用:
import {info} from '@/api/user.js'
export default {
methods: {
async getUserinfo() {
let info = await info()
console.log('用戶信息==', info)
}
}
}
自定義tabBar
寫uni-app
或者小程序
基本避不開這個話題了站叼,很多情況下娃兽,官方提供的tabBar
方案并不能滿足產(chǎn)品需求/ui要求,官方也提供了自定義tabBar的方案尽楔,但此方案有很多弊端投储,比如:切換時候會tabBar
會有明顯的閃動±觯可以參考之前寫的文章小程序自定義TabBar 如何實現(xiàn)“keep-alive”玛荞,文章中是原生小程序,但思路在uni-app
中同樣適用呕寝,如果感興趣勋眯,可以評論區(qū)提問。
版本切換
很多場景下下梢,需要根據(jù)不同的環(huán)境去切換不同的請求域名客蹋、APPID
等字段,這時候就需要通過環(huán)境變量來進行區(qū)分孽江。下面案例我們就分為三個環(huán)境:開發(fā)環(huán)境(dev
)讶坯、測試環(huán)境(test
)、生產(chǎn)環(huán)境(prod
)竟坛。
建立env
文件
在項目根目錄建立下面三個文件并寫入內(nèi)容(常量名要以VUE開頭命名):
.env.dev
(開發(fā)環(huán)境)
VUE_APP_MODE=dev
VUE_APP_ID=wxbb53ae105735a06b
VUE_APP_BASE=https://www.baidu.dev.com
.env.test
(測試環(huán)境)
VUE_APP_MODE=test
VUE_APP_ID=wxbb53ae105735a06c
VUE_APP_BASE=https://www.baidu.test.com
.env.prod
(生產(chǎn)環(huán)境)
VUE_APP_MODE=wxbb53ae105735a06d
VUE_APP_ID=prod
VUE_APP_BASE=https://www.baidu.prod.com
修改package.json
文件
"scripts": {
"dev:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --mode dev",
"build:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --mode prod"
},
然后執(zhí)行
npm run dev:mp-weixin
在/src/pages/index/index.vue
下闽巩,打印:
onLoad() {
console.log(process.env.VUE_APP_MODE, '====VUE_APP_BASE')
console.log(process.env.VUE_APP_BASE, '====VUE_APP_BASE')
},
此時輸出結(jié)果就是
// dev ====VUE_APP_BASE
// https://www.baidu.dev.com ====VUE_APP_BASE
動態(tài)修改appid
如果同一套代碼担汤,需要打包生成多個小程序涎跨,就需要動態(tài)修改appid
了;文章開頭說過appid在/src/manifest.json
文件中配置崭歧,但json
文件又不能直接寫變量隅很,這時候就可以參考官方 提出的解決方案:建立vue.config.js
文件,具體操作如下率碾。
在根目錄下建立vue.config.js
文件寫入以下內(nèi)容:
// 讀取 manifest.json 叔营,修改后重新寫入
const fs = require('fs')
const manifestPath = './src/manifest.json'
let Manifest = fs.readFileSync(manifestPath, { encoding: 'utf-8' })
function replaceManifest(path, value) {
const arr = path.split('.')
const len = arr.length
const lastItem = arr[len - 1]
let i = 0
let ManifestArr = Manifest.split(/\n/)
for (let index = 0; index < ManifestArr.length; index++) {
const item = ManifestArr[index]
if (new RegExp(`"${arr[i]}"`).test(item)) ++i
if (i === len) {
const hasComma = /,/.test(item)
ManifestArr[index] = item.replace(
new RegExp(`"${lastItem}"[\\s\\S]*:[\\s\\S]*`),
`"${lastItem}": ${value}${hasComma ? ',' : ''}`
)
break
}
}
Manifest = ManifestArr.join('\n')
}
// 讀取環(huán)境變量內(nèi)容
replaceManifest('mp-weixin.appid', `"${process.env.VUE_APP_ID}"`)
fs.writeFileSync(manifestPath, Manifest, {
flag: 'w'
})
如果是通過HBuilderX可視化工具創(chuàng)建的項目,則無法去自動根據(jù)環(huán)境去修改appid
所宰,只能去手動修改绒尊。
組件庫
uni-app
最受歡迎的可能就是插件市場了,插件市場提供了很多優(yōu)秀的插件/組件庫供我們選擇仔粥,比較火的就是自家的uni-ui以及uView UI婴谱,大部分組件還是比較好用的蟹但,如果做中大型項目以及UI要求較高的情況下,還是比較推薦自己搭一套組件庫谭羔,方便擴展以及維護华糖。
結(jié)尾
關(guān)于uni-app
項目的起步工作就到這里了,后面有機會寫一套完整的uni
搭建電商小程序項目瘟裸,記得關(guān)注客叉。代碼已經(jīng)提交到github,如果對你有幫助话告,記得點個star
!