共计 10521 个字符,预计需要花费 27 分钟才能阅读完成。
应用 Taro3 + Vue3 开发微信小程序
前言
微信小程序是以微信为运行环境的一种利用,其实质是 Hybrid 技术的利用,Hybrid App 即混合模式挪动利用,因而与 H5 相似,但又比 H5 领有很多原生的能力,例如调用地位信息和摄像头等。
小程序的开发方式与 H5 十分相似,用的也是 JavaScript、HTML、CSS 语言。
因而,小程序开发能够说是一名前端工程师必须要把握的技能。
原生小程序开发有肯定的学习老本,现如今市面上有很多开发小程序的第三方多端框架,如果不是谋求极致性能和稳固,还是不要用原生小程序开发了,开发效率太低。
第三方多端框架中,taro 和 uni-app 的应用度是最广的,一般来说,做技术选型时,团队用 react,就用 taro,团队用 vue,就用 uni-app,两者之间没有什么优劣之分,都挺好用的。
但很多开发者可能不晓得,taro3.0 以上版本是反对应用 vue 的,本篇文章就来介绍一下如何应用 Taro3 + Vue3 开发微信小程序。
我依据网上的材料实现了本我的项目的搭建之后,用本我的项目开发过一个小程序,那种开发体验真的是超过了我以往开发过的所有我的项目,十分丝滑(可能是我第一次写 vue3 的 script setup 吧,用起来的确很难受)。
可间接拜访本我的项目 github 地址 clone 应用。
指标性能
集成 vue3,应用 script setup 语法开发
集成 Typescript
代码检查和格局优化
全局状态治理
小程序分包配置
款式封装,兼容刘海儿屏等款式问题
http 办法封装
次要技术栈
Taro3
Vue3
TypeScript
NutUi
Pinia
vue3 刚公布时,因为没有适合的 ui 框架反对,我学习 vue3 的激情间接被劝退了。直到现在,相似于 quasar、element-plus、ant-design-vue 等优良框架陆续反对 vue3,并且许多 vue3 我的项目被用到了生产环境中,才发现大家是把 vue3 真的用起来了。
比方咱们公司隔壁项目组,重构我的项目就用了 vue3,这时我才发现自己学习 vue3 有点晚了(tips: 前端真的太卷了 😭)
NutUI 是京东格调的挪动端组件库,它反对应用 Vue 语言来编写能够在 H5,小程序平台上的利用,帮忙研发人员晋升开发效率,改善开发体验。
我是从 Taro 文档 晓得 NutUI 的,taro 官网举荐应用 NutUI 开发,他们仿佛也都是来自京东同一个开发团队,我抱着试一试的心态上手应用,配置简略,应用体验还不错。
Pinia 是一个用于 Vue 的状态治理库,相似 Vuex, 是 Vue 的另一种状态治理计划,反对 Vue2 和 Vue3。
我第一次接触前端状态管理工具,是刚实习时公司的一个后盾管理系统,用的 dva,那可叫一个折磨啊,差点间接把我劝退。前面缓缓相熟了一些,然而不论用 redux,还是 vuex,还是感觉写着麻烦。
这次尝试应用 Pinia,用起来的确很难受,合乎直觉,易于学习,有点相似于 recoil,但没有 recoil 那么多的概念和 API,主体十分精简,极易上手。Pinia 疾速入门
vscode 需装置插件
Eslint
Prettier
Volar
与 vetur 雷同,volar 是一个针对 vue 的 vscode 插件,不过与 vetur 不同的是,volar 提供了更为弱小的性能。
Volar 介绍
搭建我的项目架构
初始化我的项目
初始化我的项目之前,需装置 taro,请参考 Taro 文档,实现 taro 装置
应用命令创立模板我的项目:
taro init myApp
复制代码
装置 cli 用来执行构建等操作,之后启动我的项目,会生成一个 dist 目录
yarn add @tarojs/cli
yarn dev:weapp
复制代码
关上微信开发工具 工程目录须要指向构建进去的 dist 文件
Hello world 呈现,我的项目胜利跑起来了!
设置代码标准
代码标准 ESlint
代码格式化 Prettier
提交前查看 husky
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
集体认为,eslint + prettier 足以应酬大部分前端代码标准问题了,且配置起来很简略,有非凡需要也可持续配置。
装置依赖
yarn add @vue/eslint-config-prettier @vue/eslint-config-typescript eslint-plugin-prettier vue-tsc husky -D
复制代码
设置代码标准和格式化规定
.eslintrc.js
module.exports = {
root: true,
env: {
node: true,
'vue/setup-compiler-macros': true
},
extends: [‘plugin:vue/vue3-essential’, ‘eslint:recommended’, ‘@vue/prettier’, ‘@vue/typescript’],
parserOptions: {
parser: '@typescript-eslint/parser'
},
rules: {
'prettier/prettier': [
'error',
{
singleQuote: true,
semi: false,
trailingComma: 'none',
arrowParens: 'avoid',
printWidth: 100
}
],
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
复制代码
.prettierrc
{
“tabWidth”: 2,
“singleQuote”: true,
“semi”: false,
“trailingComma”: “none”,
“arrowParens”: “avoid”,
“endOfLine”: “auto”,
“printWidth”: 100
}
复制代码
在 package.json 中 script 增加 Ts 查看命令和 Eslint 查看命令
“scripts”:{
“tsc”: “vue-tsc –noEmit –skipLibCheck”,
“lint”: “eslint –ext .vue –ext .js –ext .ts src/”
}
复制代码
增加 husky 触发 Git 钩子,代码提交前查看
npx husky install
复制代码
编辑 pre-commit 执行 Eslint 检查和 Ts 查看
!/bin/sh
. “$(dirname “$0″)/_/husky.sh”
echo “—eslint start—“
npm run lint
echo “—eslint end—“
echo “—ts lint start—“
npm run tsc
echo “—ts lint end—“
复制代码
至此,我的项目的代码标准和格局标准配置结束,多人合作也不是问题了。
引入 NutUI
yarn add @nutui/nutui-taro
复制代码
按需引入,装置插件 babel-plugin-import
yarn add babel-plugin-import -D
复制代码
款式解决 因为 nutui 的设计稿是 375 的 所以将框架的设计尺寸调整为 375
我的项目配置文件 config/index.js 中配置:
designWidth: 375
复制代码
app.ts
import {createApp} from ‘vue’;
import {Button} from ‘@nutui/nutui-taro’;
const app = createApp();
app.use(Button);
复制代码
index.vue 中,nut-button 组件间接在 template 中写,不必再引入
<template>
<view class=”index”>
<text>{{msg}}</text>
<nut-button type="primary"> 次要按钮 </nut-button>
</view>
</template>
复制代码
小程序分包配置
小程序主包超过 2M,就无奈真机预览了,为了提前做好筹备在一开始就进行分包解决。比方上面这个小程序的配置,分了四个包。
app.config.ts
pages: [‘pages/create/index’, ‘pages/find/index’, ‘pages/my/index’],
subpackages: [
{
root: ‘pages/featureA’,
pages: [‘index/index’]
},
{
root: ‘pagesSub/search’,
pages: [‘index’]
},
{
root: ‘pagesSub/my’,
pages: [‘detail/index’, ‘about/index’]
},
{
root: ‘pagesSub/book’,
pages: [‘detail/index’, ‘person/list/index’, ‘person/detail/index’]
}
],
复制代码
能够在小程序开发工具编辑器里的代码依赖剖析,查看主包和分包的大小
应用 script setup 语法封装小程序页面生命周期办法
hooks/life.ts
import {getCurrentInstance} from ‘@tarojs/taro’
import {onMounted} from ‘vue’
const Current = getCurrentInstance()
export function useDidShow(callback) {
onMounted(callback) Current?.page?.onShow && (Current.page.onShow = callback)
}
export function usePullDownRefresh(callback) {
Current?.page?.onPullDownRefresh && (Current.page.onPullDownRefresh = callback)
}
复制代码
应用
import {useDidShow} from ‘@/hooks/life’
useDidShow(() => {
// console.log(‘onShow’)
})
复制代码
装置 Pinia 进行状态治理
yarn add pinia
yarn add taro-plugin-pinia
复制代码
我的项目配置文件 config/index.js 中配置:
plugins: [‘taro-plugin-pinia’]
复制代码
以治理用户信息和用户登录状态为例,实现一个用户登录性能
须要解决的文件代码如下:
stores/auth.ts
import {defineStore} from ‘pinia’
interface UserInfoProp {
nickName: string
avatarUrl: string
}
const useAuth = defineStore({
id: ‘authInfo’,
state: () => ({
userInfo: {
nickName: '',
avatarUrl: ''
},
isLogin: false
}),
actions: {
login() {this.isLogin = true},
logout() {this.isLogin = false},
setUserInfo(userInfo: UserInfoProp) {this.userInfo = userInfo}
}
})
export {useAuth}
复制代码
stores/index.ts
import {createPinia} from ‘pinia’
import {useAuth} from ‘./auth’
export const store = createPinia()
const storeObj = {
auth: useAuth
}
// 封装成 useStore 的模式,这样一看援用就晓得是 store 的数据
export function useStore(key: string) {
return storeObj[key]()
}
复制代码
集体核心 index.vue
<template>
<main v-if=”isLogin” class=”my-container”>
<user-info />
</main>
<main v-else>
<nut-button type="primary" @click="handleLogin"> 微信一键登录 </nut-button>
</main>
</template>
<script lang=”ts” setup>
import Taro from ‘@tarojs/taro’
import {computed} from ‘vue’
import {useStore} from ‘@/stores’
import UserInfo from ‘./userInfo.vue’
const auth = useStore(‘auth’)
const isLogin = computed(() => auth.isLogin)
const handleLogin = () => {
setTimeout(() => {
// 模仿后端申请失去 token 和 userInfo
Taro.setStorageSync('token', 'xxxx')
auth.setUserInfo({
nickName: '林',
avatarUrl:
'https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png'
})
auth.login()
}, 500)
}
</script>
</script>
复制代码
userInfo 组件
<template>
<article class=”user-info”>
<nut-avatar size="large" :icon="userInfo.avatarUrl"></nut-avatar>
<span class="ellipsis name">{{userInfo.nickName}}</span>
</article>
</template>
<script lang=”ts” setup>
import Taro from ‘@tarojs/taro’
import {computed} from ‘vue’
import {useStore} from ‘@/stores’
const auth = useStore(‘auth’)
const userInfo = computed(() => auth.userInfo)
</script>
复制代码
总的来说,pinia 写起来是十分简洁的,这品种 react hooks 的写法,我是十分喜爱的
申请办法封装
http.ts
// 封装 axios 的申请,返回从新封装的数据格式
// 对谬误的对立解决
import {HttpResponse} from ‘@/common/interface’
import Taro from ‘@tarojs/taro’
import publicConfig from ‘@/config/index’
import axios, {
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
Canceler
} from ‘axios-miniprogram’
import errorHandle from ‘../common/errorHandle’
const CancelToken = axios.CancelToken
class HttpRequest {
private baseUrl: string
private pending: Record<string, Canceler>
constructor(baseUrl: string) {
this.baseUrl = baseUrl
this.pending = {}
}
// 获取 axios 配置
getInsideConfig() {
const config = {
baseURL: this.baseUrl,
headers: {'Content-Type': 'application/json;charset=utf-8'},
timeout: 10000
}
return config
}
removePending(key: string, isRequest = false) {
if (this.pending[key] && isRequest) {this.pending[key]('勾销反复申请')
}
delete this.pending[key]
}
// 设定拦截器
interceptors(instance: AxiosInstance) {
instance.interceptors.request.use(
config => {console.log('config :>>', config)
let isPublic = false
publicConfig.publicPath.map(path => {isPublic = isPublic || path.test(config.url || '')
})
const token = Taro.getStorageSync('token')
if (!isPublic && token) {config.headers.Authorization = 'Bearer' + token}
const key = config.url + '&' + config.method
this.removePending(key, true)
config.cancelToken = new CancelToken(c => {this.pending[key] = c
})
return config
},
err => {errorHandle(err)
return Promise.reject(err)
}
)
// 响应申请的拦截器
instance.interceptors.response.use(
res => {
const key = res.config.url + '&' + res.config.method
this.removePending(key)
if (res.status === 200) {return Promise.resolve(res.data)
} else {return Promise.reject(res)
}
},
err => {errorHandle(err)
return Promise.reject(err)
}
)
}
// 创立实例
request(options: AxiosRequestConfig) {
const instance = axios.create()
const newOptions = Object.assign(this.getInsideConfig(), options)
this.interceptors(instance)
return instance(newOptions)
}
get(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> | Promise<HttpResponse> {
const options = Object.assign(
{
method: 'get',
url: url
},
config
)
return this.request(options)
}
post(url: string, data?: unknown): Promise<AxiosResponse> | Promise<HttpResponse> {
return this.request({
method: 'post',
url: url,
data: data
})
}
}
export default HttpRequest
复制代码
request.ts
import HttpRequest from ‘./http’
import config from ‘@/config/index’
const baseUrl = process.env.NODE_ENV === ‘development’ ? config.baseUrl.dev : config.baseUrl.pro
const request = new HttpRequest(baseUrl)
export default request
复制代码
以获取图书列表和图书详情为例
apis/book.ts
import request from ‘../request’
export function getBookList() {
return request.get(‘books/getBookList’)
}
export function getBookDetail(id: number) {
return request.post(‘books/getBookDetail’, {
id
})
}
复制代码
申请办法封装还是用到了 axios,只是用的是 axios-miniprogram,写法和 web 端基本一致,http.js 文件援用的一些模块太多,本文没有列出来,能够间接拜访本我的项目 github 地址查看。
款式封装
iPhoneX 底部横线适配
assets/styles/common.scss
.safe-area-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
复制代码
刘海儿屏适配
assets/styles/hairline.scss
@mixin hairline-common() {
position: absolute;
box-sizing: border-box;
content: ‘ ‘;
pointer-events: none;
}
@mixin hairline() {
@include hairline-common();
top: -50%;
right: -50%;
bottom: -50%;
left: -50%;
border: 0 solid #eaeaea;
transform: scale(0.5);
}
@mixin hairline-top($color, $left: 0, $right: 0) {
@include hairline-common();
top: 0;
right: $right;
left: $left;
border-top: 1px solid $color;
transform: scaleY(0.5);
}
@mixin hairline-bottom($color, $left: 0, $right: 0) {
@include hairline-common();
right: $right;
bottom: 0;
left: $left;
border-bottom: 1px solid $color;
transform: scaleY(0.5);
}
[class*=’van-hairline’] {
&::after {
@include hairline();
}
}
.van-hairline {
&,
&–top,
&–left,
&–right,
&–bottom,
&–surround,
&–top-bottom {
position: relative;
}
&–top::after {
border-top-width: 1px;
}
&–left::after {
border-left-width: 1px;
}
&–right::after {
border-right-width: 1px;
}
&–bottom::after {
border-bottom-width: 1px;
}
&,
&-unset {
&--top-bottom::after {border-width: 1px 0;}
}
&–surround::after {
border-width: 1px;
}
}
复制代码
多行文字省略
assets/styles/ellipsis.scss
@mixin multi-ellipsis($lines) {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
}
@mixin ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis {
@include ellipsis();
}
.multi-ellipsis–l2 {
@include multi-ellipsis(2);
}
.multi-ellipsis–l3 {
@include multi-ellipsis(3);
}
复制代码
总结
至此,终于实现了 Taro + Vue3 的我的项目搭建,强烈建议间接拜访我的项目 github 地址 clone 应用,有一些配置细节本文无奈一一列举,就在我的项目中去挖掘吧!