基於 uni-app 搭建多端框架

語言: CN / TW / HK

本篇文章分享來自小夥伴「liuxin」的一次學習總結分享,希望跟社區的同學一起探討。

什麼是 uni-app ?

uni-app 是一個使用 Vue.js 開發所有前端應用的框架。

  • 支持 Vue2 和 Vue3

uni-app 的作用?

開發者只需要編寫一套代碼,就可以發佈到 iOS、Android、Web(響應式)、以及各種小程序(微信/支付寶/釘釘/百度/頭條/飛書/快手/QQ/淘寶/京東)、快應用等多個平台。

  • 快應用僅支持 vivo、OPPO、華為

快速創建

node.js 版本 @14.19.0

安裝 vue-cli

全局安裝 vue-cli bash npm i @vue/cli@4 -g

  • 最新的 vue-cli 是 5.0.1,但是剛上線沒多久 (2022-02-17),穩定性未知,所以切換到 4 版本

創建 uni-app

  1. 創建正式版 bash vue create -p dcloudio/uni-preset-vue uni-app-frame

  2. 執行命令後,根據提示選擇模板,這裏創建一個默認模板,初次學習體驗建議選 Hello uni-app 模板,這個模板創建後有一套使用官方組件創建的跨端應用的 demo,開發時可以參考使用。

image.png

  1. 運行成 H5

  2. 切換到項目目錄,運行服務 bash cd uni-app-frame npm run serve

image.png

  • 瀏覽器打開,輸入 http://localhost:8080/

image.png

  1. 到此,一個簡單的基於 uni-app 框架的模板項目就創建,並運行成功

  2. 需要注意的是,這個默認安裝基於的 Vue2、webpack4

開發工具

  • 開發工具推薦使用官方的開發工具 HbuilderX,官方下載地址:http://www.dcloud.io/hbuilderx.html,這個開發工具為 uni-app 做了特別強化,比較適合開發 uni-app。

  • 如果使用的是 TypeScript 默認模板,開發工具推薦使用 VS Code,但是沒有 uni-app 的語法推薦,或者使用 HbuilderX 開發,調試時使用右鍵-外部命令-運行到相應渠道。

  • Node 版本為 12.10.0

解決兼容問題

1、引入 css-vars-ponyfill

原因:uni-app 的很多組件使用了 CSS 變量,而IE 瀏覽器和一些舊版瀏覽器不支持 CSS 變量,所以使用 css-vars-ponyfill 提供客户端支持。

  • 安裝 bash npm i css-vars-ponyfill

  • 在 src/main.js 中第一行引用

javascript import cssVars from 'css-vars-ponyfill' cssVars({})

2、引入 babel-polyfill 解決 ES6 兼容

  • 安裝 bash npm i babel-polyfill

  • 在根目錄下新建 webpack.config.js,並輸入一下代碼 javascript module.exports = { entry: { app: './src/main.js', babelPolyfill: 'babel-polyfill' } }

3、引入 es6-promise

一個解決 promise 兼容的插件,在 promise 未定義或者創建失敗的情況下,自動創建一個 promise

  • e安裝 bash npm i es6-promise

  • 在 src/main.js 中最三行引入使用 javascript import ES6Promise from 'es6-promise' ES6Promise.polyfill()

封裝常用工具

1、創建常用的工具

在src/utils/ 下創建 common.js,內容為項目常用到的一些工具

判斷變量類型的工具代碼如下:

```javascript const toString = Object.prototype.toString;

export function isObject(value) { return toString.call(value) === '[object Object]'; }

export function isString(value) { return toString.call(value) === '[object String]'; }

export function isNumber(value) { return toString.call(value) === '[object Number]'; }

export function isDefault(value) { return value === void 0; }

//防抖 export function debounce(fn, delay = 200) { let timeout = null; return function() { timeout && clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(this, arguments) //讓this指向調用者,參數綁定 }, delay); } } ```

封裝頁面跳轉

1、創建路由管理文件

在 src/router/ 下創建 index.js,內容為路由跳轉時用到的詳細信息

示例代碼

```javascript / * name:路由跳轉時的簡稱,可以使用這個直接跳轉頁面,省去長的路由 * path:要跳轉的路由 * type:路由跳轉方式,默認navigateTo, * 只能是以下的值["navigateTo", "switchTab", "reLaunch", "redirectTo"] / const routes = [ { name: 'index', path: 'pages/index/index', type: 'navigatorTo', }, { name: 'test', path: 'pages/test/test', }, ]

export default routes ```

配置説明

| 配置項 | 類型 | 必填 | 默認值 | 説明 | | --- | --- | --- | --- | --- | | name | String|Number | 是 | | 頁面跳轉時使用的路由,值唯一 | | path | String | 是 | | 頁面跳轉時真實的路由 | |

type |

String |

否 |

navigatorTo | 跳轉路由的方式 navigatorTo:保留當前頁面,跳轉到應用內的某個頁面 redirectTo:關閉當前頁面,跳轉到應用內的某個頁面 reLaunch:關閉所有頁面,打開應用內的某個頁面 switchTab:跳轉到 TabBar 頁面,並關閉所有非 TabBar 頁面 |

注意事項

  • src/router/index.js 中的 path 必須先在 src/pages.json 中 pages 數組中配置並確保頁面真實存在,src/pages.json 中的 pages 數組更新時,src/router/index.js 必須同步更新

  • src/pages.json 這個文件的更新可以在新建頁面時勾選上在 pages.json 中註冊,並輸入標題就可以自動添加(只有使用 HBuilderX 才有這個功能)

image.png

  • src/pages.json 示例代碼如下 json { "pages": [ //pages數組中第一項表示應用啟動頁 { "path": "pages/index/index", "style": { "navigationBarTitleText": "首頁" } }, { "path": "pages/test/test", "style": { "navigationBarTitleText": "測試", "enablePullDownRefresh": false } } ], "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "多端框架", "navigationBarBackgroundColor": "#FFFFFF", "backgroundColor": "#FFFFFF", "h5": { "navigationStyle": "custom" } } }

  • 引入 src/router/index.js 的目的是統一管理路由,並可以提供 src/pages.json 所不支持的配置項並進行統一處理

2、創建路由與頁面跳轉文件

在 src/router/ 下創建 router.js,對外提供支持跳轉頁面、返回頁面、獲取跳轉頁面時傳遞參數、返回頁面時傳遞參數等方法的類

配置代碼

```javascript import { isObject, isString, isNumber } from "@/utils/common"

class MinRouter { constructor(options = {}) { this._router = options.routes }

go(options) { let name = ''; let path = ''; let type = ''; let query = {}; let hasRoute = false; switch (true) { case isObject(options): ({ name, query = {}, type } = options); break; case isString(options): name = options; break; default: this.$showMessage(); throw new Error('參數必須是對象或者字符串'); } for (let i = 0, len = this.$minRoutes.length - 1; i <= len; i++) { if (this.$minRoutes[i].name === name) { ({ path } = this.$minRoutes[i]); type = type || this.$minRoutes[i].type || 'navigateTo'; hasRoute = true; break; } } if (!hasRoute) { this.$showMessage(); throw new Error(沒有 ${name} 頁面'); } if (!['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'].includes(type)) { this.$showMessage(); throw new Error(路由 ${name} 配置裏面的type必須是以下的值['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']) } return new Promise((resolve, reject) => { unitype }); } getQueryData() { let query = getApp().globalData.query || {}; getApp().globalData.query = {}; return query; } back(options) { let delta = 1; let backData = ""; let button = 0; switch (true) { case isObject(options): ({ delta = 1, backData = "", button = 0 } = options); break; case isNumber(options): delta = options; break; case isDefault(options): break; default: this.$showToast(); throw new Error('參數不傳或者類型必須是 Object 或者 Number'); } let readllyDelta = button ? delta : delta - 1; getApp().globalData.backData = backData; readllyDelta > 0 && uni.navigateBack({ delta: readllyDelta }) } getBackData() { let backData = getApp().globalData.backData || ""; getApp().globalData.backData = ""; return backData; }

showMessage(title = "敬請期待", duration = 2000) { uni.showToast({ title, duration, icon: "none" }); } }

export default MinRouter

```

3、創建路由攔截器文件

在 src/router/ 下創建 routerInterceptor.js,引入 ./index.js 和 router.js 對頁面跳轉返回進行統一攔截操作

配置代碼

```javascript import routes from './index' import MinRouter from './router' import Vue from 'vue' import {debounce} from '@/utils/common'

// 實例化 MinRouter const routerInterceptor = new MinRouter({ routes })

// 自定義插件,給 Vue 添加全局功能 const MyPlugin = function(){} MyPlugin.install = function (Vue, options) { // 添加 Vue 屬性 $routes 此屬性為路由配置信息 Vue.prototype.$minRoutes = options._router // 添加 Vue 實例方法 $go 此方法為跳轉頁面統一方法 Vue.prototype.$go = debounce(options.go, 100) // 添加 Vue 實例方法 $getQueryData 此方法為獲取跳轉頁面時傳遞的數據 Vue.prototype.$getQueryData = options.getQueryData // 添加 Vue 實例方法 $back 此方法為頁面返回的統一方法 // 添加防抖的目的是解決同一個頁面中,先調一次$back,然後在onUnload生命週期又調一次出現重複返回導致返回錯亂 Vue.prototype.$back = debounce(options.back, 100) // 添加 Vue 實例方法 $getBackData 此方法為獲取頁面返回時傳遞的數據 Vue.prototype.$getBackData = options.getBackData // 添加 Vue 實例方法 $getBackData 此方法為獲取頁面返回時傳遞的數據 Vue.prototype.$showMessage = options.showMessage }

Vue.use(MyPlugin, routerInterceptor)

export default routerInterceptor

```

使用説明

| 方法 | 可選參數 | 參數類型 | 説明 | | --- | --- | --- | --- | | this.$go(options) | name:跳轉使用的路由 query:跳轉時傳遞的數據 type:跳轉方式 | String | Object | options 為 String 時:上送的值為 name,name取值為 src/router/index.js 配置中的配置options 為 Object 時:可以傳 name、query、type 三個參數,其中 name 必傳 | | this.$getQueryData() | | | 獲取跳轉頁面時傳遞的數據,同一個頁面只能取值一次 | | this.$back(options) | delta:返回的頁數 backData:返回時傳遞的數據 button:在非頁面生命週期onUnload 中使用時必傳1,在頁面生命週期 onUnload 中使用時不傳值 | Object | Number | 頁面返回時調用的方法,如果只是返回一頁並且是點擊左上角或者系統的左右劃可以不需要使用此方法 這個方法是關閉當前頁面並返回上一級或多級頁面
例如:
this.$go('A');this.$go('B');this.$go('C');當前在 C 頁面,要返回 A 頁面使用 this.$back(2);返回 B 頁面時可以不調用方法 | | this.$getBackData() | | | 獲取頁面返回時傳遞的數據,同一個頁面只能接收一次 |

4、在 src/main.js 中引入 routreInterceptor

javascript import routerInterceptor from './router/routerInterceptor'

5、在 src/App.vue 中添加 globalData

```vue

```

總結

  • 以上文件創建並配置後,就可以在全局通過 this.xxx 來使用相應方法了。

  • 提供統一的路由跳轉返回取參方式是為了方便管理,也給以後添加如埋點等提供統一解決入口

使用 axios 封裝網絡請求

1、安裝 axios

javascript npm i axios

2、創建 _axios.js 來封裝請求

在 src/services/ 下創建 _axios.js,這個 js 使用 axios 來封裝請求

2.1、修改 adapter,使用 uni.request 來處理請求

使用 uni.request 的目的是兼容小程序,uni.request 會自動轉換成小程序專屬 API 進行操作

這裏分別使用 settle、buildURL 來處理響應和處理請求數據

settle:根據 HTTP 響應狀態,改變 Promise 的狀態,即根據響應成功失敗,分別調用 resolve、reject 來做返回

buildURL:會根據傳入的參數和指定的序列化方式,在 get 請求時把參數處理後拼接到 URL 上

```javascript import axios from 'axios'

// adapter 允許自定義處理請求,使用 uni.request 適配小程序端 // 返回一個 promise 並應用一個有效的響應 axios.defaults.adapter = (config) => { return new Promise((resolve, reject) => { // settle:根據 HTTP 響應狀態,改變 Promise 的狀態,引入省去自己處理 const settle = require('axios/lib/core/settle') // buildURL:在 get 請求時,會使用這個把請求的數據以指定序列化方式拼接到 URL 上 const buildURL = require('axios/lib/helpers/buildURL') uni.request({ method: config.method.toUpperCase(), url: config.baseURL + buildURL(config.url, config.params, config.paramsSerializer), header: config.headers, data: config.data, dataType: config.dataType, responseType: config.responseType, // 支付寶小程序不支持 withCredentials: config.withCredentials, // 僅 H5 支持 complete: (response) => { response = { data: response.data, status: response.statusCode, errMsg: response.errMsg, header: response.header, config: config }; settle(resolve, reject, response); } }) }) } ```

2.2、添加請求攔截器

  • responseType 合法值只有 text、arraybuffer,在請求之前攔截判斷上傳的是否合法
  • 支付寶、快手、京東小程序只支持 POST、GET 請求,所以加上條件編譯攔截判斷下這幾個渠道的 method 合法值

``javascript // request 攔截器,在請求之前做一些處理 axios.interceptors.request.use( // 請求的一些配置信息 config => { console.log('請求攔截成功') // 檢查 responseType 是否合法 if (!['text', 'arraybuffer'].includes(config.responseType)) { return Promise.reject(設置的響應類型 responseType:${config.responseType} 不合法,合法值:text、arraybuffer`) }

// 支付寶、快手、京東小程序不支持 POST、GET 以外的請求方式
// #ifdef MP-ALIPAY || MP-KUAISHOU || MP-JD
if (!['POST', 'GET'].includes(config.method.toUpperCase())) {
  return Promise.reject(`請求方式 method:${config.method} 不合法,合法值:POST、GET`)
}
// #endif
return config;

}, // 請求錯誤時的處理 error => { console.log("請求攔截到錯誤: ", error); return Promise.reject(error); } ) ```

2.3、添加響應攔截器

對返回數據進行處理後返回 ```javascript //配置 response 攔截器,對返回的數據進行處理 axios.interceptors.response.use( // 對響應成功進行統一處理 (response) => { console.log("響應成功: ", response); return checkStatus(response) }, // 對響應錯誤進行統一處理 (error) => { console.log("響應失敗: ", error); return Promise.reject(checkStatus(error)) } )

// 檢查響應狀態,做相應處理 function checkStatus(response) { if (response) { const status = response.status || -1000 let errorInfo = '' let responseData = {} switch (status) { case 200: case 201: case 304: case 400: responseData = response.data; break case -1: errorInfo = '遠程服務響應失敗,請稍後重試' break case 401: errorInfo = '401:訪問令牌無效或已過期' break case 403: errorInfo = '403:拒絕訪問' break case 404: errorInfo = '404:資源不存在' break case 405: errorInfo = '405:請求方法未允許' break case 408: errorInfo = '408:請求超時' break case 500: errorInfo = '500:訪問服務失敗' break case 501: errorInfo = '501:未實現' break case 502: errorInfo = '502:無效網關' break case 503: errorInfo = '503:服務不可用' break default: errorInfo = 連接錯誤${status} break } return { status, msg: errorInfo, ...responseData } } return { status: -404, msg: '網絡異常' } } ```

2.4、生成對外的請求函數

  • 在生成默認配置時,需要獲取 baseURL,引入 src/services/autoMatchBaseUrl.js 中的自動匹配基礎 url 方法

```javascript import qs from 'qs' import autoMatchBaseUrl from './autoMatchBaseUrl'

/* * 基於 axios uni.request 請求 * @param url 請求的地址 * @param method 請求方式 * @param timeout 請求超時時間 * @param prefix 用來拼接 url 地址 * @param data 請求的參數 * @param headers 請求頭 * @param dataType 數據類型,默認 json * @param responseType 響應類型,僅支持 test、arraybuffer 並且支付寶不支持此配置 * @param extraUrl 請求時額外的請求地址 * @returns {Promise.} * @private / export default function _Axios(url, { method = 'post', timeout = 60000, prefix = getApp().globalData.SERVER.PREFIX, data = {}, headers = {}, dataType = 'json', responseType = 'text', extraUrl = '' }) {

// 拼接 url url = url + extraUrl

// 合併完整的 headers headers = Object.assign({ 'Accept': 'application/json', 'Content-Type': 'application/json; charset=utf-8' }, headers)

// 設置默認配置 let defaultConfig = { url, method, timeout, headers, responseType, dataType, }

// 根據請求方式選擇上送的參數,如果沒有這個 get 請求會沒有拼接參數,post 請求會不上送參數 if (method.toUpperCase() === 'GET') { defaultConfig.params = data } else { defaultConfig.data = data }

// #ifdef H5 // 跨域時是否攜帶憑證,僅支持 H5 defaultConfig.withCredentials = true // #endif

return axios(defaultConfig) }

```

2.5、使用 application/x-www-form-urlencoded 格式

  • 默認情況下,axios 將 JavaScript 對象序列化為 JSON。要以 application/x-www-form-urlencoded 格式發送數據,需要使用 qs 編碼庫,在命令行安裝 qs bash npm i qs

  • 使用 qs ```javascript import qs from 'qs'

// 在 _Axios 函數中加上對 qs 的使用 ... // 根據 Content-Type 處理上送數據 const contentType = headers['Content-Type'] || '' if (~contentType.indexOf('application/x-www-form-urlencoded')) { defaultConfig.data = qs.stringify(data) } ... ```

2.6、使用 autoMatchBaseUrl 自動匹配基礎 url

  • 在 src/services/ 下創建 autoMatchBaseUrl.js
  • autoMatchBaseUrl.js 對外提供一個自動匹配基礎 url 的函數,代碼如下: ```javascript /**
  • @description: 根據上傳前綴,自動匹配基礎的 url
  • getApp().globalData.SERVER.TEST:這個的配置在 src/App.vue 中的 globalData 中添加
  • @param {*} prefix 前綴
  • @return {} / export default function autoMatchBaseUrl(prefix){ let baseUrl = '' switch (prefix) { default: baseUrl = getApp().globalData.SERVER.TEST break } return baseUrl } ```

  • 全局變量配置 ```javascript

```

  • 回到 src/service/_axios.js 中,使用 autoMatchBaseUrl 方法 ```javascript import autoMatchBaseUrl from './autoMatchBaseUrl'

// 在 _Axios 函數中加上對 autoMatchBaseUrl 的使用 ... // 獲取基礎 url let baseURL = autoMatchBaseUrl(prefix)

// 在 defaultConfig 中加入 baseURL 的配置 let defaultConfig = { baseURL, ... } ... ```

2.5、完整代碼

```javascript import axios from 'axios' import qs from 'qs' import autoMatchBaseUrl from './autoMatchBaseUrl'

// adapter 允許自定義處理請求,使用 uni.request 適配小程序端 // 返回一個 promise 並應用一個有效的響應 axios.defaults.adapter = (config) => { return new Promise((resolve, reject) => { // settle:根據 HTTP 響應狀態,改變 Promise 的狀態,引入省去自己處理 const settle = require('axios/lib/core/settle') // buildURL:在 get 請求時,會使用這個把請求的數據以指定序列化方式拼接到 URL 上 const buildURL = require('axios/lib/helpers/buildURL') uni.request({ method: config.method.toUpperCase(), url: config.baseURL + buildURL(config.url, config.params, config.paramsSerializer), header: config.headers, data: config.data, dataType: config.dataType, responseType: config.responseType, // 支付寶小程序不支持 withCredentials: config.withCredentials, // 僅 H5 支持 complete: (response) => { response = { data: response.data, status: response.statusCode, errMsg: response.errMsg, header: response.header, config: config }; settle(resolve, reject, response); } }) }) }

// request 攔截器,在請求之前做一些處理 axios.interceptors.request.use( // 請求的一些配置信息 config => { console.log('請求攔截成功') // 檢查 responseType 是否合法 if (!['text', 'arraybuffer'].includes(config.responseType)) { return Promise.reject(設置的響應類型 responseType:${config.responseType} 不合法,合法值:text、arraybuffer) }

// 支付寶、快手、京東小程序不支持 POST、GET 以外的請求方式
// #ifdef MP-ALIPAY || MP-KUAISHOU || MP-JD
if (!['POST', 'GET'].includes(config.method.toUpperCase())) {
  return Promise.reject(`請求方式 method:${config.method} 不合法,合法值:POST、GET`)
}
// #endif
return config;

}, // 請求錯誤時的處理 error => { console.log("請求攔截到錯誤: ", error); return Promise.reject(error); } )

//配置 response 攔截器,對返回的數據進行處理 axios.interceptors.response.use( // 對響應成功進行統一處理 (response) => { console.log("響應成功: ", response); return checkStatus(response) }, // 對響應錯誤進行統一處理 (error) => { console.log("響應失敗: ", error); return Promise.reject(checkStatus(error)) } )

// 檢查響應狀態,做相應處理 function checkStatus(response) { if (response) { const status = response.status || -1000 let errorInfo = '' let responseData = {} switch (status) { case 200: case 201: case 304: case 400: responseData = response.data; break case -1: errorInfo = '遠程服務響應失敗,請稍後重試' break case 401: errorInfo = '401:訪問令牌無效或已過期' break case 403: errorInfo = '403:拒絕訪問' break case 404: errorInfo = '404:資源不存在' break case 405: errorInfo = '405:請求方法未允許' break case 408: errorInfo = '408:請求超時' break case 500: errorInfo = '500:訪問服務失敗' break case 501: errorInfo = '501:未實現' break case 502: errorInfo = '502:無效網關' break case 503: errorInfo = '503:服務不可用' break default: errorInfo = 連接錯誤${status} break } return { status, msg: errorInfo, ...responseData } } return { status: -404, msg: '網絡異常' } }

/* * 基於 axios uni.request 請求 * @param url * @param method * @param timeout * @param prefix 用來拼接url地址 * @param data * @param headers * @param dataType * @param responseType * @param extraUrl * @returns {Promise.} * @private / export default function _Axios(url, { method = 'post', timeout = 60000, prefix = getApp().globalData.SERVER.PREFIX, data = {}, headers = {}, dataType = 'json', responseType = 'text', extraUrl = '' }) { // 獲取基礎 url let baseURL = autoMatchBaseUrl(prefix)

// 拼接 url url = url + extraUrl

// 合併完整的 headers headers = Object.assign({ 'Accept': 'application/json', 'Content-Type': 'application/json; charset=utf-8' }, headers)

// 設置默認配置 let defaultConfig = { baseURL, url, method, timeout, headers, responseType, dataType, }

// 根據請求方式選擇上送的參數,如果沒有這個 get 請求會沒有拼接參數,post 請求會不上送參數 if (method.toUpperCase() === 'GET') { defaultConfig.params = data } else { defaultConfig.data = data }

// 根據 Content-Type 處理上送數據 const contentType = headers['Content-Type'] || '' if (~contentType.indexOf('application/x-www-form-urlencoded')) { defaultConfig.data = qs.stringify(data) }

// #ifdef H5 // 跨域時是否攜帶憑證,僅支持 H5 defaultConfig.withCredentials = true // #endif

return axios(defaultConfig) }

```

3、創建 RESTFULLURL.js 統一管理請求地址

javascript // key 請求時使用的方法名 // value 請求時使用的 url 需要是相對路徑,不能是絕對路徑 export default { index: 'index', test: 'test' }

4、創建 index.js 對外提供接口 API

在 src/services/ 下創建 index.js,使用封裝好的 axios 對外提供接口 API ```javascript import _Axios from './_axios' import urls from './RESTFULLURL'

let services = {}

Object.keys(urls).forEach((key) => { services[key] = (options) => { // 處理掉 undefined、null 等情況 return _Axios(urls[key], options || {}) } })

export default services ```

5、在 src/main. js 中配置 services

```javascript import services from './services'

Vue.prototype.$services = services ```

到此就可以使用 this.$services.xxx(options) 來請求接口了 javascript this.$services.index({ method: 'get', data: {index: '首頁發的請求'} }).then((data) => { console.log("data: ",data); })

image.png

注意事項

  1. responseType:合法值只有 text、arraybuffer,並且支付寶小程序不支持此屬性

如果設置了非法值,在小程序端是會報錯的 triggerOnEvent called on a deprecated instance

  1. 小程序端是沒有 cookie 的,需要自行處理,目前解決方案,引入 weapp-cookie 對 cookie 自動管理 bash npm i [email protected]

在入口 js,src/main.js 中引入 weapp-cookie javascript // #ifndef H5 import 'weapp-cookie' // #endif

  1. 付寶、快手、京東小程序不支持 POST、GET 以外的請求方式

使用 Vuex

創建 uni-app 默認版本時會內置 Vuex,所以不需要安裝。

使用時推薦目錄結構如下:

image.png

1、配置代碼

在 src/store/ 下分別創建 index.js、getters.js、mutations.js、actions.js、modules目錄

其中 src/store/index.js 基本代碼如下,其餘文件根據使用可自行添加內容

```javascript import Vue from 'vue' import Vuex from 'vuex' import getters from './getters'

//vue的插件機制 Vue.use(Vuex);

//Vuex.Store 構造器選項 const store = new Vuex.Store({ //存放狀態 state:{ "username": '多端框架' }, getters, modules:{

} }) export default store ```

2、在 src/main.js 中引入

```javascript import cssVars from 'css-vars-ponyfill' cssVars({}) import ES6Promise from 'es6-promise' ES6Promise.polyfill() import Vue from 'vue' import App from './App' import routerInterceptor from './router/routerInterceptor' import store from './store'

Vue.prototype.$store = store Vue.config.productionTip = false

App.mpType = 'app'

const app = new Vue({ store, ...App }) app.$mount()

```

使用時可根據 Vuex 官方文檔配置使用

引入 uni-ui

1、安裝 sass

bash npm i sass -D

2、安裝 sass-loader

bash npm i [email protected] -D

3、安裝 uni-ui

bash npm i @dcloudio/uni-ui

4、配置 easycom

在 src/pages.json 中配置以下代碼,只需要新增 easycom 配置

```json {

"easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } } }

```

注意

  • 使用 npm 安裝的組件,默認情況下 babel-loader 會忽略所有 node_modules 中的文件 ,導致條件編譯失效,需要通過配置 vue.config.js 解決:

javascript module.exports = { transpileDependencies: ['@dcloudio/uni-ui'] }

  • uni-ui 的使用詳見官方文檔 uni-ui

uni-app 的避坑指南

1、運行報 TS2304 錯誤

用 vue-cli 生成 uni-app 默認模板 (TypeScript) 後,在 HbuilderX 中運行到微信小程序報 TS2304 錯誤

bash Starting type checking service... Using 1 worker with 2048MB memory limit No type errors found Version: typescript 3.9.10 Time: 1809ms [tsl] ERROR at main.ts:1 TS2304:Cannot find name 'wx'. ERROR Build failed with errors.

可以使用外部命令運行成微信 bash npm run dev:map-weixin

2、小程序和 App 是沒有瀏覽器專用的 js 對象的

小程序和 App 的 js 運行在 jscore 下而不是瀏覽器裏,沒有瀏覽器專用的 js 對象,比如 document、xmlhttp、cookie、window、location、navigator、localstorage、websql、indexdb、webgl等對象。

3、小程序端不支持自動保持 cookie

小程序端不支持自動保持 cookie,服務器應避免驗證 cookie。如果服務器無法修改,也可以使用一些模擬手段,比如可以請求時帶上 cookie 並將響應的 cookie 保存在本地,也可以藉助 weapp-cookie 插件來實現。