首頁 > 微信小程式 > 小程式開發 > 利用Taro + Vue3如何開發小程式? (實踐)

利用Taro + Vue3如何開發小程式? (實踐)

青灯夜游
發布: 2022-01-13 10:28:21
轉載
7136 人瀏覽過

如何使用 Taro3 Vue3 開發小程式?以下這篇文章為大家介紹一下使用 Taro3 Vue3 開發微信小程式的方法,希望對大家有幫助!

利用Taro + Vue3如何開發小程式? (實踐)

微信小程式是以微信為運作環境的一種應用,其實質是Hybrid 技術的應用,Hybrid App 即混合模式行動應用,因此與H5 類似,但又比H5 擁有許多原生的能力,例如呼叫位置資訊和相機等。

小程式的開發方式與 H5 十分相似,用的也是  JavaScriptHTMLCSS  語言。

因此,小程式開發可以說是前端工程師必須要掌握的技能。

原生小程式開發有一定的學習成本,現今市面上有許多開發小程式的第三方多端框架,如果不是追求極致效能和穩定,還是不要用原生小程式開發了,開發效率太低。

第三方多端框架中,tarouni-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 的熱情直接被勸退了。直到現在,類似於 quasarelement-plusant-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 安装

使用命令创建模板项目:

1

taro init myApp

登入後複製

利用Taro + Vue3如何開發小程式? (實踐)

安装 cli 用来执行构建等操作,之后启动项目,会生成一个 dist 目录

1

2

yarn add @tarojs/cli

yarn dev:weapp

登入後複製

打开微信开发工具 工程目录需要指向构建出来的 dist 文件

利用Taro + Vue3如何開發小程式? (實踐)

利用Taro + Vue3如何開發小程式? (實踐)

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 钩子,代码提交前检查

1

npx husky install

登入後複製

编辑 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

登入後複製

.babelrcbabel.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 中配置:

1

designWidth: 375

登入後複製

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>

登入後複製

利用Taro + Vue3如何開發小程式? (實踐)

说实话,配置起来还是有点麻烦,不过按照官网文档说明来配也没有踩坑,还行。

小程序分包配置

小程序主包超过 2M,就无法真机预览了,为了提前做好准备在一开始就进行分包处理。比如下面这个小程序的配置,分了四个包。

app.config.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

pages: [&#39;pages/create/index&#39;, &#39;pages/find/index&#39;, &#39;pages/my/index&#39;],

subpackages: [

{

  root: &#39;pages/featureA&#39;,

  pages: [&#39;index/index&#39;]

},

{

  root: &#39;pagesSub/search&#39;,

  pages: [&#39;index&#39;]

},

{

  root: &#39;pagesSub/my&#39;,

  pages: [&#39;detail/index&#39;, &#39;about/index&#39;]

},

{

  root: &#39;pagesSub/book&#39;,

  pages: [&#39;detail/index&#39;, &#39;person/list/index&#39;, &#39;person/detail/index&#39;]

}

],

登入後複製

可以在小程序开发工具编辑器里的代码依赖分析,查看主包和分包的大小

利用Taro + Vue3如何開發小程式? (實踐)

使用 script setup 语法封装小程序页面生命周期方法

hooks/life.ts

1

2

3

4

5

6

7

8

9

10

11

import { getCurrentInstance } from &#39;@tarojs/taro&#39;

import { onMounted } from &#39;vue&#39;

 

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 &#39;@/hooks/life&#39;

 

useDidShow(() => {

  // console.log(&#39;onShow&#39;)

})

登入後複製

安装 Pinia 进行状态管理

1

2

yarn add pinia

yarn add taro-plugin-pinia

登入後複製

项目配置文件 config/index.js 中配置:

1

plugins: [&#39;taro-plugin-pinia&#39;]

登入後複製

以管理用户信息和用户登录状态为例,实现一个用户登录功能

利用Taro + Vue3如何開發小程式? (實踐)

需要处理的文件代码如下:

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 &#39;pinia&#39;

 

interface UserInfoProp {

  nickName: string

  avatarUrl: string

}

 

const useAuth = defineStore({

  id: &#39;authInfo&#39;,

  state: () => ({

    userInfo: {

      nickName: &#39;&#39;,

      avatarUrl: &#39;&#39;

    },

    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 &#39;pinia&#39;

import { useAuth } from &#39;./auth&#39;

 

export const store = createPinia()

 

const storeObj = {

  auth: useAuth

}

 

// 封装成useStore的形式,这样一看引用就知道是store的数据

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 &#39;@tarojs/taro&#39;

import { computed } from &#39;vue&#39;

import { useStore } from &#39;@/stores&#39;

 

import UserInfo from &#39;./userInfo.vue&#39;

 

const auth = useStore(&#39;auth&#39;)

const isLogin = computed(() => auth.isLogin)

 

const handleLogin = () => {

  setTimeout(() => {

    // 模拟后端请求得到token和userInfo

    Taro.setStorageSync(&#39;token&#39;, &#39;xxxx&#39;)

    auth.setUserInfo({

      nickName: &#39;林&#39;,

      avatarUrl:

        &#39;https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png&#39;

    })

    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 &#39;@tarojs/taro&#39;

import { computed } from &#39;vue&#39;

import { useStore } from &#39;@/stores&#39;

 

const auth = useStore(&#39;auth&#39;)

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

// 封装axios的请求,返回重新封装的数据格式

// 对错误的统一处理

import { HttpResponse } from &#39;@/common/interface&#39;

import Taro from &#39;@tarojs/taro&#39;

import publicConfig from &#39;@/config/index&#39;

import axios, {

  AxiosInstance,

  AxiosRequestConfig,

  AxiosResponse,

  Canceler

} from &#39;axios-miniprogram&#39;

import errorHandle from &#39;../common/errorHandle&#39;

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: {

        &#39;Content-Type&#39;: &#39;application/json;charset=utf-8&#39;

      },

      timeout: 10000

    }

    return config

  }

 

  removePending(key: string, isRequest = false) {

    if (this.pending[key] && isRequest) {

      this.pending[key](&#39;取消重复请求&#39;)

    }

    delete this.pending[key]

  }

 

  // 设定拦截器

  interceptors(instance: AxiosInstance) {

    instance.interceptors.request.use(

      config => {

        console.log(&#39;config :>> &#39;, config)

        let isPublic = false

        publicConfig.publicPath.map(path => {

          isPublic = isPublic || path.test(config.url || &#39;&#39;)

        })

        const token = Taro.getStorageSync(&#39;token&#39;)

        if (!isPublic && token) {

          config.headers.Authorization = &#39;Bearer &#39; + token

        }

        const key = config.url + &#39;&&#39; + 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 + &#39;&&#39; + 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: &#39;get&#39;,

        url: url

      },

      config

    )

    return this.request(options)

  }

 

  post(url: string, data?: unknown): Promise<AxiosResponse> | Promise<HttpResponse> {

    return this.request({

      method: &#39;post&#39;,

      url: url,

      data: data

    })

  }

}

 

export default HttpRequest

登入後複製

request.ts

1

2

3

4

5

6

7

import HttpRequest from &#39;./http&#39;

import config from &#39;@/config/index&#39;

const baseUrl = process.env.NODE_ENV === &#39;development&#39; ? 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 &#39;../request&#39;

 

export function getBookList() {

  return request.get(&#39;books/getBookList&#39;)

}

 

export function getBookDetail(id: number) {

  return request.post(&#39;books/getBookDetail&#39;, {

    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: &#39; &#39;;

  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*=&#39;van-hairline&#39;] {

  &::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中文網其他相關文章!

相關標籤:
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板