在这篇文章中,我们将首先看看Snowpack--特别是Snowpack 3,在写这篇文章的时候它刚刚发布。Snowpack是一个前端构建工具,由于提供了与webpack等工具不同的方法,在社区中得到了很多关注,而我也一直很想看看它。让我们深入了解一下!
构建工具的历史
在我们研究Snowpack之前,我们需要花点时间来了解像webpack这样的捆绑工具是如何产生的以及为什么产生的。在ES2015的模块之前,JavaScript缺乏模块系统,这意味着在浏览器中,我们能得到的最接近模块的方法是将我们的代码分割成文件,将代码放入全局范围,因为这就是我们在文件之间共享代码的方式。这样的代码很常见。
window.APP = {}
window.APP.Authentication = {...}
window.APP.ApiLoader = {...}
当Node.js到来并获得普及时,它有一个CommonJS形式的模块系统。
const Authentication = require('./Authentication.js')
const APILoader = require('./APILoader.js')
一旦它作为Node的一部分变得流行,人们就希望能够在浏览器中使用它。这时,开始出现了这样的工具;他们可以将使用CommonJS模块的应用程序,捆绑成一个大的JavaScript文件,并删除所有的require,可以在浏览器中执行。Browserify是我记得的第一个这样做的工具,说实话,它感觉就像魔术一样。那是在webpack出现的时候,其他工具也支持使用CommonJS。
当ES模块第一次被引入时(见 "理解ES6模块"以获得复习),人们热衷于使用它们,但有两个问题。
- 虽然规范已经完成,但浏览器并不支持ES模块。
- 即使浏览器支持ES模块,你可能仍然希望在生产中进行捆绑,因为如果模块被定义为单独的文件,加载所有的模块需要时间。
Webpack(和其他)更新了对ES模块的支持,但他们总是将你的代码捆绑成一个文件,无论是用于开发还是用于生产。这意味着,一个典型的工作流程是。
- 在你的应用程序中编辑一个文件。
- Webpack会查看哪个文件发生了变化,然后重新捆绑你的应用程序。
- 你可以刷新浏览器,看到你的变化。通常,这是由一个webpack插件为你完成的,如热模块重载。
这里的问题在于第二步,因为你的应用程序的规模在不断扩大。webpack发现一个文件的变化,然后找出你的应用程序的哪些部分要重新捆绑到主捆绑中的工作可能需要时间,在大型应用程序中,这可能会导致严重的速度下降。这就是Snowpack的作用......
Snowpack的方法
对我来说,Snowpack的关键卖点是他们文档中的这句话。
在开发过程中,Snowpack为你的应用程序提供非捆绑式的服务。每个文件只需要被构建一次,然后被永远地缓存起来。当一个文件发生变化时,Snowpack会重新构建该文件。
Snowpack充分利用了所有主要浏览器都支持ES模块的优势,在开发过程中不捆绑你的应用程序,而是将每个模块作为一个单独的文件提供,让浏览器通过ES模块导入你的应用程序。参见 "今天在浏览器中使用ES模块",以了解更多关于浏览器及其对非捆绑ES模块的支持的细节。
在这一点上需要注意的是,你必须使用ES模块来使用Snowpack。你不能在你的应用程序中使用CommonJS。
然而,这提出了一个问题:如果你从npm安装了一个确实使用CommonJS的依赖项,怎么办?虽然我希望有一天大多数npm包都能以ES模块的形式发货,但我们离这个目标还有一段距离,而且现实是即使你完全用ES模块构建一个应用程序,在某些时候你也很可能需要一个用CommonJS编写的依赖。
幸运的是,Snowpack也能处理这个问题。当它在你的node_modules 文件夹中看到一个依赖项(比方说React)时,它可以将该依赖项捆绑到自己的迷你捆绑包中,然后可以使用ES Modules导入。
希望你能明白为什么Snowpack引起了我的注意。让我们启动并运行它,看看它在一个应用程序中的使用感觉如何。
开始使用
首先,我创建了一个新的空项目文件夹,并运行npm init -y ,让我开始运行。这将创建一个基本的package.json ,如果我想的话,我可以在以后进行编辑。你也可以运行npm init ,但不包括-y ,这将使npm提示你回答问题,以便在你的package.json 中填写细节。我喜欢用-y 来快速启动和运行;我可以稍后编辑package.json 。
然后我把Snowpack作为一个开发者依赖项来安装。
npm install --save-dev snowpack
现在我在我的package.json 中加入两个脚本。
"scripts": {
"start": "snowpack dev",
"build": "snowpack build"
},
这为我们设置了两个npm run 命令。
npm run start将在开发模式下运行Snowpack。npm run build将运行Snowpack的生产构建,我们稍后会更多地讨论这个问题。
当我们运行我们的应用程序时,Snowpack启动了一个小的开发服务器,它将在本地运行我们的应用程序。它将寻找一个index.html 文件,所以让我们创建一个这样的文件,同时创建app.js ,现在它只是将hello world 记录到控制台。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snowpack testing</title>
</head>
<body>
<script src="./app.js"></script>
</body>
</html>
console.log('hello world')
现在我们可以运行npm run start (或简称为npm start -start 是npm生命周期方法之一,你不需要在它前面加上run)。
你应该看到你的终端输出看起来像这样。
snowpack
http://localhost:8080 • http://172.18.33.234:8080
Server started in 80ms.
▼ Console
[snowpack] Hint: run "snowpack init" to create a project config file. Using defaults...
[snowpack] Nothing to install.
输出的第一部分告诉我们,Snowpack正在运行在localhost:8080 。下一行提示我们创建一个Snowpack配置文件,我们很快就会做,但我想强调的是最后一行。
[snowpack] Nothing to install.
这是Snowpack告诉我们,它已经检查了任何需要处理的npm模块,但没有发现任何模块。稍后,我们将添加一个npm包,看看Snowpack如何处理它。
生成一个配置文件
你可以运行npx snowpack init ,按照命令行输出的建议生成配置文件。在我们为生产进行捆绑之前,我们不需要改变Snowpack的行为,但是如果你需要,你可以创建这个文件,并配置一系列的选项,让Snowpack按照你想要的方式运行。
在ES模块中写作
让我们再创建一个JavaScript文件,看看Snowpack如何处理多个文件。我创建了api.js ,它导出了一个函数,接收一个用户名并从GitHub上获取他们的一些公共仓库。
export function fetchRepositories(user) {
return fetch(`https://api.github.com/users/${user}/repos`)
.then(response=> response.json());
}
然后,在app.js ,我们可以导入并使用这个函数。请随意将我的GitHub用户名替换成你自己的用户名。
import {fetchRepositories} from './api.js';
fetchRepositories('jackfranklin').then(data => console.log(data));
保存这个文件,如果你之前没有让Snowpack运行,可以再次运行它。在浏览器的控制台,你会看到一个错误。
Uncaught SyntaxError: Cannot use import statement outside a module
这是因为我们的HTML文件中的<script> 标签。
<script src="./app.js"></script>
因为ES模块的行为与不使用ES模块的代码略有不同,所以浏览器不可能在所有脚本中都开始支持ES模块。这样做几乎肯定会破坏一些现有的网站,而JavaScript的主要目标之一是任何新功能都是向后兼容的。否则,每一个新的JS功能都可能破坏成千上万的现有网站
type 为了使用ES模块,我们需要做的就是通过给script 标签一个module ,告诉浏览器。
<script type="module" src="./app.js"></script>
当你保存时,你的浏览器应该自动刷新(Snowpack开箱即用的另一个好东西),你会看到GitHub仓库的列表被记录到控制台。