• 前端模块化之CommonJS,ES6,AMD,CMD


    最近在搞跨平台解决方案,讨论关于模块划分的问题以及如何尽量多的复用逻辑代码。于是就有了此文章,之前的博客也写过,不过由于主机商跑路,宝贵的资源也就没了,说多了都是泪~ 这里按模块化发展的历史回溯的时间序

    一. ES6 Moudle

    这个是目前前端小伙伴接触的最多的,是浏览器和服务端通用的模块化解决方案,主要命令为:exportimport。export用于导出本模块对外的接口,import用于导入某个模块的功能。

    //modulA.js
    
    export var goingta = "goingta";
    
    export function hello() { console.log("hello world"); }
    
    const fn = function() { goingta = "new goingta"; console.log("fn"); };
    
    export { fn };
    
    const come = 1; export default come;
    
    // export default const errorDefault = 2 // default 不能导出变量表达式
    
    //index.js
    
    import come, { fn, hello } from "./moduleA.js";
    
    import * as aModule from "./moduleA.js";
    
    fn();
    
    hello();
    
    console.log("come", come);
    
    console.log("aModule", aModule); //aModule Module{default: 1, fn: ƒ (),…}
    
    console.log("aModule.goingta", aModule.goingta); //new goingta
    

    从上面例子,我们可以看到export可以在任意地方导出变量,方法,并且也可以将已有变量或方法包在一个{}对象里面导出,但是最终都会包在一个{}里面,简单理解就是:

    1. 如果单独导出一个变量或方法则是往将要导出{}对象里面添加属性。
    2. 如果导出的是{},则和已生成的导出{}对象合并。

    然后说一下特例export default,这个是在导出对象里面加一个default属性,还有一点值得注意的是export default后面不能跟变量表达式。

    最后还有一个重点是,ES6 Moudle是编译时输出,并且是值引用。

    二. CommonJS

    CommonJS最主要的代表就是Node.js,主要命令:moduleexportsrequire。其中有个令人疑惑的点是exportsmodule.exports,其实理解起来也很简单,就是在模块里面加了一句: exports = module.exports = {}; exportsmodule.exports指向同一个内存区域,只要在exports加了属性,则module.exports会跟着变化,但是最终导出对外的接口是以module.exports为准,所以不推荐直接使用exports。例如:

    //moduleA.js
    
    let goingta = "我是goingta";
    
    exports.goingta = goingta;
    
    exports.fn = function() { goingta = "new goingta"; };
    
    exports = "把exports指向其他区域";
    
    module.exports = "我现在没有goingta了,也没有fn了"; // 这行代码注释掉会有不一样的结果
    
    // index.js
    
    let obj = require("./moduleA.js");
    
    console.log(obj);
    

    最终输出的是:"我现在没有goingta了,也没有fn了",如果把最后一行代码注释掉则输出:{goingta: "我是goingta", fn:ƒ ()}

    对于CommonJS规范来说,很重要的一点是CommonJS输出的是一个值拷贝,并且是运行时加载。

    把上面的示例代码简化一下就可以看出:

    //moduleA.js
    
    let goingta = "我是goingta";
    
    module.exports = { goingta };
    
    module.exports.fn = function() { goingta = "new goingta"; };
    
    //index.js
    
    let obj = require("./moduleA.js");
    
    obj.fn();
    
    console.log("obj.goingta", obj.goingta);
    

    最终结果输出:"我是goingta"

    三. ES6 Module和CommonJS的区别

    从上面的结论我们不难得出,ES6 ModuleCommonJS的本质区别:

    1. 引用方式:CommonJS模块输出是值的拷贝,ES6 Module模块输出的值是引用
    2. 时机:CommonJS是运行时加载,ES6 Module是编译是输出

    四. 模块化历史

    差不多在6年前,当项目越来越大,用JS模拟划分出来的类导致文件越来越多以及需要前置依赖的各种库(那个时代的三叉戟jQueryBackboneUnderscore),模块化以及各种脚手架开始兴起,于是有了AMDCMD以及当年这两种规范之争-AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。AMD的代表是require.jsCMD的代表是玉伯的Sea.js

    五. AMD和CMD

    AMD规范是采用异步方式,依赖前置必须一开始就写好,所有的依赖加载完成后才会执行回调函数里的内容:

    // moduleB.js
    //一开始就要写好这个模块所有的依赖
    define(["jQuery", "underscore", "moduleA"], function($, _) {
    
        if (false) {
            // 即便没用到moduleA,也会被提前执行
            moduleA.doSomething()
        }
    });
    
    `CMD`规范按玉伯大大的原话`"是 Sea.JS 在推广过程中对模块定义的规范化产出"`,并且当时在国内认可度很高。依赖就近书写:
    
    // moduleB.js
    
    define(function(require, exports, module) {
        var moduleA = require('moduleA.js');//用到时才写
    
        moduleA..doSomething()
        //一堆其他业务代码
        var $ = require('jquery.js');
    
        $.xxxx()
    });
    

    六. 更早以前

    在07年左右正式有前端开发这个职位后(以前都是后端研发兼写前端代码),SEO、性能优化、语义模板、浏览器兼容、代码混淆加密等等都需要前端开发承接是,往往是一个公用的base.js外加几个业务js放在html里面,然后用deferasync标记:

    <script src="base.js" async></script>
    
    <script src="moduleA.js" defer></script>
    

    deferasync的区别在于:defer是“渲染完再执行”,async是“下载完就执行”。

  • 相关阅读:
    关于SQL
    win10商店或者账户连不上网
    pom.xml红叉
    3D球状标签云(兼容IE8)
    网页宽高自适应大小
    html5定位并在百度地图上显示
    【转】Javascript 中的false,零值,null,undefined和空字符串对象
    jQuery checkBox 全选的例子
    jQuery 表单验证 jquery.validator.js
    jQuery 手风琴侧边菜单
  • 原文地址:https://www.cnblogs.com/tanglei/p/10656429.html
Copyright © 2020-2023  润新知