了解本地 JavaScript 模块

更新日期: 2024-02-27阅读: 436标签: 模块

自从 JavaScript 项目开始变得越来越复杂以来,已经有很长一段时间了。因此,将代码分割成易于管理的片段变得至关重要。在 JavaScript 开发的历史上,我们经历了许多不同的将代码拆分成模块的方法。NodeJS 中使用的 CommonJS 就是很好的例子。同样,RequireJS 在过去也被用于最早的 angular框架中。

JavaScript 语言在 2015 年进行了一次重大更新,称为 ES6 或 ECMAScript 2015。除其他功能外,它还引入了处理模块的官方语法。这种方法允许我们使用导出关键字来暴露模块中的各种值。然后,我们可以使用 import 语句在其他模块中访问它们。

遗憾的是,在 JavaScript 语言中添加一项功能后,浏览器并不会自动实现它。因此,我们使用 webpackbabel工具将我们编写的遵循最新标准的代码转换为所有浏览器都能理解的代码。不过,浏览器早在一段时间前就跟上了步伐,推出了对 ES6 模块的本地支持。在本文中,我们将解释它们,以及使用它们与依赖 Webpack 等工具有何不同。


加载 JavaScript 模块

让我们先创建一个简单的 JavaScript 文件。

index.js

const rootElement = document.querySelector('#root');

if (rootElement) {
rootElement.innerhtml = 'Hello world!';
}

它的工作就是找到一个具有根 id 的元素,并在其中渲染一些文本。现在,我们可以将其包含在 HTML 中。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Native modules</title>
<script src="index.js" type="module"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

由于使用了 type="module",我们的 JavaScript 文件被作为模块加载。这会影响浏览器的处理方式。


延迟执行

浏览器从上到下读取加载的 HTML 文档,并在 <body> 部分之前解析 <head> 部分。如果我们以常规方式将 JavaScript 文件放入 <head> 部分,它将在浏览器解析 <body> 部分之前被解析和执行。这意味着我们的 JavaScript 代码无法访问 id 为 root 的元素,因为它还不存在。

然而,JavaScript 模块的执行是延迟的。这意味着浏览器会在解析整个文档时执行它们。这意味着,即使我们把它们放在 <head> 部分,它们也会在 <div id="root"> 元素可用时执行。


应用 CORS 策略

对于不带 type="module" 的常规 <script> 标记,即使脚本来自不同的源,浏览器也会获取并执行。但是,当我们加载模块时,浏览器会应用同源策略。它阻止我们的网站访问来自其他来源的模块。我们可以通过 CORS 调整这种行为。跨源资源共享(CORS)是一种禁止或允许从其他源请求资源的机制。

如果您想了解什么是起源以及如何在 Node.js 应用程序中设置 CORS,请查看 API with NestJS #117。CORS - 跨起源资源共享

这也意味着,如果我们直接从文件系统打开 index.html 文件,会出现以下错误:

CORS 策略已阻止从起源 'null' 访问 'file:///home/marcin/Documents/Projects/native-modules/index.js' 处的脚本:仅支持以下协议方案的跨源请求:http、data、isolated-app、chrome-extension、chrome、https、chrome-untrusted。

在本地制作网站时,最直接的处理方法就是使用类似 serve 或 http-server 这样的库,让我们的页面在 localhost 上可用。


导出和导入

让我们创建一个简单的函数,并将其放在一个单独的文件中。

sum.js

export function sum(firstNumber, secondNumber) {
return firstNumber + secondNumber;
}

使用 export 关键字,我们可以将上述文件导入 index.js。

index.js

import { sum } from './sum.js';

const rootElement = document.querySelector('#root');

if (rootElement) {
rootElement.innerHTML = sum(1, 2);
}

重要的是,我们不必在 index.html 中包含 sum.js 文件。因为我们将 index.js 标记为模块,所以它可以导入其他模块。


导入Maps

导入时提供的模块路径可以是相对路径,也可以是绝对路径。如果我们正确配置了跨源资源共享,它也可以是不同源的 URL。

Webpack 等工具可以自动解析文件扩展名。但是,在使用本地 JavaScript 模块时,我们需要提供模块的完整路径。这就是我们在导入时包含文件扩展名的原因。

我们可以添加导入映射,指定要导入的模块的 URL。我们还可以为它们命名。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Native modules</title>
<script type="importmap">
{
"imports": {
"sum-utilities": "./sum.js"
}
}
</script>
<script src="index.js" type="module"></script>
</头>
<body>
<div id="root"></div>
</body>
</html>

上面,我们在导入映射中提供了 sum.js 的路径,并将 id 称为 sum-utilities。有了它,我们在导入该模块时就不必再提供文件的确切路径了。

index.js

import { sum } from 'sum-utilities';

const rootElement = document.querySelector('#root');

if (rootElement) {
rootElement.innerHTML = sum(1, 2);
}


动态导入

我们可以在需要时才导入特定模块。例如,通过不预先加载大型模块,我们可以帮助用户节省移动带宽。

为此,我们可以使用返回 promise 的导入函数。

index.js

const rootElement = document.querySelector('#root');
const renderButton = document.querySelector('#render-button');

renderButton?.addEventListener('click', async () => {
const { sum } = await import('sum-utilities');
if (rootElement) {
rootElement.innerHTML = sum(1, 2);
}
})


预加载模块

在我们当前的设置中,浏览器不会自动开始加载 sum.js 文件。而是在执行 main.js 文件时遇到 import 语句时才开始加载。

我们可以通过添加带有 rel="modulepreload" 的 <link /> 元素,提前预加载 sum.js 文件,从而提高性能。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Native modules</title>
<script type="importmap">
{
"imports": {
"sum-utilities": "./sum.js"
}
}
</script>
<link rel="modulepreload" href="sum.js" />
<script src="index.js" type="module"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

因此,sum.js 和 main.js 文件可以并行下载。


处理旧版浏览器兼容

遗憾的是,有些旧版浏览器不支持本地 JavaScript 模块。如果我们的网站需要在旧版浏览器(如 Internet Explorer)上运行,我们可以使用 nomodule 属性提供一个单独的 JavaScript 文件。

<script src="forOlderBrowsers.js" nomodule></script>

通过添加 nomodule,我们表明该脚本不应在支持本地 JavaScript 模块的浏览器中执行。


原生 JavaScript 模块与 Webpack 和类似工具的比较

使用原生 JavaScript 模块的优势之一是,它们可以直接在所有现代浏览器中运行。这意味着我们不需要任何工具来构建项目。不过,在浏览器原生支持导入和导出功能之前,我们可以使用 Webpack 等工具。它的作用类似,但工作方式不同。

Webpack 是一个捆绑工具。它将我们所有的 JavaScript 文件及其依赖关系整合到一个文件中。在此过程中,Webpack 可以使用各种加载器和插件来转换我们的代码,例如,加入浏览器尚未支持的其他功能。

捆绑包可以分成多个文件,以提高性能。由于 react 或 Redux 等依赖关系不会经常变化,我们可以将它们捆绑到一个单独的文件中,然后由浏览器缓存。

Webpack 最重要的优势在于它提供了许多优化应用程序的方法。它可以在不改变代码功能的情况下,通过删除代码中不必要的代码字符来精简代码,从而减小文件大小。它可以通过移除捆绑包中不需要提供给浏览器的未使用代码来实现摇树。它可以压缩图片等资产,以减少应用程序的加载时间。


总结

浏览器遵循 JavaScript 标准并原生实现模块等功能是件好事。虽然它们肯定有自己的用武之地,但并不会让 Webpack 等工具过时。在捆绑代码的同时,Webpack 有机会以各种方式优化我们的应用程序。有趣的是,Vite 等工具使用的是原生 JavaScript 模块。这样可以在开发过程中快速、高效地加载模块。了解 JavaScript 模块的工作原理很有必要,因为它们可能是前端开发的未来。掌握 Webpack 等传统工具和使用原生模块的方法,可以提高我们的代码效率。

链接: https://fly63.com/article/detial/12660

ES6模块功能:export和import的加载方式

ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范。commonjs主要应用于服务器,实现同步加载,如nodejs。AMD规范应用于浏览器,如requirejs,为异步加载。

Node的https模块_创建HTTPS服务器

Node的https模块:HTTPS服务器使用HTTPS协议,需要证书授权,SSL安全加密后传输,使用443端口

如何让 node 运行 es6 模块文件,及其原理

最新版的 node 支持最新版 ECMAScript 几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从 ES2015 开始定义的模块化机制。而现在我们很多项目都是用 es6 的模块化规范来写代码的,包括 node 项目

module、export、require、import的使用

module每个文件就是一个模块。文件内定义的变量、函数等等都是在自己的作用域内,都是自身所私有的,对其它文件不可见。在module中有一个属性exports,即:module.exports。它是该模块对外的输出值,是一个对象。

Node.js - 模块系统

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。Node.js 提供了 exports 和 require 两个对象

ES模块基础用法及常见使用问题

ES6中引入了模块(Modules)的概念,相信大家都已经挺熟悉的了,在日常的工作中应该也都有使用。本文会简单介绍一下ES模块的优点、基本用法以及常见问题。

ES6 export 和 export default的区别

ES6中 export 和 export default 与 import使用的区别,使用 react native 代码详解,现在流行的前端框架,angular+ 主要使用 export 导出模块,react native 中使用 export default 导出模块,如今编辑器非常强大,安装插件会自动弹出模块名称,知道其导出怎么使用就可以了

export和export default的区别

export与export default均可用于导出常量、函数、文件、模块;你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用;

关于export和export default你不知道的事

网上有很多关于export和export default的文章,他们大部门都是只讲了用法,但是没有提到性能,打包等关键的东西。大家应该应该能理解import * from xxx会把文件中export default的内容都打包到文件中,而import {func} from xxx只会把文件中的func导入

最全的前端模块化方案

模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。将一个复杂的系统分解为多个模块以方便编码。会讲述以下内容:CommonJS、AMD 及 核心原理实现、CMD 及 核心原理实现

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!