【原创翻译】给前端小白的现代JavaScript工作流程详解 | Modern JavaScript Explained For Dinosaurs

【原创翻译】给前端小白的现代JavaScript工作流程详解 | Modern JavaScript Explained For Dinosaurs

 

如果你从一开始就没有接触过JavaScript,那么学习现代JavaScript是很困难的。生态系统的增长和变化如此之快,以至于很难理解不同工具试图解决的问题。我1998年开始编程,但直到2014年才开始认真学习JavaScript。我记得当我看到Browserify时,我盯着它的标语:

 

“Browserify lets you require(‘modules’) in the browser by bundling up all of your dependencies.”

“Browserify 可以让你在浏览器中使用require(‘modules’) 语句来打包处理所有的依赖”

我几乎不理解这句话中的任何一个词,我努力理解这对作为开发人员的我有什么帮助。

本文的目标是提供一个JavaScript工具链是如何发展到2017年(这篇文章写于2017年)的历史背景。我们将从头开始,建立一个像上图中恐龙做的样例网站-不适用任何工具,只是简单的HTML和JavaScript。然后,我们将逐步介绍不同的工具,以了解它们一次解决一个问题。有了这样的历史背景,您将能够更好地学习和适应不断变化的JavaScript前景。让我们开始吧!

更新:我做了一个这篇文章视频教程版本,它一步一步地讲解每个部分,更清晰,请点击这里查看:

 https://firstclass.actualize.co/p/modern-javascript-explained-for-dinosaurs

以“老派”的方式使用JavaScript

 让我们从一个使用HTML和JavaScript的“老派”网站的写法开始,这种方式需要到手动下载和链接文件。下面是一个简单的index.html文件,它链接到一个JavaScript文件:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

这行<script src="index.js"></script>指向的是同一个目录下名为index.js的单独的一个JavaScript文件:

 

// index.js
console.log("Hello from JavaScript!");

用这种方式,你已经做出了一个网站!但是现在,我们假设您想要添加一个别人编写的库,比如moment.js(一个可以帮助以人类可读的友好方式格式化日期的库)。例如,你可以像下面这样在JavaScript中使用moment函数:

moment().startOf('day').fromNow();        // 20 hours ago

但使用这种方式,你首先必须要在你的网页里加载上moments .js! 在moment.js这个库的主页上,你可以看到以下说明:

【原创翻译】给前端小白的现代JavaScript工作流程详解 | Modern JavaScript Explained For Dinosaurs

 

 

 

 呃,在右边的安装教程部分提供了很多种方式。但现在我们先忽略它(因为我们要用古老的前端开发方式)——我们可以下载moment.min.js文件并将其放在index.html文件中,从而将moment.js添加到我们的网站中。

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example</title>
  <link rel="stylesheet" href="index.css">
  <script src="moment.min.js"></script>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

注意,moment.min.js需要在index.js之前加载,然后你可以像下面这样在index.js中使用moment函数:

// index.js
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

这就是我们以前用JavaScript的库制作网站的方法!好的地方是它很容易理解。不好的地方是,每次库有更新时都我们要找到并下载新版本的库,替换旧文件,这很烦人。

使用JavaScript的包管理器npm

大约从2010年开始,出现了几个相互竞争的JavaScript包管理器,它们可以帮我们从*存储库自动下载和升级JS库。Bower可以说是2013年最受欢迎的包管理器,但逐渐在2015年左右被npm超越。(值得注意的是,从2016年年底开始,yarn作为npm接口的替代品获得了很多关注,但它在内部仍然使用npm包。)

注意,npm最初是专门为node.js设计的包管理器,node.js是一个JavaScript运行时环境,运行在服务器,而不是前端。因此,很多人会困惑,为什么要用npm来管理运行在浏览器中的前端JS库。

注意:使用包管理器通常涉及到使用命令行,这在过去的前端开发中是不需要的。如果你从未使用过命令行,你可以阅读这个教程来获得一个大概的认识。无论是好是坏,知道如何使用命令行是现代JavaScript开发的重要组成部分(它也为其他开发领域打开了大门)。

让我们看看如何使用npm来自动安装moment.js包,而不是手动下载它。如果你已经安装了node.js,那么你已经安装了npm,这意味着你可以在命令行中切换到index.html文件所在的文件夹,然后输入:

$ npm init

然后它会给你一些提示问题(默认设置就行,你可以为每个问题直接按下“Enter”),并生成一个名为package.json的新文件。这是npm用来保存所有项目信息的配置文件。默认情况下,包的内容package.json的内容应该是类似这样的:

{
  "name": "your-project-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

为了安装moment.js这个JavaScript包,我们现在可以按照它的主页上的npm的指令,在命令行中输入以下命令:

$ npm install moment --save

这条命令做两件事——首先,它将the moment.js包中的所有代码下载到一个名为node_modules的文件夹中。其次,它会自动修改package.json文件以跟踪moment.js作为项目依赖项。

{
  "name": "modern-javascript-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.22.2"
  }
}

这在以后与他人共享项目时很有用——你不需要共享node_modules文件夹(这个文件夹可能会变得非常大),您只需要共享package.son文件,其他开发人员可以使用npm install命令自动安装所需的包。

所以现在我们不再需要手动从网站下载moments .js,我们可以使用npm自动下载和更新它。在node_modules文件夹中,我们可以看到node_modules/moment/min目录中的moment.min.js文件。这意味着我们可以在index.html文件中链接到npm下载的moment.min.js,如下所示:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="node_modules/moment/min/moment.min.js"></script>
  <script src="index.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

所以这样做的好处是,现在我们可以使用npm通过命令行下载和更新我们的包。不好的一点是,我们现在正在进入node_modules文件夹,找到每个包的位置,并手动将其包含在HTML中。这是非常不方便的,所以接下来我们将看看如何自动化这一过程。

【原创翻译】给前端小白的现代JavaScript工作流程详解 | Modern JavaScript Explained For Dinosaurs

 

 

 

使用JavaScript模块绑定器 (webpack)

大多数编程语言都提供了一种将代码从一个文件导入到另一个文件的方法。JavaScript最初并没有设计这个特性,因为JavaScript被设计为只能在浏览器中运行,不能访问客户端计算机的文件系统(出于安全原因)。因此,在很长一段时间里,将JavaScript代码组织到多个文件中需要使用全局共享的变量来加载每个文件。

这实际上是我们在上面的moment.js例子中所做的——整个moment.min.js文件被加载在HTML中,它定义了一个全局变量moment,然后它对在moment.min.js之后加载的任何文件都可用(不管这些文件是否需要访问它)。

2009年,一个名为CommonJS的项目开始了,其目标是为浏览器之外的JavaScript指定一个生态系统。CommonJS的很大一部分是它的模块规范,这使得JavaScript能像大多数编程语言一样跨文件导入和导出代码,而不需要求助于全局变量。node.js就是CommonJS模块规范最广为人知的实现。

【原创翻译】给前端小白的现代JavaScript工作流程详解 | Modern JavaScript Explained For Dinosaurs

 

 

 如前所述,node.js是一个用于在服务器端运行的JavaScript运行时环境。前面那个网页的示例如果用node.js模块语句来写就是如下所示的代码。不用使用HTML脚本标签来加载moment.min.js,你可以直接在JavaScript文件中加载它,如下所示:

// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

这就是node.js中模块加载的方法,因为node.js是一种服务器端语言,它可以访问计算机的文件系统。Node.js还知道每个npm模块的路径,所以不必写require('./node_modules/moment/min/moment.min.js),你可以简单地写require('moment')——非常好用。

这种机制对于node.js来说很好,但如果你试图在浏览器中使用上面的代码,你会得到一个错误,说require没有定义。浏览器不能访问文件系统,这意味着以这种方式加载模块是非常难的——加载文件必须动态完成,要么同步加载(会减慢执行速度),要么异步加载(会有时间问题)。

这就是模块绑定器的作用所在。JavaScript模块绑定器是一种工具,它通过build(构建)步骤(这一步运行在本地,因此可以访问文件系统)来解决问题,以创建与浏览器兼容的最终输出的打包好的JS文件(不需要访问文件系统)。在这种情况下,我们需要一个模块绑定器来查找所有require语句(require语法在浏览器中不支持),并将它们替换为每个所需文件的实际内容。最终的结果是一个捆绑的JavaScript文件(没有require语句)!

最流行的模块绑定器是Browserify,它于2011年发布,最早在前端使用node.js风格的require语句(这本质上使npm成为首选的前端包管理器)。大约在2015年,webpack逐渐成为了更广泛使用的模块绑定器(得益于React前端框架的流行,它充分利用了webpack的各种特性)。

让我们来看看如何使用webpack来让上面的require('moment')示例在能够浏览器中工作。首先,我们需要将webpack安装到项目中。Webpack本身是一个npm包,所以我们可以从命令行安装它:

$ npm install webpack webpack-cli --save-dev

注意,我们正在安装两个包——webpack和webpack-cli(它允许你从命令行使用webpack)。还要注意--save-dev参数——它将它保存为开发依赖项,这意味着它是开发环境中需要的包,而不是生产服务器上需要的包(生产环境上只需打包好的JS文件即可,不需要打包工具)。你可以在package.json文件看到,它已经自动自动更新了:

{
  "name": "modern-javascript-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "moment": "^2.19.1"
  },
  "devDependencies": {
    "webpack": "^4.17.1",
    "webpack-cli": "^3.1.0"
  }
}

现在我们已经在node_modules文件夹中以包的形式安装了webpack和webpack-cli。你可以在命令行中使用webpack-cli,如下所示:

$ ./node_modules/.bin/webpack index.js --mode=development

该命令将运行安装在node_modules文件夹中的webpack工具,从index.js文件开始,找到任何require语句,并用适当的代码替换它们,创建出单个输出文件(默认为dist/main.js)。--mode=development参数是为了保持JavaScript对开发人员的可读性,而--mode=production则是打包成利于服务器部署环境的最小化输出。

现在我们有了webpack生成的dist/main.js输出,我们将在浏览器中使用这个文件而不是index.js,因为index.js包含无效的require语句。相应的index.html应该修改如下:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JavaScript Example</title>
  <script src="dist/main.js"></script>
</head>
<body>
  <h1>Hello from HTML!</h1>
</body>
</html>

如果您刷新一下浏览器,您应该看到一切都像以前一样正确运行!

注意,每次修改index.js时,我们都需要运行webpack命令。这很麻烦 ,而且当我们使用webpack的更高级特性时(比如生成源代码映射以帮助从编译的代码调试原始代码),会变得更加乏味。Webpack可以从名为Webpack .config.js的项目根目录下的配置文件中读取选项,在我们的例子中是这样的:

 

上一篇:Modern Fluent UI controls in Power Apps


下一篇:人工智能:一种现代方法(Artificial Intelligence : A modern approach )第四版【2020年】第一章:导论