跳到主内容

如何在浏览器运行JS原生模块?

· 4分钟阅读

现代的浏览器原生支持运行模块化的功能,不需要通过 Webpack,ESbuild 等工具构建打包。例如现在的 Vite 就是利用了浏览器支持原生模块的特性,才能够在启动的时候不需要提前编译。本文将介绍下如果在浏览器运行原生模块

在 Vite 官网的 NPM 依赖解析和预构建 描述到

  1. 预构建 它们可以提高页面加载速度,并将 CommonJS / UMD 转换为 ESM 格式。预构建这一步由 esbuild 执行,这使得 Vite 的冷启动时间比任何基于 Javascript 的打包器都要快得多。

那么为什么要做预构建呢?主要是因为浏览器的原生模块运行不支持 import { add } from './math'; 这种写法,需要先转为 import { add } from './math.js'; 才能支持,下面通过实例来验证一下。

项目创建

根据下面的目录结构新创建文件

|- math.js
|- main.js
|- index.html

math.js 是用工具类,main.js 是项目的入口文件,index.html 是项目的首页。对应的代码如下:

math.js文件

export const add = (a, b) => a + b;

main.js文件

import { add } from "./math";

document.getElementById("btn").onclick = () => {
alert(add(3, 4));
};

index.html文件

<!DOCTYPE html>
<html lang="en">
<body>
<button id="btn">click</button>
<script src="./main.js"></script>
</body>
</html>

代码很简单,一个页面有一个按钮,引入 main.js 文件,main.js 调用了 math.js 模块的 add 方法,我们直接浏览器本地打开,发现报错了

Access to script at 'file:///xxx/xxx/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

意思是本地加载 script 脚本有跨域限制,必须要通过 http,data,chrome,chrome-extension,chrome-untrusted,https 协议才能加载。

http 服务

我们启动一个静态资源服务,通过 http 来访问,通过 http://localhost:8080/ 来访问,打开控制台,发现又报错了

main.js:1 Uncaught SyntaxError: Cannot use import statement outside a module

意思是不能在没有声明 module 的情况下使用 import 语句,我们来给 script 标签新增一个 type="module" 属性

<!DOCTYPE html>
<html lang="en">
<body>
<button id="btn">click</button>
<script type="module" src="./main.js"></script>
</body>
</html>

重新访问,发现又报错了,

main.js:1 GET http://localhost:8080/math net::ERR_ABORTED 404 (Not Found)

原因是,浏览器原生模块不支持不带文件名后缀,毕竟是通过 http 请求,所以我们需要把 math 文件名改为 math.js,修改 main.js 文件,这也是为什么 Vite 需要通过 esbuild 预构建处理。

import { add } from "./math.js";

document.getElementById("btn").onclick = () => {
alert(add(3, 4));
};

这时候运行没报错,点击 click 按钮,发现弹出了 7。完美运行原生模块成功!!!

image.png