最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • ES6的学习笔记(十五)Module的加载实现。。。

    正文概述 掘金(Get-sunshine)   2020-12-31   497

    Module的加载实现

    1.浏览器加载

    传统方法

    在HTML网页中,浏览器通过<script>标签加载JavaScript脚本。

    <script type="application/javascript">
      // 这个是页面内部的脚步
    </script>
    
    <script type="application/javascript" src="path/to/myModule.js">
    	// 这个是页面外部的脚本
    </script>
    
    // 浏览器默认的脚本语言是JavaScript,所以 type="application/javascript"可以省略。
    // 默认,浏览器同步加载JS脚本,所以引擎遇到<script>标签,就停下来,等到脚本执行完毕后,再向下渲染。如果是外部脚本,必须加入脚本下载的时间。
    

    若脚本体积很大,下载和执行的时间会很长,造成浏览器堵塞,用户体验不好,因此,脚本可以异步加载。

    <script src="path/to/myModule.js" defer></script>
    <script src="path/to/myModule.js" async></script>
    // 打开defer或者async属性,脚本就会异步加载。
    // defer:等到整个页面在内存中正常渲染结束(DOM结构完全生成,以及其他脚本执行完成),才会执行;
    // async:一旦下载完成,渲染引擎就会中断渲染,执行这个脚本后,再继续渲染。
    // defer渲染完再执行,async下载完就执行。有多个defer脚本时,会按照在页面中出现的顺序加载,多个async脚本不能保证加载顺序。
    

    加载规则

    浏览器加载ES6模块时要加入type="module"属性。

    <script type="module" src="./foo.js"> </script>
    // 对于带有type="module"的脚本,浏览器都是异步加载,即等到整个页面渲染完成后,再执行模块脚本,等同于defer属性。
    // 等同于
    <script type="module" src="./foo.js" defer></script>
    // 网页中的多个<script type="module">,会按照在页面中出现的顺序依次执行。
    

    async属性也可以打开,但是打开async属性之后,<script type="module">就不会再按照在页面中出现的顺序执行了,而是只要该模块加载完成,就执行该模块,渲染引擎就中断渲染,直到模块执行完成之后,再恢复渲染。

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

    ES6模块可以内嵌在网页中,语法同加载外部脚本。

    <script type="module">
    	import utils from './utils.js'
        // code
    </script>
    // jQuery就持模块加载
    <script type="module">
    	import $ from './jquery/src/jquery.js'
        $("#message").text('学习ES6,学好一点!')
    </script>
    

    对于外部的模块脚本,例如上述的foo.js。

    • 代码不是在全局作用域中运行,而是在模块作用域中运行。模块内部的顶层变量,外部不可见。
    • 模块脚本自动采用严格模式,不管是否声明use strict。
    • 模块之中,可以使用import命令加载其他模块,(.js后缀不可省略,需要提供绝对URL和相对URL),可以使用export命令输出对外接口。
    • 模块中,顶层的this关键字返回undefined,不是指向window。
    • 多次加载同一个模块,只执行一次。

    2.ES6模块与CommonJS模块的差异

    ES6模块与CommonJS模块完全不同,有三个重大差异。

    • CommonJS输出值的拷贝,ES6模块输出值的引用。
    • CommonJS模块是运行时加载,ES6模块是编译时输出接口。
    • CommonJS模块的require()是同步加载模块,ES6模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

    其中,第二个差异是因为CommonJS加载的是一个对象(即module.exports属性),此对象在脚本运行完后才会生成。ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。 解释第一个差异:
    CommonJS模块输出值的拷贝,即一旦输出一个值,模块内部的变化就影响不到这个值。

    // lib.js
    let counter = 3;
    function incCounter(){
    	counter++; 
    }
    module.exports = {
    	counter:counter,
        incCounter:incCounter
    }
    // main.js  在main.js中加载这个模块。
    var mod = require('./lib');
    console.log(mod.counter) //3
    mod.incCounter();
    console.log(mod.counter) // 3 输出的值没有任何变化。
    // 说明,lib.js模块加载之后,它的内部变化就影响不到输出的mod.counter。
    // 因为mod.counter是一个原始类型的值,会被缓存。写成一个函数,才能得到内部变动后的值。
    let counter = 3;
    function incCounter(){
    	counter++;
    }
    module.exports = {
    	get counter(){
        	return counter;
        },
        incCounter: incCounter
    }
    // 上述代码中,输出的counter属性实际上是一个取值器函数。再执行main.js,就能正确的读取内部变量counter的变动了。
    

    ES6模块的运行机制。
    当JS引擎对脚本静态分析的时候,遇到import命令就生成一个只读引用。等脚本真正执行时,再到被加载的那个模块去取值。
    即,ES6模块是动态引用,不会缓存值,模块里面的变量绑定其所在的模块。

    // lib.js
    export let counter =3;
    export function incCounter(){
    	counter++;
    }
    // main.js
    import {counter,incCounter} from './lib.js'
    console.log(counter) // 3
    incCounter()
    console.log(counter) // 4
    // 即输入的变量counter是活的,反应其所在模块lib.js内部的变化。
    

    另一个例子

    // m1.js
    export var foo ='bar'
    setTimeout(() => {
        foo = 'baz'
    }, 500);
    // m2.js
    import {foo} from './circle'
    console.log(foo)
    setTimeout(() => {
        console.log(foo)
    },500)
    // m2.js 会正确读取foo的变化,证明ES6模块是动态的去被加载的模块取值,并且变量总是绑定其所在的模块。
    

    ES6输入的模块变量,只是符号连接,这个变量仅为可读,对其赋值会产生错误。

    // lib.js
    export let obj = {}
    // main.js
    import { obj } from './circle.js'
    obj.pro = 'hello'
    obj = {} // TypeError:Assignment to constant variable
    // 变量obj指向的地址仅为可读,不能重新赋值,即相当于在main.js中创造了一个const变量。
    

    export通过接口输出的是同一个值,不同的脚本加载这个接口,得到的是同一个实例。

    // f.js
    function C() {
        this.num = 0
        this.add = function () {
            this.num += 1
        }
        this.show = function () {
            console.log(this.num)
        }
    }
    export let c = new C()
    
    // f1.js
    import {c} from './profile.js'
    c.add()
    
    // f2.js
    import {c} from './profile.js'
    c.show()
    
    // main.js
    import './f1.js'
    import './f2.js'
    // 最终在控制台输出的值是1,证明f1模块和f2模块中引入的的是同一个实例。
    

    起源地下载网 » ES6的学习笔记(十五)Module的加载实现。。。

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元