• es6 模块化


    ES6 In Depth是一系列关于ECMAScript标准第6版(简称ES6)中JavaScript编程语言新增功能的文章。

    不久之前,JavaScript的主要用途是表单验证,可以肯定的是,您的平均<input-onchange=>处理程序将是……一行代码。

    事情发生了变化。JavaScript项目已经发展到令人瞠目结舌的规模,社区已经开发了大规模工作的工具。您需要的最基本的东西之一是模块系统,这是一种将您的工作分散到多个文件和目录中的方法,但仍要确保您的所有代码位可以根据需要相互访问,并且能够有效地加载所有代码。因此,JavaScript自然有一个模块系统。实际上有好几个。还有几个包管理器,用于安装所有软件和处理高级依赖项的工具。您可能会认为ES6使用了新的模块语法,已经有点晚了。

    好吧,今天我们将看看ES6是否为这些现有系统添加了什么,以及未来的标准和工具是否能够在此基础上进行构建。但首先,让我们深入了解一下ES6模块是什么样子的。

    模块基础知识

    ES6模块是一个包含JS代码的文件。没有特殊的模块关键字;一个模块读起来就像一个脚本。有两个不同之处。

    • ES6模块是自动严格模式代码,即使你不写use strict “使用严格”;在他们身上。
    • 您可以在模块中使用导入import和导出export。

    让我们先谈谈export。默认情况下,模块内声明的所有内容都是模块的本地内容。如果希望模块中声明的内容是公共的,以便其他模块可以使用它,则必须导出该功能。有几种方法可以做到这一点。最简单的方法是添加export关键字。

    您可以导出任何顶级 function, class, var, let, 或 const

    这就是编写模块所需了解的全部内容!你不必把所有东西都放在IIFE或回调中。只要继续,申报你需要的一切。因为代码是一个模块,而不是脚本,所以所有声明的作用域都是该模块,而非在所有脚本和模块中全局可见。导出构成模块公共API的声明,就完成了。

    除了导出之外,模块中的代码几乎只是普通代码。它可以使用诸如Object和Array之类的全局变量。如果模块在web浏览器中运行,它可以使用document和XMLHttpRequest。

    在单独的文件中,我们可以导入并使用detectCats()函数:

    运行包含导入声明的模块时,首先加载它导入的模块,然后在依赖关系图的深度优先遍历中执行每个模块体,通过跳过已执行的任何内容来避免循环。
    这些是模块的基础。这真的很简单。;-)

    导出列表

    不必标记每个导出的要素,您可以写出一个包含要导出的所有名称的列表,并用大括号括起来:

    export {detectCats, Kittydar};
    
    // no `export` keyword required here
    function detectCats(canvas, options) { ... }
    class Kittydar { ... }
    

    导出列表不必是文件中的第一个内容;它可以出现在模块文件顶级范围的任何位置。您可以有多个导出列表,或者将导出列表与其他导出声明混合,只要没有名称被多次导出。

    重命名导入和导出
    偶尔,导入的名称会与您还需要使用的其他名称发生冲突。因此,ES6允许您在导入时重命名内容:

    // suburbia.js
    
    // Both these modules export something named `flip`.
    // To import them both, we must rename at least one.
    import {flip as flipOmelet} from "eggs.js";
    import {flip as flipHouse} from "real-estate.js";
    ...
    

    同样,您可以在导出对象时重命名它们。如果要以两个不同的名称导出相同的值,这是很方便的,这种情况偶尔会发生:

    // unlicensed_nuclear_accelerator.js - media streaming without drm
    // (not a real library, but maybe it should be)
    
    function v1() { ... }
    function v2() { ... }
    
    export {
      v1 as streamV1,
      v2 as streamV2,
      v2 as streamLatestVersion
    };
    

    默认导出

    新标准旨在与现有的CommonJS和AMD模块进行互操作。假设您有一个Node项目,并且已经完成了npm安装lodash。您的ES6代码可以从Lodash导入单个函数:

    import {each, map} from "lodash";
    
    each([3, 2, 1], x => console.log(x));
    
    
    

    但也许你已经习惯了看_.each,而不是each,你仍然想这样写。或者您可能想将_用作函数,因为这在Lodash中很有用。

    为此,您可以使用稍微不同的语法:导入不带花括号的模块。

    import _ from "lodash";
    
    
    

    这个简写相当于import {default as _} from "lodash";。所有CommonJS和AMD模块都以default导出的形式呈现给ES6,这与您为该模块(即导出对象)请求require()时得到的结果相同。

    ES6模块设计为允许您导出多个内容,但对于现有的CommonJS模块,您只能使用默认导出。例如,在撰写本文时,据我所知,著名的颜色软件包https://github.com/Marak/colors.js 没有任何特殊的ES6支持。它是CommonJS模块的集合,就像npm上的大多数包一样。但您可以将其直接导入ES6代码。

    // ES6 equivalent of `var colors = require("colors/safe");`
    import colors from "colors/safe";
    

    如果您希望自己的ES6模块具有默认导出,这很容易做到。默认导出没有什么神奇之处;它与任何其他导出一样,只是名称为“default”。您可以使用我们已经讨论过的重命名语法:

    let myObject = {
      field1: value1,
      field2: value2
    };
    export {myObject as default};
    

    或者更好的做法是,使用以下速记:

    export default {
      field1: value1,
      field2: value2
    };
    

    关键字export-default后面可以跟任何值:函数、类、对象文本,可以自己命名。

    模块对象

    导入*时,导入的是模块名称空间对象。其属性是模块的导出。因此,如果“cows”模块导出一个名为moo()的函数,那么在以这种方式导入“ cows”之后,可以编写:cows.moo()。

    聚合模块

    有时,包的主模块只不过是导入包的所有其他模块并以统一的方式导出它们。为了简化这类代码,有一个一体化的导入和导出速记:

    // world-foods.js - good stuff from all over
    
    // import "sri-lanka" and re-export some of its exports
    export {Tea, Cinnamon} from "sri-lanka";
    
    // import "equatorial-guinea" and re-export some of its exports
    export {Coffee, Cocoa} from "equatorial-guinea";
    
    // import "singapore" and export ALL of its exports
    export * from "singapore";
    

    这些导出自语句中的每一个都类似于导入自语句,然后是导出。与实际导入不同,这不会将重新导出的绑定添加到范围中。所以,如果你打算在world-foods.js 中编写一些代码,就不要使用这种速记法。使用Tea。你会发现它不在那里。

    如果“ singapore”出口的任何名称与其他出口产品发生冲突,这将是一个错误,因此请谨慎使用export*。

    呼!语法已经完成了!转到有趣的部分。

    导入实际上做了什么?

    你会相信…什么都不相信吗?

    哦,你没有那么容易上当受骗。那么,你会相信标准基本上没有说什么是import吗?这是件好事吗?

    ES6让模块加载的细节完全取决于实现。https://262.ecma-international.org/6.0/#sec-hostresolveimportedmodule 。模块执行的其余部分将详细指定。 https://262.ecma-international.org/6.0/#sec-toplevelmoduleevaluationjob

    粗略地说,当你告诉JS引擎运行一个模块时,它必须表现得像是在执行以下四个步骤:

    • 解析:实现读取模块的源代码并检查语法错误。
    • 加载:实现加载所有导入的模块(递归)。这是尚未标准化的部分。
    • 链接:对于每个新加载的模块,实现将创建一个模块范围,并用该模块中声明的所有绑定(包括从其他模块导入的内容)填充该范围。
      • 在这一部分,如果您尝试import {cake} from "paleo",但“ paleo”模块实际上没有导出任何名为cake的内容,您将得到一个错误。这太糟糕了,因为您离实际运行一些JS代码太近了。
    • 运行时:最后,实现运行每个新加载模块主体中的语句。此时,导入处理已经完成,所以当执行到有import声明的代码行时……什么也没有发生!
      看见我告诉过你答案是“没什么”。我不会在编程语言方面撒谎。

    但现在我们来看看这个系统中有趣的部分。有一个很酷的技巧。因为系统没有指定加载是如何工作的,而且您可以通过查看源代码中的导入声明来提前确定所有依赖项,所以ES6的实现可以在编译时自由完成所有工作,并将所有模块捆绑到一个文件中,以便通过网络将其发送!像webpack这样的工具实际上可以做到这一点。

    这是一件大事,因为通过网络加载脚本需要时间,而且每次获取一个脚本时,您可能会发现它包含需要加载更多脚本的导入声明。一个简单的加载程序需要很多网络往返。但有了webpack,现在不仅可以将ES6与模块一起使用,还可以获得所有软件工程方面的好处,而不会影响运行时性能。

    最初计划并构建了ES6中模块加载的详细规范。它不在最终标准中的一个原因是,在如何实现这个捆绑功能方面没有达成共识。我希望有人能理解,因为正如我们将看到的,模块加载确实应该标准化。捆绑销售太好了,不能放弃。

    静态与动态,或:规则以及如何打破它们

    对于动态语言来说,JavaScript拥有一个令人惊讶的静态模块系统。

    • 所有风格的导入和导出都只允许在模块的顶层进行。没有条件导入或导出,您不能在函数范围内使用导入。
    • 所有导出的标识符必须在源代码中按名称显式导出。不能通过编程方式循环数组并以数据驱动的方式导出一系列名称。
    • 模块对象被冻结。没有办法将新特性嵌入模块对象中,即polyfill样式。
    • 在运行任何模块代码之前,必须立即加载loaded、解析parsed和链接linked模块的所有依赖项。导入没有语法可以根据需要延迟加载。
    • 导入错误没有错误恢复。一个应用程序可能包含数百个模块,如果有任何模块加载或链接失败,则不会运行任何模块。不能在try/catch块中导入。(好处是,由于系统是静态的,webpack可以在编译时为您检测这些错误。)
    • 没有钩子允许模块在加载依赖项之前运行一些代码。这意味着模块无法控制其依赖项的加载方式。

    只要你的需求是静态的,这个系统就相当不错。但你可以想象有时需要一点破解,对吗?

    这就是为什么您使用的任何模块加载系统都会有一个编程API来配合ES6的静态导入/导出语法。例如,webpack包含一个API,您可以使用它进行“代码拆分code splitting”,根据需要延迟加载一些模块包。相同的API可以帮助您打破上面列出的大多数其他规则。

    ES6模块语法非常静态,这很好,它以强大的编译时工具的形式获得了回报。但是,静态语法被设计为与丰富的动态编程加载器API一起工作。

    何时可以使用ES6模块?
    现在要使用模块,您需要一个编译器,如Traceur或Babel。在本系列的早期,Gastón I.Silva展示了如何使用Babel和Broccoli为web编译ES6代码;https://hacks.mozilla.org/2015/06/es6-in-depth-babel-and-broccoli/在这篇文章的基础上,Gastón有一个支持ES6模块的工作示例。Axel Rauschmayer的这篇文章包含了一个使用Babel和webpack的示例。

    ES6模块系统主要由Dave Herman和Sam Tobin Hochstadt设计,他们通过多年的争议为系统的静态部分向所有来者(包括我)进行了辩护。Jon Coppeard正在Firefox中实现模块。关于JavaScript Loader Standard的其他工作正在进行中。https://github.com/whatwg/loader。接下来需要将类似<script-type=module>的内容添加到HTML中。

    这就是ES6。

    https://harthur.github.io/kittydar/

    https://hacks.mozilla.org/2015/08/es6-in-depth-modules/

  • 相关阅读:
    团队项目第二阶段冲刺站立会议07
    Alpha阶段项目总结
    团队项目第二阶段冲刺站立会议06
    团队项目第二阶段冲刺站立会议05
    团队项目第二阶段冲刺站立会议04
    团队项目第二阶段冲刺站立会议03
    团队项目第二阶段冲刺站立会议02
    团队项目第二阶段冲刺站立会议01
    软件测试
    针对各小组提出的意见作答
  • 原文地址:https://www.cnblogs.com/Running00/p/16768823.html
Copyright © 2020-2023  润新知