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 ```