溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么用Taro+Vue3開發(fā)小程序

發(fā)布時(shí)間:2022-01-13 10:36:57 來源:億速云 閱讀:292 作者:小新 欄目:移動(dòng)開發(fā)

這篇文章主要介紹了怎么用Taro+Vue3開發(fā)小程序,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

微信小程序是以微信為運(yùn)行環(huán)境的一種應(yīng)用,其實(shí)質(zhì)是 Hybrid 技術(shù)的應(yīng)用,Hybrid App 即混合模式移動(dòng)應(yīng)用,因此與 H5 類似,但又比 H5 擁有很多原生的能力,例如調(diào)用位置信息和攝像頭等。

小程序的開發(fā)方式與 H5 十分相似,用的也是  JavaScript、HTML、CSS  語言。

因此,小程序開發(fā)可以說是一名前端工程師必須要掌握的技能。

原生小程序開發(fā)有一定的學(xué)習(xí)成本,現(xiàn)如今市面上有很多開發(fā)小程序的第三方多端框架,如果不是追求極致性能和穩(wěn)定,還是不要用原生小程序開發(fā)了,開發(fā)效率太低。

第三方多端框架中,tarouni-app 的使用度是最廣的,一般來說,做技術(shù)選型時(shí),團(tuán)隊(duì)用 react,就用 taro,團(tuán)隊(duì)用 vue,就用 uni-app,兩者之間沒有什么優(yōu)劣之分,都挺好用的。

但很多開發(fā)者可能不知道,taro3.0 以上版本是支持使用 vue 的,本篇文章就來介紹一下如何使用 Taro3 + Vue3 開發(fā)微信小程序。

我根據(jù)網(wǎng)上的資料完成了本項(xiàng)目的搭建之后,用本項(xiàng)目開發(fā)過一個(gè)小程序,那種開發(fā)體驗(yàn)真的是超越了我以往開發(fā)過的所有項(xiàng)目,非常絲滑(可能是我第一次寫 vue3 的 script setup 吧,用起來確實(shí)很舒服)。

目標(biāo)功能

  • 集成 vue3,使用 script setup 語法開發(fā)

  • 集成 Typescript

  • 代碼檢查和格式優(yōu)化

  • 全局狀態(tài)管理

  • 小程序分包配置

  • 樣式封裝,兼容劉海兒屏等樣式問題

  • http 方法封裝

主要技術(shù)棧

  • Taro3

  • Vue3

  • TypeScript

  • NutUi

  • Pinia

vue3 剛發(fā)布時(shí),由于沒有合適的 ui 框架支持,我學(xué)習(xí) vue3 的熱情直接被勸退了。直到現(xiàn)在,類似于 quasar、element-plus、ant-design-vue 等優(yōu)秀框架陸續(xù)支持 vue3,并且許多 vue3 項(xiàng)目被用到了生產(chǎn)環(huán)境中,才發(fā)現(xiàn)大家是把 vue3 真的用起來了。

比如我們公司隔壁項(xiàng)目組,重構(gòu)項(xiàng)目就用了 vue3,這時(shí)我才發(fā)現(xiàn)自己學(xué)習(xí) vue3 有點(diǎn)晚了(tips:前端真的太卷了)

NutUI 是京東風(fēng)格的移動(dòng)端組件庫,它支持使用 Vue 語言來編寫可以在 H5,小程序平臺(tái)上的應(yīng)用,幫助研發(fā)人員提升開發(fā)效率,改善開發(fā)體驗(yàn)。

我是從 Taro 文檔 知道 NutUI 的,taro 官方推薦使用 NutUI 開發(fā),他們似乎也都是來自京東同一個(gè)開發(fā)團(tuán)隊(duì),我抱著試一試的心態(tài)上手使用,使用體驗(yàn)還不錯(cuò)。

Pinia 是一個(gè)用于 Vue 的狀態(tài)管理庫,類似 Vuex, 是 Vue 的另一種狀態(tài)管理方案,支持 Vue2 和 Vue3。

我第一次接觸前端狀態(tài)管理工具,是剛實(shí)習(xí)時(shí)公司的一個(gè)后臺(tái)管理系統(tǒng),用的 dva,那可叫一個(gè)折磨啊,差點(diǎn)直接把我勸退。后面慢慢熟悉了一些,但是不管用 redux,還是 vuex,還是覺得寫著麻煩。

這次嘗試使用 Pinia,用起來確實(shí)很舒服,符合直覺,易于學(xué)習(xí) ,有點(diǎn)類似于 recoil,但沒有 recoil 那么多的概念和 API,主體非常精簡,極易上手。Pinia 快速入門

vscode 需安裝插件

  • Eslint

  • Prettier

  • Volar

vetur相同,volar是一個(gè)針對(duì) vue 的 vscode 插件,不過與 vetur 不同的是,volar 提供了更為強(qiáng)大的功能。

Volar 介紹

搭建項(xiàng)目架構(gòu)

初始化項(xiàng)目

初始化項(xiàng)目之前,需安裝 taro,請(qǐng)參考 Taro 文檔,完成 taro 安裝

使用命令創(chuàng)建模板項(xiàng)目:

taro init myApp

怎么用Taro+Vue3開發(fā)小程序

安裝 cli 用來執(zhí)行構(gòu)建等操作,之后啟動(dòng)項(xiàng)目,會(huì)生成一個(gè) dist 目錄

yarn add @tarojs/cli
yarn dev:weapp

打開微信開發(fā)工具 工程目錄需要指向構(gòu)建出來的 dist 文件

怎么用Taro+Vue3開發(fā)小程序

怎么用Taro+Vue3開發(fā)小程序

Hello world 出現(xiàn),項(xiàng)目成功跑起來了!

設(shè)置代碼規(guī)范

  • 代碼規(guī)范 ESlint

  • 代碼格式化 Prettier

  • 提交前檢查 husky

個(gè)人認(rèn)為,eslint + prettier 足以應(yīng)付大部分前端代碼規(guī)范問題了,且配置起來很簡單,有特殊需求也可繼續(xù)配置。

安裝依賴

yarn add @vue/eslint-config-prettier @vue/eslint-config-typescript eslint-plugin-prettier vue-tsc husky -D

設(shè)置代碼規(guī)范和格式化規(guī)則

.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 觸發(fā) Git 鉤子,代碼提交前檢查

npx husky install

編輯 pre-commit 執(zhí)行 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---"

至此,項(xiàng)目的代碼規(guī)范和格式規(guī)范配置完畢,多人協(xié)作也不是問題了。

引入 NutUI

yarn add @nutui/nutui-taro

.babelrcbabel.config.js 中添加配置:

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

yarn add babel-plugin-import -D

樣式處理 因?yàn)?nutui 的設(shè)計(jì)稿是 375 的 所以將框架的設(shè)計(jì)尺寸調(diào)整為 375

項(xiàng)目配置文件 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>

怎么用Taro+Vue3開發(fā)小程序

說實(shí)話,配置起來還是有點(diǎn)麻煩,不過按照官網(wǎng)文檔說明來配也沒有踩坑,還行。

小程序分包配置

小程序主包超過 2M,就無法真機(jī)預(yù)覽了,為了提前做好準(zhǔn)備在一開始就進(jìn)行分包處理。比如下面這個(gè)小程序的配置,分了四個(gè)包。

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']
}
],

可以在小程序開發(fā)工具編輯器里的代碼依賴分析,查看主包和分包的大小

怎么用Taro+Vue3開發(fā)小程序

使用 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 進(jìn)行狀態(tài)管理

yarn add pinia
yarn add taro-plugin-pinia

項(xiàng)目配置文件 config/index.js 中配置:

plugins: ['taro-plugin-pinia']

以管理用戶信息和用戶登錄狀態(tài)為例,實(shí)現(xiàn)一個(gè)用戶登錄功能

怎么用Taro+Vue3開發(fā)小程序

需要處理的文件代碼如下:

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的數(shù)據(jù)
export function useStore(key: string) {
  return storeObj[key]()
}

個(gè)人中心 index.vue

<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(() => {
    // 模擬后端請(qǐng)求得到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>
    <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 的寫法,我是非常喜歡的

請(qǐng)求方法封裝

http.ts

// 封裝axios的請(qǐng)求,返回重新封裝的數(shù)據(jù)格式
// 對(duì)錯(cuò)誤的統(tǒng)一處理
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]('取消重復(fù)請(qǐng)求')
    }
    delete this.pending[key]
  }

  // 設(shè)定攔截器
  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)
      }
    )

    // 響應(yīng)請(qǐng)求的攔截器
    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)
      }
    )
  }

  // 創(chuàng)建實(shí)例
  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
  })
}

請(qǐng)求方法封裝還是用到了 axios,只是用的是 axios-miniprogram ,寫法和 web 端基本一致,http.js 文件引用的一些模塊太多,本文沒有列出來,可以直接訪問本項(xiàng)目 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);
}

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“怎么用Taro+Vue3開發(fā)小程序”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI