<script>直接引入模块化的js文件踩坑记录

场景描述

需求的起因很简单,想写一个es6模块化验证demo,在index.html中通过<script>标签引入main.js,在main.js中,通过import的方式引入写好的func1模块,并请求,拿到返回值后显示在页面上。

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>引入js</title>
</head>
<body>
    <script src="./main.js" type="text/javascript"></script>
</body>
</html>

// main.js

import {func1} from "./func1";

window.onload = function() {
    func1();
}
// func1.js

export function func1() {
    document.write("hello world !");
}

看起来没啥问题,执行起来浏览器会报错“Uncaught SyntaxError: Cannot use import statement outside a module”。
翻译过来就是,无法在模块外使用import关键字。
那使用script标签的新属性,type="module",直接在引入的时候把main.js声明成一个module就可以?

// index.html

<body>
    <script src="./main.js" type="module"></script>
</body>

结果报错跨域问题,如下:

"Access to script at 'file:///F:/xxxx/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标签引入来自不同域的代码块,这也是jsonp跨域的原理,那么为什么type="module"时,就不支持跨域了呢?

简单来说,就是当给script标签设置属性type="module"时,会使用同源策略(请求头中可以找到这个配置:Sec-Fetch-Mode: cors),但是同源策略一般针对http/https协议,加载本地文件走的是file协议打开本地文件,Origin是null,因此报错。

知识点

为什么无法在模块外使用import关键字?

module模块是相对于script脚本存在的。脚本只有全局作用域,而模块有自己的独立作用域;脚本从上到下顺序执行,模块需要预加载才能确定执行顺序。因此,当浏览器以默认脚本方式加载了模块时,会报语法错误,本质上是因为浏览器对脚本和模块的支持方式不同,没法在支持脚本的模式里面识别import关键字。
存在的另一种情况是nodejs环境,nodejs环境,在es6的module标准化出来之前,是遵从CommonJS定义的,自然没办法解析import语句。但是现在新版本的Node环境已经逐步放开了对 ES Module的支持。

解决方案

方案一 将js模块打包成单一脚本文件引入

打包工具很多,以webpack举例。npm init进行webpack环境的初始化,项目中有了package.json,可以写个wepack.config.js配置一个项目出入口和打包后的目录,最后把打包好的js文件在index.html中以默认方式引入。

方案二 创建本地文件服务器

这个方法仍然以模块方式引入js,但是将file://xxx通过文件服务器映射成http://localhost:8080/xxx,然后就可以正常引入啦。
具体操作有很多种方式,比如在vscode中装一个liveserver插件,开启一个小的本地文件服务;比如安装gulp等等。

posted @ 2022-07-22 15:49  有洸  阅读(6193)  评论(0)    收藏  举报