Taro3+Vue3.0做實戰項目的總結和方法

語言: CN / TW / HK

theme: channing-cyan highlight: androidstudio


前言

技術棧:

  • Taro3.0
  • Vue3.0
  • Pinia
  • nutUI 功能點:
  • nutUI按需引入定製化和抽離
  • http封裝以及如何全局使用,多人協同開發
  • 劫持生命週期做路由的鑑權/對路由跳轉的二次封裝
  • setup語法糖的使用
  • 分包,主包太大開啟壓縮

首先創建項目我們一定要使用穩定的版本

npm info @tarojs/cli

image.png

我這裏使用的是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文件

image.png 具體的顏色屬性可以參考: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);

定製樣式效果如下

image.png

將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

image.png 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文件

image.png ``` 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 }) }

其他跳轉方法同理 ```

分包,和主包太大,無法手機掃碼預覽的問題

image.png

image.png 主包太大無法預覽在package.json中 ``` "dev:test": "taro build --type weapp --watch --env production",

// yarn dev:test ```