Vue架构全景图解带你从入门到深入理解组件化与响应式核心原理
一、Vue到底是什么?先把它当成一个智能的”乐高套装”
先抛掉那些复杂的术语。想象一下你在玩乐高积木,每一块积木都有自己的颜色、形状和功能,你可以随意拼接组合。Vue框架就是这样一个东西,它让你用”积木”的方式构建网页应用。
传统网页开发:
HTML → 一堆散乱的标签,JavaScript修改DOM就像用手去推动一堵墙
Vue开发:
数据变化 → Vue自动帮你更新界面
举个例子,假设你要做一个待办事项清单:
// 没有Vue,你得手动操作DOM
// 添加一个任务...
let taskInput = document.getElementById('taskInput');
let taskList = document.getElementById('taskList');
taskInput.addEventListener('keyup', function(e) {
if (e.key === 'Enter') {
let newTask = document.createElement('li');
newTask.textContent = taskInput.value;
taskList.appendChild(newTask);
taskInput.value = '';
}
});
// 用Vue,只需要描述数据
const app = new Vue({
el: '#app',
data: {
taskList: [],
newTask: ''
},
methods: {
addTask() {
if (this.newTask.trim()) {
this.taskList.push(this.newTask);
this.newTask = '';
}
}
}
});
看到区别了吗?Vue帮你省去了手动操作DOM的繁琐,你只需要告诉它数据是什么,它会自动帮你更新界面。
二、Vue的核心三板斧:组件化、响应式、虚拟DOM
2.1 组件化:像搭积木一样构建界面
组件化是Vue最核心的思想之一。每个组件都有自己的模板、逻辑和样式,它们可以像俄罗斯套娃一样嵌套使用。
┌─────────────────────────────────────────────────────────────┐
│ App(根组件) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Header组件 │ │ Main组件 │ │ Footer组件 │ │
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ │
│ │ │ Logo │ │ │ │ TaskList │ │ │ © 2024 │ │
│ │ │ Nav │ │ │ │ TaskForm │ │ │ │ │
│ │ └───────────┘ │ │ └───────────┘ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
每个组件都像是一个独立的小应用,它们通过props传递数据,通过events交流信息:
// 父组件
<template>
<div id="app">
<TaskList :tasks="taskList" @add-task="addTask"></TaskList>
<TaskForm @submit="addTask"></TaskForm>
</div>
</template>
<script>
import TaskList from './TaskList.vue'
import TaskForm from './TaskForm.vue'
export default {
components: { TaskList, TaskForm },
data() {
return {
taskList: []
}
},
methods: {
addTask(task) {
this.taskList.push(task);
}
}
}
</script>
// TaskList子组件
<template>
<ul>
<li v-for="(task, index) in tasks" :key="index">
{{ task }}
</li>
</ul>
</template>
<script>
export default {
props: {
tasks: {
type: Array,
required: true
}
}
}
</script>
props向下传递,events向上传递 —— 这是Vue组件通信的黄金法则。
2.2 响应式系统:数据的”自动同步”魔法
响应式系统是Vue最强大的地方。当你修改一个数据时,所有用到这个数据的界面都会自动更新。这背后的原理就像是一个”消息通知系统”:
数据变化 → 通知所有依赖 → 重新渲染界面
Vue 2的响应式原理(基于Object.defineProperty):
// 简化版的响应式实现
function defineReactive(data, key, value) {
// 递归处理嵌套对象
observe(value);
// 创建依赖收集器
const dep = new Dep();
Object.defineProperty(data, key, {
get() {
// 在计算属性或渲染函数执行时收集依赖
if (Dep.target) {
dep.addSub(Dep.target);
}
return value;
},
set(newVal) {
if (newVal === value) return;
value = newVal;
// 通知所有依赖更新
dep.notify();
}
});
}
function observe(data) {
if (!data || typeof data !== 'object') return;
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key]);
});
}
// 依赖收集器
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
// 观察者
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
this.getter = expOrFn;
Dep.target = this;
this.value = this.get();
Dep.target = null;
}
get() {
return this.getter.call(this.vm, this.vm);
}
update() {
const newValue = this.get();
if (newValue !== this.value) {
this.value = newValue;
this.cb.call(this.vm, newValue, this.value);
}
}
}
简单说,这就是一个”订阅-发布”模式:
- Watcher(观察者):监听数据变化,当数据改变时执行回调
- Dep(依赖收集器):收集所有依赖这个数据的Watcher
- defineReactive:劫持数据的getter/setter,实现数据的响应式
Vue 3的响应式原理(基于Proxy):
// Vue 3使用Proxy,更强大更简洁
function reactive(data) {
const handlers = {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
// 收集依赖(在computed或watch中)
track(target, key);
return result;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
// 触发依赖更新
if (oldValue !== value) {
trigger(target, key);
}
return result;
}
};
return new Proxy(data, handlers);
}
// 依赖收集
const targetMap = new WeakMap();
function track(target, key) {
if (Dep.target) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
dep.add(Dep.target);
}
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(sub => {
sub.update();
});
}
}
Vue 3的Proxy方案相比Vue 2的优势:
- ✅ 可以监听对象属性的添加和删除
- ✅ 可以监听数组的索引和长度变化
- ✅ 性能更好,不需要递归遍历所有属性
- ✅ 支持更多数据类型(Map、Set等)
2.3 虚拟DOM:高效更新的”中间人”
虚拟DOM是Vue性能优化的核心。它是一棵用JavaScript对象表示的DOM树,当数据变化时,Vue会先更新虚拟DOM,然后对比新旧虚拟DOM的差异,最后只更新实际需要变化的部分。
// 虚拟DOM的表示
const vnode = {
tag: 'div',
attrs: { id: 'app' },
children: [
{
tag: 'h1',
children: ['Hello Vue']
},
{
tag: 'p',
children: ['这是虚拟DOM']
}
]
};
// 渲染到真实DOM
function render(vnode) {
const element = document.createElement(vnode.tag);
// 设置属性
if (vnode.attrs) {
Object.keys(vnode.attrs).forEach(key => {
element.setAttribute(key, vnode.attrs[key]);
});
}
// 递归渲染子节点
if (vnode.children) {
vnode.children.forEach(child => {
if (typeof child === 'string') {
element.appendChild(document.createTextNode(child));
} else {
element.appendChild(render(child));
}
});
}
return element;
}
Diff算法的核心逻辑:
function patch(oldVnode, newVnode) {
// 1. 判断节点类型是否相同
if (oldVnode.tag !== newVnode.tag) {
// 类型不同,直接替换
const newElement = render(newVnode);
oldVnode.el.parentNode.replaceChild(newElement, oldVnode.el);
return newElement;
}
// 2. 类型相同,比较属性
const element = oldVnode.el;
// 更新属性
updateAttrs(element, oldVnode.attrs, newVnode.attrs);
// 3. 比较子节点
if (oldVnode.children && newVnode.children) {
patchChildren(element, oldVnode.children, newVnode.children);
} else if (newVnode.children) {
// 新增子节点
newVnode.children.forEach(child => {
element.appendChild(render(child));
});
}
return element;
}
function patchChildren(container, oldChildren, newChildren) {
const oldLen = oldChildren.length;
const newLen = newChildren.length;
// 简单的双端比较算法
let oldStart = 0;
let oldEnd = oldLen - 1;
let newStart = 0;
let newEnd = newLen - 1;
while (oldStart <= oldEnd && newStart <= newEnd) {
const oldStartVnode = oldChildren[oldStart];
const oldEndVnode = oldChildren[oldEnd];
const newStartVnode = newChildren[newStart];
const newEndVnode = newChildren[newEnd];
// 比较头部
if (oldStartVnode.key === newStartVnode.key) {
patch(oldStartVnode, newStartVnode);
oldStart++;
newStart++;
}
// 比较尾部
else if (oldEndVnode.key === newEndVnode.key) {
patch(oldEndVnode, newEndVnode);
oldEnd--;
newEnd--;
}
// 头尾交叉比较
else if (oldStartVnode.key === newEndVnode.key) {
patch(oldStartVnode, newEndVnode);
// 移动到正确位置
container.insertBefore(oldStartVnode.el, container.children[oldEnd + 1]);
oldStart++;
newEnd--;
}
// 尾头交叉比较
else if (oldEndVnode.key === newStartVnode.key) {
patch(oldEndVnode, newStartVnode);
// 移动到正确位置
container.insertBefore(oldEndVnode.el, container.children[oldStart]);
oldEnd--;
newStart++;
}
else {
// 找不到匹配的,删除旧节点
container.removeChild(oldStartVnode.el);
oldStart++;
}
}
// 处理剩余节点
if (oldStart <= oldEnd) {
// 删除多余的旧节点
while (oldStart <= oldEnd) {
container.removeChild(oldChildren[oldStart].el);
oldStart++;
}
}
if (newStart <= newEnd) {
// 新增多余的节点
while (newStart <= newEnd) {
container.appendChild(render(newChildren[newStart]));
newStart++;
}
}
}
虚拟DOM的核心价值:
- 跨平台:虚拟DOM不依赖真实的DOM API,可以渲染到Canvas、移动端等
- 性能优化:通过Diff算法最小化DOM操作
- 可预测性:数据的每次变化都有明确的渲染结果
三、Vue的数据流:从数据到界面的完整旅程
3.1 数据如何变成界面
理解Vue数据流,就像理解一条河流的走向:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户数据 │────▶│ Vue实例 │────▶│ 虚拟DOM │
│ (data) │ │ (实例化) │ │ (编译) │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌────────────────────┘
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 状态变化 │────▶│ 依赖收集 │────▶│ 重新渲染 │
│ (修改) │ │ (Watcher) │ │ (Patch) │
└─────────────┘ └─────────────┘ └─────────────┘
完整的数据流路径:
- 模板编译:将HTML模板编译成渲染函数
- 初始化响应式:将data中的数据变成响应式
- 创建Watcher:创建渲染Watcher,收集依赖
- 首次渲染:执行渲染函数,生成虚拟DOM,更新真实DOM
- 数据变化:修改数据,触发setter,通知Watcher
- 重新渲染:Watcher执行,生成新的虚拟DOM,Diff比较,更新真实DOM
3.2 编译过程详解
// Vue的编译过程
class Compiler {
constructor(vm) {
this.vm = vm;
this.el = document.querySelector(vm.$el);
this.compile(this.el);
}
compile(el) {
const children = el.childNodes;
children.forEach(child => {
// 处理元素节点
if (child.nodeType === 1) {
this.compileElement(child);
// 递归处理子节点
this.compile(child);
}
// 处理文本节点
else if (child.nodeType === 3) {
this.compileText(child);
}
});
}
compileElement(el) {
const attributes = el.attributes;
// 遍历所有属性
Array.from(attributes).forEach(attr => {
const name = attr.name;
const value = attr.value;
// 处理v-model
if (name.startsWith('v-model')) {
const key = this.compilerUtils.getMethodKey(value);
new CompilerWatchers(this.vm, key, el, 'input', 'input');
}
// 处理v-on
if (name.startsWith('v-on')) {
const eventName = name.substring(4);
const key = this.compilerUtils.getMethodKey(value);
el.addEventListener(eventName, () => {
this.vm[key]();
});
}
// 处理插值表达式
if (name === '{{') {
const key = this.compilerUtils.getMethodKey(value);
new CompilerWatchers(this.vm, key, el, 'textContent');
}
});
}
compileText(textNode) {
const content = textNode.textContent;
const reg = /\{\{(.+?)\}\}/;
if (reg.test(content)) {
const key = RegExp.$1.trim();
new CompilerWatchers(this.vm, key, textNode, 'textContent');
}
}
}
四、组件化开发的进阶技巧
4.1 Props验证
// 定义组件时验证props
export default {
props: {
// 基础类型检查
title: {
type: String,
required: true
},
// 多种类型
age: {
type: [Number, String],
default: 18
},
// 自定义验证函数
email: {
type: String,
validator: value => {
return value.includes('@');
}
},
// 对象/数组的默认值
tags: {
type: Array,
default: () => [] // 重要:对象/数组必须用函数返回
}
}
};
4.2 插槽(Slot):组件的”预留位置”
插槽让组件更加灵活:
<!-- 普通插槽 -->
<template>
<div class="card">
<div class="card-header">
<!-- 默认内容 -->
<slot>默认标题</slot>
</div>
<div class="card-body">
<!-- 内容插槽 -->
<slot name="body"></slot>
</div>
</div>
</template>
<!-- 使用 -->
<Card>
<template #body>
<p>这是卡片内容</p>
</template>
</Card>
<!-- 具名插槽 -->
<template #header>
<h1>自定义标题</h1>
</template>
4.3 作用域插槽:子组件向父组件传数据
<!-- 子组件 -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="item.index"></slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
]
}
}
}
</script>
<!-- 父组件使用 -->
<TodoList>
<template #default="{ item, index }">
<span>{{ index + 1 }}. {{ item.name }}</span>
</template>
</TodoList>
五、响应式系统的核心:依赖收集与触发
5.1 Dep类和Watcher类详解
// Dep类:依赖收集器
class Dep {
constructor() {
this.subs = []; // 存储所有依赖此数据的Watcher
}
// 添加依赖
addSub(sub) {
this.subs.push(sub);
}
// 移除依赖
removeSub(sub) {
const index = this.subs.indexOf(sub);
if (index > -1) {
this.subs.splice(index, 1);
}
}
// 通知依赖更新
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
// 全局唯一的Watcher
Dep.target = null;
// Watcher类:观察者
class Watcher {
constructor(vm, expOrFn, cb, options = {}) {
this.vm = vm;
this.cb = cb;
this.getter = expOrFn;
this.deep = !!options.deep;
this.value = this.get();
}
// 获取当前值
get() {
// 设置全局target,让依赖收集器知道当前是哪个Watcher
Dep.target = this;
let value;
try {
value = this.getter.call(this.vm, this.vm);
} finally {
Dep.target = null;
}
return value;
}
// 更新
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
5.2 响应式数据的核心实现
// 响应式数据的核心
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 创建响应式对象
let proxy = new Proxy(data, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
// 如果是对象,递归创建响应式
if (result && typeof result === 'object') {
observe(result);
}
// 收集依赖
track(target, key);
return result;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
// 通知依赖更新
if (oldValue !== value) {
trigger(target, key);
}
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key);
if (result) {
trigger(target, key);
}
return result;
}
});
return proxy;
}
// 依赖收集
const targetMap = new WeakMap();
function track(target, key) {
if (!Dep.target) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
dep.add(Dep.target);
}
// 触发更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(sub => {
sub.update();
});
}
}
5.3 处理数组的变化
// 数组的响应式处理
function observeArray(items) {
items.forEach((item, index) => {
observe(item);
});
}
// 数组方法重写
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
const original = arrayProto[method];
def(arrayMethods, method, function(...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
// 新增的元素需要观测
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
}
if (inserted) ob.observeArray(inserted);
// 通知更新
ob.dep.notify();
return result;
});
});
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
六、Vue的生命周期:组件的一生
创建阶段:
beforeCreate → created → beforeMount → mounted
更新阶段:
beforeUpdate → updated
销毁阶段:
beforeDestroy → destroyed
6.1 各生命周期的用途
export default {
// 实例创建之后,数据观测和事件配置完成,但DOM还没生成
beforeCreate() {
console.log('beforeCreate');
// 此时不能访问data和methods
},
// 实例创建完成,数据观测和事件配置完成,可以访问data和methods
created() {
console.log('created');
// 适合发起API请求,初始化数据
this.fetchData();
},
// 挂载前,虚拟DOM已生成,但还没渲染到页面
beforeMount() {
console.log('beforeMount');
},
// 挂载完成,DOM已生成
mounted() {
console.log('mounted');
// 适合操作DOM,初始化第三方库
this.initChart();
},
// 数据更新前
beforeUpdate() {
console.log('beforeUpdate');
},
// 数据更新后
updated() {
console.log('updated');
// 避免在这里修改数据,会导致无限循环
},
// 销毁前
beforeDestroy() {
console.log('beforeDestroy');
// 清理定时器、事件监听等
clearInterval(this.timer);
window.removeEventListener('scroll', this.handleScroll);
},
// 销毁完成
destroyed() {
console.log('destroyed');
}
}
6.2 生命周期在Composition API中的对应
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue';
export default {
setup() {
onBeforeMount(() => {
console.log('组件即将挂载');
});
onMounted(() => {
console.log('组件已挂载');
});
onBeforeUpdate(() => {
console.log('组件即将更新');
});
onUpdated(() => {
console.log('组件已更新');
});
onBeforeUnmount(() => {
console.log('组件即将销毁');
});
onUnmounted(() => {
console.log('组件已销毁');
});
return {};
}
}
七、Vue的进阶理解:从源码角度思考
7.1 一个简化版的Vue实现
class MiniVue {
constructor(options) {
this.$options = options;
this.$data = options.data || {};
this.$el = typeof options.el === 'string'
? document.querySelector(options.el)
: options.el;
// 响应式处理
this._proxyData(this.$data);
// 编译模板
this._compile();
// 创建 watcher
this._watcher = new Watcher(this, this.$options.template, (newVal) => {
this._render(newVal);
});
}
// 代理数据
_proxyData(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
get() {
return data[key];
},
set(newVal) {
data[key] = newVal;
}
});
});
}
// 编译模板
_compile() {
const fragment = this._node2fragment(this.$el);
this.$el.appendChild(fragment);
}
// 节点转片段
_node2fragment(el) {
const fragment = document.createDocumentFragment();
let child;
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
}
// 渲染
_render(newVal) {
this.$el.innerHTML = newVal;
}
}
// Watcher类
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.expOrFn = expOrFn;
this.cb = cb;
this.value = this.get();
}
get() {
Dep.target = this;
const value = typeof this.expOrFn === 'function'
? this.expOrFn(this.vm)
: this.vm[this.expOrFn];
Dep.target = null;
return value;
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
// Dep类
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
Dep.target = null;
7.2 计算属性的实现
class ComputedWatcher extends Watcher {
constructor(vm, expOrFn, watcherOptions) {
super(vm, expOrFn, watcherOptions);
this.dirty = watcherOptions.dirty;
this.dep = new Dep();
}
get() {
// 如果是脏数据,重新计算
if (this.dirty) {
this.value = this.getAndInvoke(() => {
return typeof this.getter === 'function'
? this.getter.call(this.vm, this.vm)
: this.vm[this.getter];
});
this.dirty = false;
}
// 收集依赖
if (Dep.target) {
this.dep.addSub(Dep.target);
}
return this.value;
}
evaluate() {
this.value = this.getAndInvoke(() => {
return typeof this.getter === 'function'
? this.getter.call(this.vm, this.vm)
: this.vm[this.getter];
});
this.dirty = false;
}
depend() {
if (Dep.target) {
this.dep.addSub(Dep.target);
}
}
teardown() {
if (this.vm._computedWatchers) {
const index = this.vm._computedWatchers.indexOf(this);
if (index > -1) {
this.vm._computedWatchers.splice(index, 1);
}
}
}
}
// 计算属性定义
function defineComputed(target, key, userDef) {
const shouldCache = !userDef;
Object.defineProperty(target, key, {
get: typeof userDef === 'function'
? function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
return watcher.get();
}
}
: function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
return watcher.get();
}
},
set: typeof userDef === 'function'
? function computedSetter(v) {
userDef.call(target, v);
}
: undefined
});
}
八、Vue 3 Composition API:组织复杂逻辑的新方式
8.1 为什么需要Composition API?
在Vue 2中,当组件逻辑变得复杂时,Options API(data、methods、computed等)会导致代码难以维护。Composition API提供了更好的逻辑复用和代码组织能力。
// Vue 2 Options API
export default {
data() {
return {
count: 0,
timer: null
}
},
methods: {
increment() {
this.count++;
},
startTimer() {
this.timer = setInterval(() => {
this.count++;
}, 1000);
}
},
mounted() {
this.startTimer();
},
beforeUnmount() {
clearInterval(this.timer);
}
}
// Vue 3 Composition API
import { ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const count = ref(0);
let timer = null;
function increment() {
count.value++;
}
function startTimer() {
timer = setInterval(() => {
count.value++;
}, 1000);
}
onMounted(() => {
startTimer();
});
onUnmounted(() => {
clearInterval(timer);
});
return {
count,
increment
};
}
}
8.2 组合函数(Composables)
// useCounter.js - 计数器逻辑复用
import { ref, computed } from 'vue';
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const doubled = computed(() => count.value * 2);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
function reset() {
count.value = initialValue;
}
return {
count,
doubled,
increment,
decrement,
reset
};
}
// useFetch.js - 数据获取逻辑复用
import { ref, onMounted } from 'vue';
export function useFetch(url) {
const data = ref(null);
const loading = ref(true);
const error = ref(null);
async function fetchData() {
loading.value = true;
try {
const response = await fetch(url);
data.value = await response.json();
} catch (e) {
error.value = e;
} finally {
loading.value = false;
}
}
onMounted(() => {
fetchData();
});
return {
data,
loading,
error,
fetchData
};
}
8.3 响应式API详解
import { ref, reactive, computed, watch, watchEffect } from 'vue';
// ref:处理基本类型
const count = ref(0);
console.log(count.value); // 0
count.value = 1; // 正确
// count = 1; // 错误,ref必须通过.value访问
// reactive:处理对象类型
const state = reactive({
count: 0,
name: 'Vue'
});
state.count = 1; // 直接赋值,不需要.value
state.name = 'React';
// computed:计算属性
const doubleCount = computed(() => {
return state.count * 2;
});
// watch:监听数据变化
watch(count, (newVal, oldVal) => {
console.log(`count从${oldVal}变为${newVal}`);
});
// watchEffect:自动收集依赖
watchEffect(() => {
console.log(`count is: ${count.value}`);
// 当count.value变化时,这个函数会自动执行
});
九、总结:Vue架构的核心思想
通过以上的详细图解和代码示例,我们可以总结出Vue架构的几个核心思想:
9.1 数据驱动
Vue的核心思想是数据驱动。你只需要关心数据的变化,Vue会自动帮你更新界面。这与传统的命令式编程(手动操作DOM)完全不同。
9.2 组件化
组件化让代码更加模块化和可复用。每个组件都是独立的,它们通过props和events进行通信。
9.3 响应式
响应式系统是Vue的灵魂。通过Object.defineProperty(Vue 2)或Proxy(Vue 3),Vue能够自动跟踪数据的变化,并通知相关的组件进行更新。
9.4 虚拟DOM
虚拟DOM是Vue性能优化的关键。它通过Diff算法最小化DOM操作,提高渲染效率。
9.5 组合式API
Vue 3的Composition API提供了更好的逻辑复用和代码组织能力,让大型项目的维护变得更加容易。
理解了这些核心原理,你就真正掌握了Vue的精髓。当然,这只是开始,Vue的生态系统非常庞大,还有路由、状态管理、服务端渲染等高级话题等待你去探索。希望这篇文章能帮助你建立起对Vue架构的整体认识,为你后续的学习打下坚实的基础!
