模块加载

模块加载

1.web浏览器中使用模块

es6之前(脚本加载):

1.在script元素中通过src属性指定一个加载代码的地址来加载js文件
2.将js代码内嵌到没有src属性的script元素中
3.通过web worker 或者Service Worker方法加载执行js文件

<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
// module code
</script>


<!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>

 

在script中,type='module'时,支持加载模块;将type设置为module可以让浏览器将所有内联代码或包含在src指定的文件中的代码按照模块而非脚本的方式加载。

如demo
<!--加载一个js模块文件-->
<script type='module' src='module.js'></script>

<!--内联引入一个模块,嵌入在网页中的模块-->
<script type='module'>

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

let result = sum(1,2)

</script>

// 变量result没有暴露到全局作用域,只是存在于模块中
// 浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性


什么是defer属性?defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行
延伸: 什么是async属性? async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。

defer:渲染完再执行 (如果有多个defer脚本,会按照它们在页面出现的顺序加载)
async:下载完就执行 (而多个async脚本是不能保证加载顺序的)

 


2. web浏览器中模块加载顺序
<!--先执行这个标签-->
<script type='module' src='module1.js'></script>

<!--再执行这个标签-->
<script type='module'>
import { sum } from '../example.js'

let result = sum(1,2)
</script>

<!--最后执行这个标签-->
<script type='module' src='module2.js'></script>

注意:如果网页有多个<script type="module">,它们会按照在页面出现的顺序依次执行

**加载脚本文件时,defer是可选属性;
**加载模块时,defer是必须属性

总之,每个模块都可以从一个或多个其他的模块导入,虽然会使得问题复杂化,首先解析模块以识别所有导入的语句;然后每个导入语句都会触发一次获取过程(网络或缓存),
并且在所有导入资源都被加载和执行后才会执行当前模块。

我们再来看,用script type='module' 显示引入和用import隐式导入所有模块都是按需加载并执行的。分析上述加载顺序看看

1. 下载并解析module1.js
2. 递归下载并解析module1.js中导入的资源
3. 解析内联模块
4. 递归下载并解析内联模块中导入的资源
5. 下载并解析module2.js
6. 递归下载并解析module2.js中导入的资源

加载完成后,只有当文档完全被解析(dom渲染完)之后才会执行其他操作。而当dom渲染完之后,发生的动作:

1.递归执行module1.js中导入的资源
2.执行module1.js
3.递归执行内联模块中导入的资源
4.执行内联模块
5.递归执行module2.js中导入的资源
6.执行module2.js

3. web浏览器异步模块加载

<script>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染

<script type="module" src="./foo1.js" async></script>
<script type="module" src="./foo2.js" async></script>

注意:无法保证这两个哪个先执行
一旦使用了async属性,<script type="module">就不会按照在页面出现的顺序执行,而是只要该模块加载完成(包含所有导入的资源),就执行该模块

ecmacript 6中:http://es6.ruanyifeng.com/#docs/module-loader

4.将模块作为worker加载

web worker
Service worker

可以在网页上下文之外执行js代码。创建一个worker实例

// 按照脚本的方式加载script.js
// @params: 传入js文件的地址
// 默认的加载机制是按照脚本的方式加载文件
let worker = new Worker('script.js')


// 按照模块的方式加载module.js
let worker = new Worker('module.js', {type: 'module'})

给第二个参数传入一个对象,type属性为 module

使用worker要注意:

1.worker脚本只能从与引用的网页相同的源加载,worker模块不会完全受限
2.worker模块具有相同的默认限制,但是他们还是可以加载并访问具有适当的跨域资源共享cors头的文件
3.worker脚本可以使用self.importScripts()方法将其他脚本加载到worker中;而worker模块使用的是import进行导入

5. 浏览器模块说明符解析

模块说明符 module specifier 使用的都是相对路径 ('./index.js')

浏览器要求模块说明符具有的几种格式:

1. 以 / 开头的解析为根目录开始
2. 以./开头的解析为从当前目录开始
3. 以../开头的解析为从父目录开始
4. URL格式

 

posted @ 2018-08-05 13:30  加勒比大橙子  阅读(166)  评论(0编辑  收藏  举报