• commonJS和ES6模块化


    commonJS和ES6模块化

    面试题

    • commonJS的特性
    • commonjs和es6的模块化区别
    • commonjs 和 esm 是怎么解决循环引用问题的
    • 模块化了解过吗?延伸出说一下CommonJS和Es Modulde的底层原理?

    commonJS

    common规范的应用

    • node 是CommonJS在服务器端一个具有代表性的实现
    • Browserify 是CommonJS在浏览器中的一种实现
    • webpack打包工具对CommonJS的支持和转换,前端应用在编译之前使用CommonJS进行开发

    commonjs的特点

    • commonjs中每个js文件都是一个单独的模块
    • 使用require()方法加载其他模块时,会执行被加载模块中的代码
    • module.exports 指向暴露的对象,exports是简写情况,默认情况下,exportsmodule.exports指向同一个对象 exports==module.exports,如果不同module.exports为准
    • CommonJS 同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。采用深度优先算法
    • require() 可以在任意的位置,动态加载(运行时加载)模块,不会提升到最开头

    commonJs实现原理

    需要关注的问题

    • 如何解决变量污染的问题

    script标签直接引入的方式,没有模块化,script内部的变量是可以相互渲染的

    • module.exports,exports,require 三者是如何工作的?又有什么关系?
    模块执行的原理
    1. 在编译过程中,commonJs对js的代码块进行了收尾包装
      每个模块文件上存在requiremoduleexports方法,但是这三个变量在文件中是没有定义的。 Commonjs 会将我们写代码包装起来,形成包装函数,requiremoduleexports本质是通过形参的方式传递到包装函数中的。
      require: 引入模块的方法
      module: 记录当前模块信息
      exports:当前模块导出的属性
    2. 在模块加载的时候,会传入requiremoduleexports等参数
    const sayName = require('./hello.js')
    module.exports = function say(){
        return {
            name:sayName(),
            author:'ranan'
        }
    }
    //包装后
    (function(exports,require,module,__filename,__dirname){
    	const sayName = require('./hello.js')
    module.exports = function say(){
        return {
            name:sayName(),
            author:'ranan'
        	}
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    require模块加载原理
    • CommonJS 模块同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。采用深度优先算法
    • require() 可以在任意的位置,动态加载(运行时加载)模块,不会提升到最开头

    module :在 Node 中每一个 js 文件都是一个 module ,module 上保存了 exports 等信息之外,还有一个 loaded 表示该模块是否被加载,没有加载过就先缓存后执行,已经加载过,就直接去缓存不用执行来避免循环引用问题

    • 为 false 表示还没有加载;
    • 为 true 表示已经加载
    /*
    根据文件标识符,先查找有没有缓存,有缓存直接返回缓存的内容
    没有缓存,创建module对象,将其缓存到module上,然后加载文件,将 loaded 属性设置为 true ,然后返回 module.exports 对象。
    */
     // id 为路径标识符
    function require(id) {
       /* 查找  Module 上有没有已经加载的 js  对象*/
       const  cachedModule = Module._cache[id]
       
       /* 如果已经加载了那么直接取走缓存的 exports 对象  */
      if(cachedModule){
        return cachedModule.exports
      }
     
      /* 创建当前模块的 module  */
      const module = { exports: {} ,loaded: false , ...}
    
      /* 将 module 缓存到  Module 的缓存属性中,路径标识符作为 id */
      //只会在第一次加载时运行一次,后面都会从缓存中读取,  
      Module._cache[id] = module
      /* 加载文件 */
      runInThisContext(wrapper('module.exports = "123"'))(module.exports, require, module, __filename, __dirname)
      /* 加载完成 *//
      module.loaded = true 
      /* 返回值 */
      return module.exports
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Es Module

    Es Module

    • Es Module 的静态导入导出的优势,实现了 tree shaking
    • Es Module 还可以 import() 懒加载方式实现代码分割。

    ES6 module的特性

    1. ES6 module 的引入和导出是静态的,import 会自动提升到代码的顶层 import , export 不能放在块级作用域或条件语句中。

    这种静态语法,在编译过程中确定了导入和导出的关系,所以更方便去查找依赖,更方便去 tree shaking

    1. ES6 模块提前加载并执行模块文件,ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块
    2. 使用import 导入的变量是只读的,可以理解默认为 const 装饰,无法被赋值
    3. 使用 import 导入的变量是与原变量绑定/引用的,可以理解为 import 导入的变量无论是否为基本类型都是引用传递。

    import() 函数 动态引入

    import() 返回一个 Promise 对象, 返回的 Promise then 成功回调中,可以获取模块的加载成功信息。

    特点

    • import() 动态加载一些内容,可以放在条件语句或者函数执行上下文中
    if(isRequire){
        const result  = import('./b')
    }
    
    • 1
    • 2
    • 3
    • import() 可以实现懒加载
    [
       {
            path: 'home',
            name: '首页',
            component: ()=> import('./home') ,
       },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    import() 可以很轻松的实现代码分割。避免一次性加载大量 js 文件,造成首次加载白屏时间过长的情况。

    commonJs和es module的区别

    -commonJses module
    导入方式require()动态加载模块,可以在任意的位置,不会被提升到最前面导入方式分为静态导入和动态导入
    静态导入:不能放在块级作用域和条件中,会提升到最前面
    动态导入import()类似require(),但他是异步加载的
    构建模块依赖的时期require同步加载并执行模块文件,CommonJS 模块在执行阶段分析模块依赖。在编译阶段就建立起了模块之间的依赖关系
    ES6 模块在预处理阶段分析模块依赖,在执行阶段执行模块
    输出的值输出值的拷贝值,一旦输出了某个值,如果模块内部发生变化,不会影响外部的值输出的是值的引用,JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用,到被加载的那个模块里去取值。所以内部发生变化会影响外部的值。
  • 相关阅读:
    快照表转换成拉链表的方式(hive)初始化拉链&增量更新拉链
    MySQL常见报错
    高防CDN:保障网络安全的未来之路
    [前端基础] CSS3 篇
    浏览器窗口间的通信
    python_selenium自动化测试框架
    uniapp企业微信web-view父子通信问题
    “PairElimination“ app Tech Support(URL)
    并发编程的工具类
    深度学习-第二章-Window下Yolov5训练打电话
  • 原文地址:https://blog.csdn.net/qq_41370833/article/details/126706725