黑马程序员前端项目uniapp小兔鲜儿微信小程序项目视频教程,基于Vue3+Ts+Pinia+uni-app的最新组合技术栈开发的电商业务全流程_哔哩哔哩_bilibili

参考

有代码,还有app、h5页面、小程序的演示

小兔鲜儿-vue3+ts-uniapp-一套代码多端部署: 小兔鲜儿-vue3+ts-uniapp 项目已上线,小程序搜索《小兔鲜儿》即可体验。🎉🎉🎉 <br/> 配套项目接口文档,配套笔记。

接口文档

说明 - 小兔鲜儿-小程序版

创建uni-app项目 

官方文档:

uni-app官网

npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project

could not fetch 

本质是拉取模板,网络不好就会拉取失败

(拉取失败了,但因为过去创建过,所以使用了缓存)

使用vscode开发uni-app项目

uni-create-view 快速创建 uni-app 页面

安装后进行设置 

配置好后,直接创建uniapp页面 

前一个是页面,后一个是页面路由pages路径名称 

能否自动注册页面路由

uni-helper 代码提示

工具包,包含多个插件

uniapp小程序扩展 鼠标悬停查文档

完善ts类型校验

uni-helper的配置(使用pnpm)

官网 

uni-app-types | Uni Helper

安装依赖

pnpm i -D @uni-helper/uni-app-types

设置 .npmrc避免幽灵依赖

npm 和 pnpm 共同使用同一个 .npmrc 是文件

win下,.npmrc文件的位置为:%USERPROFILE%/.npmrc

找到文件,然后添加或修改成true

shamefully-hoist=true

查看配置

pnpm config get shamefully-hoist

微信小程序配置

插件github仓库:

GitHub - wechat-miniprogram/api-typings: Type definitions for APIs of Wechat Mini Program in TypeScript

微信开放文档: 

微信开放文档 / 开发

安装依赖

pnpm i -D miniprogram-api-typings

配置tsconfig.json

// tsconfig.json
{
  "extends": "@vue/tsconfig/tsconfig.json",
  "compilerOptions": {
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "lib": ["esnext", "dom"],
    // 类型声明文件
    "types": [
      "@dcloudio/types", // uni-app API 类型
      "miniprogram-api-typings", // 原生微信小程序类型
      "@uni-helper/uni-app-types" // uni-app 组件类型
    ]
  },
  // vue 编译器类型,校验标签类型
  "vueCompilerOptions": {
    // 原配置 experimentalRuntimeMode 已废弃,请升级 Vue - Official 插件至最新版本
    "plugins": ["@uni-helper/uni-app-types/volar-plugin"] 
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

tsconfig报错

方法一:加上"ignoreDeprecations":"5.0"后重启

在 TypeScript 5.0 及以上版本中,importsNotUsedAsValues 和 preserveValueImports 选项已被弃用,并将在 TypeScript 5.5 中停止支持。取而代之的是使用 verbatimModuleSyntax 选项

如果希望继续使用旧版本的配置,可以在 tsconfig.json 中添加 ignoreDeprecations 选项,使编译器忽略弃用警告

"ignoreDeprecations":"5.0"

方法二:更新@vue/tsconfig 

使用命令查看vue/tsconfig版本

我的版本是0.1.3

pnpm list @vue/tsconfig

升级

pnpm update @vue/tsconfig

允许pages.json和manifest.json允许注释

在uniapp项目中,允许pages.json和manifest.json允许注释

但是vscode默认严格json 

只有这两个json文件允许注释

值jsonc,表示允许注释

项目配置(运行到小程序)

微信小程序appid

安装依赖,运行 

安装依赖

pnpm i
pnpm dev:mp-weixin

mp表示mini program,微信小程序

导入mp-weixin

自动生成dis目录

编译完成的结果在dis目录下

打开微信小程序,导入mp-weixin

分离窗口

分离窗口,方便查看

安装uni-ui(可以不装)

官网:

uni-app官网

pnpm i @dcloudio/uni-ui

配置vite.config.ts 

官网提供的是对Vue-cli项目的vue.config.js的配置

import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";

export default defineConfig({
  plugins: [uni()],
});

安装sass

官网提供的是对 Vue CLI 项目的说明

Vite 项目中无需手动安装 sass-loader(Vite 内置了对 SASS/SCSS 的支持,底层已处理 loader),只需安装 sass 本体即可

pnpm i sass -D

安装uni-ui组件库 

pnpm i @dcloudio/uni-ui

配置easycom完成组件自动导入

开启自动扫描,在components文件夹是否有符合uniapp标准的组件,有就自动导入

但我们通过pnpm包管理器的方式下载,组件在node_modules里

所以这个配置走的是第二个custom正则的规则 

^表示开头,用()包裹.*,表示提取出这个组件的名字,()表示提取,提取的内容会把后面的$1替换

("^Xtx(.*)": "@/components/Xtx$1.vue"这一行是之后的项目要用的,官方文档里没有)

  // 组件自动引入规则
	"easycom": {
    // 是否开启自动扫描
		"autoscan": true,
    // 以正则方式自定义组件匹配规则
		"custom": {
		  // uni-ui 规则如下配置
		  "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
		}
	},

运行微信小程序

设置->项目设置->取消勾选过滤无依赖文件

第一次运行时报错了,取消勾选就成功运行了

然后我再次勾选上,之后也成功运行了

不明所以 

为uni-ui提供ts类型声明

官网:

起步 | @uni-helper/uni-typed

uni-ui开发时使用js,没有升级到ts

官方提供的组件库,并没有对应的类型声明文件

有人为uni-ui提供了配套的类型声明文件 

安装文件

pnpm i -D @uni-helper/uni-app-types@latest @uni-helper/uni-ui-types@latest

配置tsconfig.json

//tsconfig.json
"@uni-helper/uni-ui-types"

小程序端pinia持久化 

官方文档:

配置 | Pinia Plugin Persistedstate

Pinia 用法与 Vue3 项目完全一致,uni-app 项目仅需解决持久化插件兼容性问题

插件默认使用 localStorage 实现持久化,小程序端不兼容,需要替换持久化 API

安装pinia

pnpm install pinia

 安装持久化存储插件

pnpm i pinia-plugin-persistedstate

配置

// src/main.ts
import { createSSRApp } from "vue";
import App from "./App.vue";

// 导入pinia实例
import pinia from '@/stores';
export function createApp() {
  // 创建vue实例
  const app = createSSRApp(App);

  // 挂载pinia实例
  app.use(pinia);

  return {
    app,
  };
}
// src/stores
import persist from 'pinia-plugin-persistedstate';
import {createPinia} from 'pinia';

// 创建 Pinia 实例
const pinia = createPinia();

// 添加持久化插件
pinia.use(persist);

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

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

参考 

路由

因此,UniApp 提供了自己的路由系统,与标准 vue-router 不兼容

请求工具的封装 

拦截器

文档:

uni.addInterceptor(STRING, OBJECT) | uni-app官网

设置请求基地址:

如果首选服务器挂了,换成备用服务器,有基地址设置,只需要改一边就可以了

在项目中用到路径也更简洁

设置超时时间:

默认的超时时间是60s,太长了

添加请求头标识:

项目服务端除了服务于小程序端,也会服务于pc端和app端

后端需要通过标记识别出数据请求的来源,后端就能知道来源哪个端

还有根据不同的端,返回不同的数据

添加token:

用户登录后拿到token,部分接口必须在登录之后,携带token才能访问

请求函数

封装请求函数,借鉴axios(axios返回值是Promise对象,配合async,await,能更方便地获取到请求成功的数据)

uniapp的拦截器没有axios拦截器完善,uniapp的响应拦截器对类型支持并不友好

所以前面的拦截器只完成了请求前的拦截,没有实现响应拦截器,要通过自己封装的请求函数去实现axios响应拦截器做过的业务(响应分为成功、失败)

成功:

在uni.request发完请求之后,后端返回的数据会包裹在res.data里,为了方便使用,直接把数据提取出来

uni.request函数不支持添加类型,在自己封装的函数里添加泛型,通过泛型确定使用时的具体类型

获取数据失败

success()只有服务器有响应,都会走到这里面

如果服务器挂了,或者网络出错,才会走到fail()响应失败

如果没有token,但调用了一个需要携带token才能访问的接口,会走到success()响应成功里面。因为服务器有响应,响应的结果只不过是token失败

对于我们也页面中使用,业务理解会有差异

所以借鉴axios 

完整代码

ts版(utils/http.ts)

// 请求基地址
const baseURL = 'http://localhost:8080'

// 拦截器配置
const httpInterceptor = {
  // 拦截前触发
  invoke(options: UniApp.RequestOptions) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseURL + options.url
    }
    // 2. 请求超时
    options.timeout = 10000
    // 3. 添加小程序端请求头标识
    options.header = {
      // 如果有请求头标识,就保留再添加
      ...options.header,
      'source-client': 'miniapp',
    }
    // 4. 添加 token 请求头标识

    // token在登录成功后获取,当前项目登录成功后的信息存放位置
    /*
    // 样例
    const memberStore = useMemberStore()
    const token = memberStore.profile?.token
    if (token) {
      options.header.Authorization = token
    }
    */
  },
}

// 上传同一台服务器,所以拦截器的业务共用同一个配置
// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)

/**
 * 请求函数
 * @param  UniApp.RequestOptions
 * @returns Promise
 *  1. 返回 Promise 对象,用于处理返回值类型
 *  2. 获取数据成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型,支持泛型
 *  3. 获取数据失败
 *    3.1 401错误  -> 清理用户信息,跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */
type Data<T> = {
  code: string
  msg: string
  result: T
}
// 2.2 添加类型,支持泛型
export const http = <T>(options: UniApp.RequestOptions) => {
  // 1. 返回 Promise 对象,resolve标记成功,reject标记失败
  return new Promise<Data<T>>((resolve, reject) => {
    uni.request({
      ...options,
      // 2. 响应成功
      success(res) {
        // 状态码 2xx,参考 axios 的设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 提取核心数据 res.data
          resolve(res.data as Data<T>)
        } else if (res.statusCode === 401) {
          // 401错误  -> 清理用户信息,跳转到登录页

          /*
          样例
          const memberStore = useMemberStore()
          memberStore.clearProfile()
          */
          uni.navigateTo({ url: '/pages/login/login' })
          reject(res)
        } else {
          // 其他错误 -> 根据后端错误信息轻提示
          uni.showToast({
            icon: 'none',
            title: (res.data as Data<T>).msg || '请求错误',
          })
          reject(res)
        }
      },
      // 响应失败
      fail(err) {
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
        })
        reject(err)
      },
    })
  })
}

js版

// 请求基地址
const baseURL = 'http://localhost:8080'

// 拦截器配置
const httpInterceptor = {
  // 拦截前触发
  invoke(options) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseURL + options.url
    }
    // 2. 请求超时
    options.timeout = 10000
    // 3. 添加小程序端请求头标识
    options.header = {
      // 如果有请求头标识,就保留再添加
      ...options.header,
      'source-client': 'miniapp',
    }
    // 4. 添加 token 请求头标识

    // token在登录成功后获取,当前项目登录成功后的信息存放位置
    /*
    // 样例
    const memberStore = useMemberStore()
    const token = memberStore.profile?.token
    if (token) {
      options.header.Authorization = token
    }
    */
  },
}

// 上传同一台服务器,所以拦截器的业务共用同一个配置
// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)

/**
 * 请求函数
 * @param  UniApp.RequestOptions
 * @returns Promise
 *  1. 返回 Promise 对象,用于处理返回值类型
 *  2. 获取数据成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型,支持泛型
 *  3. 获取数据失败
 *    3.1 401错误  -> 清理用户信息,跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */
// 2.2 添加类型,支持泛型
export const http = (options) => {
  // 1. 返回 Promise 对象,resolve标记成功,reject标记失败
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      // 2. 响应成功
      success(res) {
        // 状态码 2xx,参考 axios 的设计
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 提取核心数据 res.data
          resolve(res.data)
        } else if (res.statusCode === 401) {
          // 401错误  -> 清理用户信息,跳转到登录页

          /*
          样例
          const memberStore = useMemberStore()
          memberStore.clearProfile()
          */
          uni.navigateTo({ url: '/pages/login/login' })
          reject(res)
        } else {
          // 其他错误 -> 根据后端错误信息轻提示
          uni.showToast({
            icon: 'none',
            title: res.data.msg || '请求错误',
          })
          reject(res)
        }
      },
      // 响应失败
      fail(err) {
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
        })
        reject(err)
      },
    })
  })
}

统一代码风格、git工作流规范

小兔鲜儿 - 项目起步 | uniapp+vue3+ts

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐