Vue现在已经迭代到 3+ 版本,阅读官方文档的过程中发现作者的一些理念和思路很合我口味,很多概念与方案都是基于解决实际问题提出并实现的,且在权衡利弊后勇于打破常规,比如如何看待关注点分离?。可见,Vue 之所以流行,不单单因为作者是国人,更应该是由于 Vue 作为新一代的解决方案提升了前端编程的体验与效率。

本文介绍几个核心概念。

选项式 vs 组合式

Vue 提供两种代码的书写风格——选项式组合式。可简单理解为:前者面向对象编程;后者函数式编程。

选项式:如果你有微信小程序的开发经验,就知道选项式是什么样子,其实就是将组件的逻辑封装到一个对象中,这个对象预定义多个字段和方法(如 data、methods 和 mounted),开发人员需要在适当的地方组织代码。对于有面向对象语言背景的用户来说,这通常与基于类的心智模型更为一致,同时,响应性相关的细节由框架本身处理,对初学者而言更为友好。

组合式:传统的自由无约束的编码风格,顶层就是各个成员变量和 functions,及一些钩子函数。似乎回到了 js 最初的模样,在对象、类、prototype 这些概念普及以前,大多数代码就是一坨变量加一坨 function,然后 onclick 调用。但是 Vue 的组合式风格依托其底层的依赖注入系统,及完善的响应式 API,使得情况不像看上去那么简单,而是呈现出一种螺旋向上的味道,耐人寻味。

官方文档对这两种风格有一些比较,个人比较倾向于组合式,所以本文 Vue 代码都是组合式的。

响应式基础

所谓响应式,就是视图会随着 JS 对象状态的改变而自动改变(也就是MVVM模式),有这种效果的对象就叫作响应式对象(其实就是 JavaScript Proxy)。在组合式 API 中,我们需要显式声明响应式对象,有两种方式——reactive()ref()

reactive()

该 API 返回的对象,是传入对象的代理对象,其所有属性及深层的子属性,都是响应式的。响应式对象的内嵌对象也是响应式对象,就算给它赋值普通对象,如:

const proxy = reactive({})

const raw = {}
proxy.nested = raw // proxy.nested 自动就是响应式对象 console.log(proxy.nested === raw) // false,代理对象和原始对象不是全等的

reactive() 的注意事项和原理

reactive() 有一定的局限:它仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的基础类型无效;需要尽量避免对一个响应式变量重新赋值,除非我们有办法将新对象和视图重新建立连接;且当我们将响应式对象的属性(基础类型)赋值或解构至本地变量时,或是将该属性传入一个函数时,我们会失去响应性,如:

let state = reactive({ count: 0 })
// 官方文档这里的表述不是很准确,下面是我的表述:
// 表面上看是重新赋值的 state 状态变化没有引起视图的变化,似乎响应连接丢失了,
// 其实原对象上的响应式连接还在,但是原对象在此处已无法继续访问,所以响应式连接在不在不重要了,
// 重建响应就需要建立视图和新对象的连接。
state = reactive({ count: 1 }) let n = state.count // 基础类型赋值,失去响应性连接
n++ // 不影响 state let { count } = state
count++ // 同上 // state.count 值传递给基础类型形参,也失去响应性连接了
callSomeFunction(state.count)

对于这些情况,有后端经验的同学如果将 reactive() 得到的响应式对象类比成引用类型对象就很好理解,这就是引用类型和值类型在使用过程中需要注意的一些点——变量赋值,如果是引用类型的话,那么指向的是对象的内存地址(新旧对象的内存地址自然是不一样的);如果是值类型,虽然代码看上去都是指向 state.count,其实是拷贝源值到自己的内存块,拷贝完了之后就和源没有关系了。

对于引用类型的“问题”,只要注意点就好了,但是在响应式的场景下,值类型的“拷贝”特性确实让人有点闹心。有没有类似于后端的装箱操作呢?

ref()

该 API 返回的也是响应式对象,它用于将值类型(基础类型)封装成引用类型(对象类型)。也就是说,我们将上一小节代码改造一下,就能保持基础类型数据在各个变量间传递后的响应性,如下:

const state = reactive({
count: ref(0)
}) let n = state.count // 现在 state.count 是引用类型,所以它和 n 指向的是同一个对象
n.value++ // 需要用 value 操作值
// 注意 value 也是响应式的,也就是传递给它的普通对象会自动转为响应式对象,和 reactive() 那边的情况一样
// 同时要注意直接替换掉整个对象会导致出现响应连接丢失的问题(上面提到过)
n.value = { name: 'Tony' }

简言之,我们可以将 ref 对象就看作引用类型对象,就能很快理解它的特性了。唯一要注意的是访问和操作它的值需要 .value,但是在某些时候框架也会帮我们自动解包(不需要使用 .value),可以参看官方文档。

组合式函数

是利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。说白了,就是业务逻辑封装,它表现形式不是对象,但是有状态,状态作为响应式对象对外暴露使用(如果有的话)。推测是为了和对象形式区分,才称之为组合式函数(就像选项式风格和组合式风格的区别)。

Vue 2 的用户可能会对mixins选项比较熟悉。它也让我们能够把组件逻辑提取到可复用的单元里,然而 mixins 没有自我范围的约束,就像页面里使用<script>引入的 js 文件,容易和其它 js 文件产生命名冲突,对象来源也不清晰,编码时不注意的话也容易产生模块和模块之间隐性的依赖。

其它几个 API

nextTick():DOM 更新是有间隔时间的,在间隔时间内每个组件发生的所有状态改变汇总后一次更新。可以给该函数传递一个回调,在最近的一次 DOM 更新后执行。类似于 HTML5 新增的 window.requestAnimationFrame()

watchEffect(callback):callback 中涉及到的响应式对象状态的变更会触发 callback 执行,如下:

const count = ref(0)
watchEffect(() => console.log(count.value)) // 马上执行一次,-> 输出 0
count.value++ // -> 输出 1

watch():同 watchEffect() 不同在于,watch() 需要显式地给它传递要监听的响应式对象。

构建工具 Vite

伴随 Vue 3 一起出来的还有新的构建工具Vite。下面会简单介绍 Vite 涉及到的关键技术和工具,以及同其它构建工具的比较。

ESM

不同于之前的CJS,AMD,CMD等,ESM是 ECMA 标准化模块系统,也就是说我们可以直接在浏览器中去执行 import,动态引入模块。作为 ECMA 标准,目前 ESM 已经得到 92% 以上浏览器的支持。

ESM 的执行可以分为三个步骤:

  1. 构建: 确定模板依赖关系,下载并将所有的文件解析为模块记录;
  2. 实例化: 将模块记录转换为一个模块实例,为所有的模块分配内存空间,依照导出、导入语句把模块指向对应的内存地址;
  3. 运行:运行代码,填充内存空间。

ESM 使用引用模式指向模块,也就是说如果引用的模块已经存在,那么直接返回模块的内存地址。而 CJS 采用的是拷贝模式,即所有导出模块都是独立的实例。可见前者比后者的效率要高。

基于 ESM,还能做到按需加载模块(碰到 import 再去请求加载文件)。但是我们一般只在开发环境下使用这个特性(不需要每次改动都导致整个 bundle 模块全量打包编译),原因如下段所述。

尽管原生ESM现在得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。为了在生产环境中获得最佳的加载性能,最好还是将代码预先进行Tree Shaking(移除那些没被使用的代码)、懒加载和 chunk 分割(以获得更好的缓存)。

Rollup

Rollup就是基于 ESM 模块的打包工具,比WebpackBrowserify使用的 CommonJS 模块机制更高效。Rollup 能针对源码进行 Tree Shaking,以及 Scope Hoisting 以减小输出文件大小提升运行性能。

Esbuild

Esbuild提供了与WebpackRollup等工具相似的资源打包能力,但其打包速度却是其他工具的 10~100 倍,原因有二:

  • 大多数前端打包工具都是基于 JavaScript 实现的,边运行边解释。而 Esbuild 则选择使用 Go 语言编写,编译为机器语言,在启动的时候直接执行,性能更高;
  • JavaScript 本质上是一门单线程语言,直到引入Web Worker后才有可能在浏览器、Node 中实现多线程操作,目前大部分打包工具未必有使用 Web Worker 提供的多线程能力。而 GO 则没这方面的“缺陷”,更不用说还有成熟的协程特性。

但是,虽然Esbuild快得惊人,并且已经是一个在构建库方面比较出色的工具,但一些重要功能仍然还在持续开发中——特别是代码分割和 CSS 处理方面(ESM 小节提到的加载性能)。就目前来说,Rollup 在应用打包方面更加成熟和灵活。所以,我们一般在开发时,使用Esbuild进行构建,而在生产环境,则是使用 Rollup 进行打包。

HMR 热更新

Webpack ——重新编译,请求变更后模块的代码,客户端重新加载。

Vite ——请求变更的模块,再重新加载。

Vite 通过chokidar监听文件系统的变更,使相关模块与其临近的 HMR 边界连接失效,只对发生变更的模块重新加载,这样 HMR 更新速度就不会因为应用体积的增加而变慢而 Webpack 还要经历一次打包构建。所以 HMR 场景下,Vite 表现也要好于 Webpack。


关于构建,需要注意的是,如果使用传统 <script src="xxx.js"> 方式引入 Vue 的话,那么就不会涉及到构建步骤,但同时将无法使用单文件组件 (SFC) 语法。传统方式一般用在对现有项目进行局部 Vue 改造的场景下。

Vue 关键概念介绍的更多相关文章

  1. Vue基本概念介绍及vue-cli环境搭建

    1 js中初始化一个Vue对象,传的参数就是对象属性. 挂载点.模板.实例之间的关系. var vm = new Vue({ el:"#app", template:'<di ...

  2. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)

    集群概念介绍(一)) 白宁超 2015年7月16日 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习 ...

  3. 《AngularJS深度剖析与最佳实践》笔记: 第二章 概念介绍

    第二章 概念介绍 2.1 什么是UI? 用户界面包括内容(静态信息+动态信息), 外观, 交互. 在前端技术栈中分别由HTML, CSS和JS负责. 进一步抽象, 分别对应于MVC三个主要部分: Mo ...

  4. 转载:【Oracle 集群】RAC知识图文详细教程(一)--集群概念介绍

    文章导航 集群概念介绍(一) ORACLE集群概念和原理(二) RAC 工作原理和相关组件(三) 缓存融合技术(四) RAC 特殊问题和实战经验(五) ORACLE 11 G版本2 RAC在LINUX ...

  5. 【转】【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)

    原文地址:http://www.cnblogs.com/baiboy/p/orc1.html 阅读目录 目录 集群概念介绍 什么是集群 为什么搭建数据库集群 数据库集群的分类 可扩展的分布式数据库架构 ...

  6. Docker关键概念阐述

    要了解Docker需要对其体系结构中的几个关键概念有所了解,主要包括image.container.service.swarm.stack等. 在介绍这几个概念时,会使用到一个测试环境,这个测试环境是 ...

  7. 【NS-3学习】ns3-模拟基础:关键概念,日志,命令行参数

    前言 本篇博客先介绍在仿真过程中会使用到的一些关键概念,然后介绍便于调试仿真脚本的常用技术:日志.命令行参数. 关键概念 节点 在因特网术语中,主机(终端)是指任何一台连接到网络的计算设备.ns-3并 ...

  8. Libra教程之:Libra协议的关键概念

    文章目录 Libra协议 交易和状态 交易详解 账本状态详解 版本数据库 账户 账户地址 Proof 验证节点 存储 Libra协议 Libra协议是Libra区块链的基础,本文主要讲解Libra协议 ...

  9. Linux LVM硬盘管理之一:概念介绍

    一.LVM概念介绍: LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauelshagen在Linux 2.4内核上实现.LVM将一个或多个硬盘的分区在逻 ...

  10. Java SE/ME/EE的概念介绍

    转自 Java SE/ME/EE的概念介绍 多数编程语言都有预选编译好的类库以支持各种特定的功能,在Java中,类库以包(package)的形式提供,不同版本的Java提供不同的包,以面向特定的应用. ...

随机推荐

  1. Kubernetes(K8S) 配置管理-ConfigMap 介绍

    作用:存储不加密数据到 etcd,让 Pod 以变量或者 Volume 挂载到容器中 场景:配置文件 创建配置文件 redis.properties redis.host=127.0.0.1 redi ...

  2. 【每日一题】【第n个 n-->0】19./NC53 【删除】链表的倒数第 N 个结点-211123/220127

    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点. 答案: import java.util.*; /* * public class ListNode { * int val; * ...

  3. 详记apache-poi的使用,将word,excel,ppt转换为html

    原文:https://blog.51cto.com/yunyaniu/5210961 java:Java的jar包之POI的简介.安装.使用方法(基于POI的转换-Word.Excel.Ppt等转ht ...

  4. 前端(js部分讲解)

    BOM操作 BOM概念 BOM:Browser Object Model 是浏览器对象模型,浏览器对象模型提供了独立与内容的.可以与浏览器窗口进行互动的对象结构,BOM由多个对象构成,其中代表浏览器窗 ...

  5. 图解B树及C#实现(2)数据的读取及遍历

    目录 前言 查询数据 算法说明 代码实现 查询最值 算法说明 代码实现 B树的遍历 算法说明 代码实现 Benchmarks 总结 参考资料 前言 本文为系列文章 B树的定义及数据的插入 数据的读取及 ...

  6. 【机器学习】李宏毅——Anomaly Detection(异常检测)

    异常检测概述 首先要明确一下什么是异常检测任务.对于异常检测任务来说,我们希望能够通过现有的样本来训练一个架构,它能够根据输入与现有样本之间是否足够相似,来告诉我们这个输入是否是异常的,例如下图: 那 ...

  7. pg_basebackup恢复:unrecognized configuration parameter "restore_command"

    问题描述:2022年最后一个工作日,时间过的真快,一晃又一年过去了,祝愿看到的各位元旦快乐. 使用pg_basebackup进行pg的备份恢复,在恢复的过程中,配置文件添加恢复的参数,一直启动报错. ...

  8. 2020强网杯青少赛Pursuing_The_Wind战队WRITEUP

    在线文档:https://docs.qq.com/doc/DZkN0RFFaR1ZDdHhD    旧事拾荒,偶遇该文档,既发. 战队信息 战队名称:Pursuing_The_Wind 战队排名:12 ...

  9. [深度学习] imgaug库使用笔记

    imgaug是一款非常有用的python图像增强库,非常值得推荐应用于深度学习图像增强.其包含许多增强技术,支持图像分类,目标检测,语义分割,热图.关键点检测等一系列任务的图像增强.本文主要介绍img ...

  10. [OpenCV实战]8 深度学习目标检测网络YOLOv3的训练

    目录 1 数据集 1.1 下载openImages雪人数据[约1.5小时] 1.2 训练集测试集拆分 2 Darknet 2.1 下载并构建Darknet 2.2 修改代码以定期保存模型文件 2.3 ...