你有没有过这样的困惑:
- 明明写了同样的功能,为什么 React 和 Vue 的写法看起来“天差地别”?
- 用 Vite 创建项目后,
main.jsx、App.jsx、router.jsx这些文件到底谁管谁? - 路由到底是怎么“接管一切”的?
<Link>和<a>真的不一样吗?
今天,我们就来以一个最简单的多页面应用为切入点,深入剖析 React 与 Vue 在现代前端工程化中的架构设计哲学。不讲虚的,直接上手实战 + 源码级解读,带你从“会用”走向“懂原理”。
🚀第一步:我们先造个轮子 —— 初始化项目
npm init vite@latest my-app -- --template react
# 或者
npm init vite@latest my-app -- --template vue
一句话,Vite 就给你搭好了一个现代化前端项目的骨架。它背后是尤雨溪对构建工具的深刻理解 —— 利用浏览器原生 ESM 实现极速冷启动和热更新,开发体验拉满!
💡小知识:Vite ≠ Webpack。Vite 在开发阶段不打包,而是按需加载模块;生产环境才真正构建。
🧱项目结构一览:两个框架,两种美学
| 目录/文件 | React 项目 | Vue 项目 |
|---|---|---|
| 入口文件 | main.jsx | main.js |
| 根组件 | App.jsx | App.vue |
| 组件存放 | src/components/ | src/components/ |
| 页面级组件 | src/pages/Home.jsx | src/views/Home.vue |
| 路由配置 | src/router.jsx | src/router/index.js |
| 全局样式 | index.styl / css | style.css |
虽然目录相似,但内核完全不同 —— 我们接下来就看看它们是如何“殊途同归”地实现路由跳转的。
🔗第二步:让页面动起来 —— 前端路由登场
❓什么是前端路由?
传统网页每次点击链接都会向服务器请求新页面。而前端路由是在不刷新页面的前提下,通过 JS 动态渲染不同内容,实现 SPA(单页应用)体验。
举个🌰:
用户访问
/about,不是真的去服务器拿一个叫 about.html 的文件,而是由前端自己决定:“哦,用户要点关于我,那我就把 About 组件显示出来。”
这就需要一个“总调度官”—— 路由管理器。
🔄React 中的路由:声明式 + JSX 的极致优雅
✅ 使用 react-router-dom
安装命令:
npm install react-router-dom
核心组件三件套:
<Router>:路由顶层容器(一般用BrowserRouter)<Routes>:路由匹配器<Route>:具体路径映射规则<Link>:替代<a>的路由跳转标签
📂代码结构拆解
App.jsx —— 路由入口 + 导航栏
import { BrowserRouter as Router, Link } from 'react-router-dom';
import AppRouters from './router';
function App() {
return (
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
<AppRouters />
</Router>
);
}
🔍 关键点解析:
-
不再使用
<a href="/">
因为原生<a>会触发页面跳转,破坏 SPA 特性。<Link>内部阻止默认行为,并调用history.pushState()实现无刷新跳转。 -
<AppRouters />是独立封装的路由表
高内聚低耦合,便于后期维护。
router.jsx —— 路由配置中心
import { Routes, Route } from 'react-router-dom';
import Home from '../pages/Home';
import About from '../pages/About';
export default function AppRouters() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
);
}
🎯 注意:React Router v6 开始使用 element 属性传入 JSX 元素,取代了之前的 component。
🌈Vue 中的路由:配置式 + 模板语法的清晰之美
✅ 使用 vue-router
安装命令:
npm install vue-router@4
Vue 的路由更偏向“配置驱动”,有点像在写一份“路由说明书”。
router/index.js —— 路由注册中心
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{ path: '/', name: 'Home', component: Home },
{ path: '/about', name: 'About', component: About }
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router;
🧠 思考:这里用了 createWebHashHistory(),意味着 URL 会带 #/about。如果想去掉 #,可以用 createWebHistory(),但需要后端配合支持 History Mode。
main.js —— 安装插件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
👉 .use(router) 是 Vue 插件机制的经典体现 —— 把路由能力“注入”到整个应用中。
App.vue —— 模板中使用全局组件
<template>
<div>
<nav>
<ul>
<li><router-link to="/">Home</router-link></li>
<li><router-link to="/about">About</router-link></li>
</ul>
</nav>
<main>
<router-view></router-view>
</main>
</div>
</template>
🔑 重点来了:
<router-link>自动编译为<a>,但点击时不刷新。<router-view>是“占位符”,根据当前路径动态插入对应的组件。
路由大战 —— Hash 还是 History?
你有没有注意到网址栏里的 #?比如 http://xxx.com/#/about?
这是早期前端路由的“权宜之计”——Hash 路由。为什么用 #?
因为 # 后面的内容不会发给服务器!前端可以随便玩,不怕 404。
但后来出现了更优雅的方式:HTML5 History API。
| 类型 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| Hash | /#/about | 兼容老浏览器,稳如老狗 | 丑,像打了补丁 |
| History | /about | 干净美观,像后端路由 | 需要服务器配合 |
在 React 中你可以这样选:
import { BrowserRouter as Router } from 'react-router-dom'; // History 模式
// import { HashRouter as Router } from 'react-router-dom'; // Hash 模式
而在 Vue 中:
createRouter({
history: createWebHistory(), // or createWebHashHistory()
})
✅ 建议:新项目一律上
History模式,毕竟谁不想自己的网站看起来“高级一点”呢?
⚖️React vs Vue:路由设计理念大碰撞!
| 维度 | React (JSX + 函数式) | Vue (模板 + 配置式) |
|---|---|---|
| 编写方式 | 声明式嵌套,组件即路由 | 配置数组 + 全局指令 |
| 类型友好性 | TypeScript 天然融合 | 同样支持 TS,但需额外类型定义 |
| 学习曲线 | 需要理解 JSX、Hooks、函数组件 | 模板语法易上手,适合初学者 |
| 灵活性 | 极高,可自由组合逻辑 | 较高,但受模板限制 |
| 工具链生态 | Remix、Next.js 强大 SSR 支持 | Nuxt.js 生态完善 |
| 开发者心智模型 | “一切皆 JS” | “HTML 扩展 + 数据绑定” |
🤔举个生活化的比喻:
如果说 React 像是乐高积木 —— 你可以自由拼接每一个
<Route>成复杂的路由树;
那么 Vue 更像是宜家家具 —— 提供标准化零件(routes数组),按照说明书组装即可。
两者没有优劣,只有适用场景。
🔍进阶思考:Home 页面的数据请求该放哪?
我们来看一段 React 的 Home.jsx:
const Home = () => {
const [repos, setRepos] = useState([]);
useEffect(() => {
fetch('https://api.github.com/users/.../repos')
.then(res => res.json())
.then(data => setRepos(data));
}, []);
return (
<div>
<h1>Home</h1>
{repos.length ? (
<ul>
{repos.map(repo => (
<li key={repo.id}>
<a href={repo.html_url} target="_blank">{repo.name}</a>
</li>
))}
</ul>
) : <p>暂无仓库</p>}
</div>
);
};
📌关键知识点:
useEffect(() => {}, [])相当于 Vue 的onMounted- 空依赖数组确保只执行一次
- 数据请求放在组件内部,属于“副作用管理”
而在 Vue 中,你会这样写(Composition API):
<script setup>
import { onMounted, ref } from 'vue'
const repos = ref([])
onMounted(async () => {
const res = await fetch('https://api.github.com/users/.../repos')
repos.value = await res.json()
})
</script>
<template>
<div>
<h1>Home</h1>
<ul v-if="repos.length">
<li v-for="repo in repos" :key="repo.id">
<a :href="repo.html_url" target="_blank">{{ repo.name }}</a>
</li>
</ul>
<p v-else>暂无仓库</p>
</div>
</template>
🎯结论:无论是 React 的
useEffect还是 Vue 的onMounted,都在解决同一个问题 —— 组件挂载后的副作用处理。
🛠️最佳实践建议
| 场景 | 推荐方案 |
|---|---|
| 快速原型开发 | Vue + Volar 插件,模板直观 |
| 复杂状态管理、大型系统 | React + Redux Toolkit / Zustand |
| SEO 敏感项目 | React (Next.js) 或 Vue (Nuxt.js) |
| 团队协作统一风格 | Vue 的模板约束更强,不易写出“千奇百怪”的 JSX |
| 全栈一体化 | React 生态更成熟(如 TanStack Router、tRPC) |
🎁总结:前端的终极目标是“用户体验”而非“技术炫技”
今天我们从一个简单的多页面应用出发,穿越了:
- Vite 的构建哲学
- React 与 Vue 的路由实现差异
- 声明式 vs 配置式的编程范式之争
- 组件生命周期与副作用管理
但请记住:
🎯技术只是手段,交付价值才是目的。
当你能熟练选择合适的工具链、清晰组织项目结构、写出可维护的路由系统时,你就已经超越了大多数“只会抄 demo”的开发者。
📣互动时间
你在实际项目中更偏爱 React 还是 Vue?为什么?
有没有遇到过路由懒加载、权限控制等进阶问题?欢迎评论区留言交流👇
✅ 如果你觉得这篇文章对你有帮助,别忘了点赞、收藏、转发三连!让更多人看到前端的魅力所在。