2023.30 amd umd iife cjs es有啥区别?

大家好,我是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规范

1691842660963.png

如果在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);
    });
​
})();
​

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYj0hK3c' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片