Taro3+Vue3.0做实战项目的总结和方法

·  阅读 680
Taro3+Vue3.0做实战项目的总结和方法

前言

技术栈:

  • Taro3.0
  • Vue3.0
  • Pinia
  • nutUI

功能点:

  • nutUI按需引入定制化和抽离
  • http封装以及如何全局使用,多人协同开发
  • 劫持生命周期做路由的鉴权/对路由跳转的二次封装
  • setup语法糖的使用
  • 阿里iconfont在项目中的使用
  • 分包,主包太大开启压缩

首先创建项目我们一定要使用稳定的版本

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 具体的颜色属性可以参考:github.com/jdf2e/nutui…

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

<script setup>
import { computed, reactive, toRefs } from 'vue';
import {useStore} from '@/store'
const store=useStore();
const state=reactive({//store中state的属性使用
      address:computed(()=>store.address)
})
store.setToken('token')//store中state的方法使用
</script>

五、http封装和使用技巧(适合多人)

t.zoukankan.com/BySee1423-p… 这是哪个道友封装的,好像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],
}
方法2:hooks的方式
import {needLogin} from '@/utils/needLoginHook'
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

八、iconfont的引入

image.png 奈何iconfont服务挂了以后,到现在只能下载本地使用了,别扭。。。。

app.js中 iconfont.css的引入同上方app.js文件中已经引入

使用

image.png

<nut-icon font-class-name="iconfont" color='#E40668'  class-prefix="icon"   name="shouji" />
//font-class-name和图中箭头的class属性名字要相对应,可以自行修改
分类:
前端
分类:
前端
收藏成功!
已添加到「」, 点击更改