如何使用vue3+vite+typescript+pinia+uni-ui+pnpm从0到1创建uni-app多端小程序APP工程化项目


🙂前言

一方面是为了把 vue3+typescript+pinia+pnpm+vite+uni-app+uni-ui 玩一玩,也为和前面用 taro3+vue3+tsx+pnpm+pinia做的小程序做个思想碰撞,因为 uni-app+vue3+typescript 用的 template 的写法思路来开发,而 taro3+vue3+tsx 用的 react 的 hooks 思想,看看两个思路的开发实践差异点和效率等方面具体的情况能碰撞出怎样的火花。


😍uni-app(vue3+vite+typescript+pinia+uni-ui)微信小程序APP工程模版实体截图

uni-app(vue3+vite+typescript+pinia+uni-ui)微信小程序APP工程模版实体截图

gitee仓库传送门

工程项目暂时定义为测试阶段的初始模版,所以里面有部分小波自测冗余代码,拿去用的话需要自己看看手动删除,后续小波有时间会更新版本清理。


😦目标功能

done:

  1. 集成 vue3、typescript、pinia、vite、uni-ui、nodejs-koa、pnpm,使用 vue3 模版渲染思路开发
  2. 多人协作 eslint、prettier 代码格式校验规范, vsocde 配置
  3. jest 单元测试
  4. pinia 全局状态管理
  5. 小程序分包配置
  6. 小程序自定义顶部导航
  7. nodejs-koa 接口服务
  8. 多环境 api 接口判断
  9. 页面:首页、点餐

todo:

  • git提交 husky 校验
  • git提交 commit 校验
  • 改成 tsx 完善整个系统流程
  • 生产环境去除 console
  • nodejs-koa 提取到 vercel 做成在线 api 服务
  • 打包 app 流程
  • vite 深入学习实践

🧐主要技术栈

  • vue3
  • typescript
  • uni-ui
  • pinia
  • vite
  • pnpm
  • koa
  • nodejs
  • sass
  • jest
  • eslint+prettier
  • husky
  • commit

🙂总纲

  1. 安装 vscode 插件 vue volar 全家桶、eslint、prettier、sass,禁用或者直接删除 vetur (这个插件是 for vue2),安装微信开发工具,HBuildX
  2. 安装 pnpm 和 uni-app 脚手架并初始项目
  3. 卸载不需要多余依赖库
  4. 安装 eslint + preitter 相关依赖库,创建相关配置文件 .eslintrc.js、.prettierrc.js, vscode 配置: .vscode/settings.json ,配置 package.json 包检测命令,配置别名
  5. 安装 pinia 并配置
  6. 安装 sass
  7. 封装 uni.request 请求
  8. 利用 nodejs - koa 配置 mock 数据服务器
  9. 配置 jest 单元测试
  10. 安装 uni-ui 依赖库
  11. 配置小程序渲染
  12. 配置多环境 api 接口
  13. 从0到1架构uni-app多端工程化项目遇见的问题

🤔搭建uni-app微信小程序APP工程化项目步骤

第一步:安装 vscode 插件、微信开发工具和 HBuildX

  1. vscode 插件
    • eslint

    • prettier

    • volar (ts版本也一并安装)

    • sass

    以上的插件可以在 vue volar extension pack 中直接全装

    提示

    不需要开发 vue2 则删除 vetur 插件,需要就先禁用,vue3 中会导致代码报错

  2. 微信开发工具

    微信开发工具

    小程序帐号注册,获取的AppID并导入本地的项目

  3. HBuildX

    HBuildX

    直接官网下载,主要是配置打包小程序后在微信开发工具中开发和测试


第二步:安装 pnpm 和 uni-app 脚手架

以下操作基于已经安装 nodejs 环境操作,例如小波的环境版本

1
2
node -v
v16.13.1
  • 安装 pnpm

    1
    2
    3
    4
    5
    6
    // 全局安装
    npm install pnpm -g
    // 切换淘宝源
    pnpm config set registry https://registry.npmmirror.com/
    // 查看源
    pnpm config get registry

    提示

    pnpm 跟 npm 和 yarn 的差距就是把原来每个项目安装 modules 放到统一的文件夹中,通过符号链接(软连接)和硬链接,注意项目要和 pnpm 统一存依赖的 modules 同盘,不然就等于丢失了 pnpm 的优势。

  • 安装 uni-app 脚手架(参考官网文档[2])

    1
    2
    # 创建以 typescript 开发的工程  
    npx degit dcloudio/uni-preset-vue#vite-ts vue3-vite-uniapp

    注意

    一定要选择和pnpm存依赖包相同的盘符安装创建项目哦


第三步:初始项目

  1. 切到自己创建的文件夹执行初始命令
    1
    2
    3
    // cd到自己建立的文件夹
    cd x/vue3-vite-uniapp
    pnpm install

    初始的 package.json

    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
    {
    "name": "uni-preset-vue",
    "version": "0.0.0",
    "scripts": {
    "dev:app": "uni -p app",
    "dev:app-android": "uni -p app-android",
    "dev:app-ios": "uni -p app-ios",
    "dev:custom": "uni -p",
    "dev:h5": "uni",
    "dev:h5:ssr": "uni --ssr",
    "dev:mp-alipay": "uni -p mp-alipay",
    "dev:mp-baidu": "uni -p mp-baidu",
    "dev:mp-jd": "uni -p mp-jd",
    "dev:mp-kuaishou": "uni -p mp-kuaishou",
    "dev:mp-lark": "uni -p mp-lark",
    "dev:mp-qq": "uni -p mp-qq",
    "dev:mp-toutiao": "uni -p mp-toutiao",
    "dev:mp-weixin": "uni -p mp-weixin",
    "dev:quickapp-webview": "uni -p quickapp-webview",
    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
    "build:app": "uni build -p app",
    "build:app-android": "uni build -p app-android",
    "build:app-ios": "uni build -p app-ios",
    "build:custom": "uni build -p",
    "build:h5": "uni build",
    "build:h5:ssr": "uni build --ssr",
    "build:mp-alipay": "uni build -p mp-alipay",
    "build:mp-baidu": "uni build -p mp-baidu",
    "build:mp-jd": "uni build -p mp-jd",
    "build:mp-kuaishou": "uni build -p mp-kuaishou",
    "build:mp-lark": "uni build -p mp-lark",
    "build:mp-qq": "uni build -p mp-qq",
    "build:mp-toutiao": "uni build -p mp-toutiao",
    "build:mp-weixin": "uni build -p mp-weixin",
    "build:quickapp-webview": "uni build -p quickapp-webview",
    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
    "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
    "type-check": "vue-tsc --noEmit"
    },
    "dependencies": {
    "@dcloudio/uni-app": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-app-plus": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-components": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-h5": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-alipay": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-baidu": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-jd": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-lark": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-qq": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-mp-weixin": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3070720230316001",
    "vue": "^3.2.45",
    "vue-i18n": "^9.1.9"
    },
    "devDependencies": {
    "@dcloudio/types": "^3.3.2",
    "@dcloudio/uni-automator": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-cli-shared": "3.0.0-alpha-3070720230316001",
    "@dcloudio/uni-stacktracey": "3.0.0-alpha-3070720230316001",
    "@dcloudio/vite-plugin-uni": "3.0.0-alpha-3070720230316001",
    "@vue/tsconfig": "^0.1.3",
    "typescript": "^4.9.4",
    "vite": "4.0.4",
    // tsc默认已装
    "vue-tsc": "^1.0.24"
    }
    }

    和小波已经搭建好的项目 package.json 差异的引入包

    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
    "scripts": {
    // lint 检测
    "lint": "eslint --ext .ts,tsx,vue src/** --no-error-on-unmatched-pattern --quiet",
    // lint 修复
    "lint:fix": "eslint --ext .ts,tsx,vue src/** --no-error-on-unmatched-pattern --fix",
    // koa-mock数据服务
    "mock": "cd mock && ts-node-dev mock.ts",
    // jest 自测
    "test": "jest",
    // jest 测试覆盖率
    "test:unit": "jest --coverage"
    },
    "dependencies": {
    // uni-ui
    "@dcloudio/uni-ui": "^1.4.26",
    // pinia
    "pinia": "^2.0.33",
    },
    "devDependencies": {
    "sass": "^1.60.0",
    // type申明
    "@types/faker": "5.1.5",
    "@types/jest": "^29.5.0",
    "@types/koa": "^2.13.5",
    "@types/koa-logger": "^3.1.2",
    "@types/koa-router": "^7.4.4",
    "@types/koa2-cors": "^2.0.2",
    "@types/node": "^18.15.9",
    // eslint && prettier
    "@typescript-eslint/eslint-plugin": "^5.56.0",
    "@typescript-eslint/parser": "^5.56.0",
    "@vue/eslint-config-prettier": "^7.1.0",
    "@vue/eslint-config-typescript": "^11.0.2",
    "@vuedx/typescript-plugin-vue": "^0.7.6",
    "eslint": "^8.36.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-vue": "^9.10.0",
    "prettier": "^2.8.7",
    // jest
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "@vue/test-utils": "2.0.0-rc.18",
    "babel-jest": "26.6.3",
    "jest": "26.6.3",
    "jest-environment-node": "27.5.1",
    "@testing-library/jest-dom": "^5.16.5",
    "ts-jest": "26.5.6",
    "vue-jest": "5.0.0-alpha.10",
    // koa 数据接口服务
    "chalk": "4.1.2",
    "faker": "5.1.0",
    "koa": "^2.14.1",
    "koa-body": "^6.0.1",
    "koa-logger": "^3.2.1",
    "koa-router": "^12.0.0",
    "koa2": "2.0.0-alpha.7",
    "koa2-cors": "^2.0.6",
    "lodash": "^4.17.21",
    "log4js": "^6.9.1",
    "postcss": ">=8.1.0 <9.0.0",
    "reflect-metadata": "^0.1.13",
    "ts-node-dev": "^2.0.0",
    "tslib": "^2.5.0",
    }

    初始的文件树

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    vue3-vite-uniapp
    ├─ index.html // 入口
    ├─ package.json // 安装依赖配置
    ├─ pnpm-lock.yaml // pnpm锁定配置
    ├─ README.md // md说明
    ├─ src
    │ ├─ App.vue // 小程序页面状态
    │ ├─ env.d.ts // .vue组件componet时的ts申明
    │ ├─ main.ts // 入口js,导入了app.vue
    │ ├─ manifest.json // uniapp相关配置应用名称、appid、logo、版本等打包信息
    │ ├─ components
    │ │ └─ counter
    │ │ │ └─ counter.vue // 测试pinia状态组件
    │ ├─ pages
    │ │ └─ index
    │ │ │ └─ index.vue // 首页
    │ ├─ static // 静态资源包
    │ │ └─ logo.png
    │ ├─ pages.json // 小程序路由,分包
    │ ├─ shime-uni.d.ts // vue hooks的ts申明(暂时理解)
    │ └─ uni.scss // uni-app内置的常用样式变量
    ├─ tsconfig.json // ts的配置
    └─ vite.config.ts // vite配置

    和小波已经搭建好的项目文件树的差异

    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
    uniapp_vue3_vite_pinia
    ├─ .gitignore // git忽略文件
    ├─ .eslintrc.js // eslint的配置
    ├─ .prettierrc.js // prettier的配置
    ├─ jest.config.js // jest测试配置
    ├─ vue.config.js // uni-ui依赖使用
    ├─ .hbuilderx // HBuildX配置
    ├─ .vscode
    │ └─ settings.json // vscod配置
    ├─ coverage // jest单元测试覆盖查看UI界面
    ├─ src
    │ ├─ utils
    │ │ ├─ request.d.ts // 封装请求ts申明
    │ │ └─ request.ts // uni的请求方法封装
    │ ├─ config
    │ │ └─ app.ts // 封装接口使用的常量
    │ ├─ api
    │ │ ├─ user.d.ts // 用户页面接口ts申明
    │ │ └─ user.ts // 用户页面使用接口
    │ ├─ components
    │ │ └─ uni-nav-bar // 自定义头部导航栏
    │ │ │ ├─ style.scss // 样式
    │ │ │ ├─ types.d.ts // 申明
    │ │ │ ├─ uni-nav-bar.vue // 组件入口
    │ │ │ └─ uni-status-bar.vue // 组件依赖的小组件
    │ ├─ pages
    │ │ └─ menu // 自定义头部导航栏
    │ │ │ ├─ style.scss // 样式
    │ │ │ ├─ types.d.ts // 申明
    │ │ │ ├─ index.vue // 页面入口
    │ │ │ └─ menuHooks
    │ │ │ │ └─ index.vue // hooks必须要用Index来命名,不然报错
    │ ├─ stores
    │ │ ├─ golbalSysInfo.ts // 系统信息
    │ │ └─ index.ts // 导出createPinia(主要是jest测试使用,不然会报错)
    │ ├─ subPages // 分包文件夹
    ├─ tests // jest 测试用例
    ├─ types
    │ └─ global.d.ts // ts全局申明

    ├─ mock
    │ ├─ controller
    │ │ ├─ user.ts // 用户相关接口控制器
    │ │ └─ banner.ts // banner图接口控制器
    │ ├─ middleware // 前后端交互最重要的就是两个参数 request 和 respond ,每一个中间件执行完毕应该进入下一个中间件,因此还需要一个 next 参数,用来启动下一个中间件。
    │ │ └─ resultHandler.ts // 用来给每个响应对象包装响应码等,输出ctx.body
    │ ├─ mockdb // 各种假数据
    │ ├─ utils
    │ │ └─ logger.ts // 输出错误日志函数
    │ ├─ constant.ts // 常量
    │ ├─ mock.ts // 主入口
    │ ├─ requestDecorator.ts // 生成 http method 装饰器,创建类路径装饰器
    │ ├─ router.ts // 路由(拿到controller中定义的接口,结合meta数据添加路由 和 验证)
    │ ├─ tsconfig.json // ts配置
    │ └─ type.d.ts // ts申明
  2. 启动命令 h5模式
    1
    pnpm dev:h5

    确认初始么有问题,则开始后续操作


第四步:卸载不需要默认安装的依赖库

uni-app 脚手架默认安装了 vue-i18n 多语言依赖库,可根据需求自行选择是否卸载

1
pnpm remove vue-i18n

第五步:安装 eslint + preitter 相关依赖库,配置 vscode

标准三件套

  • 代码规范 ESlint
  • 代码格式美化 Prettier
  • 多人协作保持代码风格一致配置 vscode
  1. 安装相关依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    pnpm add @typescript-eslint/eslint-plugin -D
    pnpm add @typescript-eslint/parser -D
    pnpm add @vue/eslint-config-prettier -D
    pnpm add @vue/eslint-config-typescript -D
    pnpm add @vuedx/typescript-plugin-vue -D
    pnpm add eslint -D
    pnpm add eslint-plugin-prettier -D
    pnpm add eslint-plugin-vue -D
    pnpm add prettier -D

    提示

    @vuedx/typecheck@vuedx/typescript-plugin-vue 不安装似乎也没啥影响,npm 上的描述是一个命令行检查 vue 项目的工具。在我的理解中一般使用于 githooks。

    安装也没关系,反正开发环境使用

  2. 设置代码规范和格式化规则

    项目根目录创建 .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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    module.exports = {
    // true: 它就会停止在父级目录中寻找
    root: true,
    // 预定义的全局变量,这里是浏览器环境
    env: {
    browser: true,
    es2021: true,
    node: true, // 如果defineProps报错
    "vue/setup-compiler-macros": true,
    },
    // ESTree 只是一个 AST 的某一种规范,ESTree 本质上还是 AST
    // ESLint 默认的 parser ,只转换 js,默认支持 ES5 的语法: 默认采用vue-eslint
    parser: "vue-eslint-parser",
    // 子配置:优先使用typescript-eslint,支持es2021
    parserOptions: { parser: "@typescript-eslint/parser", ecmaVersion: 2021 },
    // 扩展校验风格 合并 eslint 中的 plugins,rules 的
    extends: [
    "plugin:vue/base",
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "@vue/prettier",
    "@vue/typescript",
    ],
    // 它的默认 parser 参数会将代码转换为 AST,AST 被 plugin&rules 用来校验和生成错误信息
    plugins: [],
    rules: {
    // 检测未使用的变量,函数和函数的参数
    "no-unused-vars": "off",
    // 检测未使用的变量,函数和函数的参数 for typescript
    "@typescript-eslint/no-unused-vars": "off",
    // 语句强制分号结尾
    semi: 0,
    // 如果报错回车结尾错误 window开发环境,但是上传git又是linux
    endOfLine: "off",
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    // 在rules中添加自定义规则 关闭组件命名规则
    "vue/multi-word-component-names": "off",
    },
    // 忽略文件
    ignorePatterns: [
    "*.css",
    "*.less",
    "*.scss",
    "*.jpg",
    "*.png",
    "*.gif",
    "*.svg",
    "*vue.d.ts",
    ],
    };

    提示

    1. extends:[]

      配置语法说明:

      plugin:vue/base 只是使解析工作的基本规则。还没有 lint 任何东西。

      plugin:vue/essential 以上,加上仅用于防止 Vue 中的错误或意外行为的规则。

      plugin:vue/strongly-recommended 以上,加上通常被认为是最佳实践的规则。

      plugin:vue/recommended 以上,加上一些经常被建议的样式规则。

      extends: [...] 大概语义:

      使用vue最基本规则,vue3错误或意外行为的规则,eslint最佳实践的规则

      简写说明:

      引入 @vue/eslint-config-prettier 可以简写为 @vue/prettier

      vue/base 全称应该是 eslint-plugin-vue/base

      简单理解就是 eslint 默认把和自己相关命名进行了转行 eslint-config-eslint-plugin-

    2. ignorePatterns: [] 忽略文件配置是为了 eslint 命令执行检测造成非必要的混淆报错

    项目根目录创建 .prettierrc.js 并贴入以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    module.exports = {
    // 超过最大值换行
    printWidth: 120,
    // 缩进2
    tabWidth: 2,
    // ??? == useTabs
    tabs: false,
    // 末尾添加分号
    semi: false,
    // 使用单引号
    singleQuote: true,
    // 给对象里的属性名是否要加上引号,默认为as-needed,即根据需要决定,如果不加引号会报错则加,否则不加
    quoteProps: "as-needed",
    // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
    bracketSpacing: true,
    // jsx 标签的反尖括号需要换行
    jsxBracketSameLine: false,
    // 箭头函数参数只有一个时是否要有小括号 avoid: 省略括号
    arrowParens: "always",
    // 结尾是 \n \r \n\r auto
    endOfLine: "auto",
    };

    package.json 中 script 添加 Ts 检查命令和 Eslint 检查命令

    1
    2
    3
    4
    "scripts":{
    "lint": "eslint --ext .ts,tsx,vue src/** --no-error-on-unmatched-pattern --quiet",
    "lint:fix": "eslint --ext .ts,tsx,vue src/** --no-error-on-unmatched-pattern --fix"
    }
  3. 调用
    1
    2
    pnpm lint
    pnpm lint:fix
  4. 配置 vscode

    项目根目录创建 .vscode/settings.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    // 保存时格式化
    "editor.formatOnSave": true,
    // 保存时修复来自所有插件的所有可自动修复的ESlint错误
    "editor.codeActionsOnSave": { "source.fixAll.eslint": true },
    // 应通过ESLint验证的语言数组
    "eslint.validate": ["typescript", "vue", "html", "json"],
    // 默认格式化插件选择
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    // js格式化选择
    "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
    // vue格式化选择
    "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
    // html格式化选择
    "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
    // json格式化选择
    "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }
    // "json.format.enable": false
    }
  5. 代码书写规范

    请参考 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
    35
    36
    <script setup lang="ts">
    /**
    * 导入 start
    * 顺序:1.依赖库 2.请求 3.常量 4.组件 5. ts申明
    */
    import vue
    import api/data/piniaStore
    import hooks.vue/components.vue
    import x.d

    /**
    * 变量 start
    */
    const store: any = ...
    const state: stateType = reactive({})

    /**
    * 函数 start
    * @init 初始 - 判断骨架屏处理
    */
    const handleInit = async (): Promise<void> => {
    await api()
    loading.value = false
    }

    /**
    * 调用 start
    */
    handleInit()
    </script>
    <template>
    <view>...</view>
    </template>
    <style lang="scss">
    @import './style.scss';
    </style>
  6. 配置别名

    vite.config.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { resolve } from "path";
    ...
    resolve: {
    alias: [
    {
    find: "@",
    replacement: resolve(__dirname, "src"),
    },
    ],
    },

    完整 vite.config.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { defineConfig } from 'vite'
    // 需要安装 @types/node
    import { resolve } from 'path'
    import uni from '@dcloudio/vite-plugin-uni'

    // https://vitejs.dev/config/
    export default defineConfig({
    plugins: [uni()],
    resolve: {
    alias: [
    {
    find: '@',
    replacement: resolve(__dirname, 'src'),
    },
    ],
    },
    })

    tsconfig.json

    1
    2
    3
    4
    5
    6
    "compilerOptions": {    
    "baseUrl": ".",
    "paths": {
    "@/*": [ "src/*" ],
    },
    }}

    完整 tsconfig.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    "extends": "@vue/tsconfig/tsconfig.json",
    "compilerOptions": {
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
    "@/*": ["./src/*"]
    },
    "lib": ["esnext", "dom"],
    "types": ["@dcloudio/types", "jest"],
    // 导入 x.d.ts 报错 x.d.ts”不是模块。
    "typeRoots": ["node_modules/@types"],
    // 此导入从不用作值,必须使用 "import type" ,因为 "importsNotUsedAsValues" 设置为 "error"
    "importsNotUsedAsValues": "remove",
    // 是一种类型,在同时启用了 "preserveValueImports" 和 "isolatedModules" 时,必须使用仅类型导入进行导入。
    "preserveValueImports": false
    },
    "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "./types"]
    }

    注意

    因为用了 typescript,resolve_dirname 会报 ts 申明错误提示,需要在头部导入 ‘path’ 并安装 @types/nodes

    1
    pnpm add @types/node -D

第六步:安装 pinia

  1. 安装 pinia
    1
    pnpm add pinia
  2. 导入 pinia

    正常情况直接在main.ts 导入 pinia

    1
    2
    3
    4
    5
    6
    7
    // pinia状态管理
    import { createPinia } from 'pinia'
    export function createApp() {
    const app = createSSRApp(App)
    app.use(createPinia())
    return { app, }
    }

    但是为了做 jest 单元测试,所以需要做点调整,创建 store/index.ts

    1
    2
    3
    import { createPinia } from 'pinia'
    const pinia = createPinia()
    export default pinia
  3. 代码使用

    src/pages/menu/index.vue

    1
    2
    3
    4
    5
    import pinia from '@/stores/index' // == createPinia()
    import { useGolbalSysInfoStore } from '@/stores/golbalSysInfo'

    const golbalSysInfo = useGolbalSysInfoStore(pinia)
    console.log('customGlobalData------', golbalSysInfo.sysInfo)

    需要响应式则如下操作,同 vue3 组件父子传值绑定双向原理相同

    1
    2
    3
    4
    5
    6
    7
    import pinia from '@/stores/index' // createPinia()
    import { useCounterStore } from '@/stores/counter'
    import { storeToRefs } from 'pinia'

    const counter = useCounterStore(pinia)
    // 用 storeToRefs 双向后再解构出 count
    let { count } = storeToRefs(counter) // 此时count为响应式的

第七步:安装 sass

1
pnpm add sass -D

因为我们是使用 Vite 进行开发,所以只需要安装一下 sass 就可以了,不需要额外配置,不像 webpack 那样安装loader


第八步:封装 uni.request 实现网络请求

  1. src 文件夹中创建 config/app.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // app名字
    export const APP_NAME = '廿壴博客'
    // app的图片 // 静态资源的cos地址
    export const IMAGE_URL = 'https://blog.ganxb2.com/img/about/blog_log.png'
    // mock请求地址
    export const HTTP_REQUEST_URL = 'http://localhost:3300'
    // 请求头 - json
    export const HEADER = {
    'content-type': 'application/json',
    }
    // 请求头 - 表单
    export const HEADERPARAMS = {
    'content-type': 'application/x-www-form-urlencoded',
    }
    // tokenName
    export const TOKENNAME = 'Authorization'
  2. src 文件夹中创建 utils/request.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
    116
    117
    /* eslint-disable @typescript-eslint/ban-types */
    // 获取常量
    import { HEADER, HEADERPARAMS, TOKENNAME, HTTP_REQUEST_URL } from '@/config/app'
    // import { useCounterStore } from '@/stores/counter'
    // import { useStore } from 'vuex'
    // 导入申明
    import { RequestOptionsMethod, RequestOptionsMethodAll } from './request.d'
    /**
    * 发送请求
    */

    // type handleBaseRequestType()
    function handleBaseRequest(
    url: string,
    method: RequestOptionsMethod,
    data: any,
    { noAuth = false, noVerify = false }: any,
    params: unknown
    ) {
    // const store = useStore()
    // const token = store.state.app.token
    // 从pinia中获取
    const token = 'islogin'
    const Url = HTTP_REQUEST_URL
    let header = JSON.parse(JSON.stringify(HEADER))

    // 如果传了参数 => 表单
    if (params != undefined) {
    header = HEADERPARAMS
    }
    // 如果未授权
    if (!noAuth) {
    // 并且token也是false
    if (!token) {
    // 提示未登录
    return Promise.reject({
    msg: '未登录',
    })
    }
    // 有token并且不是Null 则拼token给请求头
    if (token && token !== null) header[TOKENNAME] = 'Bearer ' + token
    }

    // 返回封装请求
    // resolve: (value: unknown) => void, reject: (reason?: any) => void) => void
    return new Promise((reslove, reject): any => {
    // 加载提示
    uni.showLoading({
    title: '加载中',
    mask: true,
    })

    // uni请求
    uni.request({
    // 常量Url拼上调用时传入的不同接口
    url: Url + url,
    method: method || 'GET',
    header: header,
    data: data || {},
    success: (res: any) => {
    console.log('uni请求封装 -------', res)
    // 成功关闭loading
    uni.hideLoading()
    // 如果有token并且token不为null则修改状态管理的token
    res.data.token && res.data.token !== 'null' && console.log('修改状态管理的token')
    // store.commit('LOGIN', {
    // token: res.data.token,
    // })
    // console.log('noVerify ------', noVerify, res)
    // 如果未验证抛出返回对象
    if (noVerify) {
    reslove(res)
    } else if (res.statusCode === 200) {
    // console.log('statusCode ------', res.statusCode)
    // 如果验证了并且 code = 200 则抛出返回的data
    reslove(res.data)
    } else {
    // code 不是200也没有验证标识抛出错误
    reject(res.data.message || '系统错误')
    }
    },
    // 请求失败
    fail: msg => {
    uni.hideLoading()
    reject('请求失败')
    },
    })
    })
    }

    // const request: Request = {}
    // 请求类型数组
    const requestOptions: RequestOptionsMethodAll[] = [
    'options',
    'get',
    'post',
    'put',
    'head',
    'delete',
    'trace',
    'connect',
    ]
    // 自定义ts的校验 methods
    type Methods = (typeof requestOptions)[number]
    // 定义request对象去接封装的请求(TS申明:如果在Methods中的一项)
    const request: { [key in Methods]?: Function } = {}

    // 循环请求类型数组
    requestOptions.forEach(method => {
    // item
    const m = method.toUpperCase() as unknown as RequestOptionsMethod
    // ge: { get(){} }
    request[method] = (api: string, data: any, opt: RequestOptionsMethod, params: unknown) =>
    handleBaseRequest(api, m, data, opt || {}, params)
    })

    export default request
  3. src 文件夹中创建 api/user.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import request from '@/utils/request'
    import { userInfoType } from './user.d'

    /**
    * 获取用户信息
    * 考虑错误返回 还要联合申明,暂时只写正确返回
    */
    export function fetchUserInfo(): Promise<userInfoType> {
    return request?.get?.('/user/userInfo', {}, { noAuth: true })
    }
  4. 调用
    1
    2
    3
    4
    5
    6
    7
    import { fetchUserInfo } from '@/api/user'
    ...
    fetchUserInfo()
    .then((r: userInfoType) => {
    console.log('用户信息---', r)
    })
    .catch((err: any) => console.log(err))

第九步:利用 nodejs - koa 配置 mock 数据服务器

因为这个服务是集成到当前 uni-app 中,所以不用把依赖装到 dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"devDependencies": {    
"koa": "^2.13.0",
"koa-body": "^4.2.0",
// koa-logger提供了输出请求日志的功能,包括请求的url、状态码、响应时间、响应体大小等信息 logger返回的信息丢给log4js
"koa-logger": "^3.2.1",
"koa-router": "^10.0.0",
"koa2-cors": "^2.0.6",
// Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。
"lodash": "^4.17.20",
// 日志
"log4js": "^6.3.0",
// Faker 模块用于生成伪造数据,不仅伪造数据,感染组织良好的伪造数据
"faker": "5.1.0",
// 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。
"reflect-metadata": "^0.1.13"
"@types/koa": "^2.11.6",
"@types/koa-logger": "^3.1.1",
"@types/koa-router": "^7.4.1",
"@types/koa2-cors": "^2.0.1",
// faker@6+ 已经默认带了申明不需要安装 @types/faker@6.6.9: This is a stub types definition. faker provides its own type definitions, so you do not need this installed.
"@types/faker": "5.1.5",
"ts-node-dev": "^2.0.0",
"tslib": "^2.5.0",
}
  1. 安装依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    pnpm add ts-node-dev -D
    pnpm add koa -D
    pnpm add koa-body -D
    pnpm add koa-logger -D
    pnpm add koa-router -D
    pnpm add koa2-cors -D
    // 一个一致性、模块化、高性能的 JavaScript 实用工具库
    pnpm add lodash -D
    // 日志
    pnpm add log4js -D
    // ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。
    pnpm add reflect-metadata -D
    // 终端打印信息增加个性化颜色 特别注意 chalk 一定要指定版本
    pnpm add chalk@4.1.2 -D
    // 生成伪造数据 特别注意 faker 一定要指定版本
    pnpm add faker@5.1.0 -D
    pnpm add @types/faker@5.1.5 -D
    pnpm add @types/koa -D
    pnpm add @types/koa-logger -D
    pnpm add @types/koa-router -D
    pnpm add @types/koa2-cors -D
    pnpm add @types/koa2-cors -D
    pnpm add tslib -D
  2. 贴入小波仓库中的 mock 文件夹
  3. package.json 里配置 mock 服务器启动命令
    1
    2
    3
    "scripts": {
    "mock": "cd mock && ts-node-dev mock.ts"
    }
  4. 开启 mock 服务

    新建个终端窗口,然后切到 uni-app 工程项目文件夹执行以下代码

    1
    2
    cd your uni-app
    pnpm mock

你可能遇到错误请参考如下解决方案

报错

1
[INFO] 17:13:02 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 4.9.5)Error [ERR_REQUIRE_ESM]: require() of ES Module C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\chalk@5.2.0\node_modules\chalk\source\index.js from C:\myWeb\vue3-vite-uniapp\mock\mock.ts not supported.Instead change the require of index.js in C:\myWeb\vue3-vite-uniapp\mock\mock.ts to a dynamic import() which is available in all CommonJS modules.    at require.extensions..jsx.require.extensions..js (C:\Users\ganxb\AppData\Local\Temp\ts-node-dev-hook-33103583567897243.js:114:20)    at Object.nodeDevHook [as .js] (C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\ts-node-dev@2.0.0_67kvwwhfsxe4y463wcu4dtvggu\node_modules\ts-node-dev\lib\hook.js:63:13)    at Object.<anonymous> (C:\myWeb\vue3-vite-uniapp\mock\mock.ts:12:41)    at Module._compile (C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\source-map-support@0.5.21\node_modules\source-map-support\source-map-support.js:568:25)    at Module.m._compile (C:\Users\ganxb\AppData\Local\Temp\ts-node-dev-hook-33103583567897243.js:69:33)    at require.extensions..jsx.require.extensions..js (C:\Users\ganxb\AppData\Local\Temp\ts-node-dev-hook-33103583567897243.js:114:20)    at require.extensions.<computed> (C:\Users\ganxb\AppData\Local\Temp\ts-node-dev-hook-33103583567897243.js:71:20)    at Object.nodeDevHook [as .ts] (C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\ts-node-dev@2.0.0_67kvwwhfsxe4y463wcu4dtvggu\node_modules\ts-node-dev\lib\hook.js:63:13)    at Object.<anonymous> (C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\ts-node-dev@2.0.0_67kvwwhfsxe4y463wcu4dtvggu\node_modules\ts-node-dev\lib\wrap.js:104:1)    at Module._compile (C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\source-map-support@0.5.21\node_modules\source-map-support\source-map-support.js:568:25)    at Object.require.extensions..jsx.require.extensions..js (C:\Users\ganxb\AppData\Local\Temp\ts-node-dev-hook-33103583567897243.js:95:24)[ERROR] 17:13:08 Error: require() of ES Module C:\myWeb\vue3-vite-uniapp\node_modules\.pnpm\chalk@5.2.0\node_modules\chalk\source\index.js from C:\myWeb\vue3-vite-uniapp\mock\mock.ts not supported.Instead change the require of index.js in C:\myWeb\vue3-vite-uniapp\mock\mock.ts to a dynamic import() which is available in all CommonJS modules.

原因:因为chalk版本问题

解决方案:降到4.1.2


报错

1
@prefix('/banner')⨯ Unable to compile TypeScript:controller/banner.ts(3,1): error TS2354: This syntax requires an imported helper but module 'tslib' cannot be found.

解决方案:

安装

1
pnpm add tslib -D

mock 后端服务文件夹中 tsconfig.json 增加

1
2
3
4
5
6
7
8
{
"compilerOptions": {
...
"paths": {
"tslib": ["node_modules/tslib/tslib.d.ts"]
},
}
}

报错

1
Cannot find module 'C:\myWeb\vue3-vite-uniapp\node_modules\faker\index.js'. Please verify that the package.json has a valid "main" entry

解决方案:卸载最新版本重新安装指定版本

1
2
3
4
5
pnpm remove faker
pnpm remove @types/faker

pnpm add faker@5.1.0
pnpm add @types/faker@5.1.5

第十步:配置 jest 单元测试

  1. 安装
    1
    pnpm add -D @babel/core @babel/preset-env @testing-library/jest-dom @types/jest @vue/test-utils@next babel-jest@26.6.3 ts-jest@26.5.6 vue-jest@next jest@26.6.3
  2. 项目根目录创建 jest.config.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    const path = require('path')

    module.exports = {
    rootDir: path.resolve(__dirname),
    clearMocks: true,
    coverageDirectory: 'coverage',
    coverageProvider: 'v8',
    moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node'],
    // 别名设置 src下components里组件 <rootDir>/src/components/$1
    moduleNameMapper: {
    '@/(.*)$': '<rootDir>/src/$1',
    },
    preset: 'ts-jest',
    testEnvironment: 'jsdom',
    // 测试文件 自己写的
    testMatch: ['<rootDir>/tests/unit/*.spec.ts?(x)'],
    transform: {
    '^.+\\.vue$': 'vue-jest',
    '^.+\\js$': 'babel-jest',
    '^.+\\.(t|j)sx?$': 'ts-jest',
    },
    }
  3. 配置 tsconfig.json
    1
    2
    3
    "compilerOptions": {
    "types": ["@dcloudio/types", "jest"],
    }
  4. 项目根目录中创建实例 tests/unit/HelloWorld.spec.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import { mount } from '@vue/test-utils'
    // 导入需要测试页面
    import HelloWorld from '@/components/counter/counter.vue'
    import { useCounterStore } from '../request'

    test('add stores count', async () => {
    // 获取整个wrapper
    const wrapper = mount(HelloWorld)

    // 获取dom: title的text判断是不是'0'
    expect(wrapper.find('.title').text()).toBe('0')
    // 触发按钮点击
    await wrapper.find('.button').trigger('tap')
    const acount = useCounterStore()
    expect(acount.count).toBe(1)
    // 再获取dom: title的text判断是不是'1'
    expect(wrapper.find('.title').text()).toBe('1')
    })
  5. package.json 添加命令
    1
    2
    3
    4
    5
    6
    7
    {
    "scripts": {
    ...
    "test": "jest",
    "test:unit": "jest --coverage"
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    pnpm test

    // 输出结果
    > uni-preset-vue@0.0.0 test C:\myWeb\uniapp_vue3_vite_pinia
    > jest

    PASS tests/unit/HelloWorld.spec.ts
    PASS tests/unit/testIndex.spec.ts (9.762 s)
    ● Console

    console.log
    111

    at Object.<anonymous> (tests/unit/testIndex.spec.ts:34:11)


    Test Suites: 2 passed, 2 total
    Tests: 2 passed, 2 total
    Snapshots: 0 total
    Time: 13.767 s, estimated 19 s
    Ran all test suites
    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
    pnpm test:unit

    // 输出结果
    > uni-preset-vue@0.0.0 test:unit C:\myWeb\uniapp_vue3_vite_pinia
    > jest --coverage

    PASS tests/unit/testIndex.spec.ts
    ● Console

    console.log
    111

    at Object.<anonymous> (tests/unit/testIndex.spec.ts:34:11)

    PASS tests/unit/HelloWorld.spec.ts
    ------------------------|---------|----------|---------|---------|-------------------
    File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
    ------------------------|---------|----------|---------|---------|-------------------
    All files | 97.11 | 100 | 40 | 97.11 |
    src/components/counter | 100 | 100 | 100 | 100 |
    counter.vue | 100 | 100 | 100 | 100 |
    src/stores | 96.55 | 100 | 50 | 96.55 |
    counter.ts | 96.15 | 100 | 50 | 96.15 | 12
    index.ts | 100 | 100 | 100 | 100 |
    tests | 95 | 100 | 0 | 95 |
    request.ts | 95 | 100 | 0 | 95 | 20,26
    ------------------------|---------|----------|---------|---------|-------------------

    Test Suites: 2 passed, 2 total
    Tests: 2 passed, 2 total
    Snapshots: 0 total
    Time: 9.287 s, estimated 10 s
    Ran all test suites.

    提示

    执行查询覆盖命令后同时会在项目根目录创建coverage 文件夹,coverage/lcov-report/index.html 可以网页UI模式查看。


你可能遇到错误请参考如下解决方案

报错

1
Test environment jest-environment-jsdom cannot be found.

原因:由于 jest 版本不对

解决方案:安装

1
pnpm add jest-environment-jsdom -D

报错

1
TypeError: Cannot destructure property 'config' of 'undefined' as it is undefined.

原因:同上,由于 jest 版本不对

解决方案:

1
2
3
"jest": "^26.6.3"
"ts-jest": "^26.5.6"
换 babel-jest@26.3.0

第十一步:挂载 uni-ui

  1. 安装
    1
    pnpm add @dcloudio/uni-ui
  2. 项目根目录创建并配置 vue.config.js
    1
    2
    3
    4
    // vue.config.js
    module.exports = {
    transpileDependencies: ['@dcloudio/uni-ui'],
    }
  3. easycom 配置 src/pages.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "easycom": {
    "autoscan": true,
    "custom": {
    // uni-ui 规则如下配置
    "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
    }
    },
    "pages": []
    ...
    }

    提示

    easycom规则例子:

    组件文件夹参考此路径创建组件 src/components/uni-nav-bar/uni-nav-bar.vue

    页面则不需要 component 中导入

    直接 <template>书写即可: <uni-nav-bar></uni-nav-bar>


第十二步:HBuildX 配置小程序渲染

  1. manifest.json 点击后基础配置–> uni-app 应用标识(AppID):注册dcloud自动生成

  2. 基础配置–>微信小程序配置–>微信小程序AppID

  3. 工具–>设置–>运行配置–>微信开发者工具路径,填入自己安装的路径

  4. 微信开发工具—设置–通用设置–安全–服务端口(最好去安装目录里启动后操作)

提示

  1. 生成后项目根目录会创建 .hbuilderx 文件夹
  2. 如果要用手机直接预览,需要去把 src/manifest.json"scope.userLocation": { "desc": ""} 注释哈,uni-app默认把地域调用打开了。但是初始项目未调用微信地域 api
  3. 如果是用 cli 命令启动项目,微信开发工具中需要导入最后打包成功的文件夹而不是整个源码文件,不然会报错 无法找到 app.json, 例如 dist/dev/mp-weixin

第十三步:配置多环境 api 接口

  1. src/config 文件夹分别创建 .env.dev.ts.env.pre.ts.env.pro.ts.env.test.tsenv.ts

    .env.x.ts 内容就是不同环境将要访问的 api 地址

    1
    2
    3
    4
    5
    export default {
    // 也就是你不同的环境将要访问的不同的服务器
    BASE_API: 'http://localhost:3300',
    ENV: 'dev',
    }

    env.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
    import dev from './.env.dev'
    import test from './.env.test'
    import pre from './.env.pre'
    import prod from './.env.pro'

    // import.meta.env 这是vite环境变量
    const NODE_ENV = import.meta.env.MODE
    console.log('NODE_ENV----', NODE_ENV)

    let ENV_VAR: { BASE_API: string } = { BASE_API: 'dev' }
    if (NODE_ENV === 'dev' || NODE_ENV === 'development') {
    console.log('开发---')
    ENV_VAR = dev
    } else if (NODE_ENV === 'test') {
    console.log('测试---')
    ENV_VAR = test
    } else if (NODE_ENV === 'pre') {
    console.log('预发布---')
    ENV_VAR = pre
    } else if (NODE_ENV === 'pro' || NODE_ENV === 'production') {
    console.log('生产---')
    ENV_VAR = prod
    }
    // else if (NODE_ENV === 'demo') {
    // ENV_VAR = demo
    // }

    export default ENV_VAR
  2. package.json 中增加命令

    这里增加即是为了让 import.meta.env.MODE 拿到对应的值

    1
    2
    3
    4
    5
    6
    "scripts": {
    ...
    "dev:weixin": "uni -p mp-weixin --mode dev",
    "test:weixin": "uni -p mp-weixin --mode test",
    "build:weixin": "uni build -p mp-weixin --mode pro"
    }
  3. api 接口常量文件 src/config/app.ts 获取 env.ts 返回的不同环境 api 地址

    1
    2
    3
    4
    import ENV_CONFIG from '@/config/env'
    console.log('不同环境不同地址---', ENV_CONFIG)
    // mock请求地址 'http://localhost:3300'
    export const HTTP_REQUEST_URL = ENV_CONFIG.BASE_API
  4. 启动项目

    1
    pnpm dev:weixin

参考文章[8]


😫小波搭建uni-app微信小程序APP工程化项目遇见的一些问题

报错

vue cli3中eslint报错“no-undef“和eslint规则配置

解决方案:

.eslintrc.jsrules 增加如下代码

1
2
3
rules: {
'no-undef': 0,
}

报错

[@vue/compiler-sfc] type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type.

原因及解决方案:因为组件中 defineProps 不支持外部导入的ts申明,只能写在组件页面中

报错

Type '{}' is not assignable to type '(props: Readonly) => object'. Type '{}' provides no match for the signature '(props: Readonly): object'.

当使用基于类型的声明时,我们失去了为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决:

解决方案:

数组,对象需要用函数返回

1
2
3
4
5
6
7
8
9
10
11
12
13
goods: () => [
{
id: 6905,
name: '',
icon: '',
sort: 1,
is_show_backstage: 0,
goods_list: [],
},
],
computedMenuCartNum: () => {
return 0
},

报错

Set "volar.inlayHints.eventArgumentInInlineHandlers": false to hide Event Argument in Inline Handlers.

上面翻译过来就是:设置“volar.inlayHints。eventArgumentInInlineHandlers": false在内联处理程序中隐藏事件参数。

解决方案:

vscode 的 setting.json 中添加

1
"editor.inlayHints.enabled": "off"

报错

Component is not found in path "pages/menu/hooks/useNav" (using by "pages/menu/index")

原因及解决方案:uniapp 如果在页面文件夹引入子 hooks 组件,则只能用 menuHook/index.vue 模式, 不然报错

报错

JSON 中不允许有注释。

解决方案:右下角的 json 点一下,然后输入 comment,下拉选择 json with comments 就可以了

报错

微信开发工具启用手机预览编译后报错小程序按需注入

解决方案:

manifest.json 中增加以下代码

1
2
3
4
"mp-weixin": {
// 小程序按需注入
"lazyCodeLoading": "requiredComponents"
}

😚直接克隆项目后安装步骤

  1. pnpm install 后, 用 HBuildX 启动(选择项目–>运行–>运行到小程序模拟器) 或者 pnpm dev:mp-weixin
  2. 微信开发工具勾选 不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书

😊来自小波的bilibili视频教程

  1. 安装搭建项目如果遇到什么问题,请留言,小波会尽快回复。
  2. 小波主参考 Cobyte 大大写的文章[3]。万分感谢 !

🙂小波用到的相关参考资料链接

关注廿壴(GANXB2)微信公众号

『旅行者』,帮小波关注一波公众号吧。

小波需要100位关注者才能申请红包封面设计资格,万分感谢!

关注后可微信小波,前66的童鞋可以申请专属红包封面设计。

THE END
作者
chopin gump chopin gump
小尾巴
Stay Hungry, Stay Foolish「求知若饥, 虚心若愚」 — 廿壴(GANXB2)
许可协议
如何使用vue3+vite+typescript+pinia+uni-ui+pnpm从0到1创建uni-app多端小程序APP工程化项目
https://blog.ganxb2.com/3462.html
微信

微信

支付宝

支付宝

- ( ゜- ゜)つロ 填写QQ邮箱自动获取头像亦更快获得回复通知
评论区显示“刷新”,多点几下或过会儿再来康康 _(≧∇≦」∠)_