uni-app --- 如何快速从Vue转入Uni-app

目录

一.什么是uni-app?

二.创建项目:

三.Vue与小程序的区别?

四.pages.json详解:

1.组件自动引入规则(easycom):

2.应用页面配置(pages):

五.入门uniapp:

1.项目结构:

2.编写第一个页面:

3.运行项目:

4.配置微信小程序的AppID:

5.配置页面路由:

6.pinia的使用:

7.拦截器的使用:

(1)定义请求拦截器:

[(2)使用 uni.addInterceptor 添加拦截器:](#(2)使用 uni.addInterceptor 添加拦截器:)

(3)请求函数封装:

(4)请求封装:

[(5)发起 HTTP 请求并处理返回的数据:](#(5)发起 HTTP 请求并处理返回的数据:)


一.什么是uni-app?

uni-app 是一个使用 Vue.js 开发所有前端应用的框架,它支持一次开发,多个平台运行。具体来说,uni-app 可以编译成 iOS、Android、H5、以及各种小程序(如微信小程序、支付宝小程序、百度小程序等)应用,使得开发者能够在多个平台上以同一套代码实现应用。总的来说,uni-app 是一个非常适合需要多平台支持的项目的开发框架,尤其适合快速开发和迭代的产品。

下面是uniapp的官网:uni-app官网

二.创建项目:

uni-app支持通过 可视化界面、vue-cli命令行 两种方式快速创建项目。

我们在开发uniapp通常使用其自主开发的编译器:HBuilderX,下面是下载链接地址:HBuilderX-高效极客技巧,HBuilderX是通用的前端开发工具,但为uni-app做了特别强化。

  • 打开 HBuilderX
  • 点击左上角的 文件 > 新建 > 项目
  • 选择 uni-app,选择一个模板(例如"Hello uni-app"模板),然后设置项目名称和路径。
  • 点击 创建,HBuilderX 会自动为你生成一个初始项目。

同时我们也可以按照官网教程使用 cli 脚手架创建项目工程:uni-app官网

npm install -g @vue/cli

vue create -p dcloudio/uni-preset-vue my-project

这将通过 Vue CLI 创建一个基于 uni-app 的项目。

三.Vue与小程序的区别?

我们可以参考下面的博客:https://segmentfault.com/a/1190000015684864

四.pages.json详解:

TypeScript 复制代码
{
  // 组件自动引入规则
  "easycom": {
    "autoscan": true, // 开启自动扫描
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
    }
  },
  "pages": [
    //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
    {
      "path": "pages/my/my",
      "style": {
        "navigationBarTitleText": "我的"
      }
    },
    // ...
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "",
    "navigationBarBackgroundColor": "#F8F8F8",
    "backgroundColor": "#F8F8F8"
  },
  // 设置 TabBar
  "tabBar": {
    "color": "#333",
    "selectedColor": "#27ba9b",
    "backgroundColor": "#fff",
    "borderStyle": "white",
    "list": [
      {
        "text": "首页",
        "pagePath": "pages/index/index",
        "iconPath": "static/tabs/home_default.png",
        "selectedIconPath": "static/tabs/home_selected.png"
      },
      // ...
    ]
  }
}

1.组件自动引入规则(easycom):

TypeScript 复制代码
"easycom": {
  "autoscan": true, // 开启自动扫描
  "custom": {
    "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
  }
}

easycom 是 uni-app 的 Easycom 组件自动引入功能的配置。

  • autoscan: true: 开启自动扫描功能,允许自动扫描项目中的组件,无需手动引入。uni-app 会自动根据组件名称进行匹配,加载对应的组件。
  • custom : 自定义组件的自动引入规则。"^uni-(.*)" 表示所有以 uni- 开头的组件都会自动从 @dcloudio/uni-ui/lib/uni-$1/uni-$1.vue 路径引入。这是针对 uni-ui 的自定义规则。比如 uni-button 组件会自动引入 @dcloudio/uni-ui/lib/uni-button/uni-button.vue

2.应用页面配置(pages):

TypeScript 复制代码
"pages": [
  {
    "path": "pages/my/my",
    "style": {
      "navigationBarTitleText": "我的"
    }
  },
  // ...
]

pages 数组定义了 uni-app 应用的各个页面及其配置信息。每个对象代表一个页面的配置:

  • path : 页面路径,指向相应页面的 .vue 文件(以 pages/ 开头的路径表示项目内的页面)。
  • style : 页面样式配置,设置页面的 navigationBarTitleText 即导航栏的标题文字。例如,首页页面设置了 "首页" 作为标题。

3.全局样式配置(globalStyle):

TypeScript 复制代码
"globalStyle": {
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "",
  "navigationBarBackgroundColor": "#F8F8F8",
  "backgroundColor": "#F8F8F8"
}

globalStyle 配置了全局的样式:

TypeScript 复制代码
navigationBarTextStyle: 设置导航栏文字颜色,这里设置为黑色 ("black")。
navigationBarTitleText: 设置默认的导航栏标题文本,这里为空字符串 "" 表示没有默认标题。
navigationBarBackgroundColor: 设置导航栏的背景颜色,这里设置为 #F8F8F8,即浅灰色。
backgroundColor: 设置应用的背景色,这里也设置为浅灰色 #F8F8F8。

4.TabBar 配置(tabBar):

TypeScript 复制代码
"tabBar": {
  "color": "#333",
  "selectedColor": "#27ba9b",
  "backgroundColor": "#fff",
  "borderStyle": "white",
  "list": [
    {
      "text": "首页",
      "pagePath": "pages/index/index",
      "iconPath": "static/tabs/home_default.png",
      "selectedIconPath": "static/tabs/home_selected.png"
    },
    // ... 
  ]
}

tabBar 配置了应用底部导航栏的外观和内容:

  • color : 设置 TabBar 中未选中时的文字颜色,这里是灰色 #333

  • selectedColor : 设置 TabBar 中选中时的文字颜色,这里是绿色 #27ba9b

  • backgroundColor : 设置 TabBar 的背景色,这里设置为白色 #fff

  • borderStyle : 设置 TabBar 的边框样式,这里为白色 white,表示没有边框。

  • list : list 数组定义了 TabBar 的各个项。每个对象配置了:

    TypeScript 复制代码
    text: TabBar 项的文本(例如 "首页"、"分类")。
    pagePath: 点击该 Tab 时跳转的页面路径。
    iconPath: 未选中状态下的图标路径。
    selectedIconPath: 选中状态下的图标路径。

五.入门uniapp:

1.项目结构:

uni-app 项目结构通常包括以下文件和文件夹:

  • /pages :存放应用页面的目录,每个页面都是一个 .vue 文件。
  • /components:存放自定义组件的目录。
  • /static:静态资源,如图片、字体、文件等。
  • /unpackage:项目打包生成的文件目录。
  • pages.json:配置文件,用于配置页面路径、导航栏样式等。
  • manifest.json:项目的配置信息,包括应用名称、版本号等。
  • global.css:全局样式文件。
  • uni.scss:统一的样式文件,适用于全局的布局、颜色等

2.编写第一个页面:

pages 文件夹中,我们默认会看到一个 index.vue 页面。我们来修改这个页面,添加一个简单的内容。

打开 pages/index/index.vue,将文件内容修改为:

html 复制代码
<template>
  <!-- 类似于 div 的容器元素,uni-app 使用 view 替代 HTML 中的 div -->
  <view class="content">
    <!-- 似于 span 或 p,用来显示文本内容 -->
    <text>{{ message }}</text>
    <!-- 用来创建按钮,点击时调用 changeMessage 方法更新文本 -->
    <button @click="changeMessage">Change Message</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello, uni-app!"
    };
  },
  methods: {
    changeMessage() {
      this.message = "Hello, uni-app updated!";
    }
  }
};
</script>

<style scoped>
.content {
  text-align: center;
  margin-top: 50rpx;
}
button {
  margin-top: 20rpx;
}
</style>

3.运行项目:

(1)在 HBuilderX 中,点击 运行 按钮,选择 运行到浏览器,你的应用将会在 Web 浏览器中启动。

(2)如果想在微信小程序等平台上运行:

  1. 在 HBuilderX 中,选择 运行 > 运行到小程序
  2. 选择你的小程序平台(如微信、支付宝等)。
  3. HBuilderX 会自动打开该平台的开发者工具,并加载你的应用。

4.配置微信小程序的AppID:

每一个小程序都有一个唯一的 AppID ,它用于标识该小程序,并与微信平台进行关联。当你在微信开发者工具或真实环境中访问小程序时,系统需要通过 AppID 来确定这个小程序的身份。没有 AppID,微信无法识别和加载你的程序。

  • 登录微信公众平台,注册并创建一个小程序。
  • 在小程序的管理后台,你可以找到你的 AppID,它是一个由字母和数字组成的字符串。

在 uni-app 中,你需要在 manifest.json 文件中配置你的 AppID

html 复制代码
{
  "mp-weixin": {
    "appid": "your-app-id-here"
  }
}

5.配置页面路由:

在 uni-app 中,页面和路由是通过 pages.json 文件来配置的。你可以在该文件中设置页面路径和页面的导航栏配置。

pages.json 配置示例

html 复制代码
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "Home"
      }
    },
    {
      "path": "pages/about/about",
      "style": {
        "navigationBarTitleText": "About"
      }
    }
  ]
}
  • "path":指定页面的路径。
  • "style":设置页面的样式和配置(如导航栏标题)。

我们可以在 pages 文件夹中创建一个新页面,如 about.vue,然后在 pages.json 中配置它。

6.pinia的使用:

uni-app 中使用 Pinia 进行持久化存储,可以确保当用户刷新页面或重新打开应用时,状态数据不会丢失。

首先index.ts文件中导入pinia需要的配置代码:

TypeScript 复制代码
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'

// 创建 pinia 实例
const pinia = createPinia()
// 使用持久化存储插件
pinia.use(persist)

// 默认导出,给 main.ts 使用
export default pinia

// 模块统一导出
export * from './modules/member'

然后我们在modules目录下创建ts文件:

TypeScript 复制代码
import { defineStore } from 'pinia'
import { ref } from 'vue'

// 定义 Store
export const useMemberStore = defineStore(
  'member',
  () => {
    // 会员信息
    const profile = ref<any>()

    // 保存会员信息,登录时使用
    const setProfile = (val: any) => {
      profile.value = val
    }

    // 清理会员信息,退出时使用
    const clearProfile = () => {
      profile.value = undefined
    }

    // 记得 return
    return {
      profile,
      setProfile,
      clearProfile,
    }
  },
  // TODO: 持久化
  {
    // 网页端配置:
    // persist: true,
    // 小程序端配置:
    // persist:{
    //   storage:{
    //     getItem(key){
    //       return uni.getStorageSync(key)
    //     },
    //     setItem(key,value){
    //       uni.setStorageSync(key,value)
    //     }
    //   }
    // }
    // 网页端配置: process.env.VUE_APP_PLATFORM 来判断当前平台
      persist: process.env.VUE_APP_PLATFORM === 'h5' ? true : {
        storage: {
          getItem(key) {
            return uni.getStorageSync(key); // 同步
          },
          setItem(key, value) {
            uni.setStorageSync(key, value); // 同步
          }
        }
      }
  },
)

当平台不是 H5 时,persist 选项被设置为一个自定义的存储对象,其中包含 getItemsetItem 方法,用于指定如何读取和写入存储。这里使用的是 uni.getStorageSyncuni.setStorageSync

有关storage的知识可以跳转下面链接:uni.setStorage(OBJECT) @setstorage | uni-app官网

随后我们就可以在vue项目文件中来使用持久化存储的数据:

TypeScript 复制代码
<script setup lang="ts">
import { useMemberStore } from '@/stores';
const memberStore = useMemberStore();
</script>

<template>
  <view class="my">
    <view>会员信息:{{ memberStore.profile }}</view>
    <button
      @tap="
        memberStore.setProfile({
          username: '记得开心一点嘛',
          token : '12345'
        })
      "
      size="mini"
      plain
      type="primary"
    >
      保存用户信息
    </button>
    <button @tap="memberStore.clearProfile()" size="mini" plain type="warn">清理用户信息</button>
  </view>
</template>

<style lang="scss">
//
</style>

7.拦截器的使用:

TypeScript 复制代码
import { useMemberStore } from "@/stores";
const baseURL = "https://pcapi-xiaotuxian-front-devtest.itheima.net";

// 添加拦截器
const httpInterceptor = {
  // 拦截前触发,该方法会在每次发起 HTTP 请求之前被调用
  invoke(options:UniApp.RequestOptions){
    // 1.非 http 开头需拼接地址
    if(!options.url.startsWith('http')){
      options.url = baseURL + options.url
    }
    // 2.请求超时,60s
    options.timeout = 10000;
    // 3.添加小程序端请求头标识
    options.header = {
      ...options.header,
      'source-client' : 'miniapp',
    }
    // 4.添加 token 请求头标识
    const memberStore = useMemberStore();
    const token = memberStore.profile?.token;
    if(token){
      options.header.Authorization = token
    }
    console.log(options);
  }
}
uni.addInterceptor('request',httpInterceptor)
uni.addInterceptor('uploadFile',httpInterceptor)

// 请求函数封装
interface Data<T>{
  code: string,
  msg: string,
  data: T
}
export const http = <T>(options:UniApp.RequestOptions) => {
  // 1.返回 Promise 对象
  return new Promise<Data<T>>((resolve,reject)=>{
    uni.request({
      ...options,
      // 2.请求成功
      success(res){

        if(res.statusCode >= 200 && res.statusCode < 300){
          // 获取数据成功,调用 resolve
          resolve(res.data as Data<T>) // 断言
        }else if(res.statusCode === 401){
          // 401错误,调用 reject,清理用户信息并跳转到登录页
          const memberStore = useMemberStore()
          memberStore.clearProfile();
          // 跳转
          uni.navigateTo({
            url: '/src/pages/login/login'
          })
          reject(res)
        }else{
          // 通用错误,调用 reject
          uni.showToast({
            icon: 'none',
            title: (res.data as Data<T>).msg || '请求错误'
          })
          reject(res)
        }
      },
      // 响应失败
      fail(err){
        // 网络错误,调用reject
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试'
        })
        reject(err)
      }
    })
  })
}

①请求拦截器 (httpInterceptor):用于在请求发送之前对请求进行修改和增强,比如拼接请求 URL、设置请求头、添加 token 等。

②封装请求函数 (http):使用 uni.request 封装一个返回 Promise 的 HTTP 请求函数,统一处理请求成功和失败的逻辑,并根据响应的状态码进行不同的处理。

下面我们来分段解析一下上述代码:

(1)定义请求拦截器:

invoke 方法:这是一个请求拦截器的回调函数,它在每次发起 HTTP 请求之前都会被调用。你可以在此修改请求的配置,加入统一的参数、头部信息等。

TypeScript 复制代码
const httpInterceptor = {
  // 拦截前触发,该方法会在每次发起 HTTP 请求之前被调用
  invoke(options: UniApp.RequestOptions) {
    // 1.非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseURL + options.url;
    }
    // 2.请求超时,60s
    options.timeout = 10000;
    // 3.添加小程序端请求头标识(为了标识当前请求来源于小程序)
    options.header = {
      ...options.header, // ...options.header 是 JavaScript 的扩展运算符语法
                         // 它用于将原来的请求头属性保留并展开,同时可以新增或覆盖某些字段。
      'source-client': 'miniapp', // 添加 source-client 标识
    };
    // 4.添加 token 请求头标识
    const memberStore = useMemberStore();
    const token = memberStore.profile?.token;
    if (token) {
      options.header.Authorization = token;
    }
    console.log(options); // 在控制台打印请求的配置信息,便于调试
  }
};

(2)使用 uni.addInterceptor 添加拦截器:

TypeScript 复制代码
uni.addInterceptor('request', httpInterceptor);
uni.addInterceptor('uploadFile', httpInterceptor);
  • 使用 uni.addInterceptor 添加请求拦截器和文件上传拦截器。
  • httpInterceptor 是前面定义的拦截器对象,它会在每次 uni.requestuni.uploadFile 请求之前触发,进行统一的配置修改。
  • 这样,所有的 HTTP 请求和文件上传请求都可以使用统一 的拦截器进行处理 ,确保请求头、URL 拼接、token 等的统一管理

(3)请求函数封装:

TypeScript 复制代码
interface Data<T> {
  code: string;
  msg: string;
  data: T;
}

Data<T> :这是一个泛型接口,用于定义请求成功后的数据结构。codemsg 是 API 返回的状态码和消息,data 是返回的实际数据。通过泛型 <T>,我们可以灵活地处理不同类型的响应数据。

(4)请求封装:

TypeScript 复制代码
export const http = <T>(options: UniApp.RequestOptions) => {
  // 1.返回 Promise 对象
  return new Promise<Data<T>>((resolve, reject) => {
    uni.request({
      ...options, // 2.将传入的请求配置(options)解构并传入 uni.request
      // 通过 ...options 将传入的 options 配置对象解构并传给 uni.request
      // 这样可以动态修改请求的各项设置(如 URL、请求头等)。

这段代码是一个 uni-app 中的请求封装,使用了 Promiseuni.request 来处理 HTTP 请求。它的功能是向服务器发起请求并根据响应结果执行不同的逻辑
return new Promise<Data<T>>((resolve, reject) => {...})

该函数返回一个 Promise 对象,类型为 Data<T>,表示异步操作的最终结果Data<T> 是一个接口,包含了 codemsgdata 三个字段,其中 data 的类型是泛型 T,即返回的数据类型。resolve 用于处理成功的回调,reject 用于处理失败的回调。

TypeScript 复制代码
uni.request({
      ...options,
      // 2.请求成功
      success(res){

        if(res.statusCode >= 200 && res.statusCode < 300){
          // 获取数据成功,调用 resolve
          resolve(res.data as Data<T>) // 断言
        }else if(res.statusCode === 401){
          // 401错误,调用 reject,清理用户信息并跳转到登录页
          const memberStore = useMemberStore()
          memberStore.clearProfile();
          // 跳转
          uni.navigateTo({
            url: '/src/pages/login/login'
          })
          reject(res)
        }else{
          // 通用错误,调用 reject
          uni.showToast({
            icon: 'none',
            title: (res.data as Data<T>).msg || '请求错误'
          })
          reject(res)
        }
      }
}

①success(res)

当请求成功并且响应返回时,success 回调函数会被触发,res 是响应的结果对象。

②resolve(res.data as Data<T>)

如果响应成功且 statusCode 合法,调用 resolve 返回数据,res.data 是服务器返回的数据。as Data<T> 是 TypeScript 的类型断言,告诉编译器 res.data 确实符合 Data<T> 类型,这样可以确保返回的数据结构是我们期望的。

③**uni.navigateTo({ url: '/src/pages/login/login' })**:

跳转到登录页面,让用户重新登录。

④**reject(res)**:

调用 reject,将响应对象 res 作为参数传递,表示请求失败。后续可以通过 catch 来处理这个错误。

⑤**uni.showToast({ icon: 'none', title: (res.data as Data<T>).msg || '请求错误' })**:

使用 uni.showToast 显示一个提示消息,提示用户请求出错。消息内容是从 res.data 中提取的 msg 字段(如果存在),如果没有,则使用默认消息 '请求错误'

TypeScript 复制代码
fail(err) {
        // 网络错误,调用 reject
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试'
        });
        reject(err);
}

fail(err)

fail 回调函数用于处理请求失败的情况,如网络不可用、请求超时等错误。

(5)发起 HTTP 请求并处理返回的数据:

TypeScript 复制代码
<script setup lang="ts">
import { useMemberStore } from '@/stores'
import '@/utils/http'
import { http } from '@/utils/http';
const memberStore = useMemberStore()
const getData = async () =>{
  const res = await http<string[]>({
    method:'GET',
    url: '/home/banner',
    header : {},
  })
  console.log("请求成功",res);
}
</script>

<template>
  <view class="my">
    <view>会员信息:{{ memberStore.profile }}</view>
    <button
      @tap="
        memberStore.setProfile({
          nickname: '黑马先锋',
          token : '12345'
        })
      "
      size="mini"
      plain
      type="primary"
    >
      保存用户信息
    </button>
    <button @tap="memberStore.clearProfile()" size="mini" plain type="warn">清理用户信息</button>
    <button @tap="getData()" size="mini" plain type="warn">测试请求</button>
  </view>
</template>

<style lang="scss">
//
</style>

await http<string[]>({...})

调用之前引入的 http 函数发起请求。这里传入了请求的配置:

  • method: 'GET':请求方法是 GET,意味着从服务器获取数据。
  • url: '/home/banner':请求的 URL 地址,这里是请求首页的横幅数据。
  • header: {}:请求头,这里为空对象,表示没有添加额外的请求头(实际应用中可能需要添加认证信息、请求类型等)。

await

等待 http 请求返回的结果。由于 http 返回的是一个 Promise 对象,await 会让函数暂停,直到 Promise 被解决(即请求完成)。

const res

请求的响应结果会赋值给 res,类型是 string[](字符串数组)。这意味着我们期望返回的是一个字符串数组,可能是首页的横幅数据。

相关推荐
三翼鸟数字化技术团队2 分钟前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei30 分钟前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯
圈圈编码38 分钟前
Spring Task 定时任务
java·前端·spring
转转技术团队39 分钟前
代码变更暗藏危机?代码影响范围分析为你保驾护航
前端·javascript·node.js
Spark23844 分钟前
关于vue3整合tiptap的slash菜单的ts支持
vue.js
Mintopia1 小时前
Node.js高级实战:自定义流与Pipeline的高效数据处理 ——从字母生成器到文件管道的深度解析
前端·javascript·node.js
Mintopia1 小时前
Three.js深度解析:InstancedBufferGeometry实现动态星空特效 ——高效渲染十万粒子的底层奥秘
前端·javascript·three.js
北凉温华1 小时前
强大的 Vue 标签输入组件:基于 Element Plus 的 ElTagInput 详解
前端
随笔记1 小时前
Flex布局下,label标签设置宽度依旧对不齐,完美解决(flex-shrink属性)
javascript·css·vue.js
原生高钙1 小时前
LLM大模型对话框实践:大文件的分片上传
前端