Taro3+Vue3.0做實戰專案的總結和方法
theme: channing-cyan highlight: androidstudio
前言
技術棧:
- Taro3.0
- Vue3.0
- Pinia
- nutUI 功能點:
- nutUI按需引入定製化和抽離
- http封裝以及如何全域性使用,多人協同開發
- 劫持生命週期做路由的鑑權/對路由跳轉的二次封裝
- setup語法糖的使用
- 分包,主包太大開啟壓縮
首先建立專案我們一定要使用穩定的版本
npm info @tarojs/cli
我這裡使用的是V3.4.7,初始化專案吧,沒啥可說的,我這裡初始化的時候選擇nutUi的模板,或者你可以建立完手動安裝yarn add @nutui/nutui-taro/cnpm i @nutui/nutui-taro -S
nutUI
的按需引入
需要藉助babel-plugin-import ,這是一款 babel 外掛,它會在編譯過程中將 import 語句自動轉換為按需引入的方式。
cnpm install babel-plugin-import --save-dev
或者
yarn add babel-plugin-import -D
babel.config.js檔案
plugins: [
[
"import",
{
"libraryName": "@nutui/nutui",
"libraryDirectory": "dist/packages/_es",
"camel2DashComponentName": false
},
'nutui3-vue'
],
[
"import",
{
"libraryName": "@nutui/nutui-taro",
"libraryDirectory": "dist/packages/_es",
"camel2DashComponentName": false
},
'nutui3-taro'
]
]
nutUi的顏色自定義
在assets的檔案內建立theme.scss檔案
具體的顏色屬性可以參考:https://github.com/jdf2e/nutui/blob/next/src/packages/styles/variables.scss
在config/index.js
中
const {resolve}=require('path');
sass: {
// 預設京東 APP 10.0主題 > @import "@nutui/nutui/dist/styles/variables.scss";
// 京東科技主題 > @import "@nutui/nutui/dist/styles/variables-jdt.scss";
// additionalData: `@import "@/assets/theme.scss";`
resource: [
resolve(__dirname, '..', 'src/assets/theme.scss') // 預載入自定義的主題scss
],
data: `@import "@nutui/nutui-taro/dist/styles/variables.scss";`
},
在app.js中
import { createApp } from 'vue'
import { Button } from '@nutui/nutui-taro'
const app = createApp();
app.use(Button);
定製樣式效果如下
將nutUi和createAPP抽離
新建utils資料夾,在utils中新建 createApp.js 和 nutPlguin.js
createApp.js
import { createApp } from 'vue'
import {getStorage,getOpenId} from './tools'
import Taro,{useRouter} from '@tarojs/taro';
const App = createApp({
onShow (options) {
Taro.setTabBarBadge({ //這裡是給底部導航設定角標的
index: 2,
text: '9',
})
},
mounted () { // 儲存openid;
!getStorage(`${process.env.OPENID}`)&&getOpenId();
},
// 入口元件不需要實現 render 方法,即使實現了也會被 taro 所覆蓋
})
export default App
nutPlguin.js
```
import app from './createApp'
import '@nutui/nutui-taro/dist/styles/themes/default.scss';
import {Button, Toast, Icon, Tabs, } from '@nutui/nutui-taro';
app.use(Button)
app.use(Toast)
app.use(Icon)
app.use(Tabs)
修改後的app.js
import App from './utils/createApp' //createApp import { createPinia } from 'pinia' // 引入pinia import request from './utils/allApis' import './utils/nutPlguin' //nutUi元件 import './app.less' import './assets/iconfont/iconfont.css'; App.use(createPinia()) // 全域性的 App.config.globalProperties.$request=request; export default App
```
引入pinia
安裝pinia
yarn add pinia
yarn add taro-plugin-pinia
專案配置檔案 config/index.js 中配置:
plugins: ['taro-plugin-pinia']
在app.js中同上
pages同級別目錄新建store檔案,新建index.js
``
import { defineStore } from 'pinia'
import {setStorage,getStorage} from '@/utils/tools'
export const useStore = defineStore({
id: 'store',
state: () => ({
ishow:true,
Token:getStorage(
${process.env.TOKEN}`)||'',
list:[],
test:'store',
address:{
provinceName:'廣東省',
cityName: "廣州市",
countyName: "白雲區",
detailInfo: "白雲機場"
},
}),
// getters
getters: {
getIsshow(state) {
return this.ishow
},
},
// actions
actions: {
setAddress(params){
this.address=params;
},
setToken(params){
this.Token=params;
setStorage(${process.env.TOKEN}
,params)
},
change(params){
this.test=params
},
getdata() {
fetch('http://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json =>this.list=json)
},
},
})
```
使用pinia
```
```
http封裝和使用技巧(適合多人)
http://t.zoukankan.com/BySee1423-p-14470276.html 這是哪個道友封裝的,好像Taro社群也能看到這個文章。
新建service檔案並且新建request.js
``` import Taro from '@tarojs/taro'; // import QS from 'qs' import {getStorage,clearStorage,getCurrentPageUrlWithArgs} from '@/utils/tools' import {useStore} from '@/store' let needLoadingRequestCount = 0; // loading配置,請求次數統計 function startLoading() { Taro.showLoading({ title: '載入中', icon: 'loading', mask: true }) } function endLoading() { Taro.hideLoading(); } // 宣告一個物件用於儲存請求個數
function showFullScreenLoading() { if (needLoadingRequestCount === 0) { startLoading(); } needLoadingRequestCount++; }; function tryHideFullScreenLoading() { if (needLoadingRequestCount <= 0) return; needLoadingRequestCount--; if (needLoadingRequestCount === 0) { endLoading(); } }; //loading是做了多個請求同時發起的時候防止動畫疊加
export default function request(url,config={},needLoading=false) {//預設載入都帶動畫設定false不載入
const store=useStore();
needLoading&&showFullScreenLoading();
return new Promise((resolve, reject) => {
Taro.request({
url:${process.env.BASE_URL}${url}
,
method:config.type.toUpperCase()||'GET',
data:config.data||{},
header: {
'Content-type': 'application/json',
Authorization:store.Token,
...config.header
},
success:(res)=>{
const success200 = () => {//-----------處理200成功 這裡根據公司情況
let {statusCode}=res;
let {code,msg}=res.data;
resolve(res&&res.data&&res.data.data)
tryHideFullScreenLoading();
};
const success401 = () => {//-----------------處理401 去登入
// let url=getCurrentPageUrlWithArgs();
// clearStorage(${process.env.TOKEN}
)
// clearStorage(${process.env.USERINFO}
)
// Taro.redirectTo({ url:/pages/login/index?url=${encodeURIComponent(url)}
});
};
const other=()=>{}//---------------------這裡是擴充套件其他
const actions = new Map([
["code_200", success200],
["code_401", success401],
// ["code_500", success500],
["default", other],
//...
]);
const events = (identity, status) => {
let action = actions.get(${identity}_${status}
) || actions.get("default");
action.call(this);
};
events("code",res.statusCode);
},
fail:(error)=>{
tryHideFullScreenLoading();
Taro.showToast({
title: error.errMsg,
icon: 'warn',
duration: 2000
})
// }
throw new Error(error);
},
complete:(res)=>{
}
})
.catch(error => {
Taro.showToast({
title: error.errMsg,
icon: 'warn',
duration: 2000
})
reject(error);
throw new Error(error);
});
})
} ```
新建api檔案
```
login 模組
import request from '../service/request'
const login={
getCode(params){//獲取openid
return request(/weixin/mini/getOpenid?code=${params}
,{type:'Get'})
},
WxLogin(data){//微信授權登入
return request(/weixin/mini/autoLogin
,{type:'post',data})
},
}
export default {login}
```
在utils中新建 allApis.js
require.context我的文章講訴過很多了
let apiObject = {}; const importAll = r => { r.keys().forEach(key => Object.assign(apiObject, r(key).default)); }; importAll(require.context("../api", false, /\.js$/)); export default { ...apiObject, };
app.js同上配置,將所有介面掛載到vue的全域性
元件中使用
``` import { onMounted, reactive,getCurrentInstance,ref, toRefs } from "vue"; const { proxy } = getCurrentInstance(); try { let {token}=await proxy.$request['login'].WxLogin(params);//login的WxLogin方法 let user=await proxy.$request['user'].getUserInfo();//user模組的getUserInfo方法
} catch (error) {
console.log(error)
}
```
全域性的路由鑑權
場景1:在我下單的時候我需要登入,或者跳轉到下個頁面,下個頁面也是需要登入才可以檢視,我們可以在介面401的時候做跳轉,如果不借助後端呢
場景2:我們在開啟別人分享的小程式詳情的時候,如果是第一次需要授權才登入,我們怎麼去攔截頁面直接去先登入,完事後再跳轉回來
我的解決辦法是劫持vue的生命週期,Vue3.0
的話,我們可以使用Hooks或者mixin
utils中新建needLoginHook.js
import {useStore} from '@/store'
import Taro from '@tarojs/taro';
import {getCurrentPageUrlWithArgs} from './tools';
import { onMounted} from 'vue'
// 方法1,沒必要使用全域性的,因為有些頁面是不需要混入的
// export const needLogin = {//mixin的方式
// mounted(){
// const state=useStore();
// const url=getCurrentPageUrlWithArgs();//當前帶引數的路徑
// if(!state.Token){
// Taro.redirectTo({url:`/pages/login/index?url=${encodeURIComponent(url)}`});
// }
// }
// }
// 方法2
export default ()=>{
onMounted(()=>{
const state=useStore();
const url=getCurrentPageUrlWithArgs();//當前帶引數的路徑
if(!state.Token){
Taro.redirectTo({url:`/pages/login/index?backUrl=${encodeURIComponent(url)}`});
}
})
}
這個函式獲取到的是帶引數的連結,要自己拼裝的,Taro獲取連結攜帶的引數會丟失
export const getCurrentPageUrlWithArgs = () => { //獲取帶參連結
const pages = Taro.getCurrentPages()
const currentPage = pages[pages.length - 1]
const url = currentPage.route
const options = currentPage.options
let urlWithArgs = `/${url}?`
for (let key in options) {
const value = options[key]
urlWithArgs += `${key}=${value}&`
}
urlWithArgs = urlWithArgs.substring(0, urlWithArgs.length - 1);
return urlWithArgs;
}
元件中使用
``` 方法1:mixin的方式 import {needLogin} from '@/utils/needLoginHook' export default { mixins:[needLogin], }
```
這裡注意如果你是用setUp語法糖的方式即:
<script setup></script>
,你就要從新加一個script標籤使用minxin<script>export default {mixins:[needLogin]}</script>
路由跳轉攔截就是重新包裝路由跳轉
``
import Taro from '@tarojs/taro'
import {useStore} from '@/store'
// 開啟新頁面並跳轉
function navigateTo(url, params) {
const store=useStore();
//這裡做路由的載入
const avtor = Taro.getStorageSync('avtor');
const paramsStr = handleParams(params)
url = store.Token ? url + paramsStr :
/pages/login/index?backUrl=${url}`
Taro.navigateTo({
url
})
}
其他跳轉方法同理 ```
分包,和主包太大,無法手機掃碼預覽的問題
主包太大無法預覽在package.json中
```
"dev:test": "taro build --type weapp --watch --env production",
// yarn dev:test ```