面向对象与面向过程、函数式编程
1. 面向过程编程(Procedure-Oriented Programming)
面向过程编程将程序视为一系列函数的集合,数据和操作数据的函数是分离的。在 Vue
3 中,这种风格通常表现为使用组合式 API
(Composition API
)以函数式的方式组织代码。
示例:Todo 应用(面向过程)
ts
// Todo 应用 - 面向过程实现
import { ref, computed, onMounted } from 'vue';
// 定义数据结构
interface Todo {
id: number;
title: string;
completed: boolean;
}
// 状态管理(使用 ref 和 reactive)
const todos = ref<Todo[]>([]);
const newTodoTitle = ref('');
// 计算属性
const completedTodos = computed(() =>
todos.value.filter(todo => todo.completed)
);
// 方法
const addTodo = () => {
if (newTodoTitle.value.trim()) {
todos.value.push({
id: Date.now(),
title: newTodoTitle.value,
completed: false
});
newTodoTitle.value = '';
}
};
const toggleTodo = (id: number) => {
const todo = todos.value.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
};
const deleteTodo = (id: number) => {
todos.value = todos.value.filter(t => t.id !== id);
};
// 生命周期钩子
onMounted(() => {
// 从本地存储加载数据
const savedTodos = localStorage.getItem('todos');
if (savedTodos) {
todos.value = JSON.parse(savedTodos);
}
});
// 监听变化,保存到本地存储
watch(todos, (newTodos) => {
localStorage.setItem('todos', JSON.stringify(newTodos));
});
// 导出所有需要在模板中使用的内容
export {
todos,
newTodoTitle,
completedTodos,
addTodo,
toggleTodo,
deleteTodo
};
特点
- 数据和逻辑分离 :状态(
todos
、newTodoTitle
)和操作(addTodo
、toggleTodo
)是分开定义的。 - 线性结构:代码按照功能逻辑组织成独立的函数。
- 灵活组合:可以轻松地复用单个函数或组合多个功能。
2. 面向对象编程(Object-Oriented Programming)
面向对象编程将数据和操作数据的方法封装在一起,形成对象,通过类来创建对象实例。在 Vue 3
中,可以使用类组件或在组合式 API
中封装类来实现。
示例:Todo 应用(面向对象)
ts
// Todo 应用 - 面向对象实现
import { ref, computed, onMounted, watch } from 'vue';
// 定义 Todo 类
class Todo {
constructor(
public id: number,
public title: string,
public completed: boolean = false
) {}
toggle() {
this.completed = !this.completed;
}
}
// Todo 管理器类
class TodoManager {
private _todos = ref<Todo[]>([]);
private _newTodoTitle = ref('');
constructor() {
// 初始化
onMounted(() => this.loadFromLocalStorage());
watch(this._todos, () => this.saveToLocalStorage());
}
// 获取 todos 的计算属性
get todos() {
return this._todos;
}
get newTodoTitle() {
return this._newTodoTitle;
}
get completedTodos() {
return computed(() =>
this._todos.value.filter(todo => todo.completed)
);
}
// 方法
addTodo() {
if (this._newTodoTitle.value.trim()) {
this._todos.value.push(
new Todo(Date.now(), this._newTodoTitle.value)
);
this._newTodoTitle.value = '';
}
}
toggleTodo(id: number) {
const todo = this._todos.value.find(t => t.id === id);
todo?.toggle();
}
deleteTodo(id: number) {
this._todos.value = this._todos.value.filter(t => t.id !== id);
}
// 私有方法
private saveToLocalStorage() {
localStorage.setItem(
'todos',
JSON.stringify(this._todos.value)
);
}
private loadFromLocalStorage() {
const savedTodos = localStorage.getItem('todos');
if (savedTodos) {
this._todos.value = JSON.parse(savedTodos).map(
(todo: any) => new Todo(todo.id, todo.title, todo.completed)
);
}
}
}
// 创建单例实例
const todoManager = new TodoManager();
// 导出需要在模板中使用的内容
export {
todoManager
};
特点
- 封装性:将数据(
_todos
、_newTodoTitle
)和操作(addTodo
、toggleTodo
)封装在TodoManager
类中。 - 继承与多态:可以通过继承扩展功能(例如创建
AdvancedTodoManager
)。 - 状态内聚:相关的状态和方法被组织在一起,便于维护和复用。
维度 | 面向过程 | 面向对象 |
---|---|---|
代码结构 | 函数和数据分离,线性组织 | 数据和方法封装在类中,层次化组织 |
复用方式 | 函数复用 | 类的继承和组合 |
状态管理 | 分散管理(多个 ref/reactive) | 集中管理(类的实例属性) |
适用场景 | 简单逻辑、工具函数、快速迭代 | 复杂业务逻辑、需要高内聚低耦合的系统 |
Vue 3 实现方式 | 组合式 API | 类组件或在组合式 API 中封装类 |
在实际项目中,通常会混合使用两种范式。例如:
- 使用面向过程的方式处理简单逻辑
- 使用面向对象的方式封装复杂业务领域模型
ts
// 混合使用示例:在组合式 API 中使用类
import { ref, computed, onMounted } from 'vue';
// 定义领域模型(面向对象)
class User {
constructor(public id: number, public name: string) {}
changeName(newName: string) {
this.name = newName;
}
}
// 在组合式 API 中使用(面向过程风格)
export function useUser() {
const currentUser = ref<User | null>(null);
const fetchUser = async (id: number) => {
// API 请求...
const userData = { id, name: 'John' };
currentUser.value = new User(userData.id, userData.name);
};
const userName = computed(() =>
currentUser.value?.name || 'Guest'
);
return {
currentUser,
userName,
fetchUser
};
}
- 面向过程适合简单、灵活的场景,在 Vue 3 中自然地体现为组合式 API 的使用。
- 面向对象适合复杂、需要高内聚的场景,可以通过类来封装业务逻辑。
3.函数式编程
3.1 对比
编程范式 | 核心思想 | 关键特性 |
---|---|---|
面向过程(POP) | 将程序分解为一系列步骤(函数),数据与操作分离 | 函数、顺序执行、共享状态 |
面向对象(OOP) | 将数据和操作封装为对象,通过类创建实例,强调继承和多态 | 类、对象、继承、封装、多态 |
函数式编程(FP) | 将计算视为函数的求值,避免共享状态和可变数据,强调纯函数和不可变性 | 纯函数、不可变数据、高阶函数、函数组合、无副作用 |
3.2 函数式编程与面向过程的关联
3.2.1 相似点
- 函数为基本单元:两者都使用函数作为代码的基本构建块。
- 过程式执行:都可以按照步骤顺序执行代码。
3.2.2 不同点
- 状态管理 :
- POP:依赖共享状态和可变数据。
- FP:避免共享状态,强调不可变性和纯函数。
- 函数特性 :
- POP:函数可以有副作用,修改外部状态。
- FP:函数是纯的,不产生副作用,相同输入始终返回相同输出。
POP 风格:
ts
// 面向过程:使用共享状态和有副作用的函数
const count = ref(0);
const increment = () => {
count.value++; // 修改共享状态
};
FP 风格:
ts
// 函数式:使用不可变数据和纯函数
const state = ref({ count: 0 });
const increment = () => {
state.value = { ...state.value, count: state.value.count + 1 }; // 创建新对象
};
3.3 函数式编程与面向对象的关联
3.3.1 互补性
- OOP擅长处理复杂的状态管理和对象关系(如继承、多态)。
- FP擅长处理数据转换和无副作用的操作。
3.3.2 不同点
- 封装方式 :
- OOP:通过类和对象封装数据和行为。
- FP:通过函数封装行为,数据和操作分离。
- 数据流动 :
- OOP:通过对象方法修改内部状态(可变数据)。
- FP:通过纯函数产生新数据(不可变数据)。
OOP 管理领域模型:
ts
class ShoppingCart {
private items = ref<Product[]>([]);
addItem(product: Product) {
this.items.value = [...this.items.value, product]; // 使用 FP 思想的不可变更新
}
}
FP 处理数据转换:
ts
// 使用纯函数处理数据
const calculateTotal = (items: Product[]) =>
items.reduce((total, item) => total + item.price, 0);
3.4. 函数式编程在 Vue 3 中的典型应用
3.4.1 纯函数组件
ts
// 纯函数组件(无状态)
const Button = (props: { label: string; onClick: () => void }) => {
return (
<button onClick={props.onClick}>{props.label}</button>
);
};
3.4.2 不可变数据处理
ts
// 使用 Immer 简化不可变数据操作
import produce from 'immer';
const updateUser = (userId: number, newData: Partial<User>) => {
users.value = produce(users.value, draft => {
const user = draft.find(u => u.id === userId);
if (user) {
Object.assign(user, newData);
}
});
};
3.4.3 高阶函数与组合
ts
// 高阶函数:创建可复用的状态处理器
const withLoading = <T>(fetcher: () => Promise<T>) => {
const loading = ref(false);
const data = ref<T | null>(null);
const error = ref<string | null>(null);
const execute = async () => {
loading.value = true;
try {
data.value = await fetcher();
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
return { loading, data, error, execute };
};
3.5. 三种范式的协作模式
3.5.1 分层架构
- 表现层 :
Vue
组件(可以是OOP
或POP
风格) - 业务逻辑层 :领域模型(
OOP
)+ 纯函数(FP
) - 数据访问层 :函数式的数据转换(
FP
)
3.5.2 混合使用示例
ts
// 组合式 API 中混合使用三种范式
import { ref, computed } from 'vue';
// OOP:领域模型
class Todo {
constructor(public id: number, public title: string) {}
markAsCompleted() {
return new Todo(this.id, this.title, true); // 使用 FP 思想创建新对象
}
}
// FP:纯函数处理数据
const filterTodos = (todos: Todo[], status: 'all' | 'completed' | 'active') => {
if (status === 'completed') return todos.filter(t => t.completed);
if (status === 'active') return todos.filter(t => !t.completed);
return todos;
};
// POP:组合式 API 组织逻辑
export function useTodos() {
const todos = ref<Todo[]>([]);
const filter = ref<'all' | 'completed' | 'active'>('all');
const filteredTodos = computed(() =>
filterTodos(todos.value, filter.value)
);
const addTodo = (title: string) => {
todos.value = [...todos.value, new Todo(Date.now(), title)];
};
return {
todos,
filter,
filteredTodos,
addTodo
};
}
场景 | 推荐范式 | 理由 |
---|---|---|
简单脚本或工具函数 | 面向过程(POP) | 快速实现,逻辑直接 |
复杂业务对象建模 | 面向对象(OOP) | 封装状态和行为,利用继承和多态 |
数据处理、流式计算 | 函数式编程(FP) | 纯函数和不可变性避免副作用,便于测试和调试 |
状态管理与组件交互 | 混合使用 | Vue 3 的组合式 API 天然支持多种范式,根据具体需求选择或组合使用 |