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

本文介绍了使用Taro3和Vue3开发项目的经验,包括nutUI的按需引入与颜色自定义、组件抽离、pinia状态管理、http请求封装、全局路由鉴权策略以及分包优化。同时讲解了如何在项目中引入和使用阿里iconfont。

前言

技术栈:

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

功能点:

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

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

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文件

<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/78443e40e9a3463793a42515db1c4e87~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 具体的颜色属性可以参考:[github.com/jdf2e/nutui…](https://link.juejin.cn/?target=url “url”” style=“margin: auto” />

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 
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: "白云机场" },}),// gettersgetters: {getIsshow(state) {return this.ishow},},// actionsactions: {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文件
login 模块

import request from '../service/request'
 const login={getCode(params){//获取openidreturn 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.routeconst options = currentPage.optionslet 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})
}

其他跳转方法同理 

七、分包,和主包太大,无法手机扫码预览的问题

"dev:test": "taro build --type weapp --watch --env production",

//
yarn dev:test 

八、iconfont的引入

app.js中 iconfont.css的引入同上方app.js文件中已经引入
使用
<nut-icon font-class-name="iconfont" color='#E40668'class-prefix="icon" name="shouji" />
//font-class-name和图中箭头的class属性名字要相对应,可以自行修改 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值