前言
技术栈:
- 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文件
具体的颜色属性可以参考: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);
定制样式效果如下
将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
<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){//获取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