Vue 关键概念介绍
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 的执行可以分为三个步骤:
- 构建: 确定模板依赖关系,下载并将所有的文件解析为模块记录;
- 实例化: 将模块记录转换为一个模块实例,为所有的模块分配内存空间,依照导出、导入语句把模块指向对应的内存地址;
- 运行:运行代码,填充内存空间。
ESM 使用引用模式指向模块,也就是说如果引用的模块已经存在,那么直接返回模块的内存地址。而 CJS 采用的是拷贝模式,即所有导出模块都是独立的实例。可见前者比后者的效率要高。
基于 ESM,还能做到按需加载模块(碰到 import 再去请求加载文件)。但是我们一般只在开发环境下使用这个特性(不需要每次改动都导致整个 bundle 模块全量打包编译),原因如下段所述。
尽管原生ESM现在得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。为了在生产环境中获得最佳的加载性能,最好还是将代码预先进行Tree Shaking(移除那些没被使用的代码)、懒加载和 chunk 分割(以获得更好的缓存)。
Rollup
Rollup就是基于 ESM 模块的打包工具,比Webpack和Browserify使用的 CommonJS 模块机制更高效。Rollup 能针对源码进行 Tree Shaking,以及 Scope Hoisting 以减小输出文件大小提升运行性能。
Esbuild
Esbuild提供了与Webpack、Rollup等工具相似的资源打包能力,但其打包速度却是其他工具的 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 关键概念介绍的更多相关文章
- Vue基本概念介绍及vue-cli环境搭建
1 js中初始化一个Vue对象,传的参数就是对象属性. 挂载点.模板.实例之间的关系. var vm = new Vue({ el:"#app", template:'<di ...
- 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)
集群概念介绍(一)) 白宁超 2015年7月16日 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习 ...
- 《AngularJS深度剖析与最佳实践》笔记: 第二章 概念介绍
第二章 概念介绍 2.1 什么是UI? 用户界面包括内容(静态信息+动态信息), 外观, 交互. 在前端技术栈中分别由HTML, CSS和JS负责. 进一步抽象, 分别对应于MVC三个主要部分: Mo ...
- 转载:【Oracle 集群】RAC知识图文详细教程(一)--集群概念介绍
文章导航 集群概念介绍(一) ORACLE集群概念和原理(二) RAC 工作原理和相关组件(三) 缓存融合技术(四) RAC 特殊问题和实战经验(五) ORACLE 11 G版本2 RAC在LINUX ...
- 【转】【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)
原文地址:http://www.cnblogs.com/baiboy/p/orc1.html 阅读目录 目录 集群概念介绍 什么是集群 为什么搭建数据库集群 数据库集群的分类 可扩展的分布式数据库架构 ...
- Docker关键概念阐述
要了解Docker需要对其体系结构中的几个关键概念有所了解,主要包括image.container.service.swarm.stack等. 在介绍这几个概念时,会使用到一个测试环境,这个测试环境是 ...
- 【NS-3学习】ns3-模拟基础:关键概念,日志,命令行参数
前言 本篇博客先介绍在仿真过程中会使用到的一些关键概念,然后介绍便于调试仿真脚本的常用技术:日志.命令行参数. 关键概念 节点 在因特网术语中,主机(终端)是指任何一台连接到网络的计算设备.ns-3并 ...
- Libra教程之:Libra协议的关键概念
文章目录 Libra协议 交易和状态 交易详解 账本状态详解 版本数据库 账户 账户地址 Proof 验证节点 存储 Libra协议 Libra协议是Libra区块链的基础,本文主要讲解Libra协议 ...
- Linux LVM硬盘管理之一:概念介绍
一.LVM概念介绍: LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauelshagen在Linux 2.4内核上实现.LVM将一个或多个硬盘的分区在逻 ...
- Java SE/ME/EE的概念介绍
转自 Java SE/ME/EE的概念介绍 多数编程语言都有预选编译好的类库以支持各种特定的功能,在Java中,类库以包(package)的形式提供,不同版本的Java提供不同的包,以面向特定的应用. ...
随机推荐
- .net6&7中如何优雅且高性能的使用Json序列化
.net中的SourceGenerator让开发者编可以写分析器,在项目代码编译时,分析器分析项目既有的静态代码,允许添加源代码到GeneratorExecutionContext中,一同与既有的代码 ...
- 【小项目】微信定时推送天气预报Github项目使用及原理介绍-包含cron、天气预报、常用api
一.资料链接 1.github地址 https://github.com/qq1534774766/wx-push 2.教程地址 https://blog.csdn.net/qq15347747/ar ...
- Zabbix6.0使用教程 (一)—zabbix新增功能介绍1
使用zabbix的小伙伴应该都有关注到目前zabbix的大版本已经更新到了6.0,后面乐乐将会对如何使用zabbix6.0做一个使用教程的系列,大家可以持续关注,这篇我们主要聊聊zabbix6.0新增 ...
- day35-JSON&Ajax03
JSON&Ajax03 4.jQuery的Ajax请求 原生Ajax请求问题分析: 编写原生的Ajax要写很多的代码,还要考虑浏览器兼容问题,使用不方便 在实际工作中,一般使用JavaScri ...
- 实时采集MySQL数据之轻量工具Maxwell实操
@ 目录 概述 定义 原理 Binlog说明 Maxwell和Canal的区别 部署 安装 MySQL准备 初始化Maxwell元数据库 Maxwell进程启动 命令行参数 配置文件 实时监控Mysq ...
- 安装node.js与webpack创建vue2项目
本文为博主原创,转载请注明出处: 1.安装node.js 下载地址:http://nodejs.cn/download/ (可查看历史版本) node.js 中文网:http://nodejs.cn/ ...
- [编程基础] Python命令行解析库argparse学习笔记
Python argparse教程展示了如何使用argparse模块解析Python中的命令行参数. 文章目录 1 使用说明 1.1 Python argparse可选参数 1.2 Python ar ...
- 使用 GPG 签名提交
GPG 签名是对代码提交者进行身份验证的一种补充,即证明代码提交来密钥持有者,理论上可以确保在目前的破译技术水平下无法篡改内容.您可以使用 GPG 工具 (GNU Privacy Guard) 生成密 ...
- linux 高效压缩工具之xz的压缩解压使用
xz是什么 高压缩率的工具,它使用 LZMA2 压缩算法,生成的压缩文件比传统使用的 gzip.bzip2 生成的压缩文件更小, 不过xz也有一个坏处就是压缩时间比较长,比7z压缩时间还长一些.不过压 ...
- 标准&有效的项目开发流程
代码版本管理 在项目中,代码的版本管理非常重要.每个需求版本的代码开发在版本控制里都应该经过以下几个步骤. 在master分支中拉取该需求版本的两个分支,一个feature分支,一个release分支 ...