乐趣区

关于javascript:JS模块化CJSAMDCMDES6前端面试知识点查漏补缺

本文从以工夫为轴从以下几个方面进行总结 JS 模块化。从无模块化 => IIFE => CJS => AMD => CMD => ES6 => webpack 这几个阶段进行剖析。

历史

幼年期:无模块化

形式

  1. 须要在页面中加载不同的 js,用于动画,组件,格式化
  2. 多种 js 文件被分在了不同的文件中
  3. 不同的文件被同一个模板所援用
<script src="jquery.js"></script>
<script src="main.js"></script>
<script src="dep1.js"></script>

此处写法文件拆分是最根底的模块化(第一步)

* 面试中的诘问

script 标签的参数:async & defer

<script src="jquery.js" async></script>

总结

  • 三种加载
  • 一般加载:解析到立刻阻塞,立即下载执行以后 script
  • defer 加载:解析到标签开始异步加载,在后盾下载加载 js,解析实现之后才会去加载执行 js 中的内容,不阻塞渲染
  • async 加载:(立刻执行)解析到标签开始异步加载,下载实现后开始执行并阻塞渲染,执行实现后持续渲染
  • 兼容性:> IE9
  • 问题可能被疏导到 => 1. 浏览器的渲染原理 2. 同步异步原理 3. 模块化加载原理
  • 呈现的问题
  • 净化全局作用域

成长期(模块化前夜)– IIFE(语法测的优化)

作用域的把控

let count = 0;
const increase = () => ++count;
const reset = () => {count = 0;}

利用函数的块级作用域

(() => {
    let count = 0;
    ...
})
// 最根底的局部

实现一个最简略的模块

const iifeModule = (() => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {count = 0;}
    console.log(count);
    increase();})();
  • 诘问:独立模块自身的额定依赖如何优化

优化 1:依赖其余模块的传参型

const iifeModule = ((dependencyModule1,dependencyModule2) => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {count = 0;}
    console.log(count);
    increase();
    ...// 能够解决依赖中的办法
})(dependencyModule1,dependencyModule2)

面试 1:理解 jquery 或者其余很多开源框架的模块加载计划

将自身的办法裸露进来

const iifeModule = ((dependencyModule1,dependencyModule2) => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {count = 0;}
    console.log(count);
    increase();
    ...// 能够解决依赖中的办法
    return 
        increase,reset
    }
})(dependencyModule1,dependencyModule2)
iifeModule.increase()

=> 揭示模式 revealing => 下层无需理解底层实现,仅关注形象 => 框架

  • 诘问:
  • 持续模块化横向开展
  • 转向框架:jquery|vue|react 模块细节
  • 转向设计模式

成熟期

CJS(Commonjs)

node.js 指定

特色:

  1. 通过 module + exports 对外裸露接口
  2. 通过 require 去引入内部模块,参考 前端进阶面试题具体解答

main.js

const dependencyModule1 = require('./dependencyModule1')
const dependencyModule2 = require('./dependencyModule2')

let count = 0;
const increase = () => ++count;
const reset = () => {count = 0;}
console.log(count);
increase();

exports.increase = increase;
exports.reset = reset;

module.exports = {increase, reset}

exe

const {increase, reset} = require(./main.js)

复合应用

(function(this.value,exports,require,module){const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')
}).call(this.value,exports,require,module)

诘问:一些开源我的项目为何要把全局、指针以及框架自身作为参数

(function(window,$,undefined){const _show = function(){$("#app").val("hi zhuawa")
    }
    window.webShow = _show;
})(window,jQuery)

阻断思路

  • 一. window

    1. 全局作用域转换为部分作用域,window 是全局作用域,如果不转成部分作用域会有一个向上查找晓得全局的过程,晋升执行效率
    2. 编译时优化:编译后会变成(优化压缩老本,销毁)
    (function(c){})(window) // window 会被优化成 c
    //window 在外面所有别的执行所有的变动都会随着执行结束都会跟着 c 一起被销毁
  • 二. jquery

    1. 独立定制复写和挂载
    2. 避免全局串扰
  • 三. undefined
    避免改写:在执行外部这段代码的时候保障 undefined 是正确的,不会被改写,如在内部定义一个 undefined =1
    undefined 对 jquery 自身是一个很重要的一个存在

优缺点

  • 长处:CommonJS 率先在服务端实现了,从框架层面解决了依赖,全局变量未然的问题
  • 毛病:针对服务端的解决方案,异步拉取,依赖解决不是很敌对

=> 异步依赖的解决

AMD

通过异步执行 + 容许指定回调函数
经典实现框架:require.js

新增定义形式:

//define 来定义模块
define(id, [depends], callback)//require 来进行加载
reuqire([module],callback)

模块定义的中央

define('amdModule',[dependencyModule1,dependencyModule2],(dependencyModule1,dependencyModule2) => {
    // 业务逻辑
    let count = 0;
    const increase = () => ++count;
    module.exports = {increase}
})

引入的中央

require(['amdModule'],amdModule => {amdModule.increase()
})

面试题:如果在 AMDModule 中想兼容已有代码,怎么办?

define('amdModule',[],require => {const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')
    // 业务逻辑
    let count = 0;
    const increase = () => ++count;
    module.exports = {increase}
})

面试题:手写兼容 CJS&AMD

// 判断的要害:1. object 还是 function
    2. exports ?
    3. define

(define('AMDModule'),[],(require,export,module) => {const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')

    let count = 0;
    const increase = () => ++count;
    const reset = () => {count = 0;}
    console.log(count);
    export.increase = increase();})(
    // 指标:一次性辨别 CJS 还是 AMD
    typeof module === 'object' && module.exports && typeof define !== function ? //CJS
    factory => module.exports = factory(require,exports,module)
    : //AMD
    define
)

优缺点

  • 长处:适宜在浏览器中加载异步模块的计划
  • 毛病:引入老本

CMD

按需加载
次要利用框架:sea.js

define('module',(require,exports,module) => {let $ = require('jquery')
    let dependencyModule1 = require('./dependencyModule1')
})

优缺点

  • 长处:按需加载,依赖就近
  • 毛病:依赖打包,加载逻辑存在于每个模块中,扩充了模块体积,同时性能上依赖编译

ES6 模块化

新增定义:

  • 引入:import
  • 引出:export

面试:

  1. 性能 – 按需加载
// ES11 原生解决方案
import('./esMModule.js').then(dynamicModule => {dynamicModule.increase();
})

长处:
通过一种对立各端的状态,整合了 js 模块化的计划
毛病:实质上还是运行时剖析

解决模块化新思路 – 前端工程化

遗留

基本问题:运行时进行依赖剖析
解决方案:线下执行

编译时依赖解决思路

<script src="main.js"></script>
<script>
  // 给构建工具一个标识位
  require.config(__FRAME_CONFIG__);
</script>
<script>
  require(['a', 'e'], () => {    // 业务逻辑})
</script>

define('a', () => {let b = require('b')
    let c = require('c')
})

齐全体:webpack 为外围的前端工程化 + mvvm 框架的组件化 + 设计模式

退出移动版