从刀耕火种到现代框架:DOM编程 vs Vue/React 进化史
前端开发就像造房子:原生JS是手工砌砖,框架则是预制板建筑,效率提升100倍!
原生JS时代:刀耕火种的DOM编程
让我们回到"原始时代"的前端开发,那时的程序员像工匠一样手工操作每个DOM元素:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原生JS</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!--引入Bootstrap 3.3.6样式表实现响应式布局-->
</head>
<body>
<!-- 挂载点 -->
<div class="container">
<table id="friends" class="table table-striped">
<thead>
<tr>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
<tr>
<td>小李</td>
<td>抚州</td>
</tr>
</tbody>
</table>
</div>
<script>
//挂载点 这个位置的HTML由JS动态生成(html和js挂载的地方),表面这个地方的数据将会改变,是动态的
// 通过document.querySelector()方法,基于CSS选择器规则,获取文档中ID为"friends"的表格元素下的tbody子元素,并将其引用赋值给常量oBody,以便后续操作表格内容。
const oBody= document.querySelector('#friends tbody')
//JSON 数组
//DOM 编程 动态页面
//数据
const friends = [
{
"name":"王子",
"hometown":"九江"
},
{
"name":"小美",
"hometown":"赣州"
}
]
// html 字符串 = 数组 类型转换
//使用数组的`map()`方法遍历`friends`数组
// 为每个元素创建包含`<tr>`和两个`<td>`的 HTML 字符串
//使用`join('')`将生成的字符串数组连接为一个完整的 HTML 片段
//// `oBody.innerHTML = ...` 将片段插入 tbody,替换原有内容。
oBody.innerHTML = friends.map(friend=>`
<tr>
<td>${friend.name}</td>
<td>${friend.hometown}</td>
</tr>
`).join('')
</script>
</body>
</html>

代码中一些关键的的解释
-
script标签一般放于body的标签的最后:
- 可避免阻塞HTML渲染,让页面内容优先展示,减少白屏时间;
- 确保脚本执行时DOM已加载完成,避免找不到元素的错误;
- 还能优化资源并行加载,提升性能,是兼顾体验与效率的最佳实践。
-
挂载点: 挂载点是前端框架渲染的 DOM 容器,特点:
- 唯一性 :通过唯一 ID(如
id="app"
)定位; - 空容器:初始无内容,由框架动态填充;
- 专用性:仅用于框架挂载,不参与普通 DOM 操作。
作用:作为视图渲染入口,连接虚拟 DOM 与真实 DOM,隔离框架逻辑与页面其他部分。
- 唯一性 :通过唯一 ID(如
-
不用去写细节和重复代码 focus于业务 (熟)
- 引入第三方库 booststrap PC css框架,业务类
- .container容器 固定宽度
- table标签
DOM编程的痛点 😫
- 繁琐的DOM操作:每个元素都要手动创建、插入、更新
- 数据与视图分离:数据变化时需要手动更新DOM
- 状态管理困难:随着应用复杂,状态分散在各处
- 性能问题:频繁操作DOM导致页面重绘
DOM操作就像在瓷器店里打棒球------稍有不慎就会搞砸整个页面!
现代框架的救赎:Vue/React的革命
为什么需要框架?
原生JS痛点 | 框架解决方案 |
---|---|
DOM操作繁琐 | 声明式渲染 |
数据视图分离 | 数据驱动视图 |
状态管理混乱 | 组件状态管理 |
代码复用困难 | 组件化开发 |
Vue:渐进式框架的魅力
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>聚焦于业务,而不是底层API</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!--引入 Bootstrap CSS 框架,提供响应式布局和样式(如表格条纹、容器居中),后续可以使用第三方库进行样式设计,`table table-striped`:为表格添加斑马条纹样式。 -->
</head>
<body>
<div class="container" id="app">
<!--`<div id="app">` 是 Vue 应用的挂载容器,Vue 将接管该元素内部的渲染。-->
<h1>{{title}}</h1><!--双大括号是 Vue 的文本插值语法,用于显示动态数据。-->
<table id="friends" class="table table-striped">
<thead>
<tr>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
<tr v-for="friend in friends"><!--用于循环渲染列表数据-->
<td>{{ friend.name }}</td>
<td>{{ friend.hometown }}</td>
</tr>
</tbody>
</table>
</div>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/3.2.31/vue.global.min.js"></script>
<!--引入 Vue 3 的全局构建版本(CDN 链接),该版本将 Vue 挂载到全局作用域-->
<script>
// 做一个App 定义 Vue 应用的根组件,这是一个包含组件选项的对象
const App = {
// 申明数据的业务
//组件的数据函数,必须返回一个对象
// Vue 会将返回的对象转换为响应式数据
data() {
return {
title:'ECUT 未来之星',
friends: [
{
"name": "王子",
"hometown": "九江"
},
{
"name": "公主",
"hometown": "赣州"
},
]
}
}
}
// 挂载点
// 使用 Vue 的 createApp 函数创建应用实例
// 将 App 组件作为根组件传入
//使用 mount 方法将应用挂载到 DOM 中 id 为 "app" 的元素上
//这意味着页面中需要有一个<div id="app"></div>作为挂载点
Vue.createApp(App).mount('#app')
</script>
</body>
</html>
Vue的核心优势 ✨
- 响应式系统:数据变化自动更新视图
- 指令系统 :
v-for
、v-if
等简化逻辑 - 单文件组件:HTML/CSS/JS三位一体
- 渐进式架构:可按需引入功能
Vue就像贴心的管家------你只需要告诉它"我想要什么",它就会处理好所有细节!
React:函数式的力量
jsx
function FriendsTable() {
const [friends] = useState([
{ name: '王子', hometown: '九江' },
{ name: '公主', hometown: '赣州' }
]);
return (
<table>
{friends.map(friend => (
<tr key={friend.name}>
<td>{friend.name}</td>
<td>{friend.hometown}</td>
</tr>
))}
</table>
);
}
React的独特之处 💡
- JSX语法:JavaScript和HTML的完美融合
- 虚拟DOM:高效更新界面
- 函数式组件:简洁的UI构建方式
- 强大的生态系统:Redux、React Router等配套工具
React就像乐高积木------用组件搭建出无限可能的UI世界!
框架的核心价值:聚焦业务逻辑
数据驱动视图的魔力
graph LR
A[数据变化] --> B[框架检测变化]
B --> C[计算最小更新]
C --> D[更新真实DOM]
组件化:搭积木式开发
graph TB
App --> Header
App --> FriendList --> FriendItem
App --> Footer
现代框架的三大支柱
- 声明式编程:描述"应该是什么",而不是"如何做"
- 组件化架构:高内聚、低耦合的代码组织
- 响应式系统:数据变化自动反映到UI
为什么说框架是必然选择?
- 开发效率提升:减少70%的DOM操作代码
- 代码可维护性:清晰的组件结构
- 性能优化:虚拟DOM减少重绘
- 团队协作:统一的设计模式
- 生态支持:路由、状态管理等解决方案
使用框架就像从自行车升级到高铁------同样的目的地,完全不同的体验!
真实案例:需求变更的应对
假设老板突然要求:
- 增加"年龄"列
- 添加筛选功能
原生JS方案:
- 修改数据结构
- 重写HTML拼接逻辑
- 添加事件监听器
- 手动更新表格
Vue/React方案:
js
// Vue
{
data() {
return {
// 1. 添加age字段
friends: [
{ name: '...', hometown: '...', age: 20 }
],
// 2. 添加过滤条件
filter: ''
}
},
computed: {
// 3. 创建计算属性
filteredFriends() {
return this.friends.filter(f =>
f.name.includes(this.filter))
}
}
}
jsx
// React
function FriendList() {
const [filter, setFilter] = useState('');
const filteredFriends = friends.filter(f =>
f.name.includes(filter));
return (
<>
{/* 添加筛选输入框 */}
<input onChange={e => setFilter(e.target.value)} />
{filteredFriends.map(friend => (
<tr>
<td>{friend.name}</td>
<td>{friend.hometown}</td>
{/* 添加年龄列 */}
<td>{friend.age}</td>
</tr>
))}
</>
);
}
friends.filter(...)
:
filter()
是数组的内置方法,遍历每个元素并返回一个新数组。新数组只包含 回调函数返回
true
的元素。
- 回调函数
f => f.name.includes(filter)
:
f
是当前遍历的朋友对象(例如{ name: "王子", hometown: "九江" }
)。f.name.includes(filter)
:检查f.name
是否包含变量filter
的值。includes()
是字符串方法,返回true
或false
。filter
必须是一个已定义的变量(通常是用户输入的搜索词)。
结论:拥抱框架,聚焦业务
维度 | 原生JS | Vue/React |
---|---|---|
开发速度 | 🐢 慢 | 🚀 快 |
代码量 | 📜 多 | 📄 少 |
可维护性 | ❌ 差 | ✅ 好 |
学习曲线 | ⬇️ 平缓 | ⬆️ 陡峭 |
适用场景 | 简单页面 | 复杂应用 |
前端开发箴言:
"不要重复造轮子,但要理解轮子的构造!"
学习框架的同时,不要忘记原生JS的基础。理解框架背后的原理,才能成为真正的高手!
最后的小测验:
当你下次看到document.querySelector()
时,问问自己:
"这个操作真的必要吗?有没有更优雅的框架解决方案?" 💭
Happy coding! 🎉