当前位置:主页 > 资料 >

万岁,浏览器原生支持ES6 export和import模块啦!
栏目分类:资料   发布日期:2018-08-02   浏览次数:

导读:本文为去找网小编(www.7zhao.net)为您推荐的万岁,浏览器原生支持ES6 export和import模块啦!,希望对您有所帮助,谢谢! 这篇文章发布于 2018年08月2日,星期四,01:09,归类于js API。 阅读

本文为去找网小编(www.7zhao.net)为您推荐的万岁,浏览器原生支持ES6 export和import模块啦!,希望对您有所帮助,谢谢!

本文来自去找www.7zhao.net



这篇文章发布于 2018年08月2日,星期四,01:09,归类于js API。 阅读 38 次, 今日 32 次 www.7zhao.net

byzhangxinxu from

欢迎访问www.7zhao.net

本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。 去找(www.7zhao.net欢迎您

一、前言

欢迎访问www.7zhao.net

JS中直接 import 其他模块是个很棒的能力,ES6规范中就提供了这样的特性。然后,长久以来,都只有在Node.js中才能无阻使用,浏览器都没有原生支持。 www.7zhao.net

Node.js对于我而言,就像是个在另外一个城市结交的好朋友,简单了解,能和睦相处即可,因此,Node.js支持 import 功能,就好像朋友升职赚了大钱一样,替他开心,不过也就只是替他开心,自己其实还是淡然的。但是,web浏览器就不一样了,这个可是我打算厮守一生的伴侣,因此,web浏览器原生支持 import 功能,那就好像自己的老婆升职赚了大钱一样,那比自己赚了大钱还开心,心中一百个“万岁”。 copyright www.7zhao.net

ES6在浏览器中的import功能分为 静态import动态import

去找(www.7zhao.net欢迎您

其中静态import出现更早, 更好,支持浏览器包括:Safari 10.1+,Chrome 61+,Firefox 60+,Edge 16+。 www.7zhao.net

www.7zhao.net

动态import支持晚一些, 要差一些,目前Chrome浏览器和Safari浏览器支持,不过相信很快其他浏览器也会跟进。 本文来自去找www.7zhao.net

去找(www.7zhao.net欢迎您

本文会对这两种模块导入都做介绍,因此,本文内容篇幅较长,且有一定深度,需要预留较多时间阅读。

www.7zhao.net

二、静态import

我们先从最简单的案例说起,例如,我想想,demo比较方便演示的效果,啊,那就实现改变 <p> 元素的文字颜色。 copyright www.7zhao.net

主页面相关script代码如下: www.7zhao.net

<script type="module">
  // 导入firstBlood模块
  import { pColor } from './firstBlood.mjs';
  // 设置颜色为红色
  pColor('red');
</script> 
copyright www.7zhao.net

然后firstBlood.mjs文件中代码为: copyright www.7zhao.net

// export一个改变<p>元素颜色的方法
export function pColor (color) {
  const p = document.querySelector('p');
  p.style.color = color;
} 本文来自去找www.7zhao.net 

您可以狠狠地点击这里:

欢迎访问www.7zhao.net

可以看到 <p> 文字变红了:

去找(www.7zhao.net欢迎您

去找(www.7zhao.net欢迎您

有了案例,下面基础知识就更好消化与理解了。 欢迎访问www.7zhao.net

  • 对于需要引入模块的 <script> 元素,我们需要添加 type="module" ,这个时候,浏览器会把这段内联script或者外链script认为是ECMAScript模块。
  • 模块JS文件,业界或者官方约定俗成命名为 .mjs 文件格式,一来可以和普通JavaScript文件( .js 后缀)进行区分,一看就知道是模块文件;二来Node.js中ES6的模块化特性只支持 .mjs 后缀的脚本,可以和Node.js。当然,我们直接使用 .js 作为模块JS文件的后缀也是可以的。

    在浏览器侧进行import模块引入,其对模块JS文件的mime type要求非常严格,务必和JS文件一致。这就导致,如果我们使用 .mjs 文件格式,则需要在服务器配置mime type类型,否则会报错:

    内容来自www.7zhao.net

    Failed to load module script: The server responded with a non-JavaScript MIME type of “”. Strict MIME type checking is enforced for module scripts per HTML spec.
     
    欢迎访问www.7zhao.net

    www.7zhao.net

    Nginx对于不识别后缀默认会给一个 application/octet-stream 的MIME type,方便下载等处理,但是,不好意思,在模块化引入这里,这个MIME type无效,需要足够精准才行,为 application/javascript ,然后根据自己测试,IIS服务器中 application/x-javascript 也是可以的。

    copyright www.7zhao.net

    无论是Apache服务器还是Nginx,都可以修改mime.types文件使 .mjs 的MIME type和 .js 文件一样。

    去找(www.7zhao.net欢迎您

除了 export 普通的 function ,我们还可以 export const 或者其他任何变量或者声明。也支持 default 命令。再看下面一个例子, <p> 文字变红,以及垂直翻转,演示 constdefault 使用。 本文来自去找www.7zhao.net

假设模块脚本文件名是doubleKill.mjs,其代码如下: www.7zhao.net

// doubleKill.mjs
// const 和 default功能演示
export default () => {
  const p = document.querySelector('p');
  p.style.transform = 'scaleY(-1)';
};
export const pColor = (color) => {
  const p = document.querySelector('p');
  p.style.color = color;
} 
去找(www.7zhao.net欢迎您

import部分逻辑代码为: 去找(www.7zhao.net欢迎您

<script type="module">
  // 导入doubleKill模块
  import * as module from './doubleKill.mjs';
  // 执行默认方法
  module.default();
  // 设置颜色为红色
  module.pColor('red');
</script> 

www.7zhao.net

就可以实现 <p> 元素文字变红同时垂直翻转的效果,如下截图:

本文来自去找www.7zhao.net

copyright www.7zhao.net

您可以狠狠地点击这里: 欢迎访问www.7zhao.net

三、nomodule与向下兼容

模块脚本我们可以使用 type="module" 进行设定,对于并不支持 exportimport 的浏览器,我们可以使用nomodule进行向下兼容。 copyright www.7zhao.net

<script type="module" src="module.mjs"></script>
<script nomodule src="fallback.js"></script> 欢迎访问www.7zhao.net 

对于支持ES6模块导入的浏览器,自然也支持原生的 nomodule 属性,此时 fallback.js 是忽略的;但是,对于不支持的老浏览器,无视 nomodule ,此时 fallback.js 就会执行,于是浏览器全兼顾。 www.7zhao.net

理论就如上面分析得这么完美,然后实际上,还是存在问题的。

www.7zhao.net

主要问题在低端浏览器 .mjs 资源会冗余加载,例如这个测试demo在IE11下的网络请求:

copyright www.7zhao.net

www.7zhao.net

不过这并不是什么大问题,多一点请求和流量,功能这块可以不影响的。

欢迎访问www.7zhao.net

四、静态import更多细节

1. 目前import不支持裸露的说明符

目前import不支持裸露的说明符,用白话讲就是import的地址前面不能是光秃秃的。例如下面这些就不支持:

内容来自www.7zhao.net

// 目前不支持,以后可能支持
import {foo} from 'bar.mjs';
import {foo} from 'utils/bar.mjs'; 
去找(www.7zhao.net欢迎您

下面这些则支持,可以是根路径的 / ,同级路径 ./ 亦或者是父级 ../ ,甚至完整的非相对地址也是可以的。

欢迎访问www.7zhao.net

// 支持
import {foo} from 'https://www.zhangxinxu.com/utils/bar.mjs';
import {foo} from '/utils/bar.mjs';
import {foo} from './bar.mjs';
import {foo} from '../bar.mjs'; 去找(www.7zhao.net欢迎您 

2. 默认Defer行为

传统 <script> 属性支持一个名为 defer 的属性值,可以让JS资源异步加载,同时保持顺序。例如: 本文来自去找www.7zhao.net

<!-- 同步 -->
<script src="1.js"></script>

<!-- 异步但顺序保证 -->
<script defer src="2.js"></script>
<script defer src="3.js"></script> 
欢迎访问www.7zhao.net

加载顺序一定是 1.js , 2.js , 3.js 。我们只要看 2.js3.js ,由于设置了 defer ,这两个JS异步加载,因此,就算 1.js 放在最下面,也多半 1.js 先加载完。而多个 <script> 同时设置 defer 会从前往后依次加载执行。因此,一定是先加载完 2.js 然后是 3.js

本文来自去找www.7zhao.net

回到本文的ES6 module导入,对于 type="module"<script> 元素,天然外挂 defer 特性,也就是天然异步,所有module脚本按顺序,因此,下面这段脚本执行顺序就好理解了:

copyright www.7zhao.net

<!-- 此script稍后执行 -->
<script type="module" src="1.mjs"></script>

<!-- 硬加载嘛 -->
<script src="2.js"></script>

<!-- 比第一个要晚一点 -->
<script defer src="3.js"></script> 
copyright www.7zhao.net

最终的加载执行顺序是: 2.js , 1.mjs , 3.js2.js 同步,解析这里就加载。 1.mjs 虽然没有设置 defer ,但默认 defer ,因此和 3.js 其实是一样的,都是异步 defer 加载。由于 1.mjs 对于的 <script>3.js 前面,因此,先 1.mjs3.js

www.7zhao.net

相信不难理解。 copyright www.7zhao.net

3. 内联script同样defer特性

如下代码: copyright www.7zhao.net

<script type="module">
  console.log("Inline module执行");
</script>

<script src="1.js"></script>

<script defer>
  console.log("Inline script执行");
</script>

<script defer src="2.js"></script> 
www.7zhao.net

最后的执行顺序是: 1.jsInline scriptInline module2.js

www.7zhao.net

从在线demo控制台输出可以证明上面的结论。

去找(www.7zhao.net欢迎您

www.7zhao.net

原因在于,传统的内联 <script> 是没有 defer 这种概念的,从不异步,大家可以直接忽略,认为什么也没设置即可;而 type="module"<script> 天然 defer 。因此,先 1.jsInline script ;然后按照 defer 规则,从前往后依次是 Inline module2.jswww.7zhao.net

4. 支持async

无论是内联的module <script> 还是外链的 <script> ,都支持 async 这个异步标识属性。这个有别于传统的 <script> ,也就是传统 <script> 仅外链JS才支持 async ,内联JS直接忽略 async

www.7zhao.net

asyncdefer 都可以让JavaScript异步加载,区别在于 defer 保证执行顺序,而 async 谁先加载好谁先执行。这个特性表现在 type="module"<script> 元素这里同样适用。

欢迎访问www.7zhao.net

例如下面例子: 去找(www.7zhao.net欢迎您

<!-- firstBlood模块一加载完就会执行 -->
<script async type="module">
  import { pColor } from './firstBlood.mjs';
  pColor('red');
</script>

<!-- doubleKill模块一加载完就会执行 -->
<script async type="module" src="./doubleKill.mjs"></script> www.7zhao.net 

无论是 firstBlood.mjs 还是 doubleKill.mjs 都是异步加载,然后执行顺序不固定,有可能先code>firstBlood.mjs,也有可能先 doubleKill.mjs ,这样看哪个模块脚本先加载完毕。 本文来自去找www.7zhao.net

5. 模块只会执行一次

传统的 <script> 如果引入的JS文件地址是一样的,则JS会执行多次。但是,对于 type="module"<script> 元素,即使模块地址一模一样,也只会执行一次。例如:

copyright www.7zhao.net

<!-- 1.mjs只会执行一次 -->
<script type="module" src="1.mjs"></script>
<script type="module" src="1.mjs"></script>
<script type="module">
  import "./1.mjs";
</script>

<!-- 下面传统JS引入会执行2次 -->
<script src="2.js"></script>
<script src="2.js"></script> 本文来自去找www.7zhao.net 

我们看下在线demo控制台输出的结果, 2.js 执行了2次,而 1.mjs 模块虽然3次引入,但只执行了一次。截图如下:

本文来自去找www.7zhao.net

内容来自www.7zhao.net

6. 总是CORS跨域

传统JS文件的加载,我们直接跨域也可以解析,例如,我们会使用一些大网站的CDN服务,例如,加载个百度提供的jQuery地址:

www.7zhao.net

<script src="//apps.bdimg.com/libs/jquery/1.9.0/jquery.min.js"></script> copyright www.7zhao.net 

可以正常解析。但是,如果是module模式下 import 脚本资源,则不会执行,例如:

本文来自去找www.7zhao.net

<script type="module" src="//apps.bdimg.com/.../jquery.min.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
    console.log(window.$);
});
</script> 去找(www.7zhao.net欢迎您 

我们使用Chrome浏览器跑一下在线demo,结果浏览器报CORS policy跨域相关错误,自然 window.$undefined

copyright www.7zhao.net

去找(www.7zhao.net欢迎您

如何使支持跨域呢? 内容来自www.7zhao.net

需要模块资源服务端配置 Access-Control-Allow-Origin ,可以指定具体域名,或者直接使用 * 通配符, Access-Control-Allow-Origin:*本文来自去找www.7zhao.net

本站cdn.zhangxinxu.com域名有配置 Access-Control-Allow-Origin ,所以,下面代码打印出来的值就不是 undefined

本文来自去找www.7zhao.net

<script type="module" src="//cdn.zhangxinxu.com/study/js/jquery-1.4.2.min.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
    console.log(window.$);
});
</script> 

www.7zhao.net

访问在线demo,打开控制台,可以看到输出如下内容:

本文来自去找www.7zhao.net

www.7zhao.net

7. 无凭证

如果请求来自同一个源(域名一样),大多数基于CORS的API将发送凭证(如cookie等),但 fetch() 和模块脚本是例外 – 除非您要求,否则它们不会发送凭证。

www.7zhao.net

我们通过下面例子理解上面这句话的含义:

内容来自www.7zhao.net

<!-- ① 获取资源会带上凭证(如cookie等)-->
<script src="1.js"></script>

<!-- ② 获取资源不带凭证 -->
<script type="module" src="1.mjs"></script>

<!-- ③ 获取资源带凭证 -->
<script type="module" crossorigin src="1.mjs?"></script>

<!-- ④ 获取资源不带凭证 -->
<script type="module" crossorigin src="//cdn.zhangxinxu.com/.../1.mjs"></script>

<!-- ⑤ 获取资源带凭证 -->
<script type="module" crossorigin="use-credentials" src="//cdn.zhangxinxu.com/.../1.mjs?"></script> 内容来自www.7zhao.net 

这里出现了一个HTML属性 crossorigin ,该属性在“ ”这篇文章有介绍,可以明确 <script> 以及 <img> 等可外链元素在获取资源时候,是否带上凭证。

www.7zhao.net

crossOrigin 可以有下面两个值: 去找(www.7zhao.net欢迎您

关键字 释义
anonymous 元素的跨域资源请求不需要凭证标志设置。
use-credentials 元素的跨域资源请求需要凭证标志设置,意味着该请求需要提供凭证。

其中,只要 crossOrigin 的属性值不是 use-credentials ,全部都会解析为 anonymous本文来自去找www.7zhao.net

回到本节案例。

欢迎访问www.7zhao.net

  1. 传统JS加载,都是默认带凭证的(对应注释①)。
  2. module模块加载默认不带凭证(注释②)。
  3. 如果我们设置 crossOrigin 为匿名 anonymous ,又会带凭证(注释③)。
  4. 如果import模块跨域,则设置 crossOriginanonymous 不带凭证(注释④)。
  5. 如果import模块跨域,且明确设置 crossOrigin 为使用凭证 use-credentials ,则带凭证(注释⑤)。

注意,如果跨域,需要同时服务器侧返回 Access-Control-Allow-Credentials:true 头信息。

本文来自去找www.7zhao.net

然后,上面的凭证规则以后有可能会调整,欢迎大家及时反馈。

内容来自www.7zhao.net

8. 天然严格模式

import的JS模块代码天然严格模式,如果里面有不太友好的代码会报错,例如:

www.7zhao.net

www.7zhao.net

四、动态import

静态import在首次加载时候会把全部模块资源都下载下来,但是,我们实际开发时候,有时候需要动态import(dynamic import),例如点击某个选项卡,才去加载某些新的模块,这个动态import特性浏览器也是支持的。 去找(www.7zhao.net欢迎您

具体是使用一个长得像函数的 import() ,注意,只是长得像函数, import() 实际上就是个单纯的语法,类似于 super() 。这就意味着 import() 不会从 Function.prototype 获得继承,因此您无法 callapply 它,并且 const importAlias = import 之类的东西不起作用,甚至 import() 都不是对象!

www.7zhao.net

语法为:

copyright www.7zhao.net

import(moduleSpecifier); 

copyright www.7zhao.net

moduleSpecifier 为模块说明符,其实就是模块地址,规则和静态 import 一样,不能是裸露的地址。 www.7zhao.net

案例

静态 import() 那个红色翻转案例我们改造成动态 import ,也就是把 import xxxx from 'xxxx' 改成 import('xxxx') ,代码如下:

欢迎访问www.7zhao.net

<script type="module">
  // 导入doubleKill模块
  import('./doubleKill.mjs').then((module) => {
    // 执行默认方法
    module.default();
    // 设置颜色为红色
    module.pColor('red');
  });
</script> 
www.7zhao.net

最后效果和静态import一样:

copyright www.7zhao.net

内容来自www.7zhao.net

您可以狠狠地点击这里:

内容来自www.7zhao.net

由于 import() 返回一个promise,所以,我们可以使用async/await来代替 then 这种回调形式。 去找(www.7zhao.net欢迎您

<script type="module">
(async () => {
  // 导入doubleKill模块
  const module = await import('./doubleKill.mjs');
  // 执行默认方法
  module.default();
  // 设置颜色为红色
  module.pColor('red');
})();
</script> 本文来自去找www.7zhao.net 

您可以狠狠地点击这里:

欢迎访问www.7zhao.net

五、交互中的动态import

不像静态 import 只能用在 <script type="module>" 一样,动态 import() 也可以用在普通的script,我们来看一个更接近真实开发的案例——选项卡内容动态加载。

www.7zhao.net

首先,页面HTML代码如下:

www.7zhao.net

<nav>
    <a href="javascript:" class="active" data-module="mm1">美女1</a>
    <a href="javascript:" data-module="mm2">美女2</a>
    <a href="javascript:" data-module="mm3">美女3</a>
</nav>
<main><img src="mm1.jpg"></main> 内容来自www.7zhao.net 

需求如下,点击不同的美女选项卡的时候,去加载对应的模块,模块有个方法可以改变 <main> 元素内容。

内容来自www.7zhao.net

则,我们的的交互JS和动态 import() JS如下: www.7zhao.net

<script>
  const main = document.querySelector('main');
  const links = document.querySelectorAll('nav > a');
  for (const link of links) {
    link.addEventListener('click', async (event) => {
      const module = await import(`./${link.dataset.module}.mjs`);
      // 模块暴露名为`loadPageInto`的方法,内容是写入一段HTML
      module.loadPageInto(main);
    });
  }
</script> 

copyright www.7zhao.net

结果,当我们点击其他选项卡的时候, <main> 元素中的美女图片就会发生变化,例如默认是这个:

去找(www.7zhao.net欢迎您

www.7zhao.net

点击“美女2”选项卡按钮,此时浏览器会动态加载 mm2.mjs 这个模块,然后执行这个模块中暴露的 loadPageInfo 方法,从而改变呈现内容。

copyright www.7zhao.net

copyright www.7zhao.net

您可以狠狠地点击这里: 本文来自去找www.7zhao.net

六、结语

这篇文章写了一个月,从7月30号写到8月2号,是不是跨了一个月? copyright www.7zhao.net

最近看自己很多年前写的技术文章,不太正经,插科打诨的东西比较多,甚至有时候会花一般篇幅讲一个不知所云的故事。后来,有人说啰嗦,于是自己文风尝试简洁,持续了差不多2年,最近发现这样不行,完全就成了干巴巴的技术科普,很无聊,很没劲,没有辨识度,缺少有趣的灵魂,时间流逝,很容易湮没在茫茫多的技术洪流中,所以呢,决定,还是回到过去,本站就是个个人网站,所谓个人网站,不就是用来展示自己的特质的嘛,精神无限自由的自留地,不必因为某些言论而局促自己。 www.7zhao.net

哎呀呀呀,这世上很多事情都是这样,实践了一圈下来,发现,还是最初的决策是最准确的。 去找(www.7zhao.net欢迎您

参考文章

感谢阅读,行为仓促,如果文中有表述不准的地方,欢迎指正。

copyright www.7zhao.net

欢迎访问www.7zhao.net

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。

欢迎访问www.7zhao.net

本文地址: 本文来自去找www.7zhao.net

(本篇完)

www.7zhao.net

copyright www.7zhao.net


本文原文地址:https://www.zhangxinxu.com/wordpress/2018/08/browser-native-es6-export-import-module/

以上为万岁,浏览器原生支持ES6 export和import模块啦!文章的全部内容,若您也有好的文章,欢迎与我们分享!

内容来自www.7zhao.net

Copyright ©2008-2017去找网版权所有   皖ICP备12002049号-2 皖公网安备 34088102000435号   关于我们|联系我们| 免责声明|友情链接|网站地图|手机版