框架设计

官方框架设计

平台入口

APP以/src/main.ts为入口

h5以/index.html为入口

index.html

安全区域概念

viewport-fit概念

viewport-fit=cover 内容可以延伸到安全区域

既然viewport-fit=cover内容可以延伸到安全区域内,那么它就需要通过css设置安全区域范围避开遮挡。

css概念env和constant
// index.html
<script>
      var coverSupport =
        'CSS' in window &&
        typeof CSS.supports === 'function' &&
        (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'));
      document.write(
        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
          (coverSupport ? ', viewport-fit=cover' : '') +
          '" />'
      );
</script>

看到如果设置viewport-fit=cover,需要浏览器支持env和constant

支持:返回像素值(如 44px、34px)
不支持:返回 0px

适应viewport-fit=cover的css配置
// uno.config.ts
...
rules: [
    // 提供一个类 它能够将调整内边距使内容不会在安全区域内
    [
      'p-safe',
      {
        padding:
          'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)'
      }
    ]
  ]
<!-- 全方向安全区域内边距 -->
<div class="p-safe">内容</div>

其他例子:

框架运行

编译依赖(这些代码会被编译进最终产物)
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";

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

vite只用到了@dcloudio/vite-plugin-uni插件。这个插件在不同平台的环境里编译会根据scripts里的配置动态引入依赖:

// package.json 用-p指定依赖
// build命令同理
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:app-harmony": "uni -p app-harmony",
"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:mp-xhs": "uni -p mp-xhs",
"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",
pnpm dev:h5          # 只加载 @dcloudio/uni-h5
pnpm dev:mp-weixin   # 只加载 @dcloudio/uni-mp-weixin  
pnpm dev:app         # 只加载 @dcloudio/uni-app-plus

这些依赖包裹着对应不同环境平台的处理逻辑:

为什么需要这么多依赖?
平台差异巨大:每个平台的原生 API、组件、渲染机制都不同
按需加载:避免打包时包含所有平台的代码,减小包体积
统一开发体验:开发者只需要写一套代码,框架负责转换
性能优化:每个平台都有针对性的优化
开发依赖

@dcloudio/types

用途: 为uni-app提供完整的TypeScript类型支持,包括API、组件、生命周期等类型定义

@vue/tsconfig

作用: 提供Vue 3项目的标准TypeScript配置,包括编译选项和路径映射

// tsconfig.json
{
   // 继承标准ts配置
  "extends": "@vue/tsconfig/tsconfig.json",
  "compilerOptions": {
    "sourceMap": true,
    "baseUrl": ".",   // 模块解析基准目录
    "paths": {
      "@/*": ["./src/*"]
    },
    "lib": ["esnext", "dom"], // 可用的内置类型库 使ts能够识别这些api
    "types": ["@dcloudio/types"] // 仅加载 uni-app 类型定义,不加载其他 @types/* 包
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

@dcloudio/vite-plugin-uni

将uni-app框架集成到Vite构建系统中,处理多端编译和资源转换

@dcloudio/uni-cli-shared

构建时:源码 → Vite 插件(@dcloudio/vite-plugin-uni) → 通用基建(@dcloudio/uni-cli-shared) → 平台实现(@dcloudio/uni-h5 或 @dcloudio/uni-mp-) → 目标产物

平台实现层虽然在处理不同环境下的转换逻辑各不相同,但是其底层的处理逻辑是一致的(比如条件编译处理等),这些可以看作是基础能力,被封装在@dcloudio/uni-cli-shared

@dcloudio/uni-stacktracey

vue-tsc

src/shime-uni.d.ts

怎么生效的?

// tsconfig.json 
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]

作用:是一个 TypeScript 声明文件,用于在 uni-app 中扩展 Vue 组件的类型,让组件能使用 uni-app 的生命周期钩子

注意,App和Page都是uniapp的生命周期。只是把它们两者糅合在一起再加到vue的生命周期里

问题一:

declare module "vue"不会覆盖原先vue模块的定义吗?

问题二:这些全局命名都在哪里被定义?

答案是@dcloudio/types(不包括vue的任何内容)和@vue/runtime-core

问题三:@vue/runtime-core的ts定义是在什么时候被加载的?

以下三种情况会去找@vue/runtime-core的ts定义

我的框架设计

package.json

dependencies
  "dependencies": {
    "@dcloudio/uni-app": "3.0.0-alpha-4010520240507001", 
    "@dcloudio/uni-app-plus": "3.0.0-alpha-4010520240507001",
    "@dcloudio/uni-components": "3.0.0-alpha-4010520240507001",
    "@dcloudio/uni-h5": "3.0.0-alpha-4010520240507001",
    "@dcloudio/uni-mp-weixin": "3.0.0-alpha-4010520240507001",
    "dayjs": "1.11.10", // 工具函数
    "pinia": "2.0.36", //状态管理器
    "pinia-plugin-persistedstate": "3.2.1", // 见下文
    "qs": "6.5.3", // 格式化url请求,作用请见下文的http封装
    "vue": "3.4.21",
    "wot-design-uni": "^1.2.26", // ui库,不用它
  },
pinia-plugin-persistedstate

是一个 Pinia 状态持久化插件,用于将 Pinia store 的数据自动保存到本地存储中,页面刷新或应用重启后数据不会丢失。

// 初始化pinia
import { createPinia } from 'pinia';

const pinia = createPinia();

import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);

app.use(pinia)

开启持久化功能:

...
const pinia = createPinia();
import { createPersistedState } from 'pinia-plugin-persistedstate'; 
pinia.use(
  createPersistedState({
    storage: {
      getItem: uni.getStorageSync, // uni提供的从本地缓存中同步获取指定 key 对应的内容
      setItem: uni.setStorageSync // uni提供的从本地缓存中同步存储指定 key 对应的内容
    }
  })
);
...

某个状态仓库启用持久化:

devDependencies

只讲新增部分

{
    ...,
    "@commitlint/cli": "^18.6.1",  // 见提交规范章节
    "@commitlint/config-conventional": "^18.6.3", // 见提交规范章节
    "@esbuild/darwin-arm64": "0.20.2", // 下文
    "@esbuild/darwin-x64": "0.20.2",    // 下文
    "@iconify-json/carbon": "^1.1.35", // uno库配置需要使用
    "@rollup/rollup-darwin-x64": "^4.18.0", // 与@esbuild同理
    "@types/node": "^20.14.2", // node类型定义包
    "@typescript-eslint/eslint-plugin": "^6.21.0", // 见eslint配置
    "@typescript-eslint/parser": "^6.21.0", // 见eslint配置
    "@uni-helper/vite-plugin-uni-manifest": "^0.2.6",// 见unihelper
    "@uni-helper/vite-plugin-uni-pages": "0.2.20",// 见unihelper
    "@uni-helper/vite-plugin-uni-platform": "^0.0.4",// 见unihelper
    "@vue/runtime-core": "^3.4.29",
    "commitlint": "^18.6.1",
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.1.0", // 见eslint配置
    "eslint-config-standard": "^17.1.0", // 见eslint配置
    "eslint-import-resolver-typescript": "^3.6.1", // 见eslint配置
    "eslint-plugin-import": "^2.29.1", // 见eslint配置
    "eslint-plugin-prettier": "^5.1.3", // 见eslint配置
    "eslint-plugin-vue": "^9.26.0", // 见eslint配置
    "husky": "^8.0.3",
    "lint-staged": "^15.2.7",
    "postcss": "^8.4.38", // 见postcss
    "postcss-html": "^1.7.0", // 见postcss
    "postcss-scss": "^4.0.9", // 见postcss
    "rollup-plugin-visualizer": "^5.12.0", //打包分析工具,见下文
    "sass": "^1.77.5",
    "stylelint": "^16.6.1", // 见stylelint
    "stylelint-config-html": "^1.1.0", // 见stylelint
    "stylelint-config-recess-order": "^4.6.0", // 见stylelint
    "stylelint-config-recommended": "^14.0.0", // 见stylelint
    "stylelint-config-recommended-scss": "^14.0.0", // 见stylelint
    "stylelint-config-recommended-vue": "^1.5.0", // 见stylelint
    "stylelint-prettier": "^5.0.0", // 见stylelint
    "terser": "^5.31.1",  // 代码压缩
    "typescript": "^4.9.5",
    "unocss": "^0.58.9", // 见uno
    "unocss-applet": "^0.7.8",  // 见uno
    "vite-plugin-restart": "^0.4.0", // 见下方
    "vue-tsc": "^1.8.27"
}
@esbuild/xxx

@esbuild/darwin-arm64 -> M1/M2 Mac

@esbuild/darwin-x64 -> Intel Mac

显式声明,但在vite配置或者整个项目里不会被引用,Vite在编译时自然会去找它们。它们不影响window、linux等其他环境的工作,只是统一了在可能出现问题的平台环境下指定依赖的版本。

@unihelper

@uni-helper/vite-plugin-uni-pages 页面管理

自动根据配置文件和vue文件里的路由生成src/pages.json。不再需要手动书写了。

启用:

// vite.config.ts
import UniPages from '@uni-helper/vite-plugin-uni-pages'
import Uni from '@dcloudio/vite-plugin-uni'
export default ({ command, mode }) => {
    plugins: [
        UniPages(
            exclude: ['**/components/**/**.*'], //这些目录下的文件导出的路由排除在外
            routeBlockLang: 'json5', //指定 Vue 文件中 <route> 块使用的语言格式 可以是json或json5
            dts: 'src/types/uni-pages.d.ts', // 自动生成页面声明文件,影响uni跳转路由传参
        )
        // UniXXX 需要在 Uni 之前引入
        Uni(),
    ]
}

routeBlockLang:

vue里的lang其实可以不用写,因为vite已经配置了json5格式解析。vue里的lang其实只是为了IDE语法高亮。

// 首页
<route lang="json5" type="home">
{
  style: { navigationBarTitleText: '首页', navigationStyle: 'custom' }
}
</route>

// 页面
<route lang="json5" type="page">
{
  style: { navigationBarTitleText: '横屏功率谱', navigationStyle: 'custom' }
}
</route>

src/types/uni-pages.d.ts

当我们声明一个页面时,uniPage插件就会自动生成一份d.ts文件保存到src/types里。

这个types会被ts识别到并更新Uni里的四个关于路由的api的传参声明:

pages.config.ts请见下文专门讲解

@uni-helper/vite-plugin-uni-platform (平台判断)

plugins: [
    UniPlatform(),
    Uni(),
    ...
]

挂载插件后,可以:

注意,只能在node环境下通过UniPlatform获取环境变量。想在项目里用只能注入为全局变量

@uni-helper/vite-plugin-uni-manifest (清单配置)

使用 TypeScript 配置 manifest.json,提供类型提示和校验

plugins: [
    UniManifest(),
    Uni(),
    ...
]

/manifest.config.ts见下文

rollup-plugin-visualizer

打包分析工具,可视化查看项目打包后各个文件的大小占比。

// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default ({ command, mode }) => {
    ...,
    return defineConfig({
        plugins: [
            Uni(),
            UNI_PLATFORM === 'h5' && mode === 'production' &&
            visualizer({
              filename: './node_modules/.cache/visualizer/stats.html', // 报告保存位置
              open: true, // ← 构建完成后自动打开浏览器
              gzipSize: true,  // ← 显示 gzip 压缩后的大小
              brotliSize: true, // ← 显示 brotli 压缩后的大小
            }),
        ]
    })
}

vite-plugin-restart

用于监听配置文件的变化,自动重启 Vite 开发服务器。

plugins: [
    Uni(),
    ViteRestart({
        // 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
        restart: ['vite.config.js'],
    }),
]

scripts
"preinstall": "npx only-allow pnpm",

在install前限制用户只能用pnpm安装包依赖

"uvm": "npx @dcloudio/uvm@latest",
"uvm-rm": "node ./scripts/postupgrade.js",
"postuvm": "echo upgrade uni-app success!",

uni-app 版本管理(Version Manager)简称U-VM

npx @dcloudio/uvm@latest =>将uni-app 框架及相关依赖升级到最新版本

也就是因为升级会自动安装不必要的依赖。所以有了uvm-rm命令。使用脚本清除依赖

// node环境 postupgrade.js
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { exec } = require('child_process')

// 定义要执行的命令
const dependencies = [
  '@dcloudio/uni-app-harmony',
  // TODO: 如果需要某个平台的小程序,请手动删除或注释掉
  '@dcloudio/uni-mp-alipay',
  '@dcloudio/uni-mp-baidu',
  '@dcloudio/uni-mp-jd',
  '@dcloudio/uni-mp-kuaishou',
  '@dcloudio/uni-mp-lark',
  '@dcloudio/uni-mp-qq',
  '@dcloudio/uni-mp-toutiao',
  '@dcloudio/uni-mp-xhs',
  '@dcloudio/uni-quickapp-webview',
  // i18n模板要注释掉下面的
  'vue-i18n',
]

// 使用exec执行命令
exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
  if (error) {
    // 如果有错误,打印错误信息
    console.error(`执行出错: ${error}`)
    return
  }
  // 打印正常输出
  console.log(`stdout: ${stdout}`)
  // 如果有错误输出,也打印出来
  console.error(`stderr: ${stderr}`)
})
resolutions

bin-wrapper是个常用插件,虽然项目未用到,但是方便后续用户扩展,预留了这行配置。

manifest.config.ts

node环境下读取的配置文件。

只要在vite.config.ts里配置了plugins(UniManifest()),就会自动读取根目录下的manifest.config.ts

// 如果需要js
UniManifest({
  input: 'manifest.config.js'  // 显式指定 JS 配置文件
})

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐