Vue CLI3 快速启动指南(一)
原文:
zh.annas-archive.org/md5/31ebad88f7990ce0d7b13055dbe49dcf译者:飞龙
前言
Vue 最初是由一个人 Evan You 发起的项目。令人惊讶的是,它已经发展到今天这个地步:成为最受欢迎的前端框架之一,与由公司支持的 React 和 Angular 竞争。
当然,这些并不是唯一的前端框架,但 Vue、React 和 Angular 这三者似乎是最受欢迎的,互联网上充斥着这些框架的比较和使用经验。比如,很常见的是看到一篇比较 Vue 和 React 的文章,或者一篇关于 Vue 比 Angular 更好的博客文章。无论这些文章是某人的观点,还是标题党,或者是事实陈述,这些说法肯定有一些真实性。
Vue 成功的原因是什么?是奉献、努力工作还是运气?可能都有一点。但 Vue 成功的另一个关键是 Evan 明显地优先考虑了为开发人员简化事情。Vue 不再是由一个人开发,但它仍然非常易于接近。社区一直保持着 Vue 从一开始就具有的要点:一个易于使用的框架,让你自由编码。
Vue CLI 就是这一成功的又一个例子。除了一个与其他现代前端框架相匹敌的命令行界面外,Vue CLI 3 还在前端 JavaScript 框架中树立了新的标准,并配备了图形用户界面(GUI)。这个界面使得设置、扩展、运行和服务一个 Vue 项目变得轻而易举。
当你将这个 GUI 的添加与成功地试图通过提供一个经过深思熟虑的设置过程来减轻工具链疲劳的尝试相结合时,你会得到一个非常强大的组合,开发人员也会因此而感到高兴。
这本书是为谁准备的
这本书是为网页开发人员和 JavaScript 开发人员准备的,他们想要更多地了解 Vue CLI 3。读者必须具备 HTML/CSS 和 JavaScript 的基本知识。基本上,读者还应该熟悉基本的操作系统工作流程,比如使用类 UNIX 命令行界面,包括 Git Bash、Windows PowerShell 或任何相关的命令行工具。
这本书深入探讨了 Vue CLI 3 的技术构建模块。这不是一本关于在 Vue 中编写应用程序的书。这更像是一本基础性的书,将帮助您了解 Vue CLI 内部工作原理。如果您从未完全确定 NPM 的工作原理以及如何正确使用它,这本书将通过 Vue CLI 3 的视角来解释。同样,我们将研究 webpack,HMR,使用单文件.vue组件,SCSS,ECMAScript,使用 Jest 进行单元测试以及使用 Cypress 进行端到端测试。
本书涵盖的内容包括:
第一章《介绍 Vue CLI 3》解释了如何使用 Vue CLI 3 以及为什么应该使用它。它涵盖了最佳实践以及使用 Vue CLI 3 会得到什么。我们将设置 Node 版本管理器和 NPM,安装 Vue CLI 3,并展示如何通过命令行或 GUI 启动新应用程序。
第二章《Vue CLI 3 中的 Webpack》带领读者回顾了过去几年 JavaScript 的发展概况,这导致了 webpack 的出现。它解释了一些背景概念:NPM 和 NPM 脚本,CommonJS,JS 和 Node.js 中的模块,以及模块捆绑器以及它们在浏览器中的使用。此外,我们介绍了 webpack 的工作原理以及如何运行它。最后,我们逐步解释了如何通过 NPM 添加 Vue 项目并使用 webpack。基本上,我们正在手动设置 Vue 工具链,以便我们可以欣赏 Vue CLI 3 自动为我们做了什么。
第三章《Vue CLI 3 中的 Babel》探讨了如何使用 Babel 以及使用它的好处。我们检查了 Vue 核心 Babel 插件的构建模块,包括Babel 7,babel-loader和@vue/babel-preset-app。我们还研究了使用 ES5 和 ES6 运行 webpack 的区别,并更新了我们的 webpack 配置,以便它能理解 Babel。
第四章《Vue CLI 3 中的测试》介绍了使用 Vue 插件,重点介绍了用于测试的插件。我们向 Vue 应用程序添加了 Jest 插件,使用 Jest 运行单元测试,并在 Vue CLI 3 GUI 中展示了一些额外的技术和工作流程,包括从项目任务页面运行任务以及在 GUI 中运行单元测试。我们讨论了测试驱动开发(TDD)以及使用断言,并在章节末尾概述了 Cypress。
第五章《Vue CLI 3 和路由》讨论了使用 vue-router 和 vuex 添加 Vue 项目,配置预设选项以及理解 vue-router 主题。这些包括命名路由、动态路由、使用 Vue 实例中的方法导航到路由、使用子路由以及延迟加载路由。
第六章《在 Vue CLI 3 中使用 ESlint 和 Prettier》向我们展示了 ESlint 是什么以及它的用处。我们还看了 Prettier,一个方便的代码格式化程序,可以在每次保存时格式化您的代码。我们讨论了通用的代码检查器以及它们的用途。
第七章《使用 SCSS 改进 CSS》描述了 SCSS 的基础知识,展示了它与 CSS 的不同之处以及可用的附加功能。我们使用了在第五章《Vue CLI 3 和路由》中构建的简单应用程序,并看到如何通过向应用程序添加 boostrap-vue 插件来改进其样式。在 VDOM 库中使用 SCSS 有时会令人困惑,在本章中,我们看到了一种实际的工作流选项。
第八章《在 GitHub Pages 上部署 Vue CLI 3 应用程序》解释了 Git 是什么以及如何设置它。我们讨论了一些基础知识,包括使用 Git 跟踪更改和提交应用程序中的更改。我们继续讨论了三棵树概念、分支和合并分支。我们注册了 GitHub 帐户,使用 GitHub Desktop 添加了 origin/master,并了解了如何发布本地存储库。最后,我们讨论了如何使用subtree功能在 GitHub 页面上部署 Vue 应用程序。
要充分利用本书
要充分利用本书,您应该对使用 Windows、HTML、CSS、JavaScript 的基础知识以及使用 Git Bash 等命令行工具有一定的了解。熟悉 Node、NPM 和一些基本的命令行实用程序将是有益的,但这并非强制要求。
下载示例代码文件
您可以从www.packt.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packt.com/support并注册,以便直接通过电子邮件接收文件。
您可以按照以下步骤下载代码文件:
-
登录或注册www.packt.com。
-
选择“支持”选项卡。
-
单击“代码下载和勘误”。
-
在搜索框中输入书名,然后按照屏幕上的说明操作。
文件下载后,请确保使用最新版本解压或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
该书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Vue-CLI-3-Quick-Start-Guide。如果代码有更新,将在现有的 GitHub 存储库上进行更新。
下载彩色图像
我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在这里下载它:www.packtpub.com/sites/default/files/downloads/9781789950342_ColorImages.pdf。
使用的约定
本书中使用了许多文本约定。
CodeInText:表示文本中的代码词,数据库表名,文件夹名,文件名,文件扩展名,路径名,虚拟 URL,用户输入和 Twitter 句柄。这是一个例子:“将下载的WebStorm-10*.dmg磁盘映像文件挂载为系统中的另一个磁盘。”
代码块设置如下:
{
"name": "vue-from-npm",
"version": "1.0.0",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:
let CustomArticle = Vue.component('custom-article', {
template: `
<article>
Our own custom article component!
</article>`
})
任何命令行输入或输出都以以下方式编写:
mkdir new-project-with-webpack && cd new-project-with-webpack
粗体:表示一个新术语,一个重要的词,或者你在屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“从管理面板中选择系统信息。”
警告或重要说明会以这种方式出现。提示和技巧会以这种方式出现。
第一章:介绍 Vue CLI 3
本书介绍了Vue CLI 3,并回答了诸如如何使用它,为什么使用它,最佳实践以及您将从中获得什么等问题。
在本章中,我们将看看如何在我们的系统上设置 Vue CLI 3。我们将首先检查 Vue CLI 3 是否已经可用,然后我们将看到如果我们需要进行全新安装或从先前版本进行更新的确切步骤。
然后我们将看看如何安装Node 版本管理器(NVM),以及为什么这比简单安装 Node 更好。我们将看到在 VS Code 中如何轻松开始使用 Vue CLI 3,以及如何通过使用命令行来集成所有这些工具。
我们还将讨论为什么使用 Vue CLI 3 非常好,并且我们将通过从命令行和使用内置的 Vue CLI 3 UI 功能来运行默认的 Vue CLI 3 应用程序来实践这一点。
我们将在本章中涵盖以下主题:
-
在您的系统上设置 Vue CLI 3
-
安装 Vue CLI 3
-
安装 VS Code
-
使用无配置的 Vue CLI
-
使用 Vue CLI 3 的好处
-
通过默认工具链避免 JavaScript 疲劳
我们将从设置 Vue CLI 3 开始本章。
技术要求
我们只需要一些技术要求;它们如下:
-
Windows 安装(Windows 7 或更高版本)
-
为 Windows 安装 NVM(安装的具体步骤在本章中描述)
-
安装 VS Code(代码编辑器)
让我们开始在我们的系统上设置 Vue CLI 3。
在我们的系统上设置 Vue CLI 3
使用 Vue CLI 3 的常见方式是通过一个名为命令行界面(CLI)的命令行应用程序,在那里我们运行我们的 Vue CLI 3 命令。另一个先决条件是在我们的计算机上安装 Node.js。
如果您在共享计算机上工作,比如在您的开发团队中,很有可能您已经具备了所有的先决条件。在这种情况下,您可以通过运行一些检查来验证您是否可以立即开始使用 Vue CLI 3。
Vue CLI 3 已经可用吗?
要快速检查您是否可以立即运行 Vue CLI 3 并跳过所有安装步骤,请在命令行应用程序中运行以下命令:
node --version
还可以使用此命令检查 Vue CLI 3:
vue -V
如果您得到任何高于 8.9 的 Node 版本(理想情况下,高于 8.11.0),您就可以开始了。显然,对于 Vue CLI,您希望得到任何高于 3.0.0 的版本。
此外,如果您的 Vue CLI 版本低于 V3,或者您想更新到最新的 Vue CLI,例如 3.3.0,只需运行此命令:
npm install @vue/cli
如果您没有安装 Node.js 或 Vue CLI 怎么办?
我们将使用nvm或nvm-windows来安装 Node,然后安装 Vue CLI 3。
使用 Node 版本管理器安装 Node.js
我们应该使用推荐的 Node.js 版本是多少?此信息可在以下链接找到:cli.vuejs.org/guide/installation.html。
目前,截至 2019 年初,要在 Vue CLI 中获得最佳结果,所需的 Node 的最低版本是 8.11.0+,但如果确实需要,您也可以使用 8.9。
这带我们来到另一个重要的决定:安装 NVM。
为什么要安装 NVM?
虽然不绝对需要安装 NVM 才能在系统上运行 Vue CLI 3,但出于几个原因,安装 NVM 是可取的。
首先,您永远不知道 Node 何时会推荐更新以修复安全问题,这通常意味着最好在您的计算机上安装更新。
其次,如果您需要运行除 Vue 之外的其他技术,这些其他技术可能还需要不同版本的 Node。要在系统上轻松切换这些所需的 Node 安装,您可以简单地安装 NVM。
在 Windows 上安装 NVM
您可以从此地址下载 Windows 的 NVM:
https://github.com/coreybutler/nvm-windows/releases
找到nvm-setup.zip文件,下载并从中提取nvm-setup.exe,然后按照以下安装步骤进行安装:
-
按下 Windows + R打开运行提示。在提示符中键入
cmd。 -
在提示符内部,按下Ctrl + Shift + Enter。这将以管理员权限运行命令提示符,这是下一步所需的。
-
访问
nodejs.org,查看当前的长期支持(LTS)版本号。例如,目前在 64 位 Windows 上,LTS 版本是 10.15.1。 -
要安装它,请在具有管理员权限的命令提示符中运行以下命令:
nvm install 10.15.1
- 命令提示符将记录以下消息:
Downloading node.js version 10.15.1 (64-bit) ...
- 下载完成后,我们可以使用下载的 Node 版本。我们用以下命令来做:
nvm use 10.15.1
- 最后,您可以通过运行以下命令来验证安装是否成功:
node --version
- 如果您想了解与您的 Node 安装一起提供的
npm的版本,只需运行以下命令:
npm --version
接下来,我们将安装 Vue CLI 3。
安装 Vue CLI 3
我们可以使用npm或yarn来安装 Vue CLI 3。由于npm与 Node.js 安装捆绑在一起,我们将使用npm:
npm install -g @vue/cli --loglevel verbose
上述命令会全局安装 Vue CLI 3。这就是-g标志的作用。@vue/cli的语法是我们在 Vue CLI 3 中使用的,--loglevel verbose将记录我们安装的细节,这非常有用,特别是在较慢的连接和较慢的机器上,有时我们可能会开始怀疑我们的控制台是否冻结。使用--loglevel verbose,就会更清晰,这总是好的。
完成后,让我们通过运行此命令来双重检查安装的 Vue CLI 版本:
vue --version
以下是一些其他有用的命令,您应该在控制台中尝试:
vue -h
请注意,vue -h是vue --help的别名。我使用前者是因为它更容易输入。
还要注意,您可以在每个单独的vue命令上运行-h标志,例如:
vue create -h
vue add -h
vue invoke -h
vue inspect -h
vue serve -h
vue build -h
vue ui -h
vue init -h
vue config -h
vue upgrade -h
vue info -h
运行任何上述命令将返回特定命令的用法说明,描述其功能以及要附加到每个单独命令的选项(标志)。显然,-h标志是探索 Vue CLI 功能的好方法,并且在需要时即时刷新您的记忆。
接下来,我们将安装我们选择的代码编辑器 VS Code。
安装 VS Code
要安装 VS Code,只需转到code.visualstudio.com,然后下载适合您操作系统的版本。
如果您不确定自己使用的是 32 位还是 64 位计算机,您可以通过在命令提示符(具有管理员权限)中运行以下命令来快速检查 Windows 上的情况:
wmic os get osarchitecture
输出将是OSArchitecture,在下一行,要么是32 位,要么是64 位。
一旦 VS Code 被下载,只需运行下载的安装文件并按照安装说明进行安装。
安装完 VS Code 后,您将在命令行中获得一个额外的命令,code。
code命令非常有用,我们将在下一节中看到。
在没有配置的情况下使用 Vue CLI
在本节中,我们将看到使用 Vue CLI 的最快最简单的方法。它完全不需要任何配置!使用 Vue CLI 无需配置的原因是为了进行一些快速实验,而不必回答关于项目配置的提示,这是 Vue CLI 在运行包含配置步骤的项目时通常会询问的(这是使用 Vue CLI 构建应用程序的默认方法)。
首先,按住Shift键,在桌面的空白区域右键单击。从弹出的上下文菜单中,单击“在此处打开命令窗口”命令。
打开后,输入以下命令:
mkdir noConfig
这将创建一个名为noConfig的新目录。接下来,让我们使用cd命令切换到该目录:
cd noConfig
最后,使用以下命令从命令提示符启动 VS Code:
code .
前面命令中的点表示在当前文件夹中打开 VS Code。可以关闭欢迎标签页。
接下来,使用Alt + F键盘快捷键打开文件菜单,并按下N键打开一个全新的文件。
在新文件中,打开标签页,输入以下代码:
<template>
<h1>What's up, Vue CLI 3?</h1>
<hr>
</template>
接下来,按下Ctrl + S键盘快捷键,将文件保存为App.vue。
VS Code 将保存文件。它将给出一个新的图标,Vue 标志图标,这是一个视觉提示,刚刚保存的文件确实是一个 Vue 文件。
VS Code 也可能提示您安装一个名为Vetur的扩展,具体提示如下:
The 'Vetur' extension is recommended for this file type.
通过单击弹出窗口底部的安装按钮来安装扩展。
请注意,安装Vetur扩展与使用没有配置的 Vue CLI 3 无关,但与我们在 VS Code 中使用 Vue 时更加高效有关。
现在我们可以通过运行vue serve来为我们的 Vue 应用程序提供服务。但是,在实际运行命令之前,让我们使用-h标志来查看我们有哪些可用选项:
vue serve -h
这就是我们将得到的内容:
Usage: serve [options] [entry]
serve a .js or .vue file in development mode with zero config
Options:
-o, --open Open browser
-c, --copy Copy local url to clipboard
-h, --help Output usage information
现在我们知道可以期待什么,让我们使用以下命令为我们的 Vue 应用程序提供服务:
vue serve -o -c
因此,正如之前提到的,这个命令将为我们的 Vue 应用程序提供服务,并在浏览器中打开它。它还将复制提供的 URL 到剪贴板。这使我们可以,例如,打开一个不同的非默认浏览器,并轻松地粘贴 URL 到浏览器的地址栏中,这样我们也可以在那里预览我们的应用程序。
然而,我们将遇到一个小问题。
我们将在命令中得到这个通知,而不是我们的 Vue 应用程序被提供服务:
Command vue serve requires a global addon to be installed.
Please run npm install -g @vue/cli-service-global and try again.
这是一个简单的修复。更好的是,我们将使用--loglevel verbose扩展前面的命令:
npm install -g @vue/cli-service-global --loglevel verbose
一段时间后,根据你的下载速度,你会收到npm info ok的消息。
这意味着你现在可以再次运行vue serve命令:
vue serve -o -c
这次它成功了!有点...
现在我们收到一个错误,上面写着编译失败,有 1 个错误。然后,更进一步地,我们看到了根本原因:
Component template should contain exactly one root element.
有几种方法可以解决这个问题,但它基本上是说我们可以将我们的h1和hr标签包裹在一个div标签中,然后我们就可以了。所以,让我们在 VS Code 中更新App.vue文件为这样:
<template>
<div>
<h1>What's up, Vue CLI 3?</h1>
<hr>
</div>
</template>
确保保存你的更改,现在,最后,让我们再次提供服务:
vue serve -o -c
你可能会有点惊讶,因为一个新的标签页会自动打开,加载应用程序,显示在你的默认浏览器中。
假设你的默认浏览器是 Chrome。让我们打开另一个浏览器(例如 Firefox),点击管理栏内部,并按下Ctrl + V快捷键粘贴剪贴板的内容。当然,它将是http://localhost:8080/。
通过使用-o和-c标志,我们以非常简单的方式执行了打开应用程序并复制其 URL 的重复任务只是冰山一角。Vue CLI 3 还有更多功能,可以帮助我们更快更轻松地编写我们的应用程序。
例如,让我们回到我们的代码,删除带有hr标签的行,然后保存文件。看看你的浏览器标签,打开我们的 Vue 应用程序的标签。它将自动刷新,反映代码的更改。这就是 webpack 在 Vue CLI 3 的内部运行,监视我们的 Vue 文件的更改,并相应地在浏览器中热重新加载应用程序。
如果你已经编码超过几年,你会欣赏到这种工作流的便利。过去,我们要么必须设置我们的工具,使它们在我们保存文件时自动刷新浏览器中的应用程序,要么我们必须设置我们的 IDE 或代码编辑器,或者两者兼而有之。甚至直到最近,我们仍然不得不调整 webpack 来自动化这种工作流程,而且像任何与编码相关的事情一样,有时并不像我们希望的那样顺利。
使用 Vue CLI 3,所有这些都是自动化的,非常简单。
让我们看看 Vue CLI 3 如何帮助我们更好地编码并提高生产力的其他方式。
使用 Vue CLI 3 的好处
当 Vue CLI 3 推出时,Vue 的创始人 Evan You 列出了它的这些目标:
-
通过简化设置来避免前端开发的工具链疲劳
-
遵循工具的最佳实践
-
使这些最佳实践成为 Vue 应用的默认设置
除了这些伟大的目标,Vue CLI 还带来了许多更新,比如以下内容:
-
预设的 webpack 配置
-
ES2017 和 Babel 7 支持开箱即用
-
出色的 CSS 支持,包括Sassy CSS(SCSS)和PostCSS支持
-
许多集成选项(TypeScript,PWA,Web 组件,端到端测试,Jest 等)
这是很多功能。本书的目的是遍历所有选项,并让您熟悉它们的内部工作原理。
现在,让我们来看看使用默认选项设置默认应用有多么容易,来结束本章。
默认工具链,疲劳程度为零
在本节中,我们将创建一个默认的 Vue 应用程序模板。与上一节相反,在本节中,我们将真正构建一个完整的应用程序。我们将使用两种方法:命令行上的 Vue CLI 3 和带有 GUI 的 Vue CLI 3。
您可能会问为什么我们首先没有配置运行 Vue CLI 3?答案是,这可能对快速实验和开始使用一些基本命令很有用。
通过命令行创建 Vue CLI 3 默认应用
我们使用vue create命令创建 Vue CLI 3 应用程序。让我们看看我们有哪些选项可用:
vue create -h
这就是将返回的内容:
Usage: create [options] <app-name>
create a new project powered by vue-cli-service
Options:
-p, --preset <presetName> Skip prompts and use saved or remote preset
-d, --default Skip prompts and use default preset
-i, --inlinePreset <json> Skip prompts and use inline JSON string as preset
-m, --packageManager <command> Use specified npm client when installing dependencies
-r, --registry <rul> Use specified npm registry when installing dependencies (only for npm)
-g, --git [message] Force git initialization with initial commit message
-n, --no-git Skip git initialization
-f, --force Overwrite target directory if it exists
-c, --clone Use git clone when fetching remote preset
-x, --proxy Use specified proxy when creating project
-b, --bare Scaffold project without beginner instructions
-h, --help output usage information
让我们首先跳过所有提示,使用默认选项:
vue create -d first-default-app
您的控制台将显示以下输出:
Vue CLI v3.3.0
? Creating project in C:\...
? Initializing git repository...
? Installing CLI plugins. This might take a while...
确实需要一段时间。幸运的是,有一个进度条,让我们知道我们在设置项目时进行到了哪个阶段。
准备好后,我们只需运行以下命令:
cd first-default-app
一旦我们的控制台指向正确的目录,我们可以使用以下命令运行应用:
npm run serve
现在我们可以在浏览器中查看默认应用程序:
正如我们所看到的,我们有一个欢迎消息,然后页面列出了安装的 CLI 插件。显然,babel和eslint插件是默认的。每个链接都指向 GitHub 上vue-cli存储库中的各自部分。
接下来,我们看到一些基本链接和一些链接,以了解更大的Vue.js生态系统(即,vue-router,vuex,vue-devtools,vue-loader和awesome-vue的链接)。
通过 UI 创建 Vue CLI 3 默认应用
要开始使用 Vue CLI GUI,让我们首先使用Ctrl + C快捷键停止在上一节中运行的服务器。控制台将会回应以下消息:
Terminate batch job (Y/N)?
输入Y(大小写不重要)并按下Enter键。
这将使我们重新获得当前控制台窗口的控制,并允许我们输入新命令。
让我们首先在控制台中从当前目录向上一级:
cd ..
接下来,让我们运行这个命令:
vue ui -h
然后我们将得到以下输出:
Usage: ui [options]
start and open the vue-cli ui
Options:
-H, --host <host> Host used for the UI server (default: localhost)
-p, --port <port> Port used for the UI server (by default search for available port)
-D, --dev Run in dev mode
--quiet Don't output starting messages
--headless Don't open browser on start and output port
-h, --help output usage information
这次,我们将不使用任何标志运行该命令:
vue ui
我们将在控制台中看到以下输出:
? Starting GUI...
? Ready on http://localhost:8000
这次,我们可以通过可视化方式创建一个项目。最初,我们看到当前文件夹中没有 Vue 项目:
让我们点击“创建”选项卡来创建一个项目。
将打开一个新窗口,有一个大按钮,上面写着在这里创建一个新项目:
正如我们在前面的截图中看到的,还有许多其他按钮和选项可以使用。我们将在接下来的章节中进行详细讨论;目前,我们只是熟悉我们正在使用的工具:
正如我们从前面的截图中看到的,页面底部的“下一步”按钮当前是禁用的。要启用它,只需在最顶部的输入框中输入项目文件夹名称。我们将文件夹命名为second-vue-project。现在点击“下一步”。
在下一个窗口中,您可以选择一个预设。让我们将其设置为默认预设:
选择一个预设将使“创建项目”按钮可点击。您将在屏幕中央看到一个加载图标,并显示以下消息:
Installing Vue CLI plugins. This might take a while...
在安装过程中,您将看到一些其他消息。最后,当完成时,您将会看到以下窗口:
我们的项目现在已经准备好进行工作了,我们将在下一章中进行。
摘要
在本章中,我们看了如何使用 Vue CLI 3 开始,既可以使用命令行,也可以使用 Vue CLI UI。
我们已经看到如何安装所有先决条件,并且我们看到了建议的工作流程和一些基本指针,以便轻松入门。由于了解如何在命令行和 UI 上使用 Vue CLI,我们现在可以轻松初始化 Vue 应用程序。我们知道在启动新项目时有哪些选项可供我们选择。然而,还有许多其他事情我们需要了解 Vue CLI 的内部工作原理。
在下一章中,我们将通过专注于 webpack 来进一步改进我们的工作流程,webpack 是 Vue CLI 3 核心的模块捆绑器。
第二章:Vue CLI 3 中的 Webpack
在上一章中,我们看到了如何通过命令行和 UI 开始使用 Vue CLI。在本章中,我们将从 Vue CLI 3 的角度介绍 webpack 的基础知识。我们将首先概述 webpack 是什么。我们将研究模块捆绑、摇树、webpack 加载器和输出、webpack 插件、热模块替换(HMR)、代码覆盖和代码拆分的概念,然后我们将看看这些概念如何与 Vue CLI 3 配合,如下所示:
-
从脚本标签到模块捆绑器的 JavaScript(JS)语言的演变
-
脚本标签
-
立即调用函数表达式(IIFEs),它们解决了什么问题,以及它们没有解决的问题
-
Node Package Manager (NPM)如何帮助团队在他们的代码中共享第三方库
-
JS 任务运行器和 NPM 脚本的作用
-
CommonJS 规范是什么,以及它如何在 JavaScript 和 Node.js 中工作
-
模块捆绑器是什么,以及它们如何弥合 Node.js 和浏览器之间的差距
-
webpack 是什么,以及它是如何工作的
-
如何在项目中运行 webpack
-
使用生产和开发模式使用 webpack 捆绑资产
-
通过 NPM 添加 Vue 项目并使用 webpack
准确理解 webpack 的工作原理对于理解 Vue CLI 3 的魔力至关重要。如果您熟悉 webpack,您可能仍然会发现本章的某些部分有用。如果您觉得自己是 webpack 专家,您可能可以直接跳过本章。
在深入了解 webpack 是什么以及正确理解 webpack 解决的问题之前,我们需要回顾一下过去十年中 JS 语言发生的一些变化。
JS 语言的演变
从 webpack 的角度来看,以下是 JS 生态系统中添加的方法、技术、最佳实践和模式的时间顺序列表,这些方法、技术、最佳实践和模式导致了当前的状态:
-
script标签作为向网页添加交互性的答案 -
立即调用函数表达式作为模块化库和避免代码冲突的答案
-
IIFEs 的问题
-
使用 NPM 在团队环境中共享第三方库
-
JS 任务运行器和 NPM 脚本
-
JS 中的模块
让我们更详细地看看这些解决方案中的每一个。
脚本标签
最初,将 JS 添加到您的网页意味着您需要直接在 HTML 中添加一些script标签。对于快速原型,这仍然是一种有效的做法,甚至到今天。很多时候,第三方库是通过script标签内的src属性添加的(通常放在我们的 HTML 中关闭body标签的正上方)。
不幸的是,您通常需要在 HTML 中添加多个script标签。而不管您是直接将 JS 代码添加到页面中,还是从项目中的另一个文件添加,或者从远程位置添加(例如从内容传送网络(CDN)使用src属性),最终,所有这些脚本都被添加到全局 JS 范围内。这意味着一件事,冲突。
为了避免冲突,采取了一个巧妙的方法,即使用 IIFE。
立即调用的函数表达式
IIFE 到底是什么?IIFE 简单地利用了 JS 中括号不能包含语句的事实。这个事实本身允许 JS 开发人员放入匿名函数,他们可以立即调用,而不会因为简单地将它们包装在括号中而从解析器中得到任何错误。
IIFE 本质上是 JS 语言的一个怪癖,但是非常有用;通过 IIFE,所有的代码都被限定在函数范围内,因此您的代码不会受到外部任何其他东西的影响。换句话说,使用 IIFE 是避免冲突的一种简单方法,即意外覆盖变量或函数。因此,有一段时间,许多流行的库开始将它们自己的代码包装成 IIFE。例如,如果您打开 jQuery 库的代码(code.jquery.com),或 Chart.js 库的代码(cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.js),或许多其他流行的 JS 库的代码,您会发现它们使用了 IIFE 模式。
因此,通过 IIFE,我们可以向页面添加不同的脚本,而不必担心代码冲突可能发生。
IIFE 的问题
不幸的是,仅仅使用 IIFE 并不能解决我们所有的问题。为了说明手头的问题,让我们引用 Erlang 的创始人 Joe Armstrong 的话:
“你想要香蕉,但你得到的是拿着香蕉的大猩猩,整个丛林。”
请记住,在这段引用中,阿姆斯特朗先生讨论的是面向对象语言的问题,但根本问题在 JS 代码模块化中也适用。
基本上,我们对 IIFEs 的问题在于我们无法从 JS 库中精选出我们想要使用的特定功能。使用 IIFE 模式,我们必须使用 IIFE 中包含的所有内容,即使我们只是使用特定库代码库的一小部分。当然,老实说,IIFEs 并不是这个问题的罪魁祸首。长期以来,JS 语言根本没有能力精选任何类型的代码功能,因为在 JS 中,将代码拆分成模块是不可能的。
JS 的另一个主要痛点是在团队之间重复使用第三方代码的问题。
使用 NPM 在团队环境中共享第三方库
IIFEs 解决了代码冲突的问题,但并没有解决代码重用的问题。如果我的团队中的开发人员有一个不同的、更新的库版本,其中有破坏性的更改,该怎么办?如果我决定在我的计算机上更新依赖关系,我的其他团队成员将如何处理?除了使用源代码版本控制,还有其他更快的协作选项吗?
Node Package Manager(NPM)是这些问题的答案。Node 只是一个可以在服务器上运行的 Google V8 JS 引擎。NPM 允许开发人员将新库安装到项目中,无论是用于应用程序的前端还是后端。因此,NPM 实际上是 JS 包管理器,类似于 Ruby(gems (rubygems.org/))、C#(NuGet (www.nuget.org/))或 Linux 中的apt-get、yum。
例如,假设我们想通过 NPM 安装 Vue。如果我们的计算机上安装了 Node,那么我们也会有 NPM,因为 NPM 随 Node 一起捆绑安装。
接下来,我们需要创建一个新目录。让我们将此目录的名称更改为vue-from-npm,并将命令行控制台指向它。然后我们可以跟随这个命令:
npm init -y
运行上述命令将创建一个package.json文件。-y标志接受控制台中提示的所有默认答案。
如果我们查看项目目录中新创建的package.json文件,我们会看到以下内容:
{
"name": "vue-from-npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
请注意,npm init命令只会将package.json文件添加到空目录中。就是这样!
然后,添加 Vue 就像运行这个命令一样简单:
npm install vue --save --verbose
上述命令将执行一些操作,即:
-
它将添加
node_modules目录。 -
它将整个 Vue 库放在
node_modules目录中。 -
它将在我们项目的根目录中创建
package-lock.json文件。 -
它将更新我们项目的根目录中的
package.json文件。
更新后的package.json文件现在看起来是这样的:
{
"name": "vue-from-npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"vue": "².6.7"
}
}
如果不明显的话,文件已经更新了一个新条目:dependencies。这个条目列出了项目中包含的所有依赖项。具体来说,我们已经将 Vue(版本 2.6.7 或以上)添加到了我们的项目中。
NPM 的一个很棒的地方是,我们可以像添加 Vue 一样轻松地向我们的项目添加任何其他库。例如,要使用 accounting.js 更新我们的项目,我们只需运行这个命令:
npm install accounting-js --save --verbose
安装完成后,让我们再次检查node_modules目录:
vue-npm/node_modules/
├── accounting-js/
│ ├── dist/
│ ├── lib/
│ ├── CHANGELOG.md
│ ├── package.json
│ └── README.md
├── is-string/
├── object-assign/
└── vue/
请注意,为了简洁起见,我们只显示了accounting-js文件夹内的第二级文件夹和文件。is-string,object-assign和vue文件夹都被折叠显示。
这向我们展示了有时其他 NPM 模块会捆绑实际安装的库。在accounting-js的情况下,我们还得到了is-string和object-assign NPM 模块。让我们也检查一下我们目录根目录中更新的package.json文件:
{
"name": "vue-from-npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"accounting-js": "¹.1.1",
"vue": "².6.7"
}
}
正如我们所看到的,根package.json文件已经更新为正确的accounting-js版本。让我们找到另一个package.json文件,这次是在node_modules/accounting-js文件夹中。如果你打开了那个文件,它包含了更多信息,仅仅超过 100 行代码。这些信息是特定于实际的 NPM 模块accounting-js。
好的,现在我们的项目已经准备好进行协作了。怎么做呢?让我们看看我们的一个同事,让我们称他为“约翰”,如何在他自己的电脑上添加我们刚刚创建的项目和所有项目依赖项。
为此,我们将创建一个新文件夹,让我们称之为johns-computer,然后我们只需将vue-from-npm文件夹中的根级package.json复制到我们的johns-computer文件夹中。
接下来,让我们简单地运行这个命令:
npm install --verbose
运行上述命令将安装我们在vue-from-npm文件夹中的所有项目和依赖项。
JavaScript 任务运行器和 NPM 脚本
大约在 NPM 变得流行的同时,另一种前端技术也在崛起:任务运行器。任务运行器是简单的工具;它们运行重复的任务。有时,任务运行器被称为构建工具,因为它们充当开发人员对代码库进行更新和最终生成的生产就绪代码之间的中介。这就是所谓的构建步骤,这是软件开发过程中的一部分,在这个过程中,你的代码在你编写完之后会发生一些事情。
例如,CSS3 中添加的新功能通常以供应商前缀(也称为浏览器前缀)的形式开始。换句话说,在新的 CSS 功能在所有浏览器中可用之前,它会在各个浏览器中以实验阶段实现,使用浏览器特定的前缀,如下所示:
-ms-
-moz-
-o-
-webkit-
在这个按字母顺序排列的浏览器前缀列表中,我们可以看到微软浏览器、Mozilla、旧版本的 Opera,最后是所有基于 webkit 的浏览器(Chrome、Safari、新版 Opera 等)的浏览器前缀。
跟踪浏览器前缀的更新是有点困难的。开发人员的时间可能不是最好的用法,去监视 CSS 实现的变化,然后相应地更新他们的代码。例如,在过去的某个时间点,有必要在 CSS 的transition属性上使用以下浏览器前缀:
-webkit-transition: background-color 1s;
-moz-transition: background-color 1s;
-o-transition: background-color 1s;
-ms-transition: background-color 1s;
显然,今天我们在 CSS 声明中简单地使用transition属性,而不需要任何浏览器前缀,因为transition属性在所有现代浏览器中得到了广泛支持。
不得不应对不断变化的 CSS 规范和各种浏览器中的实现带来的不断变化的情况,导致了任务运行器这种解决方案的出现。前端开发人员现在不再需要手动向他们的 CSS 代码中添加供应商前缀,而是可以简单地向他们的任务运行器添加一个插件,它会为他们做这些繁重的工作:在需要时添加供应商前缀。
当然,我们之前看到的只是任务运行器用于的一个例子。其他一些例子包括:压缩 CSS 和 JS 文件,从 ES6 转译为 ES5,从 SASS 编译 CSS,删除未使用的 CSS,在项目中保存文件时重新加载浏览器,等等。
今天,有许多不同的工具帮助我们有效地自动化开发过程中的一些任务。三个工具脱颖而出:Grunt、Gulp 和 NPM 脚本。
虽然 Grunt 和 Gulp 是独立的任务运行器,可以通过 NPM 安装,但基于 NPM 的脚本是一个有趣的替代方案,原因如下:
-
您已经在使用 NPM,为什么不更熟悉一下您已经在使用的工具呢?
-
使用 NPM 脚本而不是前面提到的任务运行器将进一步简化您的开发流程。
-
通过使用 NPM,您可以避免使用任务运行器插件来自动化 NPM 中可以自动化的任务的复杂性。
直到这一点,我们已经回顾了 JS 生态系统的历史和演变。我们已经看到了 IIFE 如何用来处理意外的作用域泄漏。我们还看到了 NPM 如何处理代码共享。我们进一步看到了如何使用任务运行器自动化一些重复的任务,以及如何使用 NPM 来通过将任务保留在 NPM 脚本中来消除不必要的抽象层。
然而,我们还没有看到解决 JS 中代码模块化问题的方法。所以,让我们接着看看。
JavaScript 中的模块
在任何编程语言中,模块都是一个独立的功能块。您可以将它们视为电视节目的不同集数。它们可以独立查看。它们可以独立存在,尽管它们是整体的一部分。
就像电视节目中的一集有一个季节和一个编号,这样我们就知道它在更大情节中的位置一样,一个模块也包含了告诉我们它依赖的其他模块(模块依赖)以及它为整个应用程序添加了什么功能的信息;这就是所谓的模块接口,对其他模块公开的 API。
我们已经看到,在开始时,JS 根本没有模块。这在 Node.js 的引入后发生了变化。Node.js 实际上是 CommonJS 的一种实现,这是由 Mozilla 的 Kevin Dangoor 在 2009 年发起的一个项目。
CommonJS 项目的目的是定义一个标准库,提供供在浏览器之外使用的 JS API。这包括一个模块规范,这导致开发人员能够在 Node.js 中使用这样的代码:
var bootstrap = require('bootstrap');
在 Node.js 中使用模块
让我们在 Node.js 中要求并使用一些模块:
-
首先,我们将创建一个新目录。让我们称之为
module-practice。让我们将 Git Bash 指向这个文件夹。 -
一旦进入其中,让我们创建两个新文件。让我们将这些文件命名为
main.js和whatever.js,如下所示:
touch main.js whatever.js
- 接下来,让我们按照以下步骤在 VS Code 中打开整个文件夹:
code .
- 现在,让我们向
whatever.js添加一些代码如下:
console.log('whatever');
这就是 JS 中代码的简单形式。
- 现在让我们看看如何使它在我们的
main.js文件中可用。我们只需要像下面这样要求whatever.js:
let whatever = require('./whatever');
- 现在它被要求了,我们可以使用它,所以让我们将
main.js更新为这样:
let whatever = require('./whatever');
whatever.returnWhatever();
- 现在让我们用以下方式运行这段代码:
node main.js
现在会发生的是,我们将在 Git Bash 中看到单词whatever被打印出来。
让我们进一步进行我们的实验。这是我们更新后的whatever.js:
module.exports = {
returnWhatever: function() {
console.log('whatever');
}
}
因此,我们需要更新main.js如下:
whatever.returnWhatever();
正如我们已经看到的,require关键字导入了一个模块的代码,并使其在另一个文件中可用;在我们的例子中,就是main.js文件。
exports关键字让我们可以将代码提供给其他文件,但有一个注意事项。它还允许我们选择我们想要向其他文件提供的模块的哪些部分。正如我们所看到的,module.exports是一个对象。这个对象的内容在我们的main.js要求whatever模块时将被返回。这使我们能够仅暴露代码的某些部分,并且使模块接口的设置成为可能。换句话说,module.exports是使我们能够保持代码的部分私有的东西。考虑对whatever.js的这个更新:
module.exports = {
returnWhatever: function() {
returnSomething();
}
}
let returnSomething = () => {
console.log('whatever');
}
我们不需要对main.js进行任何更改。如果我们从 Git Bash 运行它,仍然会在控制台输出单词whatever。但是我们已经使whatever.js的部分内容不直接可访问。
作为一个旁注,注意在前面的代码中,ES3 和 ES5 的函数语法一起使用。定义returnSomething函数的代码部分使用了更新的语法,这使我们能够在不使用function关键字的情况下编写函数定义。
模块捆绑器,一种在浏览器中使用模块的方法
不幸的是,你不能直接在浏览器中使用require关键字,正如我们刚才看到的那样。require关键字不是 JS 浏览器 API 的一部分。这里需要注意的是,Node.js 有能力读取和写入计算机文件系统。因此,如果你在项目中使用 Node.js 安装了任何 NPM 包,你就可以像之前解释的那样要求这样一个模块。
然而,浏览器中的 JS 无法访问你的操作系统文件系统,因此这给我们留下了一个难题:我们如何在浏览器中使用 JS 模块语法?
答案是:我们有一个工具可以做到这一点,它被称为模块捆绑器。
今天,在 2019 年,有许多不同的模块打包工具可用,比如 webpack(webpack.github.io/)、FuseBox(fuse-box.org/)、Parcel(parceljs.org/)、rollup.js(rollupjs.org/guide/en)或 Browserify(browserify.org/)。
什么是模块打包工具?以下是 Browserify 主页上的一句话,简洁地表达了它:
“Browserify 让你可以在浏览器中使用 require('modules')来捆绑所有你的依赖项。”
除了打包项目中通过模块所需的所有依赖项,模块打包工具还解决了循环依赖等问题;也就是说,它们使用算法来解析项目中所有依赖项应该在项目中捆绑的顺序。
我们几乎完成了对 JS 生态系统的概述。接下来,我们将看一种特定的模块打包工具,那就是 webpack。
一旦我们知道 webpack 究竟是什么,以及它在幕后是如何工作的,我们就能完全理解它在 Vue CLI 中的位置。
什么是 webpack?
Webpack 是 Web 的模块打包工具。有些人也把它称为 Web 应用程序的资产编译器。
根据 webpack 的 GitHub 页面:
“它将许多模块打包成少量的捆绑资产等等。模块可以是 CommonJs、AMD、ES6 模块、CSS、图片、JSON、CoffeeScript、LESS 等等,还有你自定义的东西。”
在本章的前面,标题为在 Node.js 中使用模块的部分中,我们只是浅尝辄止地介绍了模块在 Node 应用程序中的导出和引入。我们没有提到的是,我们可以使用各种不同的模块语法。正如前面提到的,Node.js 使用 CommonJS 模块语法。除了 CommonJS,还有异步模块定义(AMD)。除了 AMD,你还可以使用 ESM 模块。使用 ESM 模块时,语法与我们之前看到的有些不同。
让我们按照以下步骤使用 ESM 语法重写whatever模块,并在main.js中使用它:
- 为了简化事情,让我们创建一个新的文件夹如下:
mkdir es6-module-practice;
- 让我们使用
cd命令(更改目录命令)指向我们的 Git Bash 如下:
cd es6-module-practice
- 让我们按照以下方式添加我们的两个文件:
touch whatever2.mjs main2.mjs
- 现在,让我们按照以下方式打开我们的文件夹与 VS Code:
code .
- 接下来,让我们添加
main2.mjs的代码如下:
import returnWhatever from './whatever2';
returnWhatever();
- 最后,让我们按照以下方式编写
whatever2.mjs的代码:
let returnWhatever = () => {
returnSomething();
}
let returnSomething = () => {
console.log('whatever');
}
export default returnWhatever;
- 正如我们所看到的,我们需要将文件保存为 ESM 模块,使用
mjs文件扩展名。Node.js 实验性地支持 ESM 模块,因此您需要在 Git Bash 中运行以下命令:
node --experimental-modules main2.mjs
- 运行上述命令后,您将在控制台中看到以下输出:
(node:12528) ExperimentalWarning: The ESM module loader is experimental.
whatever
正如我们所看到的,除了在控制台中收到预期的输出之外,我们还收到了ExperimentalWarning消息。希望这个演示两种不同模块语法的示例能帮助我们理解 webpack 将为我们做什么。除其他事项外,它将平衡竞争环境,这样我们就可以在项目中使用各种标准和非标准的模块工作方式。
基本上,webpack 所做的是,它接受具有依赖项的模块(包括我们项目的资产,如.png、.jpeg和.scss文件),并输出静态资产(.js、.css和.image文件)。
webpack 的工作原理
我们已经看到了如何使用 CommonJS 和 ESM 模块语法。再次强调,CommonJS 是 Node.js 模块使用的语法。这意味着 Node.js 模块中的所有依赖项都是使用require命令描述的。与此相反,webpack 模块的依赖项可以用各种语法描述。例如,如果您的模块依赖于一个 SCSS 部分,您将使用@import语句。如果您正在导入 AMD 模块的依赖项,您将使用其自己的require和define语法。
这意味着,基本上webpack 模块接受导入各种依赖项的不同语法。甚至src属性(用于img HTML 元素)也被视为 webpack 模块的依赖项。
构建一个新项目并在其上运行 webpack
现在,让我们通过以下步骤构建一个项目并将 webpack 捆绑到我们的工作流程中:
- 让我们添加一个新目录。让我们运行一个不存在的命令,如下所示:
new-project-with-webpack
控制台将返回以下内容:
bash: new-project-with-webpack: command not found
- 太棒了!现在,让我们使用重复上一条命令的快捷方式,双感叹号,如下所示:
mkdir !! && cd !!
运行上述命令将创建我们的new-project-with-webpack文件夹,并将cd进入这个新目录。双和符号命令(&&)只是一种运行多个命令的方式,而不是一个接一个地输入它们。双感叹号命令(!!)表示重复上一行,所以上述命令实际上意味着以下内容:
mkdir new-project-with-webpack && cd new-project-with-webpack
- 接下来,让我们添加我们的
package.json,并接受所有默认值(使用-y标志)如下:
npm init -y
- 让我们按以下步骤检查我们在 VS Code 中的文件夹内容:
code .
- 一旦 VS Code 在我们的屏幕上运行,我们可以双击
package.json文件并验证其内容如下:
{
"name": "new-project-with-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 现在,让我们按照以下步骤将 webpack 添加到我们的项目中:
npm install --save-dev webpack webpack-cli --verbose
- 完成后,让我们回到 VS Code 并再次审查我们的
package.json如下:
{
"name": "new-project-with-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "⁴.29.5",
"webpack-cli": "³.2.3"
}
}
正如我们所看到的,一个新的键已经被添加:devDependencies。在其中,我们有webpack和webpack-cli开发依赖。这些devDependencies是你在构建项目时才会使用的依赖项,而 webpack 就是这样一个依赖的完美例子:你在生产环境中不需要 webpack。这就是为什么我们在通过 NPM 安装 webpack 时使用了--save-dev标志。
查看我们项目的文件结构,我们现在可以看到以下内容:
node_modules/
package.json
package-lock.json
如果你打开node_modules文件夹,你会看到里面有 300 多个文件夹。这个庞大的依赖列表以一个.bin文件夹开始。与我们之前的一个例子vue-from-npm相比,其中node_modules文件夹内只有四个子文件夹,尽管我们安装了vue和accounting-js两个 NPM 包。还要注意的是,在vue-from-npm文件夹内,没有.bin文件夹。无论你在运行npm install时使用--save还是--save-dev标志,情况都是如此。虽然这对于更有经验的开发人员可能是显而易见的,但对于那些在 Node.js 和 NPM 生态系统方面经验不足的开发人员来说,更好地理解这一点可能是很重要的。
那么,这个.bin文件夹是什么?它只是存储了你使用npm install安装的 Node 模块的编译后的本地二进制文件(即可执行文件)。并非所有的 NPM 模块都有这些编译后的本地二进制文件,这就是为什么你不会总是在node_modules文件夹内看到.bin文件夹的原因。在这个.bin文件夹内,有许多不同的 Node 模块。这些对于 webpack 正常工作都是必要的。
回到我们的项目,现在让我们向其中添加两个文件:index.js和whatever.js,如下所示:
touch index.js whatever.js
目前,我们不会向这些文件中添加任何代码。现在,我们将专注于在我们的项目中运行 webpack。
在一个项目上运行 webpack
回到我们的new-project-with-webpack文件夹,再次检查package.json的内容,重点关注scripts键如下:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
在 Git Bash 中使用以下命令运行test脚本:
npm run test
这将抛出一个带有exit code 1的错误。
让我们对它做一些更改,如下所示:
"scripts": {
"test": "echo \"You haven't specified any tests\""
},
让我们再次用npm run test来运行测试。这次控制台的输出不会那么可怕,因为我们删除了exit 1命令,并且改变了运行test命令时会被回显的内容。
让我们尝试完全不同的东西,如下所示:
"scripts": {
"test": "node index.js"
},
现在,我们不会得到错误,因为我们的index.js是空的。让我们添加一些内容,如下所示:
// add up 2 numbers
console.log(2+2)
保存对index.js的更改,再次运行npm run test,这次在 Git Bash 中的输出将会打印出数字4。
这告诉我们什么?它告诉我们我们完全控制我们的脚本将要做什么!所以,最初我们有一个名为 test 的脚本。这个脚本会回显一条消息,并抛出一个带有exit code 1的错误。
就像我们可以给我们的脚本任意的键名,比如test,我们也可以给它们任意的值。当然,console.log(2+2)是一个愚蠢的值给一个脚本键。我们可以给我们的脚本键更好的值,例如:
"scripts": {
"webpack": "webpack"
},
现在,当我们用 webpack 的值运行一个 NPM 脚本时,这个脚本将运行 webpack 可执行文件。让我们试一下,如下所示:
npm run webpack
这会返回一个错误,但在所有被记录下来的信息中,以下两行是最重要的:
Insufficient number of arguments or no entry found.
Alternatively, run 'webpack(-cli) --help' for usage info.
我们得到这个错误的原因是因为 webpack 在寻找入口点来启动。默认情况下,这个入口点被设置为./src/index.js。所以,让我们添加这个src文件夹,并将我们的index.js移动到其中,如下所示:
mkdir src && mv index.js $_
现在,让我们再次从命令行运行 webpack,如下所示:
npm run webpack
这次我们会得到一个更好的输出。然而,默认情况下 Git Bash 没有语法高亮。这可以很快解决。因为我们已经在使用 VS Code,只需键入Ctrl + ~的快捷键。如果你对这个符号不熟悉,它叫做tilde,位于Esc键下方,Tab键上方。按下这个快捷键将在 VS Code 中打开一个终端窗口,如果你再次执行npm run webpack命令,你会得到格式良好且带有颜色高亮的输出,就像这样:
图 2.1:在 VS Code 中将信息记录到控制台的 webpack 作为一个侧面说明,你的屏幕颜色可能会有所不同,这取决于你在 VS Code 中使用的颜色方案。要访问颜色主题,请使用以下键盘快捷键:Ctrl + K Ctrl + T。
查看控制台输出的消息,我们可以看到它可以分为两部分:实际信息(哈希、版本、时间、构建时间、入口点等)和警告。警告显示我们没有设置mode选项。
如果未设置,mode选项默认为生产模式。但是,我们也可以将其设置为development,这对于更快的构建进行了优化。这意味着我们可以在package.json的scripts部分中添加另一个脚本,然后可以用于项目的开发构建。这是更新后的scripts部分:
"scripts": {
"webpack": "webpack",
"dev": "webpack --mode=development"
},
现在,我们可以使用以下命令在 webpack 中运行开发模式:
npm run dev
这是控制台中的完整输出:
Hash: 86c0da41f48381d9bd70
Version: webpack 4.29.5
Time: 108ms
Built at: 2019-02-27 12:23:30
Asset Size Chunks Chunk Names
main.js 3.81 KiB main [emitted] main
Entrypoint main = main.js
[./src/index.js] 38 bytes {main} [built]
我们可以看到,webpack 在开发模式下花了108ms来打包我的项目。当我在生产模式下运行它(在我的设置中默认的npm run webpack命令),它花了447ms。
运行这个命令时实际上发生了什么?webpack 在后台做了什么?它构建了所有模块依赖的依赖图。回到本章前面的比喻,就好像我们给了它一堆电视剧的剧集,录在一堆蓝光光盘上,它把它们全部拿来并正确地排列起来。Webpack 找出了每个模块的正确位置,然后将它们捆绑起来并提供给dist文件夹。如果再次查看项目的文件结构,你会发现有一个新的添加:dist文件夹。如果我们检查dist文件夹的内容,我们会看到:
./dist/
|- main.js
如果我们检查main.js文件,我们会看到 webpack 添加了很多东西。即使在像我们这样的小项目上,输出也会有大约 100 行长。
我们的main.js文件的前几行如下:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
...
让我们再次运行npm run webpack命令,看看它如何影响main.js中的输出。
如果我们检查main.js,我们会看到现在只有一行代码,以以下内容开头:
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var ...
这意味着当以生产模式运行时,webpack 会对我们的代码进行混淆和缩小。
显然,这也会影响文件大小。在开发模式下,打包的main.js文件大小为 3.81 KB,而在生产模式下,它只有 944 字节。
最后,为了避免看到警告消息,我们可以将package.json中的脚本条目更新为:
"scripts": {
"webpack": "webpack --mode=production",
"dev": "webpack --mode=development"
},
在这一点上,我们可以开始使用 webpack 与 Vue。但是,我们不会使用 Vue CLI。相反,我们将看到如何手动设置所有内容。这不是做事情的最佳方式,但它将帮助我们更好地理解为什么在 Vue 生态系统中会这样做。
通过 NPM 添加一个 Vue 项目并使用 webpack
在这一部分,我们将使用 NPM 构建一个新项目,然后将 webpack 添加到其中,并最终添加一个 Vue 单文件组件。
首先,让我们按照以下步骤新建一个目录。我们将我们的项目命名为npm-vue-webpack:
- 打开 Git Bash 并按照以下方式添加一个新文件夹:
mkdir npm-vue-webpack && cd $_
- 按照以下方式初始化
npm:
npm init -y
- 接下来,按照以下步骤将 Vue 和 webpack 安装到我们的新项目中:
npm install vue webpack webpack-cli --save-dev --verbose
一旦 NPM 安装完成,我们可以像在本章前面那样验证package.json的文件夹和内容。
- 接下来,按照以下方式添加我们的项目将使用的源文件夹和输出文件夹:
mkdir dist src
- 按照以下步骤打开我们的新项目在 VS Code 中:
code .
现在,我们可以直接从 VS Code 编辑器中添加两个新文件。我们将第一个文件命名为source.js,第二个文件命名为output.js。确保在此阶段将这两个空文件添加并保存到您的项目中:source.js在src文件夹中,output.js在dist文件夹中。
将我们的 Vue 组件添加为 JavaScript 模块
现在让我们添加我们的 Vue 组件:
- 接下来,让我们按照以下方式将这段代码添加到
source.js中:
import CustomArticle from './CustomArticle.js';
new Vue({
el: '#app',
render: h => h(CustomArticle),
})
-
在第一行,我们正在导入一个名为
CustomArticle.js的文件。 -
让我们在
src文件夹内新建一个文件。我们将这个文件命名为CustomArticle.js。 -
并将以下代码添加到其中:
let CustomArticle = Vue.component('custom-article', {
template: `
<article>
Our own custom article component!
</article>`
})
export default CustomArticle;
我们可以看到,我们正在使用 ESM 语法来导出和导入 JS 模块。
使用 webpack 编译 JavaScript 模块
现在我们几乎可以准备好使用 webpack 将我们的source.js编译为output.js了。但在这之前,我们仍然需要按照以下方式更新我们的package.json中的scripts部分:
"scripts": {
"webpack": "webpack"
},
现在,我们可以在 Git Bash 中运行以下命令:
npm run webpack ./src/source.js ./dist/output.js
正如预期的那样,我们在控制台中看到了输出,以及关于设置模式选项的警告。我们现在知道这意味着什么,所以在这个时候处理它并不重要。
如果我们检查output.js的内容,我们会发现它是空的,并且默认情况下,webpack 会将我们的输出代码压缩和混淆到默认的main.js文件中,具体如下:
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i ...
那么,我们如何让 webpack 输出到一个不同的文件,而不是默认的main.js?我们使用 webpack 配置文件!
通过 webpack 配置文件添加选项
使用 webpack 配置文件,我们可以添加各种选项来打包我们的应用程序。具体如下:
- 在项目的根目录添加一个新文件。我们将这个文件命名为
webpack.config.js。代码如下:
module.exports = {
output: {
filename: 'output.js',
}
};
- 现在,再次运行我们的命令如下:
npm run webpack ./src/source.js ./dist/output.js
这次它输出到了正确的文件。
- 就像我们可以指定输出文件一样,我们也可以指定输入文件如下:
module.exports = {
entry: './src/source.js',
output: {
filename: 'output.js',
}
};
我们仍然需要在屏幕上的某个位置渲染我们的 Vue 组件。我们需要一个 HTML 文件来实现这一点。
添加一个 HTML 文件,以便渲染我们的 Vue 组件
让我们在dist文件夹中添加一个新的 HTML 文件,我们将其命名为index.html,具体如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Compiled HTML file</title>
</head>
<body>
<div id="entryPoint"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<script src="output.js"></script>
</body>
</html>
就像我们在本章开头讨论的那样,我们以老派的方式直接向 HTML 中添加脚本,只需在 HTML 文件底部堆叠script标签。我们使用的第一个script标签是从 CDN 获取 Vue,第二个script标签从dist文件夹中获取我们的output.js文件。
如果你使用 VS Code,现在可以右键单击新的dist/index.html文件,然后单击“在默认浏览器中打开”命令。
在打开的网页上会看到以下句子:
我们自己的自定义文章组件!
现在,我们需要让 webpack 能够输出 HTML 文件。为此,我们需要使用html-webpack-plugin。
赋予 webpack 输出 HTML 文件的能力
在本节中,我们将看到如何使用 webpack 插件输出 HTML 文件,具体步骤如下:
- 通过 NPM 安装
html-webpack-plugin如下:
npm install html-webpack-plugin --save-dev --verbose
- 我们的
package.json的devDependencies已相应更新如下:
"devDependencies": {
"html-webpack-plugin": "³.2.0",
"vue": "².6.7",
"webpack": "⁴.29.5",
"webpack-cli": "³.2.3"
}
- 现在,按照以下步骤更新我们的
webpack.config.js:
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/source.js',
output: {
filename: 'output.js',
},
plugins: [new HtmlWebpackPlugin()]
};
-
在继续之前,删除
dist文件夹中的index.html文件。不过不用担心删除它,因为 webpack 很快就会重新创建它。 -
接下来,再次运行 webpack 脚本如下:
npm run webpack
Webpack 刚刚为我们创建了一个新的index.html文件!文件内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="output.js"></script></body>
</html>
这都很好,但显然,我们的文件不一样了。我们丢失了 Vue 组件的入口点。此外,我们需要更新我们的 Vue 代码,使其作为单文件组件工作。
将.vue 文件作为 JavaScript 模块添加
首先,让我们更新source.js文件,如下所示:
import Vue from 'vue';
import CustomArticle from './CustomArticle.vue';
new Vue({
el: '#entryPoint',
render: h => h(CustomArticle),
})
现在我们还可以将CustomArticle.js重命名为CustomArticle.vue,并向其中添加以下代码:
<template>
<div id="entryPoint">
<article>
Our own custom article component!
</article>
</div>
</template>
不幸的是,webpack 不能直接处理.vue文件。为了解决目前的问题,我们需要使用webpack 加载器。webpack 加载器帮助 webpack 理解它正在处理的文件。有许多加载器,但现在我们需要使用 Vue。
添加 webpack 加载器以处理.vue 文件
要处理.vue文件,请按以下步骤进行:
- 通过 NPM 安装名为
vue-loader的 webpack 加载器,如下所示:
npm install vue-loader --save-dev --verbose
- 现在我们已经保存了它,我们需要使用它,我们将通过更新 webpack 配置来做到这一点:
let HtmlWebpackPlugin = require('html-webpack-plugin');
let VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/source.js',
output: {
filename: 'output.js',
},
plugins: [
new HtmlWebpackPlugin(),
new VueLoaderPlugin(),
]
};
- 现在尝试运行 webpack,如下所示。剧透警告:它会失败:
npm run webpack
我们得到的错误消息如下:
Error: [VueLoaderPlugin Error] No matching rule for .vue files found.
- 要修复此错误,我们需要为我们的 Vue 加载器添加一个规则,通过更新我们的
webpack.config.js文件如下:
let HtmlWebpackPlugin = require('html-webpack-plugin');
let VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/source.js',
output: {
filename: 'output.js',
},
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' }
]
},
plugins: [
new HtmlWebpackPlugin(),
new VueLoaderPlugin(),
]
};
rules选项中数组内的test键接收一个正则表达式作为值。这个正则表达式检查是否存在一个带有vue文件扩展名的文件。如果匹配,也就是说,如果找到了一个vue文件,它将在其上使用vue-loader模块。
- 让我们再次运行我们的 webpack 脚本,如下所示:
npm run webpack
这将抛出另一个错误,如下所示:
ERROR in ./src/CustomArticle.vue
Module Error (from ./node_modules/vue-loader/lib/index.js):
[vue-loader] vue-template-compiler must be installed as a peer dependency, or a compatible compiler implementation must be passed via options
- 控制台中记录了更多错误,但我们需要通过添加另一个 NPM 包来解决这个。
npm install vue-template-compiler --save-dev --verbose
package.json中的devDependencies条目刚刚又更新了,如下所示:
"devDependencies": {
"html-webpack-plugin": "³.2.0",
"vue": "².6.7",
"vue-loader": "¹⁵.6.4",
"vue-template-compiler": "².6.7",
"webpack": "⁴.29.5",
"webpack-cli": "³.2.3"
}
所以,现在我们可以再次运行 webpack,如下所示:
npm run webpack
在 webpack 运行后,如果此时打开output.js,你会看到它里面有完整的 Vue 库,以及我们的CustomArticle在最后。所有这些都没有任何错误编译。
修复我们的 index.html 文件的问题
我们仍然有dist文件夹中的index.html文件的问题。这很容易解决!我们只需在src文件夹中添加我们自己的index.html文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Compiled HTML file</title>
</head>
<body>
<div id="entryPoint"></div>
</body>
</html>
请注意,我们现在已经删除了自己的script标签,因为 webpack 将添加它们。另外,请确保删除dist文件夹中的index.html文件。现在,再次运行npm run webpack命令,你将在dist/index.html中得到以下输出:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="output.js"></script></body>
</html>
为什么这不起作用?
它不起作用,因为我们需要更新要输出的 JS 文件以及 HTML 文件。目前,我们只更新 JS 文件,但我们仍然需要为我们的index.html文件做同样的操作。幸运的是,我们已经有html-webpack-plugin来帮忙。
通过 html-webpack-plugin 使用 webpack 传递 HTML 文件
我们将首先更新webpack.config.js文件中的html-webpack-plugin如下:
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
]
在插件部分所做的是,我们向HtmlWebpackPlugin()调用传递了一个options对象。在这个options对象内部,我们指定了我们的模板:./src/index.html。
在我们再次运行 webpack 脚本之前,我们需要确保添加meta标签,并将charset设置为utf-8。否则,当我们在浏览器中打开dist/index.html时,我们将在控制台中收到错误。
现在让我们再次运行npm run webpack。这次,一切都正常!我们在屏幕上得到了我们看起来很简单的句子:
我们自己的自定义文章组件!
恭喜!虽然它看起来很简单,但您已成功将一个 Vue 应用程序添加到了运行在 webpack 上的 NPM 项目中。
接下来,我们将学习 HMR 以及它如何在 Vue 开发中帮助我们。
了解 Vue 中的热模块替换
HMR 在过去几年已经成为了一个热门词汇。这有什么了不起的?在本节中,我们将讨论 HMR 的工作原理。
为了做到这一点,我们将构建另一个默认的简单应用程序,就像我们在第一章中所做的那样,介绍 Vue CLI 3:
vue create -d second-default-app
过一会儿,一旦完成,我们将按以下方式进入我们应用程序的目录:
cd second-default-app
让我们按以下方式在 VS Code 中打开项目文件夹:
code .
现在,我们可以看到整个second-default-project的内容。
现在,我们可以按以下方式提供应用程序:
npm run serve
当然,我们的应用程序现在正在浏览器中提供。
要查看您的应用程序,请在浏览器中访问localhost:8080。
让我们实时查看 HMR 更新。
观察 HMR 更新
在浏览器窗口处于活动状态时,让我们按下F12键打开开发者工具。还要确保我们的元素面板是开发工具内的活动选项卡,这样我们就可以看到文档对象模型(DOM)结构,就像在浏览器中放大的截图中一样:
图 2.2:打开开发工具中元素面板的 second-default-app 的欢迎屏幕
现在,让我们看看 HMR 的实际效果。理想情况下,要看到这一点,您需要使用两个监视器。因此,您可以将 VS Code 窗口移动到左侧监视器,将为应用程序提供的浏览器移动到右侧监视器。或者,您可以使用单个监视器并将两个应用程序并排查看(每个应用程序占据屏幕宽度的一半)。这个练习的重点是能够同时看到您的 VS Code 窗口、Vue 应用程序浏览器窗口和浏览器开发工具中的 Elements 面板。
接下来,在 VS Code 中打开项目的src文件夹中的App.vue文件。查看第 4 行,目前的内容如下:
<HelloWorld msg="Welcome to Your Vue.js App"/>
我们很快就会将该行更改为其他内容。在更改该行之前,请注意这些更改如何在为您提供应用程序的浏览器中反映。浏览器会刷新吗?
现在,您专注于跟踪浏览器中的更改,让我们按照以下方式更新App.vue中的第 4 行:
<HelloWorld msg="HMR is cool"/>
在保存App.vue中的更改时,请注意浏览器。最好的方法是让 VS Code 处于焦点状态,但要观察浏览器窗口,特别是 Elements 面板。将 VS Code 置于焦点状态,您可以使用快捷键Ctrl + S保存更改。
如果您仔细观察,并且正在使用 Chrome 浏览器,您会注意到 Elements 面板内出现了一道紫色的闪光。这是 Chrome 浏览器通知我们 Vue 应用程序的 DOM 发生了变化。如果您仔细观察,您会注意到head元素上有一道闪光,以及h1元素和其子文本节点HMR is cool上也有一道闪光。
您可能会注意到浏览器没有刷新。无论是 webpack、我们的应用代码还是我们自己都没有强制浏览器刷新。这里的结论是什么?Webpack 实际上并没有使用 HMR 强制刷新页面!相反,它只是注入了 HMR。
虽然h1元素的更改是显而易见的(因为它是我们更改App.vue文件中文本的直接可见结果),但在head元素中发生的更新既更加隐晦又更有帮助。为了看到发生了什么,让我们点击 Elements 面板中head标签左侧的小黑三角形展开 head 标签,如下面的截图所示:
图 2.3:在开发工具中的 Elements 面板中展开 head 标签
接下来,我们需要滚动到最后的</head>标签。在它的上方,会有一个类似于这样的script标签:
<script charset="utf-8" src="/app.c7e7b2f6599f49948328.hot-update.js"></script>
当我们对App.vue进行另一个更改时,让我们密切关注元素面板中 DOM 树的这一部分。让我们将第 4 行的msg属性更新为这样:
<HelloWorld msg="HMR is cool indeed"/>
如果你观察了script标签,你会注意到它的变化如下:
<script charset="utf-8" src="/app.417e697f270d544a21b3.hot-update.js"></script>
你有没有注意到 Vue 在闭合的</head>标签上面注入了另一个脚本?那个注入的脚本就是 HMR 在那里发挥作用。
让我们检查附加的脚本文件的第一行,如下所示:
webpackHotUpdate("app",{
整个过程是如何工作的呢?Webpack 简单地运行我们的更改,捆绑了我们应用程序的更新,并且,由于它已经在运行,使用 Vue 加载器注入了新的代码。
所以,正如我们刚才看到的,HMR 只是 webpack 的一个功能,帮助我们更顺畅地进行操作,而不需要担心刷新我们的应用程序。
摘要
在本章中,我们讨论了 JS 语言及其生态系统的演变,以及这种演变如何导致模块捆绑器的出现。我们还看了 webpack,这是 Vue CLI 3 的首选模块捆绑器。
我们看了如何插入一个非常基本的 Vue 应用程序,运行在单文件 Vue 模板上。除了这个小项目,我们还看了一些重要的 webpack 概念。我们通过观察 Vue 项目上的 HMR 来结束了本章。
现在我们知道了 webpack 的基本工作原理,在接下来的章节中,我们将讨论一些其他相关技术,并且在 webpack 和 Vue CLI 3 的知识基础上进行深入。我们将密切关注的下一个主题是 Babel。
第三章:Vue CLI 3 中的 Babel
在本章中,我们将使用 Babel 将JavaScript(JS)的新功能带到浏览器中,使其在浏览器能够理解之前将其转换为旧版本的 JS。我们将讨论以下内容:
-
理解 Babel
-
使用 ES5 和 ES6 运行 webpack
-
更新我们的 webpack 配置以适配 Babel
-
Vue,Babel 和 JSX
-
手动添加 Babel 插件
让我们首先看看 Babel 解决了什么问题。
理解 Babel
正如我们在之前的章节中已经看到的,一旦使用 Vue CLI 构建了默认的 Vue 应用程序,您可以使用npm run serve来提供它。
您的应用程序通常会在localhost:8080上提供。查看默认内容的服务页面时,您会注意到在已安装的 CLI 插件标题下列出了两个插件:babel和eslint。
为什么这两个插件会预先安装在默认应用程序中呢?显然,Vue 框架团队正在努力遵循最佳实践,并与构建 Web 应用程序的现代方法保持最新。使用 Babel 就是其中之一。
如果您访问 Babel 网站,您会看到以下关于它的定义:
“Babel 是一个主要用于将 ECMAScript 2015+代码转换为当前和旧版浏览器或环境中的 JS 的向后兼容版本的工具链。”
那么,我们如何使用 Vue CLI Babel 插件?以及获取有关它的更多信息的最简单方法是什么?
由于我们已经使用 Vue CLI 创建了默认的 Vue 应用程序,并且已经了解了 Vue CLI 的 UI,我们可以通过打开 Git Bash 并启动 Vue CLI UI 轻松地访问官方文档:
vue ui
正如我们在第一章中所看到的,介绍 Vue CLI 3,这个命令将使 webpack 在浏览器中为我们最新的项目仪表板提供服务。在那里,我们可以点击插件图标,如下截图所示:
一旦您点击了已安装的插件链接,您将看到以下屏幕:
此应用程序列出了三个默认安装的插件:@vue/cli-service,@vue/cli-plugin-babel和*@vue/cli-plugin-eslint*。为了更容易理解,其他插件已被灰掉,并且在截图中添加了编号框:
-
更多信息链接到
cli-plugin-babel的 GitHub 存储库 -
更新
@vue/cli-plugin-babel -
带有主页图标的按钮是指向 Vue UI 项目管理器的链接,列出了所有可用的项目。
-
UI 的这一部分显示了您的操作系统中当前 Vue 项目的位置
-
单击此处可让您切换 Vue UI 的日志开关
-
正如我们之前所看到的,这使您可以在 Vue UI 的两种颜色变化之间切换
-
报告错误图标将带您到 Vue-CLI 错误报告网页。
-
如果您有兴趣翻译 UI,此按钮链接到 UI 本地化页面
-
此图标仅刷新插件的 API
如果您需要使用流行的vuex或vue-router插件,您可以简单地点击插件页面顶部的相应按钮来安装它们。
在“添加 vuex”和“添加 vue-router”按钮右侧的搜索输入框可让您过滤已安装的插件,“添加插件”按钮将带您转到localhost:8000/plugins/add屏幕,您可以从多个插件中进行选择,例如@vue/cli-plugin-unit-jest,@vue/cli-plugin-typescript,@vue/cli-plugin-pwa等。这里有大量的插件可供选择,我们将在后面的章节中更详细地了解它。
在下一节中,我们将讨论cli-plugin-babel的所有功能。
@vue/cli-plugin-babel 的构建模块
@vue/cli-plugin-babel默认提供了几个部分。这些是 Babel 7、babel-loader 和@vue/cli-plugin-babel。
@vue/cli-plugin-babel 中的 Babel 7
这就是 Babel 解决的问题。
假设您正在开发您的 Web 应用的前端,并且正在使用 JS 语言的更现代的 ES6+语法。一旦您的应用程序完成,并发布到互联网上,您的一些用户在 Internet Explorer 上运行您的 Web 应用程序。与您的 Web 应用程序的其他用户相反,他们可以顺利运行您的应用程序,Internet Explorer 用户将收到语法错误。
Babel 就是对这样的问题的答案。它平衡了竞争环境:它允许开发人员将他们的 JS 浏览器兼容性问题外包给 Babel。他们不必再担心和迎合旧版浏览器,他们可以简单地使用语言的最新功能来编写他们的 JS 代码,甚至在任何浏览器完全支持之前。然后,Babel 负责将此代码转换为旧的 JS 方言,这是旧版浏览器可以理解的。
@vue/cli-plugin-babel运行在 Babel 7 上,Babel 7 于 2018 年 8 月 27 日发布。Babel 6 和 Babel 7 之间相差三年,这一迭代带来了一系列改进。Vue CLI 支持如此近期的更新是其团队致力于尽可能跟上时代的又一个证明。
@vue/cli-plugin-babel中 babel-loader 的作用
正如我们在前一章中看到的,Vue CLI 运行在 webpack 4 上。
为了能够使用 Babel 7,@vue/cli-plugin-babel使用 babel-loader,可以在这里找到:github.com/babel/babel-loader。
如前一章所述,使用 webpack 加载器,我们可以预处理和捆绑一堆不同的资源,不仅仅是常规 JS,而是几乎任何其他静态资源。
具体来说,babel-loader接收 ES6+ JS,并将其转换为 ES5 JS。这个过程通常被称为转译。因此,@vue/cli-plugin-babel中 babel-loader 的作用是将我们的 ES6+代码转译为 ES5。
@vue/babel-preset-app的作用
@vue/cli-plugin-babel还有更多功能。它包括@vue/babel-preset-app,其唯一目的是在通过 Vue CLI 生成的项目中使用。在不深入讨论@vue/babel-preset-app的工作原理的情况下,我们可以列出其主要功能:
-
它使用
browserslist来查看您的浏览器目标 -
它自动应用所需的转换和填充(借助
@babel/preset-env实现) -
它增加了对 Vue JSX 的支持
-
它阻止在构建过程中将所有文件中的辅助程序内联
除了之前列出的功能之外,@vue/cli-plugin-babel 还有其他功能,我们将在下一节中讨论它们。
@vue/cli-plugin-babel的其他功能
除了前一节中列出的默认设置,@vue/cli-plugin-babel也是可扩展的。我们可以使用babel.config.js添加其他 Babel 预设和插件。
它使用了一些 webpack 加载器来执行另外两个主要任务:缓存(借助 cache-loader 的帮助)和利用多核处理器(借助 thread-loader 的帮助)。这被称为并行化。
在下一节中,类似于我们在第二章中所做的,Vue CLI 3 中的 Webpack,我们将介绍在 Vue 中设置 Babel 而不使用 CLI。之后,我们将看看 CLI 如何使事情变得更容易,以及如何进一步扩展。
在 Vue 2 中使用 Babel 和 webpack 而不使用 Vue CLI
让我们将我们的新项目命名为npm-vue-babel-webpack。我们将打开 Git Bash,添加此项目的文件夹,并cd进入其中:
mkdir npm-vue-babel-webpack && cd $_
我们将初始化 NPM 并接受所有默认设置:
npm init -y
在第二章中,Vue CLI 3 中的 Webpack,我们逐个安装了 NPM 包,解释了每个包的作用,并在此过程中修复了任何错误。这使我们对 webpack 的构建模块和 Vue 与 webpack 的配合方式有了深入的了解。为了避免不必要的重复,这次我们将一次性安装所有内容。
安装必要的 NPM 包
安装必要的 NPM 包:
npm install vue webpack webpack-cli html-webpack-plugin vue-loader vue-template-compiler --save-dev --verbose
现在将src和dist文件夹添加到我们的项目中,并在 VS Code 中打开我们的项目:
mkdir dist src && code .
随时在 VS Code 中检查package.json的内容,以确认所有 NPM 包确实已安装。
让我们在src文件夹内创建三个新文件,具体为main.js、App.vue和index.html,几乎与我们在第二章中所做的一样,Vue CLI 3 中的 Webpack。
以下是要添加到index.html中的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Compiled HTML file</title>
</head>
<body>
<div id="entryPoint"></div>
</body>
</html>
以下是main.js的内容:
import Vue from 'vue';
import App from './App.vue';
new Vue({
el: '#entryPoint',
render: h => h(App),
})
最后,这是App.vue的内容:
<template>
<div id="entryPoint">
<article>
Our own custom article component!
</article>
<AnotherComponent />
</div>
</template>
<script>
import AnotherComponent from './components/AnotherComponent.vue';
export default {
name: 'entryPoint',
components: {
AnotherComponent
}
}
</script>
请注意,在上述script标签内部,我们正在从components文件夹中导入AnotherComponent。
因此,让我们在项目的src文件夹内添加一个components文件夹。在components文件夹内,我们将添加一个新文件并将其命名为AnotherComponent.vue。
接下来,将此代码添加到AnotherComponent.vue中:
<template>
<p>
This is another component.
<button v-on:click="alertTime">What's the time?</button>
</p>
</template>
<script>
export default {
name: "AnotherComponent",
data() {
return {
}
},
methods: {
alertTime: function() {
alert(new Date());
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
在上述代码中,我们终于看到了一个示例,其中我们的组件具有一些基本的 JS 驱动的 Vue 功能。我们正在使用 Vue 的内置data和methods选项。在methods选项内,我们定义了alertTime函数,每当它被调用时,都会在警报框中显示当前时间。
讨论所有这些组成部分如何运作的细节超出了本书的范围。本章的重点是理解 Babel。如果您需要更多关于 Vue 的基本概念的信息,比如前几段提到的内容,请参考 Packt 图书馆中的许多有用资源之一。本书的一个很好的伴侣将是对 Vue 2 框架的快速介绍:Vue.js 快速入门指南,作者是Ajdin Imsirovic(prod.packtpub.com/in/application-development/vuejs-quick-start-guide)。
我们现在需要关注的重点是在我们的methods选项中使用 ES6+功能。目前,methods选项的代码是用 ES5 JS 编写的,因此很容易在此代码上运行 webpack,我们很快就会看到。
使用 ES5 代码运行 webpack
要运行 webpack,请执行以下操作:
- 在项目的根目录中添加另一个文件
webpack.config.js,以便我们可以设置我们的 webpack 配置如下:
let HtmlWebpackPlugin = require('html-webpack-plugin');
let VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/main.js',
output: {
filename: 'main.js',
},
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
]
};
请注意,入口和输出文件都是main.js,所以我们不必指定它们,但是在前面的代码中我们还是这样做了,以使事情更明显。
- 接下来,在
package.json中,更新scripts键:
"scripts": {
"webpack": "webpack"
},
- 现在从 Git Bash 运行我们的项目,使用以下命令:
npm run webpack
-
现在,在 VS Code 中,导航到我们项目中的
dist文件夹。 -
右键单击
index.html,然后单击“在默认浏览器中打开”命令。
我们的浏览器现在将显示以下输出(放大以便查看):
如果用户单击“现在几点了?”按钮,将在网页上出现一个警报框,显示当前时间。现在让我们将我们的methods选项更新为 ES6 语法,然后看看会发生什么。
添加 webpack-dev-server
在我们开始将代码更新为 ES6 语法之前,还有一件事可以让事情变得更快捷和更方便:
- 添加
webpack-dev-server。借助这个 NPM 包,我们的代码将不断地被提供和监视变化。让我们使用以下命令安装它:
npm install webpack-dev-server --save-dev --verbose
- 为了让 webpack 开发服务器运行并提供我们的代码,我们还需要将
package.json条目的scripts更新为以下内容:
"scripts": {
"webpack": "webpack",
"webpack-dev": "webpack-dev-server --mode development"
},
现在我们可以尝试向我们的组件添加各种功能,并在我们在 VS Code 中保存代码时,观察它们在浏览器中进行热重载。
- 现在让我们立即通过运行以下命令来测试它:
npm run webpack-dev
您可以在http://localhost:8080/上测试提供的网页,并且您会注意到它仍然像以前一样工作。
接下来,我们将在methods选项中添加一些 ES6 语法。
将方法选项更新为 ES6 语法
让我们更新AnotherComponent.vue文件中的methods选项。以下是更新后的代码:
methods: {
alertTime: () => {
alert(new Date());
alert('something else');
}
}
一旦您在 VS Code 中保存了更改,您可以单击“现在几点了?”按钮,然后会出现预期的警报,然后是另一个读取其他内容的警报。这样,我们可以确保我们正在查看更新的应用程序。
现在让我们在dist文件夹中的编译后的main.js文件中找到我们的 ES6 代码。
如果我们在开发工具中检查/dist/index.html文件,我们可以看到对main.js的引用,这是 webpack 编译的 JS 代码。如果右键单击main.js并在上下文右键菜单中点击“在新标签页中打开”命令,您将在新标签页中看到完整的代码。要找到我们的 ES6 代码,让我们按下Ctrl + F快捷键,以便输入我们的搜索词:alertTime。
在文件的底部,我们看到了我们的 ES6 箭头函数:
alertTime: () => {\r\n alert('something else');
在接下来的部分,我们将使用 babel-loader 更新我们的 webpack 配置,并看看 webpack 将如何将前面的代码转译为 ES5。
将 babel-loader 添加到我们的 webpack 配置中
在开始之前,我们需要停止 webpack-dev-server,使用 Git Bash 中的Ctrl + C组合键。
接下来,为了能够在我们的项目中转译 ES6+语法,我们需要使用 Babel 更新我们的 webpack 配置。让我们首先使用 NPM 安装 babel-loader 包:
npm install babel-loader --save-dev --verbose
接下来,让我们再次在项目上运行 webpack:
npm run webpack
不幸的是,这仍然不起作用。如果我们检查我们转译后的main.js,我们仍然会看到alertTime键和它的 ES6 匿名函数。这意味着我们仍然需要另一个包:babel core。
npm install @babel/core --save-dev --verbose
如果我们此时运行 webpack,我们会发现我们的问题仍然没有解决。
这意味着我们仍然需要添加babel-preset-env:
npm install @babel/preset-env --save-dev --verbose
此时,验证一下我们的package.json中的devDependencies是否都有预期的更新是没有坏处的:
"devDependencies": {
"@babel/core": "⁷.3.4",
"@babel/preset-env": "⁷.3.4",
"babel-loader": "⁸.0.5",
"html-webpack-plugin": "³.2.0",
"vue": "².6.9",
"vue-loader": "¹⁵.7.0",
"vue-template-compiler": "².6.9",
"webpack": "⁴.29.6",
"webpack-cli": "³.2.3",
"webpack-dev-server": "³.2.1"
}
最后,在我们重新运行 webpack 之前,我们需要设置一个babel.config.js文件,这是 Babel 自己的配置文件(类似于 webpack 的webpack.config.js)。
让我们在项目的根目录中创建一个新文件babel.config.js,并添加以下代码:
module.exports = {
presets: ['@babel/preset-env']
}
现在我们需要做的就是更新我们的 webpack 配置,使其能够与 Babel 一起工作。
更新我们的 webpack 配置以使用 babel
为了使我们的 webpack 能够使用 babel,我们需要告诉它何时使用 babel-loader。我们通过在webpack.config.js的module选项内添加一个测试规则来实现,如下所示:
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{ test: /\.vue$/, use: 'vue-loader' }
]
},
现在我们已经设置好了一切,我们可以再次在 Git Bash 中运行npm run webpack-dev命令。
这里有一个快速的方法来查看 webpack 是否与之前不同地捆绑了我们的 JS 文件:只需查看 Git Bash 中的 webpack 日志信息。在我们之前尝试将 Babel 与 webpack 配合工作时,捆绑大小恰好是 70.2 KB。然而,在webpack NPM 脚本的最后一次运行之后,main.js的捆绑大小为 70.6 KB。我们可以再次在开发工具中检查./dist/main.js文件。或者,你可以在 VS Code 中直接搜索./dist/main.js中的alertTime字符串。
无论我们如何定位它,我们捆绑的main.js文件的methods条目看起来是这样的:
methods:{alertTime:function(){alert(new Date),alert("something else")}}}...
仅仅瞥一眼前面的代码并看到function关键字,就应该明显地意识到这段代码是 ES5 的,这意味着 Babel 已经成功地被 webpack 运行,我们在src文件夹中的输入文件中的 ES6 alertTime Vue 方法已经成功地被转译到了dist文件夹中的输出文件中。
为了验证我们的设置是否有效,我们可以再次运行webpack-dev-server,并且在它运行时,对AnotherComponent.vue中的methods选项进行另一个小改动:
methods: {
alertTime: () => {
alert(new Date());
alert('whatever');
}
}
如果你查看在localhost:8080上提供的项目,你会看到它按预期工作,如果你从开发工具中打开main.js,你也会看到转译后的语法。
在下一节中,我们将简要提到一个常见的困惑来源以及在 Vue 中如何处理它。这与箭头函数语法和this关键字有关。
箭头函数中的 this 关键字在 Vue 中的问题
不幸的是,当 Babel 将箭头函数中的this关键字转译为_this时,这意味着我们的任何方法都将被破坏,我们的应用程序将无法工作。这背后的原因是箭头函数的作用域与 ES5 函数不同。
在下一节中,我们将看一下在 Vue 实例的方法选项中定义函数的推荐方法。
关键字问题的推荐解决方案
在 Vue 组件中解决this关键字的推荐解决方案是不使用箭头函数语法,因为由于作用域问题,它不会产生预期的结果。具体来说,箭头函数的作用域是父上下文。
让我们看一个简单的应用作为问题的例子。
添加一个计数器应用
在开始之前,请确保你回到了VUE-CLI-3-QSG/Chapter03文件夹,这是本章中所有项目的根文件夹。
在我们开始构建应用程序之前,我们需要提醒自己在使用vue create时有一些选项,因此让我们运行这个:
vue create --help
在选项列表中,我们可以看到-d代表--default标志,跳过提示并使用默认预设,而-b选项是--bare标志的简写,用于在不带初学者说明的情况下搭建我们的项目。
有趣的是,我们可以组合这些单独的标志,我们现在就来做。让我们通过运行以下命令来开始我们的应用程序:
vue create add-one-counter -db
正如我们所看到的,我们可以在vue create命令后附加的标志的字母别名之间进行链接,这是一个很好的小型生产力提升。
在构建的应用程序中,我们将更改src文件夹中的main.js的内容。这个文件将与之前的示例应用程序(上一节中的npm-vue-b-w-es6-syntax应用程序)完全相同,因此您可以将该文件从之前的 Vue 应用程序中复制并粘贴到我们的新示例应用程序add-one-counter中。
如果您在 VS Code 中打开我们的新的add-one-counter应用程序,您还会注意到另一个文件夹:public文件夹,其中包含index.html。我们将保留此文件不变。
回到src文件夹,我们需要更改App.vue的内容如下:
<template>
<div id="app">
<article>
Our own custom article component!
</article>
<AnotherComponent />
</div>
</template>
<script>
import AnotherComponent from './components/AnotherComponent.vue';
export default {
name: 'app',
components: {
AnotherComponent
}
}
</script>
最后,我们需要在项目的根目录下添加一个components文件夹,并在其中添加AnotherComponent.vue文件。以下是AnotherComponent.vue的内容:
<template>
<p>
This is another component.
<button v-on:click="incrementUp">Add One</button>
<br>
<span>Current value of the counter: {{ counter }}</span>
</p>
</template>
<script>
export default {
name: "AnotherComponent",
data() {
return {
counter: 0
}
},
methods: {
incrementUp: function() {
this.counter++;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
在methods选项中,我们可以看到incrementUp函数使用 ES6 语法进行定义。
如果您尝试运行此应用程序,它将无法工作。这是因为箭头函数的作用域和 Babel 设置使得在arrow函数中正确设置 Vue 应用程序的methods变得困难。
唯一的改进,也是在方法选项中编写函数的通常方式,是避免使用箭头函数语法和function关键字的使用,如下所示:
methods: {
incrementUp() {
this.counter++;
}
}
incrementUp函数被称为简写函数。您可以在以下网址阅读更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions。
让我们通过 UI 来测试驱动应用程序:
vue ui
一旦 Vue UI 在浏览器中提供服务,让我们将浏览器的地址栏指向http://localhost:8000/project/select。接下来,点击add-one-counter文件夹,然后点击导入此文件夹按钮。
接下来,点击主菜单上的 Tasks 按钮。最后,点击 Serve 图标。点击 Output 按钮查看应用程序的构建和服务情况。
最后,在http://localhost:8080/打开网站。你会看到一个正在提供服务的工作中的应用程序:
前面的例子向我们展示了如何在 Vue 实例中命名和组织方法的最佳实践。此外,我们还学会了如何使用 Vue UI 来自动化 webpack 的构建和服务应用程序,只需点击几下,比本章大部分内容中我们所做的要好得多!这让我们得出一个结论:很多的管道和功能都被抽象化了,因此,使用 Vue UI 和 Babel 设置变得更加容易和方便。
摘要
在本章中,我们简要概述了 Babel 是什么,它的作用以及使其与 Vue 一起工作所需的内容。所有这些都是通过vue-cli-service来抽象化的,它在幕后由 webpack 提供支持。现在我们已经了解了所有这些不同部分是如何一起工作的,我们将开始只使用 Vue CLI 及其 UI,并在接下来的章节中学习如何更好地使用它。
我们将从理解在 Vue CLI 中使用 Jest 进行测试开始。我们还将学习测试驱动开发(TDD)以及如何使用 Vue CLI UI 运行测试。
第四章:在 Vue CLI 3 中进行测试
在上一章中,我们研究了 Babel 在现代 JavaScript 开发中的作用。我们还看到了在 Vue 中使用它的一些实际例子。在本章中,我们将介绍 JS 中的测试。我们将了解测试的一般情况,并使用 Jest 和 Cypress 进行实践。我们将讨论断言和测试驱动开发(TDD)。然后,我们将继续了解 Jest 和 Cypress 如何与 Vue CLI 3 一起工作。我们将讨论测试工具和测试用例。具体来说,我们将看以下内容:
-
了解 Vue 插件
-
将 Jest 插件添加到我们的 Vue 应用程序
-
在 Vue 应用程序中使用 Jest 编写单元测试
-
从项目任务页面运行任务
-
在 Vue CLI UI 中运行单元测试
-
使用断言
-
实施 TDD
-
使用 Cypress
我们将从对 Vue 插件的简要概述开始本章。
了解 Vue 插件
使用 Vue CLI 从命令行创建新的 Vue 应用程序时,我们使用vue create命令。然后,我们需要选择一些步骤和提示,以便正确配置我们的应用程序。实际上,我们正在选择我们的应用程序将使用哪些 Vue 插件,而其他事情。
插件是向我们的 Vue 项目添加功能的一种方式。有些插件比其他插件更复杂;它们有时在安装过程中会出现自己的提示。我们的 Vue 应用程序的配置,即底层代码,将反映我们的选择。我们的应用程序的设置方式将基于我们对这些安装提示的回答。
项目的所有官方npm包都使用@符号进行范围限定,后面跟着项目名称。因此,由 Vue 维护者构建的官方 Vue 插件以@vue开头。
要了解有关范围限定npm包的更多信息,请访问:docs.npmjs.com/about-scopes.
要从命令行添加插件,我们使用vue add命令,但我们也可以使用 Vue UI,正如我们将在本章中看到的那样。Vue UI 也是搜索 Vue 插件的好方法,我们也将在本章中进行研究。
在全新的 Vue 应用程序上开始测试
在之前的章节中,我们已经看到了 Vue CLI 和 UI 中许多不同的选项。我们将通过使用最佳方法来开始一个新应用程序,即 Vue CLI UI,来开始本章。这将帮助我们了解 UI 的一些其他功能。在此过程中,我们还将慢慢向我们的项目引入测试。
使用 Vue CLI UI 添加新项目
现在让我们使用 Vue CLI UI 添加新项目:
-
首先,让我们打开 Git Bash 并导航到所有项目的根文件夹
vue-cli-3-qsg。 -
现在我们将运行 Vue CLI UI 命令如下:
vue ui
这将导致浏览器中提供新页面。默认地址为http://localhost:8000/dashboard。
- 接下来,单击主页图标(或者在地址栏中键入此 URL:
http://localhost:8000/project/select),这将带您到 Vue 项目管理器屏幕。
请注意,主页图标是 Vue CLI UI 页脚中最左边的图标:
图 4.1:Vue CLI UI 中的主页图标
-
无论您如何访问 Vue 项目管理器屏幕,它都会显示可用应用程序的列表,以及顶部的三个选项卡:项目,创建和导入。单击“创建”选项卡以创建新项目。
-
单击“创建”选项卡后,您需要返回到项目的根目录,然后单击“在此处创建新项目”按钮如下:
图 4.2:创建,返回所有项目的根目录,点击“在此处创建新项目”按钮
一旦单击“在此处创建新项目”按钮,您将看到“创建新项目”屏幕。我们只会输入我们新应用程序的文件夹名称。我们将其称为testing-debugging-vuecli3。我们不会更改任何其他内容:我们将接受默认的软件包管理器和其他默认选项如下:
图 4.3:添加将容纳我们新应用程序的文件夹的名称
我们已经在“详细信息”选项卡中完成了所有必要的更改。
- 点击“下一步”按钮,我们将进入预设屏幕。在那里,我们可以接受默认预设(babel,eslint)如下:
图 4.4:接受默认预设
- 接下来,我们将单击“创建项目”以搭建我们的项目。Vue CLI UI 完成项目搭建需要一些时间。完成后,我们将看到“欢迎来到您的新项目!”屏幕。
向我们的 Vue 应用程序添加 Jest 插件
现在让我们添加我们的 Jest 插件:
-
点击插件图标(以下截图中标有数字 1)。
-
一旦项目插件屏幕出现,点击“添加插件”按钮(以下截图中的 2):
图 4.5:向我们的安装添加新插件
- 这将带我们到“添加插件”屏幕,在那里我们有一个输入字段来搜索插件。我们需要找到一个单元测试插件,所以我们可以输入
cli-plugin-unit如下:
图 4.6:查找 Vue 的单元测试插件
- 输入此搜索词将显示所有可用的单元测试插件。Jest 应该就在顶部。您可以在上一张截图中看到它,标记为 2。在插件名称下的描述中,您可以看到它是一个官方插件。与我们已经看到的
@vue/cli-plugin-babel类似,您可以单击“更多信息”链接以查看有关所讨论插件的相应 GitHub 存储库(在上述截图中标记为 3)。这样做将带您到 vue-cli GitHub 页面。
您可以在以下 URL 的npm包页面上找到有关@vue/cli-plugin-unit-jest的更多信息:www.npmjs.com/package/@vue/cli-plugin-unit-jest.
- 要安装 Jest 插件,只需在插件列表中单击它。这将在 Jest 插件旁边的绿色圆圈中添加一个复选框(在下一张截图中标记为 1)。还将出现一个新的安装按钮(在下一张截图中的框 2):
图 4.7:添加 Jest 插件
-
单击“安装@vue/cli-plugin-unit-jest”按钮将导致页面上出现加载器,并显示以下消息:安装@vue/cli-plugin-unit-jest....
-
完成后,只需单击“完成安装”按钮,如下所示:
图 4.8:完成 Jest 插件的安装
-
单击“完成安装”按钮将在屏幕上显示以下消息:调用@vue/cli-plugin-unit-jest....
-
更新完成后,我们将看到另一个屏幕,显示文件的更改,并要求我们提交所做的更新:
图 4.9:Vue CLI UI 在安装 Jest 插件后显示更改
检查更新
在上述截图中,我们可以看到“更改的文件”选项卡是活动的。在“更改的文件”选项卡中,我们可以看到更改的文件数量(框 1)。
在使用 Vue CLI UI 构建项目时,我们被默认选项要求使用 Git 跟踪项目的更改,我们接受了这个默认设置;这就是为什么我们现在看到在上一个截图中标记为 2 的提交更改按钮。
我们还可以看到对两个现有文件package-lock.json和package.json所做的所有更改和更新,以及在安装插件时添加的三个新文件的内容:jest.config.js,tests/unit/.eslintrc.js和tests/unit/example.spec.js。
检查每个文件的内容将是有益的,以便更熟悉它们的设置以及对它们进行了哪些更改。我们需要注意的最重要的更改之一是在package.json文件中的scripts键中,如下所示:
"test:unit": "vue-cli-service test:unit"
前一行显示我们的vue-cli-service刚刚得到了一个新命令,test:unit,专门用于使用 Jest 进行单元测试。
一旦我们点击提交更改按钮,我们将看到一个对话框,邀请我们输入提交消息。我们可以输入一个简单的消息,比如添加 Jest 插件。
在我们添加了提交之后,我们将被带回到已安装插件的屏幕。现在我们可以看到@vue/cli-plugin-unit-jest也已添加:
图 4.10:Vue CLI UI 在安装 Jest 插件后显示的更改
在接下来的部分中,我们将添加我们的应用程序,以便我们可以开始使用 Jest 进行测试。
添加我们的 Vue 应用程序的代码
要添加我们的应用程序,我们需要执行以下操作:
-
在 Windows 资源管理器中导航到
testing-debugging-vuecli3文件夹。 -
接下来,在文件夹内的空白处右键单击,然后单击 Git Bash here 命令。
-
一旦 Git Bash 打开,输入
code .并按下Enter键。这将在 VS Code 中打开我们的testing-debugging-vuecli3项目。
我们目前的重点是 Jest。为了避免不必要的复杂性,我们将简单地复制并粘贴上一章中的整个add-one-counter应用程序。最简单的方法是将 Windows 资源管理器指向add-one-counter应用程序,并在文件夹内右键单击启动另一个 Git Bash 实例,如前所述。我们将再次在 Git Bash 中输入code .命令,另一个 VS Code 实例将打开,这次显示add-one-counter应用程序内的文件。现在只需要将所有文件和文件夹从add-one-counter复制并粘贴到testing-debugging-vuecli3中。
或者,您可以在 Git Bash 中使用 Linux 命令来复制相关文件。
无论您如何操作,更新后的项目结构现在将如下所示:
图 4.11:testing-debugging-vuecli3 的更新项目结构
现在,我们准备使用 Jest 开始我们的第一个单元测试。
使用 Jest 在 Vue 应用程序中编写我们的第一个单元测试
在tests文件夹中,还有一个名为unit的文件夹。让我们向unit文件夹添加一个新文件。我们将这个新文件命名为AnotherComponent.spec.js。
任何具有spec.js扩展名的文件都将被 Jest 识别。
为了描述一个测试,我们使用describe函数,因此让我们将其添加到AnotherComponent.spec.js中,如下所示:
describe()
describe函数接受两个参数。第一个参数是我们正在测试的 Vue 组件的名称,第二个参数是一个匿名函数,如下所示:
describe('AnotherComponent.vue', function() {} )
我们可以使用箭头函数作为第二个参数来重写前面的代码,如下所示:
describe('AnotherComponent.vue', () => {})
在匿名函数的主体中,我们调用另一个函数,test函数,如下所示:
describe('AnotherComponent.vue', () => {
test()
})
test函数接受两个参数:第一个参数是我们的测试名称,第二个参数是另一个匿名箭头函数,如下所示:
describe('AnotherComponent.vue', () => {
test('Jest is setup correctly and working', () => {})
})
我们正在指定一个名为setup working的测试,并且我们需要在第二个参数内给出一个断言,也就是在匿名函数的主体内,如下所示:
describe('AnotherComponent.vue', () => {
test('Jest is setup correctly and working', () => {
expect(true).toBeTrue();
})
})
这个断言将始终为真,因此我们给出以下代码:expect(true).toBeTrue()。
我们刚刚看到了 Jest 匹配器的一个示例。匹配器是在 Jest 中测试值的一种方式。检查某些东西是否为真的一种方式是使用toBeTrue匹配器。还有许多其他 Jest 匹配器。有关更多信息,请查看以下 URL 的官方 Jest 文档:jestjs.io/docs/en/using-matchers.
现在,让我们转到 Vue CLI UI,并运行我们的单元测试。
在 Vue CLI UI 中运行我们的第一个单元测试
要在 Vue CLI UI 中运行我们的单元测试,我们需要导航到localhost:8080/tests页面,可以直接从地址栏访问该 URL,也可以通过单击 Vue CLI UI 仪表板中最左侧列中的最低图标(Vue CLI UI 仪表板中的 Tests 图标)来导航到该页面。一旦我们这样做,我们将看到以下测试列表:
图 4.12:查看 Vue CLI UI 中可用的任务
接下来,让我们准备点击test:unit任务来运行。这样做将导致在项目任务页面的右侧出现一个面板,如下所示:
图 4.13:运行 test:unit 命令的运行任务面板
这个运行任务面板给了我们一个很好的概览。现在让我们通过单击运行任务按钮来运行我们的测试。
以下信息将显示在输出部分:
...
...
PASS tests/unit/AnotherComponent.spec.js
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.702s
Ran all test suites.
Total task duration: 3.93s
正如我们所看到的,我们的AnotherComponent.spec.js测试已经成功通过。在AnotherComponent.spec.js测试之前,有一个测试失败并记录输出的测试,那就是HelloWorld.vue组件的测试。我们在 Vue UI 构建应用程序后,已经将HelloWorld.vue文件从默认应用程序中移除了。
然而,./tests/unit文件夹中的example.spec.js文件是调用不存在的HelloWorld.vue文件的test文件。查看example.spec.js,我们可以看到它在第 2 行导入了HelloWorld.vue组件,如下所示:
import HelloWorld from '@/components/HelloWorld.vue'
为什么会这样呢?难道我们不是已经在describe函数中指定了AnotherComponent.vue吗?
事实证明,我们可以在我们的单元测试中的 describe 函数的第一个参数中指定任何名称。例如,我们可以将我们的AnotherComponent.spec.js文件更新为以下代码:
describe('whatever', () => {
test('Jest is setup correctly and working', () => {
expect(true).toBeTrue();
})
})
如果我们再次运行我们的测试,它仍然会运行。
这意味着字符串AnotherComponent.vue是我们开发者作为第一个参数传递给我们的describe函数的,这样我们在项目中更容易工作。Jest 不在乎它的名字是什么。
然而,它在乎的是导入要测试的文件。正如我们在HelloWorld.vue导入中看到的,我们需要在我们的AnotherComponent.spec.js文件中添加一个类似的导入,这样现在它看起来如下:
import AnotherComponent from '@/components/AnotherComponent.vue';
describe('AnotherComponent.vue', () => {
test('Jest is setup correctly and working', () => {
expect(true).toBeTrue();
})
})
有趣的是,我们导入了一个 Vue 文件,但我们的测试仍然通过,即使.vue扩展名不是 JS。这是如何实现的?
如果我们打开位于项目根目录的jest.config.js文件,我们可以很容易地看到发生了什么。查看这个文件的前 12 行,我们会看到以下代码:
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
正如我们所看到的,vue扩展名在第 6 行上列出,.vue文件扩展名将使用vue-jest插件进行转换,如第 9 行所指定的。
在我们继续之前,让我们将我们的example.spec.js文件重命名为example.js,这样 Jest 就不会捕捉到它。我们仍然需要文件的内容,所以让我们不要删除它,而是只是重命名它。
从 test-utils 导入 mount 并编写另一个单元测试
我们将从@vue/test-utils中的mount导入开始,放在我们的AnotherComponent.spec.js文件的第一行,如下所示:
import { mount } from '@vue/test-utils';
在我们继续之前,我们需要看一下这个语法的作用。为什么在mount周围有花括号?
要回答这个问题,了解这是被接受的 JS 语法是很重要的。为了解释发生了什么,我们需要从package.json文件开始。
这个文件是由我们的 Vue CLI 在构建项目时创建的。如果我们查看package.json文件的内容,我们会看到@vue/test-utils被列为我们项目的devDependencies之一。
在前面的代码中,我们从@vue/test-utils JS 模块中导入了一个单一函数mount。通过这样做,我们将mount函数插入到我们的AnotherComponent.spec.js文件的作用域中。
简单来说,我们从@vue/test-utils导入mount功能,这样我们就可以在AnotherComponent.spec.js文件中使用它,并且只测试这个组件。
在我们的浏览器中运行 Vue CLI UI,让我们通过访问以下 URL 来查看我们项目的依赖列表:http://localhost:8000/dependencies。
你应该会看到类似以下截图的屏幕:
图 4.14:Vue CLI UI 仪表板中列出的项目的 devDependencies
像往常一样,点击@vue/test-utils项目依赖项的“更多信息”链接将带我们到该项目对应的 GitHub 存储库:github.com/vuejs/vue-test-utils#readme.
挂载要测试的组件
我们首先导入mount方法和要测试的组件,如下所示:
import { mount } from '@vue/test-utils';
import AnotherComponent from '@/components/AnotherComponent.vue';
mount函数接收一个组件作为其参数。调用mount函数的结果是一个包装器,其中包含我们给定的组件的实例。这个包装器还带有帮助我们测试过程的附加函数。让我们首先将调用mount(AnotherComponent)的返回值分配给一个变量,如下所示:
import { mount } from '@vue/test-utils';
import AnotherComponent from '@/components/AnotherComponent.vue';
describe('AnotherComponent.vue'), () => {
test('Adds one when a user clicks the button', () => {
const wrapped = mount(AnotherComponent);
}
编写一个失败的断言
当我们的应用程序最初加载到浏览器中时,我们期望计数器的当前值为0。然而,由于我们希望我们的断言最初失败,让我们断言计数器的值将是1而不是0,如下所示:
import { mount } from '@vue/test-utils';
import AnotherComponent from '@/components/AnotherComponent.vue';
describe('AnotherComponent.vue'), () => {
test('Adds one when a user clicks the button', () => {
const wrapped = mount(AnotherComponent);
expect(wrapped.text()).toContain('Current value of the counter: 1');
}
在上述代码中,我们已经编写了一个失败的断言。我们声称我们期望我们的包装组件将包含以下文本:
Current value of the counter: 1
我们的计数器的初始值不会是1;实际上将是0,因此前面的断言应该失败。
因此,让我们保存并运行我们的单元测试,方法是转到项目任务屏幕,并按照本章前面描述的方式运行测试。
输出将显示在项目任务中的 Run task 的输出面板中,如下所示:
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.947s
Ran all test suites.
Total task duration: 3.14s
接下来,我们将通过编写一个通过的断言来修复前面的测试。
编写一个通过的断言
要编写一个通过的断言,我们只需要将我们的1恢复为0,如下所示:
expect(wrapped.text()).toContain('Current value of the counter: 0');
接下来,让我们在 Vue UI 中再次运行我们的任务,然后我们将得到以下输出:
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.418s
Ran all test suites.
Total task duration: 3.55s
接下来,我们将在我们的测试中触发一个按钮点击。
在我们的测试中触发按钮点击
我们如何在单元测试中测试按钮点击?执行以下步骤:
-
我们需要找到要点击的按钮。这很容易,因为我们的应用程序中只有一个按钮。我们将使用
find方法来做到这一点。 -
我们将使用
trigger方法触发按钮点击。 -
我们需要检查计数器的值是否从
0变为1。然而,由于我们首先需要编写一个失败的测试,我们将编写以下代码:
import { mount } from '@vue/test-utils';
import AnotherComponent from '@/components/AnotherComponent.vue';
describe('AnotherComponent.vue', () => {
test('Adds one when a user clicks the button', () => {
const wrapped = mount(AnotherComponent);
expect(wrapped.text()).toContain('Current value of the counter: 0');
const button = wrapped.find('button');
button.trigger('click');
expect(wrapped.text()).toContain('Current value of the counter: 0');
})
})
如预期的那样,在 Vue CLI UI 中运行我们的测试的输出如下:
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 2.383s
Ran all test suites.
Total task duration: 3.55s
通过编写两个测试并断言它们以使它们都通过,让事情变得更有趣,如下所示:
import { mount } from '@vue/test-utils';
import AnotherComponent from '@/components/AnotherComponent.vue';
describe('AnotherComponent.vue', () => {
const wrapped = mount(AnotherComponent);
const button = wrapped.find('button');
test('Checks that the initial counter value is 0', () => {
expect(wrapped.text()).toContain('Current value of the counter: 0');
});
test('Adds one when a user clicks the button', () => {
button.trigger('click');
expect(wrapped.text()).toContain('Current value of the counter: 1');
})
})
让我们再次保存我们的测试,并在 Vue CLI UI 中再次运行它作为一个任务。
以下是输出:
AnotherComponent.vue
√ Checks that the initial counter value is 0 (3ms)
√ Adds one when a user clicks the button (4ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.307s
Ran all test suites.
Total task duration: 3.64s
我们已经成功地在一个测试套件中为 Vue 组件编写了两个单独的单元测试,并且我们的两个测试都通过了。
在 Vue CLI 3 中的测试驱动开发
TDD 是基于“红-绿-重构”周期的开发。与我们在前面的代码中看到的类似,我们首先编写我们的代码,使我们的测试失败。接下来,我们编写我们的代码,使我们的测试通过,最后我们重构我们的代码。
对于我们应用程序中的每个新功能,我们重复相同的过程。这本质上就是 TDD。
TDD 只是以一种简化的方式在任何语言或框架中编写任何应用程序。它通过允许我们将整个项目分割成可测试的、清晰分离的功能块来简化我们的工作。
红绿重构方法在项目任务页面的输出中也是清晰可见的。如果我们编写一个失败的测试,我们会看到单词“fail”的背景是红色的。如果我们编写一个通过的测试,我们会看到单词“pass”的背景是绿色的。
在本章的其余部分,我们将通过 Vue CLI 3 的帮助来了解与测试相关的一些其他概念。
在 Vue CLI 3 中改进我们的测试
我们可以利用 Jest 和其他测试平台在 Vue CLI 3 中进行更好的测试体验的几种方式。在接下来的章节中,我们将看到以下内容:
-
在 Vue CLI 3 中观察我们的单元测试
-
为我们的任务设置参数
-
使用 Cypress 编写端到端测试
让我们从使用 --watch 标志开始。
在 Vue CLI 3 中观察我们的测试
test:unit 命令带有 --watch 标志。要看到它的效果,我们只需要回到 Vue UI 中的项目任务页面,并在选择 test:unit 任务后,点击参数按钮,如下所示:
图 4.15:test:unit 任务中的参数按钮
点击参数按钮将触发一个对话框,其中包含以下两个选项:
-
监视文件以进行更改并重新运行与更改文件相关的测试
-
在此测试运行期间重新记录每个失败的快照
点击第一个选项以打开观察模式。接下来的选项将立即出现在其下方:
- 在每次运行后显示通知
这个选项只有在观察模式启用时才可用。让我们也启用每次运行后显示通知选项,并点击保存。
你可以在package.json的 scripts 中设置这些选项。第一个选项是--watch,显示通知选项是--notify标志。
要做到这一点,只需更新项目的package.json中的 scripts 键到以下代码:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit",
"test:unit-watch": "vue-cli-service test:unit --watch --notify"
},
你会看到你的任务列表现在已经扩展到包括另一个测试任务:test:unit-watch。
然而,即使你可以,最好不要这样做。这不是最佳实践,而且这样做有点违背了使用 Vue UI 的初衷。不过,了解到这样可以做的话,我们对 Vue CLI UI 底层发生了更好的理解。
现在,让我们通过向AnotherComponent.spec.js文件添加更改来查看观察模式是否正常工作。只需在某个地方添加一个空格并保存更新即可。
使用 Cypress 编写端到端测试
端到端测试是一种测试实践,我们在其中从头到尾测试应用程序的流程。通过端到端测试,我们模拟用户从某种入口点流经我们的应用程序到达某种结果的场景。
例如,Web 应用程序的端到端测试可能包括以下流程:
-
用户在浏览器中打开 Web 应用程序的 URL
-
用户点击登录链接并登录
-
用户在 Web 应用程序中检查通知
-
用户登出
介绍 Cypress
在本节中,我们将使用 Cypress 进行端到端测试。Cypress 在 Chrome 浏览器中运行良好。或者,如果你想使用基于 Selenium 的工具,你可以在这个网站上查看 Nightwatch.js:nightwatchjs.org/.
要了解更多关于 Cypress 的信息,请访问以下网址的官方网站:www.cypress.io/。
如果你访问 Cypress 网站,你会看到它被描述为:
快速、简单、可靠的测试任何在浏览器中运行的东西。
让我们马上开始吧。
向我们的项目添加 Cypress 插件
现在让我们在运行 Vue CLI UI 的情况下向我们的项目添加一个 Cypress 插件:
- 在浏览器中打开以下地址:
http://localhost:8000/plugins/add
-
接下来,在搜索框中输入
cypress,并找到@vue/cli-plugin-e2e-cypress插件。 -
按照我们之前使用 Jest 插件的方式,按照插件安装步骤进行操作。
-
一旦我们添加了 Cypress 插件,我们需要提交更改。与 Jest 一样,我们可以只用一个简单的消息提交,比如“添加 Cypress 插件”。
注意,安装 Cypress 会在我们的tests文件夹中添加一个名为e2e的新文件夹。在e2e文件夹中,我们可以找到以下子文件夹:plugins,specs和support。
让我们接着检查package.json文件的内容。
验证 Cypress 插件安装后对 package.json 的更新
让我们在 VS Code 中检查我们项目的package.json。我们会注意到scripts选项中有一个新的条目,如下所示:
"test:e2e": "vue-cli-service test:e2e",
此外,我们的devDependencies已经通过 Cypress 插件进行了更新,我们还可以通过访问 Vue UI 仪表板并检查已安装的插件来看到这一点。
最后,如果我们点击任务图标,我们会看到test:e2e任务已添加到我们项目的任务列表底部,与我们在package.json文件中看到的完全相同。
如果我们点击test:e2e任务,右侧窗格将相应更新,运行任务按钮已准备好点击。点击运行任务按钮将产生以下输出:
$ vue-cli-service test:e2e --mode development
INFO Starting e2e tests...
INFO Starting development server...
DONE Compiled successfully in 1691ms18:06:27
App running at:
- Local: http://localhost:8082/
- Network: http://192.168.1.70:8082/
Note that the development build is not optimized.
To create a production build, run npm run build.
It looks like this is your first time using Cypress: 3.2.0
[18:06:28] Verifying Cypress can run C:\Users\W\AppData\Local\Cypress\Cache\3.2.0\Cypress [started]
[18:06:30] Verified Cypress! C:\Users\W\AppData\Local\Cypress\Cache\3.2.0\Cypress [title changed]
[18:06:30] Verified Cypress! C:\Users\WAppData\Local\Cypress\Cache\3.2.0\Cypress [completed]
Opening Cypress...
一个新的由 Electron 驱动的窗口将在我们的计算机上打开。使用 Cypress 很容易。正如“帮助您入门……”窗口所告诉我们的那样,您可以在examples文件夹中运行测试,或者将您自己的测试文件添加到cypress/integration中。
如果您看一下 Cypress 窗口右上角,您会看到“运行所有规范”按钮。默认情况下,它将在 Chrome 中运行(指定版本号)。如果您点击下拉菜单,选择 Chrome,您可以切换到 Electron。无论您选择哪个选项,您的测试都将在一个新窗口中运行,无论是一个新的 Chrome 窗口还是一个新的 Electron 窗口。
此时,我们的端到端 Cypress 测试将失败,因为 Cypress 试图在默认的 Vue 脚手架项目上运行测试,如下所示:
图 4.16:Cypress 中的一个失败测试
如果您希望从一开始就看到这些测试通过,您需要创建一个全新的项目,并在配置中设置 Cypresse2e测试。我们将在本书的后面看看这些不同的选项。现在,让我们更新我们的测试,使它们通过。
更新我们 Vue 应用中的 Cypress 测试
回到 VS Code,在./tests/e2e/specs/文件夹中打开test.js文件。您会看到一个带有两个参数的describe函数。
要更好地理解 Cypress 术语,请参考以下网址:
docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Support-file,以及
docs.cypress.io/guides/references/bundled-tools.html#Mocha.
在 Jest 中,我们看到test这个词作为单元测试函数的名称,而在 Cypress 中,我们看到使用it这个词。cy对象是实际的 Cypress 测试运行器。让我们看一下test.js文件的以下更新代码,并解释它是如何以及为什么工作的:
describe('My First Test', () => {
it('Visits the app root url', () => {
cy.visit('/')
cy.contains('article', 'Our own custom article component!')
})
})
这次我们的测试通过了。请注意,我们只需要在 VS Code 中保存更新后的测试文件,测试就会自动运行。您可以转到http://localhost:8000/tasks/,并单击test:e2e任务以获取有关正在运行的任务的更多信息,如下所示:
图 4.17:我们 Vue 应用程序主页的 Cypress 测试通过的屏幕截图
另外请注意,如果你在测试结果上悬停在“CONTAINS”部分,那么服务的 Vue 应用程序的适当部分将被突出显示,这是一个关于我们正在测试的 Vue 应用程序的确切部分的美妙视觉提示。
Summary
在本章中,我们通过 Vue CLI 3 的视角讨论了许多与测试相关的主题。TDD 是一种很棒的技术,应该被团队和个人广泛利用,而 Vue CLI 3、它的 UI 和 Jest 是优化这一过程的绝佳选择。与 Vue CLI 3 的其他方面一样,很多的管道工作都对我们隐藏起来,这使得编码体验非常棒。
在下一章中,我们将研究 Vue CLI 3 中的路由。