当前位置: 首页 > article >正文

前端--深入了解Vue3

Vue 3 是一个现代化的前端框架,基于组件化的架构构建用户界面。它的核心原理围绕 响应式系统虚拟 DOM组件化设计 展开。以下是 Vue 3 核心原理的详细描述


1. 响应式系统

Vue 3 的响应式系统是其核心部分之一,通过数据和视图之间的绑定,自动管理 UI 更新。

1.1. Proxy 机制

Vue 3 引入了 JavaScript 的Proxy对象来实现响应式的核心。每当对象中的属性被读取、修改或删除时,Proxy可以捕获这些操作,并自动更新相关的视图。

代码示例:

let target = {};
let handler = {
    get: function(target, prop, receiver) {
        console.log(`Getting ${prop}!`);
        return Reflect.get(target, prop, receiver);
    },
    set: function(target, prop, value, receiver) {
        console.log(`Setting ${prop}!`);
        return Reflect.set(target, prop, value, receiver);
    }
};

let proxy = new Proxy(target, handler);
proxy.name = "John"; // 输出:Setting name!
console.log(proxy.name); // 输出:Getting name!  John

1.2. 依赖追踪

Vue 3 通过追踪组件依赖的状态来优化视图的更新。只会重新渲染那些真正依赖数据发生改变的部分,从而减少不必要的更新。

1.3. 计算属性与侦听器

通过 computed watch,开发者可以创建依赖于其他状态的派生数据或观察特定数据的变化。computed 是基于依赖自动缓存的,而 watch 则是响应数据的变化执行特定逻辑。

  • computed 是通过 effect 函数结合 Vue 的响应式系统实现的。每次 computed 依赖的数据发生改变,Vue 会标记它为“脏数据”,并在下次访问时重新计算。

示例代码:

// 假设有一个响应式系统
let activeEffect; // 当前的 effect,用于依赖追踪

// 依赖追踪器
class Dep {
  constructor() {
    this.subscribers = new Set();
  }

  // 依赖收集
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }

  // 通知所有依赖
  notify() {
    this.subscribers.forEach(effect => effect());
  }
}

// 实现响应式数据
function reactive(target) {
  const depsMap = new WeakMap(); // 用来存储每个属性的依赖

  return new Proxy(target, {
    get(obj, key) {
      let dep = depsMap.get(key);
      if (!dep) {
        dep = new Dep();
        depsMap.set(key, dep);
      }
      dep.depend(); // 依赖收集
      return obj[key];
    },
    set(obj, key, value) {
      obj[key] = value;
      const dep = depsMap.get(key);
      if (dep) {
        dep.notify(); // 依赖更新时通知
      }
      return true;
    }
  });
}

// 实现 effect 函数,用于追踪依赖
function effect(fn) {
  activeEffect = fn;
  fn(); // 立即执行一次,收集依赖
  activeEffect = null;
}

// 简单的 computed 实现
function computed(getter) {
  let cachedValue;
  let isDirty = true;

  // 创建一个依赖于 getter 的 effect
  const runner = effect(() => {
    if (isDirty) {
      cachedValue = getter();
      isDirty = false;
    }
  });

  return {
    get value() {
      if (isDirty) {
        runner(); // 如果数据是脏的,重新运行计算
      }
      return cachedValue;
    },
    set value(newValue) {
      console.warn('Cannot set value for a computed property');
    }
  };
}

// 示例:
// 定义响应式数据
const state = reactive({ num: 2 });

// 定义计算属性
const doubleNum = computed(() => {
  console.log('计算 doubleNum');
  return state.num * 2;
});

// 依赖追踪与重新计算
console.log(doubleNum.value); // 输出:计算 doubleNum, 4
state.num = 3; // 更新 num
console.log(doubleNum.value); // 输出:计算 doubleNum, 6
  • watch 是通过 Vue 3 的响应式系统中的依赖追踪机制实现的。它会观察指定的响应式数据,并在数据变化时触发回调函数。

示例代码:

// 简单的 watch 实现
function watch(source, callback) {
  // 创建一个依赖于 source 的 effect
  let oldValue;

  const runner = effect(() => {
    const newValue = source();
    if (newValue !== oldValue) {
      callback(newValue, oldValue); // 触发回调
      oldValue = newValue; // 更新旧值
    }
  });
}

// 示例:
// 定义响应式数据
const state = reactive({ num: 0 });

// 定义 watch 监听器
watch(() => state.num, (newVal, oldVal) => {
  console.log(`num 变化了: 从 ${oldVal} 到 ${newVal}`);
});

// 修改 num 值,会触发 watch 回调
state.num = 5; // 输出:num 变化了: 从 0 到 5
state.num = 10; // 输出:num 变化了: 从 5 到 10

2. 虚拟 DOM

Vue 3 使用 虚拟 DOM (Virtual DOM) 来提高性能。虚拟 DOM 是一个 JavaScript 对象的树结构,代表 UI 的状态。每次状态发生变化时,Vue 3 会创建一个新的虚拟 DOM,并将其与旧的虚拟 DOM 进行对比,使用 diff 算法 找出发生变化的部分,最终只更新需要变动的部分。

2.1. Diff 算法

Vue 3 中的虚拟 DOM diff 算法通过逐层比较新旧虚拟 DOM 节点来找出变化点,从而进行高效的最小化 DOM 更新。

2.2. 模板与渲染函数

Vue 3 通过模板引擎生成虚拟 DOM 结构,开发者也可以手写渲染函数来直接控制虚拟 DOM 的生成。Vue 的模板语法最终会被编译成渲染函数。

3. 组合式 API(Composition API)

Vue 3 提供了新的 组合式 API,允许开发者以更加灵活和可复用的方式组织组件逻辑。它是 Vue 3 的一个重要改进,相比于 Vue 2 的选项式 API 更加适合大型应用的开发。

3.1.setup函数

setup 是组合式 API 的核心,在组件实例创建之前调用。使用setup后,不用写setup函数;组件只需要引入不需要注册;属性和方法也不需要再返回,可以直接在template模板中使用。

代码示例:

	<template>
		<my-component @click="func" :numb="numb"></my-component>
	</template>
	<script lang="ts" setup>
		import {ref} from 'vue';
		import myComponent from '@/component/myComponent.vue';
		//此时注册的变量或方法可以直接在template中使用而不需要导出
		const numb = ref(0);
		let func = ()=>{
			numb.value++;
		}
	</script>

新增的api:

  • defineProps:子组件接收父组件中传来的props
  • defineEmits:子组件调用父组件中的方法
  • defineExpose:子组件暴露属性,可以在父组件中拿到

3.2. Reactivity API

包括ref、reactive、computed、watch等函数,允许开发者创建响应式的状态和计算属性。

  • ref用来处理基本数据类型的响应式(number、String、boolean、null、对象或数组),需要通过.value访问数据的内部值

代码示例:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    // 使用 ref 创建响应式数据,适用于单一数值或原始类型
    const count = ref(0);  // 包装了一个 number 类型

    const increment = () => {
      count.value++;  // 注意需要使用 .value 来访问和更新值
    };

    return {
      count,
      increment
    };
  }
};
</script>
  •  reactive用于处理复杂的对象类型和嵌套结构的响应式(如Vuex中的store状态),不需要通过.value访问值

代码示例:

<template>
  <div>
    <p>User Info:</p>
    <p>Name: {{ user.name }}</p>
    <p>Age: {{ user.age }}</p>
    <button @click="updateUser">更新用户</button>
  </div>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    // 使用 reactive 创建复杂对象的响应式数据
    const user = reactive({
      name: 'John',
      age: 25
    });

    const updateUser = () => {
      user.name = 'Jane';  // 直接修改属性,不需要 .value
      user.age = 30;
    };

    return {
      user,
      updateUser
    };
  }
};
</script>

4. 组件化设计

Vue 3 是基于 组件 的前端框架。组件是 Vue 应用的基本构建单元,每个组件封装了其自身的模板、逻辑和样式。

4.1. 单文件组件 (SFC)

Vue 3 使用 .vue 文件作为单文件组件,将模板、逻辑和样式组合在一个文件中,这使得组件更加易于维护和重用。单文件组件可以编译成原生 JavaScript 代码和 CSS。

4.1. Props 和事件

组件之间通过 props传递数据(props是单向数据流,父组件的数据会流向子组件,但是子组件无法修改),并通过事件实现父子组件之间的通信。Vue 3 中支持emit事件进行父组件和子组件之间的互动。

代码示例:

<template>
  <div>
    <h2>父组件</h2>
    <p>父组件的数据: {{ parentMessage }}</p>

    <!-- 通过 props 传递数据,并监听子组件触发的事件 -->
    <ChildComponent :message="parentMessage" @update-message="handleUpdateMessage" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: '这是来自父组件的数据'
    };
  },
  methods: {
    // 父组件处理子组件事件的方法
    handleUpdateMessage(newMessage) {
      this.parentMessage = newMessage;
    }
  }
};
</script>
<template>
  <div>
    <h3>子组件</h3>
    <p>接收到父组件的数据: {{ message }}</p>

    <!-- 触发事件,通知父组件 -->
    <button @click="updateMessage">更新父组件数据</button>
  </div>
</template>

<script>
export default {
  props: {
    message: {
      type: String,
      required: true
    }
  },
  methods: {
    updateMessage() {
      // 触发自定义事件,并传递新的消息给父组件
      this.$emit('update-message', '这是来自子组件的更新数据');
    }
  }
};
</script>

4.1. 插槽 (Slots):Vue 通过插槽机制允许开发者在组件中插入动态内容,支持具名插槽和作用域插槽来提供更强的灵活性。

代码示例:

  <template #createTime="{ record }">
        {{ moment(record.createTime).format("YYYY-MM-DD") }}
  </template>

//script数据定义
const columns = [
  {
    title: "题目名称",
    dataIndex: "title",
  },

  {
    title: "创建时间",
    slotName: "createTime",
  },
];

5. 模板编译与优化

Vue 3 的模板语法非常直观,它会将模板编译为虚拟 DOM 渲染函数。

5.1. 编译优化

Vue 3 的编译器针对不变的节点进行了静态提升,这意味着在渲染过程中不需要重新创建不变的虚拟 DOM,从而减少了不必要的开销。

5.2. 基于静态分析的优化

Vue 3 能够通过编译期的静态分析确定哪些节点是动态的,哪些是静态的,进而只追踪动态内容,进一步提升了渲染性能。

6. Fragment 和 Teleport

6.1. Fragment

Vue 3 的组件不再要求只能有一个根节点,允许返回多个根元素,这使得开发者编写组件时更加自由。

6.2. Teleport

允许开发者将组件的 DOM 渲染到另一个 DOM 位置,而不必局限于当前组件的作用域内。

7. 性能改进

7.1. Tree-shaking

Vue 3 采用模块化设计,可以通过 tree-shaking 移除未使用的代码模块,减少打包后的文件大小。

7.2. 内存优化

通过使用 Proxy 替代 Vue 2 的 Object.defineProperty 来实现响应式系统,减少了内存占用并提高了对大型数据集的处理效率。

8. 生态与工具链

8.1. Vue CLI 和 Vite

Vue 提供了强大的 CLI 工具和快速的构建工具 Vite 来简化项目的创建和开发环境的搭建。

8.2. Vue Router 和 Vuex

Vue 3 生态中包含 Vue Router(路由管理)和 Vuex(状态管理),提供了完整的解决方案来构建大型单页应用。

Vue Router示例代码:

export const routes: Array<RouteRecordRaw> = [
  {
    path: "/user",
    name: "用户",
    component: UserLayout,
    children: [
      {
        path: "/user/login",
        name: "用户登录",
        component: UserLoginView,
      },
      {
        path: "/user/register",
        name: "用户注册",
        component: UserRegisterView,
      },
    ],
    meta: {
      hideInMenu: true,
    },
  },
];

Vuex示例代码:

export default {
  namespaced: true,
  state: () => ({
    loginUser: {
      userName: "xuwei",
    },
  }),
  actions: {
    async getLoginUser({ commit, state }, payload) {
      // 从远程请求获取登录信息
      const res = await UserControllerService.getLoginUserUsingGet();
      if (res.code === 0) {
        commit("updateUser", res.data);
      } else {
        commit("updateUser", {
          ...state.loginUser,
          userRole: ACCESS_ENUM.NOT_LOGIN,
        });
      }
    },
  },
  mutations: {
    updateUser(state, payload) {
      state.loginUser = payload;
    },
  },
} as StoreOptions<any>;

http://www.kler.cn/news/363593.html

相关文章:

  • 502 错误码通常出现在什么场景?
  • 基于SpringBoot+Vue+uniapp微信小程序的社区门诊管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
  • 在MySQL中建索引时需要注意哪些事项?
  • DevEco Studio的使用 习题答案 HarmonyOS第一课
  • 912.排序数组(归并排序)
  • SpringMVC常用注解
  • LeetCode题(二分查找,C++实现)
  • Jsoup在Java中:解析京东网站数据
  • OpenText ALM Octane,为您的 DevOps 管道提供质量保证
  • Java程序设计:spring boot(3)——spring boot核心配置
  • 学习--四元数介绍
  • Egg.js 项目的合理 ESLint 配置文件模板
  • qt 构建、执行qmake、运行、重新构建、清除
  • G - Add and Multiply Queries
  • C语言程序设计:现代设计方法习题笔记《chapter4》
  • Qt 实战(11)样式表 | 11.2、使用样式表
  • el-table动态新增/删除表单行及校验规则
  • 5G 现网信令参数学习(1) - MIB
  • 【openwrt-21.02】T750 openwrt 概率出现nat46_ipv4_input+0x90/0x4b4问题分析及解决方案
  • 【C++干货篇】——C/C++内存管理
  • yolov8s.pt转换成onxx再转换成rknn模型用于RK3588
  • arp代答观察
  • LeetCode:第1290题 二进制链表转整数
  • 构建物联网智能项目的框架与实践
  • 短视频去水印小程序流量主最新接口带配音功能
  • Python 学习笔记(十二)—— 网络编程