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 具体的颜色属性可以参考:http://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 ```