一套代码多端运行TinyVue响应式开发

一套代码多端运行:TinyVue PC + 移动端响应式开发

有人说:"做 PC 端像建别墅------空间大、功能多、细节丰富;做移动端像装修小户型------寸土寸金、每个像素都要精打细算。" 如果你要同时做两端,最痛苦的不是两端各自开发,而是"改了一个 bug,两端要改两次"------就像你有两套房子,修水管要修两次。TinyVue 的多端开发方案,目标就是"一套代码,两端运行"------你只修一次水管,两套房子同时修好。

一、TINY_MODE:多端模式的"开关钥匙"

1.1 三种运行模式

TinyVue 从 v3.17.0 开始提供三种 runtime 模式,通过 TINY_MODE 环境变量控制:

模式 说明 适用场景
PC 模式 pc 传统桌面端组件 企业后台、管理系统
移动优先模式 mobile-first 移动端优化的组件 出海 App、消费级产品
简约模式 mobile 简化的移动端组件 轻量级移动页面

这三种模式的区别不仅仅是"尺寸适配"------每种模式对应的是完全不同的组件 runtime 实现。PC 模式的 Tree 组件有完整的勾选、拖拽、搜索功能;mobile-first 模式的 Tree 组件则针对触摸交互做了优化,手势操作更友好。

1.2 Runtime 文件说明

从 v3.17.0 开始,TinyVue 提供了多种 runtime 入口文件:

Runtime 文件 对应模式 说明
tiny-vue-pc.mjs PC 桌面端完整功能
tiny-vue-mobile-first.mjs Mobile-First 移动端优先设计
tiny-vue-simple.mjs Simple 简化版,适合轻量场景

每个 runtime 文件包含了对应模式下所有组件的实现。你选择哪个 runtime,就决定了你的应用运行在哪种模式下------就像你选了哪个钥匙,就打开了哪扇门。

二、Vite 配置:设定你的运行模式

2.1 基础配置

在 Vite 项目中,通过 define 选项设定 TINY_MODE

javascript 复制代码
// vite.config.js - PC 模式
export default defineConfig({
  define: {
    'process.env': {
      TINY_MODE: 'pc'
    }
  }
})
javascript 复制代码
// vite.config.js - Mobile-First 模式
export default defineConfig({
  define: {
    'process.env': {
      TINY_MODE: 'mobile-first'
    }
  }
})

就这么一行配置,整个项目的组件渲染模式就定了。PC 模式下 <tiny-button> 渲染的是桌面端风格的按钮;mobile-first 模式下 <tiny-button> 渲染的是移动端风格的按钮------同一个组件名,不同模式下渲染不同实现

2.2 按需加载插件配置

@opentiny/vue-vite-import 是 TinyVue 的按需加载插件,它也支持模式选择:

javascript 复制代码
// vite.config.js
import importPlugin from '@opentiny/vue-vite-import'

export default defineConfig({
  plugins: [
    importPlugin({
      // 指定模式:pc | mobile | mobile-first
      mode: 'pc'
    })
  ],
  define: {
    'process.env': {
      TINY_MODE: 'pc'
    }
  }
})

mode 参数决定了按需加载时引用哪个 runtime 的组件代码。如果不指定,默认使用 PC 模式。

💡 defineTINY_MODE 和插件的 mode 要保持一致------就像方向盘和导航仪必须指向同一个目的地,否则你就是在"左转灯亮着但车往右拐"。

三、同一套代码,两端渲染

3.1 核心思路

TinyVue 多端开发的核心思路是:业务代码一份,组件实现多份

你的业务代码(页面结构、数据逻辑、交互流程)是相同的------订单列表页面不管是 PC 还是手机,都有"创建订单"、"查看详情"、"删除订单"这些操作。但组件的渲染实现根据 TINY_MODE 自动切换:

vue 复制代码
<!-- 这段代码在 PC 和移动端完全相同 -->
<template>
  <div class="order-page">
    <tiny-button type="primary" @click="handleCreate">
      {{ t('order.create') }}
    </tiny-button>

    <tiny-grid :data="orderList">
      <tiny-grid-column field="name" :title="t('order.name')" />
      <tiny-grid-column field="status" :title="t('order.status')" />
    </tiny-grid>
  </div>
</template>
  • PC 模式:<tiny-grid> 渲染完整的桌面端表格------固定列、排序、筛选、分页全功能
  • mobile-first 模式:<tiny-grid> 渲染移动端优化的列表卡片------触摸滑动、下拉刷新、底部加载

你不需要写两套模板 ------同一个 <tiny-grid> 在不同模式下自动变身。

3.2 实战项目结构

来一个完整的项目结构示例:

bash 复制代码
order-management/
├── src/
│   ├── views/
│   │   ├── OrderList.vue      # 共用的订单列表页面
│   │   ├── OrderDetail.vue    # 共用的订单详情页面
│   │   └── OrderCreate.vue    # 共用的创建订单页面
│   ├── components/
│   │   ├── OrderCard.vue      # 共用的订单卡片组件
│   │   └── StatusTag.vue      # 共用的状态标签组件
│   ├── locales/
│   │   ├── zhCN.js            # 中文翻译
│   │   ├── enUS.js            # 英文翻译
│   │   └── esLA.js            # 西班牙语翻译
│   ├── theme/
│   │   └── brand-theme.js     # 品牌主题配置
│   ├── main.js                # 应用入口
│   └── App.vue                # 根组件
├── vite.config.pc.js          # PC 端构建配置
├── vite.config.mobile.js      # 移动端构建配置
├── package.json

关键点:viewscomponents 只有一份,没有 views-pc/views-mobile/ 这样的分裂目录。两端差异通过 TINY_MODE 配置解决,而不是通过文件复制。

3.3 构建配置分离

虽然代码一份,但构建配置需要分两份------因为 PC 和移动端的构建目标不同:

javascript 复制代码
// vite.config.pc.js - PC 端构建
export default defineConfig({
  define: {
    'process.env': {
      TINY_MODE: 'pc'
    }
  },
  plugins: [
    importPlugin({ mode: 'pc' })
  ],
  build: {
    outDir: 'dist-pc',
    // PC 端不需要特别小的包体积
  }
})
javascript 复制代码
// vite.config.mobile.js - 移动端构建
export default defineConfig({
  define: {
    'process.env': {
      TINY_MODE: 'mobile-first'
    }
  },
  plugins: [
    importPlugin({ mode: 'mobile-first' })
  ],
  build: {
    outDir: 'dist-mobile',
    // 移动端需要更小的包体积
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-i18n']
        }
      }
    }
  }
})

然后在 package.json 中配置两个构建命令:

json 复制代码
{
  "scripts": {
    "build:pc": "vite build --config vite.config.pc.js",
    "build:mobile": "vite build --config vite.config.mobile.js",
    "build:all": "npm run build:pc && npm run build:mobile"
  }
}

运行 npm run build:all 就同时构建出两端的产物------PC 端在 dist-pc/,移动端在 dist-mobile/

四、响应式适配:不只是"换组件"

4.1 CSS 响应式

虽然组件自动切换了,但你的页面布局(导航、侧边栏、内容区域的排列)还需要响应式适配。这是 CSS 层面的事,和 TinyVue 的 TINY_MODE 无关:

css 复制代码
/* PC 端:左右布局 */
.order-page {
  display: flex;
}

.order-page .sidebar {
  width: 240px;
}

.order-page .main-content {
  flex: 1;
}

/* 移动端:上下布局 */
@media (max-width: 768px) {
  .order-page {
    flex-direction: column;
  }

  .order-page .sidebar {
    width: 100%;
    display: none; /* 移动端隐藏侧边栏,用底部导航替代 */
  }
}

💡 TINY_MODE 解决的是"组件交互方式"的差异,CSS 响应式解决的是"页面布局"的差异。两者配合才能真正做到"一套代码多端适配"------就像换车和换路:TINY_MODE 换了车(PC 大车 → 移动小车),CSS 响应式换了路(宽阔公路 → 狭窄巷道)。

4.2 条件渲染:极端差异场景

有些功能在 PC 端有意义,但在移动端完全不需要(比如高级筛选面板、批量操作按钮)。这时候用条件渲染:

vue 复制代码
<template>
  <div class="order-page">
    <!-- PC 端才显示的高级筛选 -->
    <tiny-filter-panel v-if="isPC" :fields="filterFields" />

    <!-- 移动端才显示的底部操作栏 -->
    <tiny-action-sheet v-if="isMobile" :actions="actions" />

    <!-- 两端都显示的核心内容 -->
    <tiny-grid :data="orderList">
      <tiny-grid-column field="name" :title="t('order.name')" />
      <!-- PC 端显示更多列 -->
      <tiny-grid-column v-if="isPC" field="manager" :title="t('order.manager')" />
    </tiny-grid>
  </div>
</template>

<script>
export default {
  computed: {
    isPC() {
      return process.env.TINY_MODE === 'pc'
    },
    isMobile() {
      return process.env.TINY_MODE === 'mobile-first' || process.env.TINY_MODE === 'mobile'
    }
  }
}
</script>

注意:条件渲染用的是 process.env.TINY_MODE------这个值在构建时就被 Vite 的 define 替换成了字符串常量,所以不会有运行时开销。不用的代码在构建时直接被剔除,就像你没带冬季外套去热带旅行------行李箱里压根就没有这件衣服。

五、高级技巧:动态模式切换

有些产品需要"在一个应用内切换 PC/移动布局"------比如平板设备可以横屏(PC布局)竖屏(移动布局)切换。这时候需要在运行时动态切换 TINY_MODE。

5.1 基于屏幕尺寸自动切换

javascript 复制代码
// utils/responsive.js
export function getDeviceMode() {
  const width = window.innerWidth
  return width >= 1024 ? 'pc' : 'mobile-first'
}

export function watchModeChange(callback) {
  const mql = window.matchMedia('(min-width: 1024px)')
  mql.addEventListener('change', (e) => {
    callback(e.matches ? 'pc' : 'mobile-first')
  })
}

5.2 结合路由的模式切换

javascript 复制代码
// router.js
import { getDeviceMode, watchModeChange } from './utils/responsive'

const router = createRouter({
  routes: [
    // 同一个路由,不同模式下渲染效果不同
    { path: '/orders', component: () => import('./views/OrderList.vue') }
  ]
})

// 监听屏幕尺寸变化,通知应用更新模式
watchModeChange((mode) => {
  // 这里可以触发全局状态更新
  // 注意:TINY_MODE 是构建时常量,运行时切换需要配合条件渲染
  console.log('模式切换建议:', mode)
})

⚠️ TINY_MODE 是构建时的静态值,不能在运行时动态修改。运行时模式切换需要通过条件渲染 + CSS 响应式来实现,而不是修改 process.env.TINY_MODE。如果你确实需要运行时动态切换组件实现,建议分别构建两端应用,通过路由分发。

六、性能优化:移动端打包策略

移动端对包体积更敏感------用户在 4G 网络下等你的页面加载,每多 100KB 就多 1 秒的等待时间。TinyVue 提供了多种优化手段:

6.1 按需加载

javascript 复制代码
// vite.config.mobile.js
import importPlugin from '@opentiny/vue-vite-import'

export default defineConfig({
  plugins: [
    importPlugin({
      mode: 'mobile-first'  // 只加载移动端组件实现
    })
  ]
})

按需加载只打包你用到的组件------就像搬家只带必需品,不带整屋家具。

6.2 选择简约模式

如果你的移动端页面功能简单(比如一个注册页、一个结果展示页),可以用 tiny-vue-simple.mjs runtime------它是 TinyVue 的精简版,只包含核心组件:

javascript 复制代码
// vite.config.js - 简约模式
export default defineConfig({
  define: {
    'process.env': {
      TINY_MODE: 'mobile'
    }
  }
})

简约模式就像经济舱------功能够用、体积更小、价格更低。如果你的移动端需要完整功能(复杂表格、树形控件、图表),那就选 mobile-first 模式------商务舱,功能全、体验好、体积稍大。

6.3 包体积对比参考

Runtime 预估体积 适用场景
tiny-vue-pc.mjs 较大 企业后台全功能
tiny-vue-mobile-first.mjs 中等 消费级移动应用
tiny-vue-simple.mjs 较小 轻量级移动页面

实际体积取决于你用了多少组件。按需加载 + mobile-first 模式 + Tree Shaking,最终打包体积可以压到很小------就像旅行前仔细规划行李,只带真正需要的。

七、完整实战:订单管理系统多端适配

来一个完整的入口配置,把国际化 + 主题 + 多端全串起来:

javascript 复制代码
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { initI18n } from '@opentiny/vue-locale'
import { TinyThemeTool } from '@opentiny/vue'
import { applyBrandTheme } from './theme/brand-theme'
import zhCN from './locales/zhCN'
import enUS from './locales/enUS'

// 1. 国际化初始化
const savedLocale = localStorage.getItem('app-locale') || 'zhCN'
const i18n = initI18n({
  locale: savedLocale,
  fallbackLocale: 'enUS',
  messages: { zhCN, enUS }
})

// 2. 主题初始化
applyBrandTheme()

// 3. 创建应用
const app = createApp(App)
app.use(i18n)
app.mount('#app')
vue 复制代码
<!-- App.vue -->
<template>
  <div :class="['app-container', modeClass]">
    <!-- 语言切换器 -->
    <language-switcher />

    <!-- 主题切换 -->
    <tiny-switch v-model="isDark" @change="toggleDark" /> 深色模式

    <!-- 页面内容 -->
    <router-view />
  </div>
</template>

<script>
import { computed } from 'vue'
import { Switch } from '@opentiny/vue'
import LanguageSwitcher from './components/LanguageSwitcher.vue'
import { toggleDarkMode } from './theme/brand-theme'

export default {
  components: {
    TinySwitch: Switch,
    LanguageSwitcher
  },
  setup() {
    const modeClass = computed(() => {
      return process.env.TINY_MODE === 'pc' ? 'pc-layout' : 'mobile-layout'
    })

    const isDark = ref(false)
    const toggleDark = (val) => {
      if (val) {
        toggleDarkMode()
      } else {
        applyBrandTheme()
      }
    }

    return { modeClass, isDark, toggleDark }
  }
}
</script>

<style>
.pc-layout {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.mobile-layout {
  padding: 12px;
}
</style>

一份代码、一套逻辑、两端适配------国际化帮你走向世界,主题帮你打造品牌,多端模式帮你覆盖所有屏幕。三个能力叠加,你的产品就是"全球通用、品牌专属、全屏适配"的满分选手。

八、多端开发 Checklist

检查项 是否完成
Vite define 中设置了 TINY_MODE? pc / mobile / mobile-first
按需加载插件 mode 和 TINY_MODE 一致? 同步配置
选了正确的 runtime 文件? pc / mobile-first / simple
CSS 响应式处理了布局差异? @media 断点
条件渲染处理了功能差异? v-if="isPC" / v-if="isMobile"
国际化初始化包含多语言? initI18n
构建命令分离了两端? build:pc / build:mobile
移动端做了包体积优化? 按需加载 + simple runtime

多端开发的终极目标不是"写两套代码",而是"写一套代码,自动适配两端"。TinyVue 的 TINY_MODE 机制做到了组件层面的自动切换------你只管写业务逻辑,组件渲染交给模式配置。再加上 CSS 响应式和条件渲染,你的应用在 PC 上是"大屏豪华版",在手机上是"小巧精致版",但底层代码只有一份。修一次 bug,两端同时修好------这才是多端开发该有的效率。


🏠 TinyVue 官网:opentiny.design 📦 GitHub 仓库:github.com/opentiny/ti...

相关推荐
SwJieJie2 小时前
Webpack vs Vite 构建工程化实战(Vue 项目深度解析)
前端·vue.js·webpack·node.js
云水一下2 小时前
Vue.js从零到精通系列(八):项目实战——构建一个完整的电商后台管理系统
前端·javascript·vue.js
Csvn2 小时前
Vue3 响应式陷阱:解构赋值后页面不动了?Proxy 的"隐形成员"在搞鬼
前端·vue.js
i220818 Faiz Ul2 小时前
药店管理|基于springboot + vue药店管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·美食分享系统
daols883 小时前
vue vxe-table 复制数据到 Excel:支持带表头复制
vue.js·excel·vxe-table
代码不加糖14 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
懂懂tty15 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
老毛肚15 小时前
软件测试期末考试
vue.js
杨若瑜16 小时前
本地开发环境慢?localhost的锅!
vue.js