美文网首页
2023.30 amd umd iife cjs es有啥区别?

2023.30 amd umd iife cjs es有啥区别?

作者: wo不是黄蓉 | 来源:发表于2023-08-16 13:46 被阅读0次

    大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。

    网上很多关于模块化的资料,但是每次提起还有遇到package.json中的打包配置还是理不清,那些之前搞不懂的知识点迟早还是要还的,每次学习也是一个查漏补缺的过程。加油!!!

    理完这些大概理解,为什么会有webpackrollup,vite这些打包工具了,模块是大佬们为了解决前期代码分割问题提出的解决方案。前期是通过拆分一个js到多个文件实现的模块化,但是文件多了之后无法保证命名问题,大佬们就剔除了模块化的方案。

    模块化的好处:

    • 解决命名冲突问题
    • 解决变量查找作用域的层级,减少性能消耗
    • 代码逻辑更清晰,将功能和模块化绑定更易维护
    • 利用模块化还能更方便的对现有库进行二次封装不会影响原来库

    打包源代码
    index.js

    
    import { add } from './func.mjs'
    console.log('hello world')
    
    function sayHi() {
        let name = 'jane'
    
        console.log(name + 'hello world', add(1, 2))
    }
    
    sayHi()
    
    window.addEventListener('error', function (env) {
        console.log(env)
    })
    
    

    func.mjs

    
    export function add(a, b) {
        return a + b
    }
    

    使用rollup打包结果

    amd 异步模块定义

    异步模块定义,采用异步方式加载模块,类似于回调函数 。使用define定义模块,不支持原生Js,需要借助库来使用。

    使用参考这边的例子,为什么需要在script标签中加一个data-main属性,只有在使用require.js的时候才这么用,表示入口文件是哪个。

    适合在浏览器环境中异步加载模块,可以并行加载多个模块

    打包工具配置打包格式可以将所有依赖项打包成一个文件,生成一个入口文件,因此查看打包好的配置为umd格式的文件时看不到后面几个参数,只有第一个参数,就是一个函数。

    第一个参数:模块名字

    第二个参数:模块依赖项数组

    第三个参数:函数的参数与前面依赖项一一对应,每一项分别为依赖项模块的导出成员。

    
    define((function () { 'use strict';
    
        function add(a, b) {
            return a + b
        }
    
        console.log('hello world');
    
        function sayHi() {
            let name = 'jane';
    
            console.log(name + 'hello world', add(1, 2));
        }
    
        sayHi();
    
        window.addEventListener('error', function (env) {
            console.log(env);
        });
    
    }));
    
    

    umd通用模块化规范

    amd 和commonjs的结合,判断是否支持node.js模块,然后使用Node.js模块模式,再判断是否支持AMD,使用AMD加载模块

    umd源码范式推演,可以参考这篇文章

    通过传参方式导出一个模块,Umd功能根据使用要求生产模块,它的职责定位是模块工厂,我们定义一个factory方法,每当执行时,返回一个模块

    
    (function(factory){
        //通过形参访问工厂方法,如果不着地那个挂载对象,就默认挂载到全局对象
        window.attr = factory()
    })(function(){})
    

    指定挂载对象

    
    (function(root,factory){
        root.attr = factory();
    }(self !== undefined ? self : this, function(){
    
    }));
    

    适配amd

    
    (function(factory){
        root.attr = factory();
        if(typeof define === 'function' && define.amd){
            define(factory)
        }
    }(self !== undefined ? self : this, function(){
    
    }));
    

    适配commonjs

    
    (function(factory){
        root.attr = factory();
        if(typeof exports === 'object' && typeof define !== 'function'){
            
            module.exports = factory()
        }
    }(self !== undefined ? self : this, function(){
    
    }));
    

    cjs commonjs

    cjs语法基本和js一致,只不过是运行在node环境的代码,node也支持js语法,官网上介绍说Node.js是一个开源、跨平台的 JavaScript 运行时环境 ,本身更是支持文件读取、http请求服务等操作,比起js只能操作浏览器对象,node可一操作一些操作系统层面的东西。

    关于npm配置在node和浏览器环境配区别问题,在es modules章节讲

    
    'use strict';
    
    function add(a, b) {
        return a + b
    }
    
    console.log('hello world');
    
    function sayHi() {
        let name = 'jane';
    
        console.log(name + 'hello world', add(1, 2));
    }
    
    sayHi();
    
    window.addEventListener('error', function (env) {
        console.log(env);
    });
    
    

    es

    可以用编译阶段就确定模块的依赖关系,commonjs和amd模块,只能在运行时确定。

    现在浏览器已经支持用script标签引入模块或脚本,需要在script中添加type="module",这样就可以解析import/export语法

    打包类型配置为es也就是esModulejs,就不做过多介绍了。

    以下属性配置是一些非标准的属性,在npm官网package.json配置项中找不到

    {
        "type":"",//声明npm包遵循的模块化规范,默认:commonjs,可设置值:commonjs/module
        "module":"",//如果npm包导出的是ESM规范的包,可以使用module来定义入口文件     
    }
    

    如果在npm package.json中设置type:'module'

    例:修改type=module后表示使用es module规范

    如果在npm package.json中设置module:'index.mjs'表示定义npm包的ESM规范的入口文件

    main字段,定义入口文件,客户端和服务端都可以使用

    browser字段,官网上描述,如果是在客户端使用,则为了更加语义化应该使用browser字段代替main字段

    如果package.json中同时指定了main、module、browser这三个字段,则有优先级

    package.json中Browser modules main字段优先级对比

    大致理解就是会先判断是否有module,因为module设置了就表示使用es module规范

    如果有module就判断browser main的配置其实兜底逻辑

    涉及到的文件加载顺序问题

    文件加载优先级mjs > js mjs设置为.mjs结尾的文件可以在node环境下原生执行ESM规范的脚本文件

    npm包怎么根据在不同环境下加载npm包不同的入口文件?

    • 使用process对象检测
    
    if (typeof process !== 'undefined') {
        console.log('运行环境是Node.js')
    } else {
        console.log('运行环境不是Node.js')
    }
    
    • 使用window对象检测
    
    if (typeof window !== 'undefined') {
        console.log('运行环境是浏览器')
    } else {
        console.log('运行环境不是浏览器')
    }
    

    打包结果

    
    function add(a, b) {
        return a + b
    }
    
    console.log('hello world');
    
    function sayHi() {
        let name = 'jane';
    
        console.log(name + 'hello world', add(1, 2));
    }
    
    sayHi();
    
    window.addEventListener('error', function (env) {
        console.log(env);
    });
    
    

    iife立即执行函数

    立即执行函数,可以利用立即执行函数创建闭包解决变量作用域问题

    还可以利用立即执行函数避免全局变量命名冲突问题

    因为创建了一个独立的作用域,因此变量查找作用域时可以减少对作用域的查找

    
    (function () {
        'use strict';
    
        function add(a, b) {
            return a + b
        }
    
        console.log('hello world');
    
        function sayHi() {
            let name = 'jane';
    
            console.log(name + 'hello world', add(1, 2));
        }
    
        sayHi();
    
        window.addEventListener('error', function (env) {
            console.log(env);
        });
    
    })();
    
    

    相关文章

      网友评论

          本文标题:2023.30 amd umd iife cjs es有啥区别?

          本文链接:https://www.haomeiwen.com/subject/nywkmdtx.html