如何使用 Taro3 Vue3 開發小程式?以下這篇文章為大家介紹一下使用 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 需安装插件
与vetur
相同,volar
是一个针对 vue 的 vscode 插件,不过与 vetur 不同的是,volar 提供了更为强大的功能。
Volar 介绍
搭建项目架构
初始化项目
初始化项目之前,需安装 taro,请参考 Taro 文档,完成 taro 安装
使用命令创建模板项目:

安装 cli 用来执行构建等操作,之后启动项目,会生成一个 dist 目录
1 2 | yarn add @tarojs/cli
yarn dev:weapp
|
登入後複製
打开微信开发工具 工程目录需要指向构建出来的 dist 文件


Hello world 出现,项目成功跑起来了!
设置代码规范
- 代码规范 ESlint
- 代码格式化 Prettier
- 提交前检查 husky
个人认为,eslint + prettier 足以应付大部分前端代码规范问题了,且配置起来很简单,有特殊需求也可继续配置。
安装依赖
1 | yarn add @vue/eslint-config-prettier @vue/eslint-config-typescript eslint-plugin-prettier vue-tsc husky -D
|
登入後複製
设置代码规范和格式化规则
.eslintrc.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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
1 2 3 4 5 6 7 8 9 | {
"tabWidth" : 2,
"singleQuote" : true,
"semi" : false,
"trailingComma" : "none" ,
"arrowParens" : "avoid" ,
"endOfLine" : "auto" ,
"printWidth" : 100
}
|
登入後複製
在 package.json 中 script 添加 Ts 检查命令和 Eslint 检查命令
1 2 3 4 | "scripts" :{
"tsc" : "vue-tsc --noEmit --skipLibCheck" ,
"lint" : "eslint --ext .vue --ext .js --ext .ts src/"
}
|
登入後複製
添加 husky 触发 Git 钩子,代码提交前检查
编辑 pre-commit 执行 Eslint 检查和 Ts 检查
1 2 3 4 5 6 7 8 9 10 | #!/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
1 | yarn add @nutui/nutui-taro
|
登入後複製
在 .babelrc
或 babel.config.js
中添加配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | module.exports = {
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'
]
]
}
|
登入後複製
按需引入,安装插件 babel-plugin-import
1 | yarn add babel-plugin-import -D
|
登入後複製
样式处理 因为 nutui 的设计稿是 375 的 所以将框架的设计尺寸调整为 375
项目配置文件 config/index.js 中配置:
app.ts
1 2 3 4 5 6 | import { createApp } from 'vue'
import { Button } from '@nutui/nutui-taro'
const app = createApp()
app. use (Button)
|
登入後複製
index.vue 中,nut-button 组件直接在 template 中写,不用再引入
1 2 3 4 5 6 | <template>
<view class = "index" >
<text>{{ msg }}</text>
<nut-button type= "primary" >主要按钮</nut-button>
</view>
</template>
|
登入後複製

说实话,配置起来还是有点麻烦,不过按照官网文档说明来配也没有踩坑,还行。
小程序分包配置
小程序主包超过 2M,就无法真机预览了,为了提前做好准备在一开始就进行分包处理。比如下面这个小程序的配置,分了四个包。
app.config.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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
1 2 3 4 5 6 7 8 9 10 11 | 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)
}
|
登入後複製
使用
1 2 3 4 5 | import { useDidShow } from '@/hooks/life'
useDidShow(() => {
})
|
登入後複製
1 2 | yarn add pinia
yarn add taro-plugin-pinia
|
登入後複製
项目配置文件 config/index.js 中配置:
1 | plugins: ['taro-plugin-pinia']
|
登入後複製
以管理用户信息和用户登录状态为例,实现一个用户登录功能
)
需要处理的文件代码如下:
stores/auth.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 | import { createPinia } from 'pinia'
import { useAuth } from './auth'
export const store = createPinia()
const storeObj = {
auth: useAuth
}
export function useStore(key: string) {
return storeObj[key]()
}
|
登入後複製
个人中心 index.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <template>
<main v- if = "isLogin" >
<user-info />
</main>
<main v- else >
<nut-button type= "primary" @click= "handleLogin" >微信一键登录</nut-button>
</main>
</template>
<script 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(() => {
Taro.setStorageSync('token', 'xxxx')
auth.setUserInfo({
nickName: '林',
avatarUrl:
'https:
})
auth.login()
}, 500)
}
</script>
</script>
|
登入後複製
userInfo 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <template>
<article>
<nut-avatar size= "large" :icon= "userInfo.avatarUrl" ></nut-avatar>
<span class = "ellipsis name" >{{ userInfo.nickName }}</span>
</article>
</template>
<script 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 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 = {}
}
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
1 2 3 4 5 6 7 | 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
1 2 3 4 5 6 7 8 9 10 11 | 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
1 2 3 4 | .safe-area-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
|
登入後複製
刘海儿屏适配
assets/styles/hairline.scss
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @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 使用,有一些配置细节本文无法一一列举,就在项目中去发掘吧!
如果我的文章能帮助到你,那将是我的荣幸!
【相关学习推荐:小程序开发教程】
以上是利用Taro + Vue3如何開發小程式? (實踐)的詳細內容。更多資訊請關注PHP中文網其他相關文章!