Vue.js 的哲学从原始到现代:前端开发的进化之旅
在前端开发的历史长河中,我们经历了的原始社会到现代化的框架开发时代。Vue.js 正是这个进化过程中的一个关键角色,它不仅改变了开发者与DOM交互的方式,还引入了组件化和响应式的数据绑定,使得前端开发变得更加高效和愉悦。
原始社会:手动DOM操作的时代
早期的前端开发类似于“刀耕火种”,直接通过JavaScript底层API进行DOM操作和事件处理。这种方式虽然直接,但随着应用复杂度的增加,代码变得难以维护,频繁地访问和修改DOM导致性能问题频出。那时,浏览器的渲染引擎和V8 JavaScript引擎独立工作,为了优化用户体验,开发者需要小心翼翼地管理二者之间的互动。
案例:
该案例使用了纯JavaScript来监听输入框的变化,并将输入的内容显示在页面上的<h2>标签中。虽然实现了输入后在上方展示的功能,但是手动DOM有许多的缺点。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2 id="app"></h2>
<input type="text" id="todo-input">
<script>
var app = document.getElementById('app');
var todoInput = document.getElementById('todo-input');
// change keyup keydown
todoInput.addEventListener('input',function(event){
var val= event.target.value //别的方法
console.log(val,todoInput.value,this.value);
app.innerHTML = val;
})
</script>
</body>
</html>
主要缺点:
- 频繁的DOM访问:每次更新UI时都需要重新访问DOM,这可能导致性能瓶颈,特别是在需要频繁更新或大量DOM节点的情况下。
- 代码冗长且复杂:随着应用功能的增加,直接操作DOM的代码会变得越来越冗长和复杂,难以理解和维护。
封建时期的变革:jQuery时代的局限与突破
随着jQuery等库的出现,前端开发进入了所谓的“封建时期”。jQuery简化了跨浏览器的兼容性问题,并提供了简洁的API来操作DOM,极大地提高了开发效率。尽管如此,当Web应用程序越来越复杂时,仅仅依靠jQuery这样的库已经不足以应对日益增长的需求。
与上面案例相同:
我们来了解一下jQuery就好,具体代码就不分析了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jQuery Todo Input Example</title>
<!-- 引入 jQuery -->
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h2 id="app"></h2>
<input type="text" id="todo-input" placeholder="Enter text here">
<script>
$(document).ready(function() {
// 初始化时设置输入框的值(可选)
$('#todo-input').val(''); // 清空输入框或设置默认值
// 监听输入框的变化
$('#todo-input').on('input', function() {
var val = $(this).val(); // 获取输入框的当前值
console.log(val); // 打印到控制台以供调试
$('#app').html(val); // 更新<h2>标签的内容为输入框的值
});
});
</script>
</body>
</html>
jQuery相对于纯DOM操作的优点
- 更简洁的语法:jQuery提供了简洁易读的方法来选择和操作DOM元素,减少了冗长的原生JavaScript代码。
jQuery相对于Vue.js的缺点
简单罗列一些重要的,
-
性能瓶颈:
- 频繁的DOM操作:每次更新UI都需要重新访问DOM,容易造成性能问题,特别是在需要频繁更新或大量DOM节点的情况下。
- 无虚拟DOM优化:不像Vue那样具备虚拟DOM机制,不能智能地最小化实际DOM操作,影响渲染效率。
-
组件化能力不足:
-
模块化差:难以实现真正的组件化开发,不利于构建可复用的UI组件。
-
缺乏单文件组件:Vue的单文件组件(SFC)允许在一个文件中定义模板、样式和脚本,极大地方便了代码组织和维护。
-
这就是为什么jQuery没有用到现在的原因,对于纯DOM,确实更优秀了,但是没有根本解决性能上的问题,
进化飞跃:Vue.js引领的开发新纪元
进入现代前端开发阶段,Vue.js成为了一股革新力量。它让开发者能够专注于业务逻辑而不是底层DOM操作,通过声明式的视图层,使数据和界面保持同步。Vue的核心思想在于其响应式系统——每当数据发生变化时,相关的视图部分会自动更新,而不需要手动干预DOM。这不仅提升了开发效率,还改善了用户体验,代表着前端开发的一次巨大进化。
质变之处:
而VUE相比于之前,对于频繁的DOM操作,Vue.js 通过其内部的响应式系统和虚拟DOM机制,实现了高效的批量收集DOM更新,减少了不必要的DOM操作。这种优化不仅提升了应用的性能,还简化了开发者的代码编写和维护工作。
简单图解:
减少了很多不必要的操作,优化了性能
选项式API(Options API)和组合式API(Composition API)
让我们一起探索Vue的魅力,感受它带来的开发体验提升。下面是一个简单的待办事项(todos)应用示例,展示了Vue如何简化任务管理:
选项式API:todos案例:
图示:简单展示功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>todos</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/3.2.31/vue.global.min.js"></script>
<style>
.done {
color: gray;
text-decoration: line-through;
}
</style>
</head>
<body>
<!-- 挂载点,vue 作用范围 -->
<div id="app">
<h2>{{title}}</h2>
<input type="text" v-model="title" @keydown.enter="addTodo" />
<ul>
<li v-for="todo in todos">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.title}}</span>
</li>
</ul>
<div>
全选<input type="checkbox" v-model="allDone">
<span>{{active}} / {{all}}</span>
</div>
</div>
<script>
const App = {
data() {
return {
title: 'todos',
todos: [
{title: '吃饭', done: false},
{title: '睡觉', done: true},
]
}
},
methods: {
addTodo() {
this.todos.push({title: this.title, done: false})
this.title = ''
}
},
computed: {
all() {
return this.todos.length
},
active() {
return this.todos.filter(todo => !todo.done).length
},
// get set 两个都做
allDone:{
get(){
// this -> 计算属性 计算属性改变 后面也会变
return this.active ===0
},
set(val){
// 数据和界面保持一致
this.todos.forEach(todo=>{
todo.done = val
})
}
}
}
}
Vue.createApp(App).mount('#app')
</script>
</body>
</html>
分析:
Script 标签:引入 Vue 3 的全局脚本
-
定义 App 组件:使用
data()定义组件的状态,包括title和todos数组。 -
定义方法:
addTodo():将当前title添加到todos数组,并清空title。
-
定义计算属性:
-
all:返回todos数组的长度。 -
active:返回未完成任务的数量。 -
allDone:这是一个带有 getter 和 setter 的计算属性,用于处理全选逻辑。get():如果所有任务都已完成,则返回true,否则返回false。set(val):接受一个布尔值参数,并将所有任务的done属性设置为这个值。
-
-
创建和挂载应用:
Vue.createApp(App).mount('#app')创建一个新的 Vue 应用实例,并将其挂载到具有 IDapp的 DOM 元素上。
模板部分:
-
<h2>{{title}}</h2>:显示标题,与title响应式变量绑定。 -
<input type="text" v-model="title" @keydown.enter="addTodo" />:文本输入框,通过v-model双向绑定到title,并监听回车键调用addTodo方法。 -
<ul>和<li v-for="todo in todos">:无序列表,遍历todos数组生成每个任务项。 -
<input type="checkbox" v-model="todo.done">:复选框,与每个任务的done状态绑定。 -
<span :class="{done: todo.done}">{{todo.title}}</span>:根据任务完成状态动态应用.done类。 -
<div>包含全选复选框和计数器,显示未完成和总任务数量。
缺点
可能在这个案例中没有太多体现选项式API的缺点,但是我们通过一张来自极客时间的图,我们就能很明显的发现:
结果:
- 选项式API分了
data、 methods、 computed三块区域,风格的优点是直观易懂,每个选项都有明确的用途。缺点是当组件变得复杂时,代码可能会分散且难以维护。你想想当你操作大型项目时,你改好了data中的数据,你要去找methods中是否有问题,中间隔开了几百行代码,是不是很繁琐。
那么就要引入组合式API
组合式API
- 模块化: 相关的数据和方法被封装在一个函数中,使得代码更加模块化。
- 复用性: 可以更容易地复用这些组合函数,提高代码的复用性。
- 可测试性: 每个组合函数可以独立测试,提高了代码的可测试性。
案例:
<script>
const { ref, computed } = Vue;
let App = {
setup() {
// title 相关模块
let title = ref('todos');
// todos 相关模块
let todos = ref([
{ title: '吃饭', done: false },
{ title: '睡觉', done: true },
]);
// 添加任务的方法
function addTodo() {
if (title.value.trim()) {
todos.value.push({ title: title.value, done: false });
title.value = '';
}
}
// 计算属性
const all = computed(() => todos.value.length);
const active = computed(() => todos.value.filter(todo => !todo.done).length);
// 全选/全不选功能
const allDone = computed({
get: () => active.value === 0,
set: (value) => {
todos.value.forEach(todo => todo.done = value);
}
});
return {
title,
todos,
addTodo,
all,
active,
allDone
};
}
};
Vue.createApp(App).mount('#App'); // 注意这里的选择器与HTML中的id匹配
</script>
</body>
</html>
总结: 组合式API允许我们将相关的逻辑封装在一起,形成所谓的“组合函数”(composables)。通过这种方式,所有与特定功能有关的状态、计算属性和方法都可以被集中管理。这种逻辑关注点分离的方法使得代码更加紧凑,减少了跨区域跳跃的需求,提高了开发效率。
计算属性是什么?
在选项式API中的computed就存放着计算属性,
概念:
在 Vue.js 中,计算属性(Computed Properties)是一种特殊的属性类型,它允许你声明性地定义一个属性,这个属性的值是基于其他数据属性的值动态计算出来的。计算属性的主要用途是在模板中简化复杂的逻辑表达式,并且它们具有缓存机制,只有在其依赖的数据发生变化时才会重新计算。
案例讲解:
我们使用选项式API中的例子进行分析其中的计算属性:
allDone (带 getter 和 setter)
allDone: {
get() {
return this.active === 0
},
set(val) {
this.todos.forEach(todo => {
todo.done = val
})
}
}
-
功能:
- getter:检查是否有未完成的任务(即
active是否等于 0)。如果有未完成的任务,则返回false;否则返回true。这决定了全选复选框的状态。 - setter:允许设置全选复选框的状态。当用户点击全选复选框时,它将遍历
todos数组,并将每个任务的done属性设置为与复选框相同的状态(val)。
- getter:检查是否有未完成的任务(即
-
依赖:getter 部分依赖于
active计算属性,而 setter 则直接操作todos数据。 -
用途:实现了全选/取消全选的功能,同时确保了数据和界面的一致性。
计算属性的特点
- 依赖响应式数据:计算属性会自动追踪其函数内部使用的响应式数据(如
data或者props),当这些数据发生变化时,计算属性会重新求值。 - 缓存机制:计算属性的结果会被缓存,除非它的相关依赖发生改变。这意味着如果计算属性所依赖的数据没有变化,那么即使多次访问该计算属性,也不会重复执行计算过程,从而提高了性能。
- 简洁的模板:通过将复杂的逻辑从模板中移出并放入计算属性中,可以使模板保持简单和易读。
- 可链式调用:计算属性可以相互依赖,即一个计算属性可以依赖另一个计算属性,形成链式调用关系。
- getter 和 setter:默认情况下,计算属性只提供 getter 方法来获取值。但也可以定义 setter 方法,允许我们对计算属性赋值时触发特定的行为。
迎接未来,共同进化
Vue.js不仅仅是另一个前端框架,它是对过去多年前端开发实践的一种总结和提升。它使得开发者可以更专注于构建应用本身,而不必纠结于复杂的DOM操作和状态管理。现在是时候加入Vue的光荣进化,开启属于你的现代化前端之旅了!🚀✨