我们可以从实际开发流程的每个环节,详细对比 uni-app 和 Vue 的核心差异。两者虽语法同源,但因定位(通用Web vs 跨端)不同,在项目结构、API、组件、路由等方面存在本质区别。
🧠 开发思路
Vue
- 核心思路:围绕 Web DOM 构建单页应用(SPA),专注于Web端的组件化开发,通过虚拟DOM优化渲染效率。
- 关注点:Web端的交互体验、浏览器兼容性、前端工程化(如构建工具、代码分割)。
uni-app
- 核心思路:“一套代码,多端运行”,基于Vue语法,但需同时兼容Web、小程序、App等多端特性。
- 关注点:跨端兼容性(条件编译)、各端原生能力调用、性能优化(如App端原生渲染、小程序分包)。
🔑 核心概念
Vue
- 核心概念:组件化、虚拟DOM、响应式数据(Proxy/Object.defineProperty)、Vue Router(路由)、Pinia(状态管理)。
- 渲染底层:依赖Web DOM,通过操作DOM元素更新页面。
uni-app
- 核心概念:除Vue的核心概念外,新增:
- 条件编译:通过 #ifdef、#ifndef 等指令,为不同端编写专属代码。
- 原生组件:view、text、image 等跨端组件(替代Web的 div、span)。
- 页面生命周期:onLoad、onShow、onHide 等(兼容小程序/App的页面逻辑)。
- 渲染底层:
- Web端:Web DOM(同Vue);
- 小程序端:转换为对应平台的原生组件;
- App端:默认WebView渲染,可选 uvue原生渲染(直接调用iOS/Android原生控件)。
🚀 第一步:项目初始化
📁 1. 创建项目目录
Vue
- 创建工具:Vite(推荐)或 Vue CLI。
- 命令示例:npm create vite@latest my-vue-app — –template vue
- 初始目录:my-vue-app/
├── src/
│ ├── components/ # 组件
│ ├── views/ # 页面
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── index.html # HTML模板
└── package.json
uni-app
- 创建工具:HBuilderX(官方推荐,可视化操作)或 uni CLI(命令行)。
- 命令示例(uni CLI):npx degit dcloudio/uni-preset-vue#vite my-uni-app
- 初始目录(跨端特有结构):my-uni-app/
├── src/
│ ├── pages/ # 页面(需在pages.json注册)
│ ├── static/ # 静态资源(图片、字体等)
│ ├── components/ # 组件
│ ├── App.vue # 应用配置(生命周期、全局样式)
│ ├── main.js # 入口文件
│ └── pages.json # 【核心】页面路由、窗口样式、tabbar配置
├── manifest.json # 【核心】各端配置(AppID、权限、原生插件)
└── package.json
📦 2. 项目结构规划
Vue
- 结构逻辑:按Web应用功能划分(如 views、components、router、store),无需考虑跨端。
- 关键文件:router/index.js(路由配置)、store/index.js(状态管理)。
uni-app
- 结构逻辑:按跨端需求划分,需重点关注:
- pages.json:统一管理所有端的页面路由、窗口样式(如导航栏颜色、下拉刷新)。
- manifest.json:配置各端的专属信息(如微信小程序AppID、Android权限)。
- 条件编译目录:可创建 platforms/mp-weixin 等目录,存放端专属代码。
📦 第二步:安装项目依赖
🔧 1. 安装生产依赖
Vue
- 核心依赖:vue、vue-router(路由)、pinia(状态管理)。
- 可选依赖:axios(HTTP请求)、element-plus(Web UI库)、sass(CSS预处理器)。
- 示例:npm install vue vue-router pinia axios
uni-app
- 核心依赖:@dcloudio/uni-app(uni-app核心)、@dcloudio/uni-mp-weixin(小程序端适配)等(根据目标端选择)。
- 可选依赖:
- HTTP请求:不能直接用 axios(部分端不支持),需用 uni.request 或封装 @dcloudio/uni-ajax。
- UI库:需用跨端UI库,如 uView Plus、uni-ui(替代Web的Element Plus)。
- 状态管理:仍可用 pinia,但需配合 uni.setStorageSync 做跨端持久化。
- 示例:npm install @dcloudio/uni-app uview-plus pinia
🔧 2. 安装开发依赖
Vue
- 核心依赖:vite(构建工具)、@vitejs/plugin-vue(Vue插件)、eslint、prettier。
- 示例:npm install -D vite @vitejs/plugin-vue eslint prettier
uni-app
- 核心依赖:@dcloudio/vite-plugin-uni(uni-app构建插件)、@dcloudio/types(uni-app类型定义)。
- 示例:npm install -D @dcloudio/vite-plugin-uni @dcloudio/types
🤔 为什么选择这些技术栈?
Vue
- 选择 vue-router、pinia:Web生态成熟,专注于单页应用的路由和状态管理。
- 选择 axios:Web端HTTP请求的标准库,支持拦截器、取消请求等高级功能。
uni-app
- 选择 uni.request 替代 axios:兼容所有端(Web、小程序、App),无需处理跨端适配。
- 选择 uView Plus:专为uni-app设计,组件样式和API在各端一致。
- 选择 pinia + uni.setStorageSync:既保留Vue的状态管理逻辑,又能实现跨端数据持久化。
⚙️ 第三步:配置开发环境
📝 1. 更新package.json的脚本
Vue
- 脚本逻辑:仅针对Web端,分为开发、构建、预览。
- 示例:{
"scripts": {
"dev": "vite", // 启动Web开发服务器
"build": "vite build", // 构建Web生产版本
"preview": "vite preview"// 预览构建结果
}
}
uni-app
- 脚本逻辑:按端拆分,每个端有独立的开发和构建命令。
- 示例:{
"scripts": {
"dev:h5": "uni -p h5", // 开发H5端
"dev:mp-weixin": "uni -p mp-weixin", // 开发微信小程序
"dev:app": "uni -p app", // 开发App端
"build:h5": "uni build -p h5", // 构建H5端
"build:mp-weixin": "uni build -p mp-weixin", // 构建微信小程序
"build:app": "uni build -p app" // 构建App端
}
}
📝 2. 创建Prettier配置文件
Vue & uni-app
- 配置基本一致:都可通过 .prettierrc 配置代码格式化规则。
- uni-app额外注意:需在配置中忽略 manifest.json、pages.json 等uni-app核心配置文件(避免格式化破坏结构)。
- 示例:{
"semi": false,
"singleQuote": true,
"ignorePath": ".gitignore"
}
📝 3. 创建环境变量文件
Vue
- 文件命名:.env(通用)、.env.development(开发)、.env.production(生产)。
- 变量前缀:需以 VITE_ 开头(Vite规定)。
- 示例(.env.development):VITE_API_BASE_URL=http://localhost:3000/api
uni-app
- 文件命名:支持按端拆分,如 .env.mp-weixin(微信小程序环境)、.env.app(App端环境)。
- 变量前缀:需以 VITE_ 或 UNI_ 开头。
- 示例(.env.mp-weixin):UNI_API_BASE_URL=https://api.example.com/mp
🎯 第四步:创建基础类型定义
📝 1. 创建通用类型
Vue
- 类型范围:针对Web API和Vue生态,如 Router、PiniaStore、AxiosResponse。
- 示例(src/types/index.ts):export interface User {
id: number;
name: string;
}export interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
uni-app
- 类型范围:除Vue的类型外,需包含 uni-app API类型(如 UniRequestOptions、UniLoginSuccess)和 端专属类型。
- 示例(src/types/index.ts):// 引入uni-app类型定义
import type { UniRequestOptions } from '@dcloudio/types';export interface User {
id: number;
name: string;
}// 封装uni.request的响应类型
export interface UniApiResponse<T> {
code: number;
data: T;
message: string;
}
🔧 第五步:创建API服务层
📝 1. 创建HTTP客户端配置
Vue
- 实现方式:用 axios 封装,支持拦截器、baseURL配置。
- 示例(src/utils/request.ts):import axios from 'axios';
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
});// 请求拦截器
request.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});// 响应拦截器
request.interceptors.response.use(
res => res.data,
err => Promise.reject(err)
);export default request;
uni-app
- 实现方式:用 uni.request 封装(不能用axios),需兼容各端的请求逻辑。
- 示例(src/utils/request.ts):const baseURL = import.meta.env.UNI_API_BASE_URL;
const request = <T>(options: UniRequestOptions): Promise<UniApiResponse<T>> => {
return new Promise((resolve, reject) => {
uni.request({
url: baseURL + options.url,
method: options.method || 'GET',
data: options.data,
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${uni.getStorageSync('token')}` || ''
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data as UniApiResponse<T>);
} else {
reject(res);
}
},
fail: (err) => reject(err)
});
});
};export default request;
📝 2. 创建认证服务
Vue
- 实现方式:用封装好的 axios 调用登录、注册接口,token存储在 localStorage。
- 示例(src/api/auth.ts):import request from '@/utils/request';
export const login = (data: { username: string; password: string }) => {
return request.post('/auth/login', data);
};export const register = (data: { username: string; password: string }) => {
return request.post('/auth/register', data);
};
uni-app
- 实现方式:用封装好的 uni.request 调用接口,token存储在 uni.setStorageSync(跨端存储);若为小程序/App端,还需调用 uni.login 获取平台登录凭证。
- 示例(src/api/auth.ts):import request from '@/utils/request';
// 普通登录
export const login = (data: { username: string; password: string }) => {
return request<User>('/auth/login', { method: 'POST', data });
};// 微信小程序一键登录
export const wechatLogin = () => {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (loginRes) => {
request<User>('/auth/wechat', {
method: 'POST',
data: { code: loginRes.code }
}).then(resolve).catch(reject);
},
fail: reject
});
});
};
📝 3. 创建文章服务
Vue & uni-app
- 逻辑基本一致:都是调用HTTP接口获取文章列表、详情等。
- 唯一区别:Vue用 axios,uni-app用 uni.request。
🎨 第六步:创建全局状态管理
📝 1. 创建认证状态管理
Vue
- 实现方式:用 pinia 管理用户信息、token,token持久化到 localStorage。
- 示例(src/stores/auth.ts):import { defineStore } from 'pinia';
import { ref } from 'vue';export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null);
const token = ref<string>(localStorage.getItem('token') || '');const setUser = (newUser: User) => {
user.value = newUser;
};const setToken = (newToken: string) => {
token.value = newToken;
localStorage.setItem('token', newToken);
};const logout = () => {
user.value = null;
token.value = '';
localStorage.removeItem('token');
};return { user, token, setUser, setToken, logout };
});
uni-app
- 实现方式:仍用 pinia,但token持久化到 uni.setStorageSync(跨端存储)。
- 示例(src/stores/auth.ts):import { defineStore } from 'pinia';
import { ref } from 'vue';export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null);
const token = ref<string>(uni.getStorageSync('token') || '');const setUser = (newUser: User) => {
user.value = newUser;
};const setToken = (newToken: string) => {
token.value = newToken;
uni.setStorageSync('token', newToken);
};const logout = () => {
user.value = null;
token.value = '';
uni.removeStorageSync('token');
};return { user, token, setUser, setToken, logout };
});
📝 2. 创建文章状态管理
Vue & uni-app
- 逻辑基本一致:用 pinia 管理文章列表、详情等状态。
🏗️ 第七步:创建基础组件
📝 1. 创建布局组件
Vue
- 组件元素:用Web的 div、header、footer、main 等语义化标签。
- 示例(src/components/Layout.vue):<template>
<div class="layout">
<header class="header">头部</header>
<main class="main"><slot /></main>
<footer class="footer">页脚</footer>
</div>
</template><style scoped>
.layout { display: flex; flex-direction: column; min-height: 100vh; }
.header { height: 60px; background: #333; color: #fff; }
.main { flex: 1; padding: 20px; }
.footer { height: 40px; background: #eee; }
</style>
uni-app
- 组件元素:必须用uni-app的跨端组件 view、text 等(不能用Web的 div、span)。
- 示例(src/components/Layout.vue):<template>
<view class="layout">
<view class="header">头部</view>
<view class="main"><slot /></view>
<view class="footer">页脚</view>
</view>
</template><style scoped>
/* 用rpx单位适配多端屏幕(750rpx = 屏幕宽度) */
.layout { display: flex; flex-direction: column; min-height: 100vh; }
.header { height: 120rpx; background: #333; color: #fff; }
.main { flex: 1; padding: 40rpx; }
.footer { height: 80rpx; background: #eee; }
</style>
📝 2. 创建主布局组件
Vue
- 布局逻辑:用 vue-router 的 <router-view /> 渲染页面内容。
uni-app
- 布局逻辑:无需 <router-view />,页面内容由 pages.json 配置的路由自动渲染;主布局通常通过 App.vue 的全局样式或自定义组件实现。
📝 3. 创建页脚组件
Vue & uni-app
- 逻辑基本一致:用对应框架的组件实现,注意uni-app需用跨端组件和rpx单位。
🔐 第八步:创建认证页面
📝 1. 创建登录页面
Vue
- 页面元素:用 form、input、button 等Web表单元素,路由跳转用 router.push。
- 示例(src/views/Login.vue):<template>
<div class="login">
<form @submit.prevent="handleLogin">
<input v-model="username" placeholder="用户名" />
<input v-model="password" type="password" placeholder="密码" />
<button type="submit">登录</button>
</form>
</div>
</template><script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useAuthStore } from '@/stores/auth';
import { login } from '@/api/auth';const router = useRouter();
const authStore = useAuthStore();
const username = ref('');
const password = ref('');const handleLogin = async () => {
const res = await login({ username: username.value, password: password.value });
authStore.setToken(res.data.token);
authStore.setUser(res.data.user);
router.push('/');
};
</script>
uni-app
- 页面元素:用 form、input、button 等uni-app表单组件,路由跳转用 uni.switchTab、uni.navigateTo 等(不能用 router.push)。
- 示例(src/pages/login/login.vue):<template>
<view class="login">
<form @submit="handleLogin">
<input v-model="username" placeholder="用户名" />
<input v-model="password" type="password" placeholder="密码" />
<button form-type="submit">登录</button>
</form>
<!– 微信小程序一键登录按钮(条件编译) –>
<button v-if="def.mp-weixin" @click="handleWechatLogin">微信一键登录</button>
</view>
</template><script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import { login, wechatLogin } from '@/api/auth';const authStore = useAuthStore();
const username = ref('');
const password = ref('');const handleLogin = async () => {
const res = await login({ username: username.value, password: password.value });
authStore.setToken(res.data.token);
authStore.setUser(res.data.user);
uni.switchTab({ url: '/pages/index/index' }); // 跳转到tabbar页面
};const handleWechatLogin = async () => {
const res = await wechatLogin();
authStore.setToken(res.data.token);
authStore.setUser(res.data.user);
uni.switchTab({ url: '/pages/index/index' });
};
</script>
📝 2. 创建注册页面
Vue & uni-app
- 逻辑同登录页面:Vue用Web表单和 router.push,uni-app用跨端表单和 uni.navigateTo。
🏠 第九步:创建首页和文章页面
📝 1. 创建首页组件
Vue
- 页面逻辑:用 axios 获取文章列表,用 v-for 渲染,点击跳转用 router-link。
- 示例(src/views/Home.vue):<template>
<div class="home">
<h1>文章列表</h1>
<div v-for="article in articles" :key="article.id" class="article-item">
<router-link :to="`/article/${article.id}`">
<h2>{{ article.title }}</h2>
<p>{{ article.summary }}</p>
</router-link>
</div>
</div>
</template><script setup>
import { ref, onMounted } from 'vue';
import { getArticles } from '@/api/article';const articles = ref([]);
onMounted(async () => {
const res = await getArticles();
articles.value = res.data;
});
</script>
uni-app
- 页面逻辑:用 uni.request 获取文章列表,用 scroll-view 渲染长列表(优化性能),点击跳转用 uni.navigateTo。
- 示例(src/pages/index/index.vue):<template>
<view class="home">
<text class="title">文章列表</text>
<!– scroll-view优化长列表滚动 –>
<scroll-view scroll-y class="article-list">
<view v-for="article in articles" :key="article.id" class="article-item" @click="goToDetail(article.id)">
<text class="article-title">{{ article.title }}</text>
<text class="article-summary">{{ article.summary }}</text>
</view>
</scroll-view>
</view>
</template><script setup>
import { ref, onLoad } from 'vue';
import { getArticles } from '@/api/article';const articles = ref([]);
// 用onLoad替代onMounted(uni-app页面生命周期)
onLoad(async () => {
const res = await getArticles();
articles.value = res.data;
});const goToDetail = (id: number) => {
uni.navigateTo({ url: `/pages/article/detail?id=${id}` });
};
</script>
📝 2. 创建文章详情页面
Vue
- 路由参数:用 useRoute().params 获取文章ID。
uni-app
- 路由参数:用 onLoad 的参数获取文章ID(如 onLoad((options) => { const id = options.id; }))。
📝 3. 创建文章编辑页面
Vue & uni-app
- 逻辑同登录页面:注意uni-app需用跨端组件和API。
🔗 第十步:配置路由系统
📝 1. 创建路由配置
Vue
- 实现方式:用 vue-router 单独创建路由配置文件。
- 示例(src/router/index.ts):import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import Login from '@/views/Login.vue';const routes = [
{ path: '/', component: Home },
{ path: '/login', component: Login },
{ path: '/article/:id', component: () => import('@/views/ArticleDetail.vue') }
];const router = createRouter({
history: createWebHistory(),
routes
});export default router;
uni-app
- 实现方式:无需单独的路由文件,所有路由在 pages.json 中配置。
- 示例(src/pages.json):{
"pages": [
{
"path": "pages/index/index", // 首页(第一个为默认页)
"style": { "navigationBarTitleText": "首页" }
},
{
"path": "pages/login/login",
"style": { "navigationBarTitleText": "登录" }
},
{
"path": "pages/article/detail",
"style": { "navigationBarTitleText": "文章详情" }
}
],
"tabBar": { // 底部tabbar配置
"list": [
{ "pagePath": "pages/index/index", "text": "首页" },
{ "pagePath": "pages/user/index", "text": "我的" }
]
}
}
📝 2. 创建路由保护组件
Vue
- 实现方式:用 vue-router 的 beforeEach 全局前置守卫。
- 示例(src/router/index.ts):router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.path !== '/login' && !token) {
next('/login');
} else {
next();
}
});
uni-app
- 实现方式:没有内置的路由守卫,需通过 App.vue 的 onLaunch、onShow 或页面的 onLoad 实现。
- 示例(src/App.vue):<script setup>
import { onLaunch } from '@dcloudio/uni-app';onLaunch(() => {
const token = uni.getStorageSync('token');
if (!token) {
uni.reLaunch({ url: '/pages/login/login' });
}
});
</script>
🎨 第十一步:创建样式和主题
📝 1. 创建全局样式
Vue
- 样式单位:用 px、rem、em 等Web常用单位。
- 样式文件:在 src/styles/global.css 中定义,在 main.js 中引入。
- 示例:* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; }
uni-app
- 样式单位:推荐用 rpx(响应式像素,750rpx = 屏幕宽度,自动适配多端)。
- 样式文件:在 src/styles/global.css 中定义,在 App.vue 的 <style> 中引入。
- 示例:/* 全局样式 */
page { font-family: Arial, sans-serif; background: #f5f5f5; }
.container { padding: 40rpx; }
📝 2. 更新主入口文件
Vue
- 入口文件:src/main.js,引入路由、状态管理、全局样式。
- 示例:import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import pinia from './stores';
import './styles/global.css';const app = createApp(App);
app.use(router);
app.use(pinia);
app.mount('#app');
uni-app
- 入口文件:src/main.js,引入状态管理、全局样式(无需引入路由)。
- 示例:import { createSSRApp } from 'vue';
import App from './App.vue';
import pinia from './stores';
import './styles/global.css';export function createApp() {
const app = createSSRApp(App);
app.use(pinia);
return { app };
}
🚀 第十二步:启动和测试
📝 1. 启动开发服务器
Vue
- 启动命令:npm run dev,在浏览器中访问 http://localhost:5173。
uni-app
- 分端启动:
- H5端:npm run dev:h5,在浏览器访问;
- 微信小程序:npm run dev:mp-weixin,用微信开发者工具打开项目目录;
- App端:npm run dev:app,用HBuilderX连接真机或模拟器预览。
📝 2. 功能测试清单
Vue
- 测试重点:Web端的交互、浏览器兼容性、响应式布局。
uni-app
- 测试重点:
- 多端兼容性:H5、小程序、App端的功能是否一致;
- 端专属功能:如微信小程序的一键登录、App端的原生渲染;
- 性能:长列表滚动、动画是否流畅。
🎯 第十三步:优化和部署
📝 1. 性能优化
Vue
- 优化方向:代码分割、路由懒加载、组件缓存(keep-alive)、图片懒加载。
uni-app
- 优化方向:
- 小程序端:分包加载(在 pages.json 中配置 subPackages)、减少主包体积;
- App端:用 uvue 替代普通Vue页面(原生渲染,提升性能);
- 通用:图片压缩、使用 scroll-view 优化长列表、避免频繁的 uni.setStorageSync。
📝 2. 构建生产版本
Vue
- 构建命令:npm run build,生成 dist 目录,部署到Web服务器。
uni-app
- 分端构建:
- H5端:npm run build:h5,生成 dist/build/h5 目录,部署到Web服务器;
- 微信小程序:npm run build:mp-weixin,生成 dist/build/mp-weixin 目录,用微信开发者工具上传代码;
- App端:npm run build:app,用HBuilderX打包成 ipa(iOS)或 apk(Android)。
📝 3. 部署配置
Vue
- 部署方式:将 dist 目录部署到Nginx、Apache等Web服务器,配置反向代理解决跨域。
uni-app
- 部署方式:
- H5端:同Vue;
- 小程序端:上传到微信公众平台、支付宝开放平台等,提交审核;
- App端:上传到App Store(iOS)、应用宝(Android)等应用市场。
总结:核心差异对比表
| 定位 | 通用Web框架 | 跨端框架(Web/小程序/App) |
| 渲染底层 | Web DOM | Web端:DOM;小程序:原生组件;App:WebView/uvue原生 |
| 项目结构 | src/views、router/index.js | src/pages、pages.json、manifest.json |
| 路由 | vue-router | pages.json配置 |
| HTTP请求 | axios | uni.request |
| 组件 | div、span等Web组件 | view、text等跨端组件 |
| 样式单位 | px、rem | rpx(推荐) |
| 状态管理 | pinia + localStorage | pinia + uni.setStorageSync |
| 启动/构建 | 仅Web端 | 分端(H5/小程序/App) |



