基於 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,官方下載地址:https://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 外掛來實現。