JS模块化-理论

58 阅读4分钟

JS本身是为了简单页面设计的补充,页面动画,表单提交等,没有模块化概念

初始:无模块化

  1. 页面增加不同类型的js文件,validate.js, moment.js...

  2. 多种js为了维护和可读性,被分在不同的js文件中

  3. 不同的js在同一个文件里被引用

    <script src='./jquery.js'>
    <script src='./main.js'>
    

优势:多个js文件是简单的模块化思想

问题:只是做了代码的拆分而已;

  • 会污染全局作用域;没有独立作用域上下文变量名/函数名重合

雏形:立即执行函数 (语法侧)

对作用域的把控

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

这里count是全局变量,可以被任意引用和修改,想要只能被increase(), reset()修改,利用函数作用域

const iifemodule = (()=>{
    let count = 0;
    return {
      increase: () => ++count,
      reset: ()=> count = 0
    }
})()
iifemodule.increase();

完成模块的封装,实现对外暴露功能,变量只在内部

IIFE优化,如果依赖其他模块 --> 传参传进来就好

const iifemodule = ((module1, module2)=>{
    // 使用module1,module2
    let count = 0;
    return {
      increase: () => ++count,
      reset: ()=> count = 0
    }
})(module1, module2)

一个模块的封装:输入(传参) + 内部逻辑 + 对外暴露接口(功能)

面试题:早期jQuery依赖处理,模块加载方案

IIFE + 传参调配(就是外部的传参以及结果的传出 ), revealing写法

 const revealingCounterModule = (() => {
    let count = 0
    // dependencyModule1, dependencyModule2
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log("count is reset")
    }
    API也以局部变量定义在作用域中
    return {
        increase,
        reset
    }
})()
revealingCounterModule.increase()

只暴露接口而没有业务逻辑,外部拿到的是组成好的,直接调用就可以,也看不到内部如何实现

本质的实现和方案上并无不同,只是在写法思想上,更强调局部API做原理逻辑,暴露出去的是可调用的面向使用方的接口抽象

成熟:CommonJS(规范化体系)

nodejs制定

  • 一个文件一个模块

  • 每个模块单独作用域

  • module + exports导出成员,require调用其他模块 具体实现

     const module1 = require('./module1')
    const module1 = require('./module2')
    let count = 0
    // 使用module1,module2
    const increase = () => ++count
    const reset = () => {
    count = 0
    console.log("count is reset")
    }
    
    exports.increase = increase
    exports.reset = reset
    
    module.exports = {
      increase,
      reset
    }
       
    // 使用
    const { increase, reset } = require('./commonJSCounterModule.js')
    increase()
    reset()
    
  • 优点:CommonJS规范在服务端率先完成了JS的模块化,解决了依赖、全局变量污染的问题

  • 缺点:CommonJs主要诞生针对的环境是服务端,对于同步加载没问题。 => 浏览器端的异步问题

成熟:AMD规范 -- 非同步模块的加载

require.js

定义方式 - 回调

 // 1. 通过define来定义一个模块
 define(id, [depends], callback) // id当前定义的模块, depends需要哪些外部的依赖,callback
 // 2. 外部加载
 require([module], callback)

上面的例子

 define(amdCounterModule,[module1,modle2],(module1,module2)={
     let count = 0
     // dependencyModule1, dependencyModule2
     const increase = () => ++count
     const reset = () => {
     count = 0
     console.log("count is reset")
  }

  return {
    increase,
    reset
   }
 })

引入模块

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

(像是前两者的结合)

问AMD使用require加载同步模块可以吗

可以的,AMD支持向前兼容,可以用require加载同步模块

   define(require => { //传入require  也可以使用exports,module
    const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')

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

    return {
        increase,
        reset
    }
})
  • 优点:适合在浏览器环境中异步加载模块,可以并行加载多个模块
  • 缺点:提高了开发成本,不能按需加载,而是必须提前加载所有依赖

成熟:CMD规范 按需加载 -- 编译型

sea.js

   define(function(require, exports, module){ // 同样向前兼容
      let $ = require('jquery');
      // 使用jquery
      let module1 = require('./module1');
      // ...使用
   }) 
  • 优点:就近加载
  • 缺点:加载逻辑存在于每个模块中

完全体 ESModule

=> 底层语言的统一 ES 新增定义方式:引入 + 导出 => import + export (ES新语法)兼容前面所有

  import module1 from './module1'
  import module2 from './moduel2' 
  let count = 0
  export const increase = () => ++count
  export const reset = () => {
    count = 0
  }
  export default {
    increase,
    reset
  }

==> 服务器端 CommonJS + 浏览器端 ESModule

UMD 兼容 AMD CJS

AMD通过define定义,retur暴露 CJS require引入,module.export暴露

   (define => define((require, exports, module) => {
    const dependencyModule1 = require('dependencyModule1')
    const dependencyModule2 = require('dependencyModule2')

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

    module.exports = {
        increase,
        reset
    }
}))(
    typeof module === 'object'
        && module.exports
        && typeof define !== 'function'
            ? // 当前为node / commonJS场景
                factory => module.exports = factory(require, exports, module)
            : // 当前amd
                define
)