我是靠谱客的博主 花痴黑米,最近开发中收集的这篇文章主要介绍js模块化,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • js模块化
      • 一、背景
      • 二、模块化的发展
        • 1.幼年期: 无模块化
        • 2.成长期: IIFE(语法侧的优化)
        • 3.成熟期
        • CJS-Commonjs
        • AMD规范
        • CMD规范
        • ES6模块化 :走向新时代
    • 解决模块化的新思路:前端工程化

js模块化

一、背景

JS本身简单的页面设计:页面动画+表单提交
并无模块化 or 命名空间的概念

JS的模块化需求日益增长

二、模块化的发展

1.幼年期: 无模块化

1.开始需要在页面中增加一些不同的js: 动画、表单、格式化等
2.多种js文件被分在不同的文件中
3.不同的文件又被同一个模板引用

  <script src="jquery.js"></script>
  <script src="main.js"></script>
  <script src="dep1.js"></script>
  //……

认可:

文件分离是最基础的模块化的第一步

缺点:

污染全局作用域,不利于大型项目的开发以及多人团队的共建

2.成长期: IIFE(语法侧的优化)

IIFE: Immediately Invoked Function Expression,立即调用的函数表达式。也就是 在函数声明的同时立即调用这个函数。

利用函数的块级作用域,定义一个函数,立即执行,初步实现了一个最最最简单的模块。

尝试去定义一个最简单的模块:

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

iifeModule.increase();
iifeModule.reset();

追问1:有额外依赖时,如何优化IIFE相关代码
答:以参数的形式传参

优化1:依赖其他模块的IIFE

const iifeModule = ((dependencyModule1, dependencyModule2) => {
  let count = 0;
  return {
    increase: () => ++count;
    reset: () => {
      count = 0;
    }
  }
})(dependencyModule1, dependencyModule2);

iifeModule.increase();
iifeModule.reset();

追问2:了解早期jquery的依赖处理以及模块加载方案吗?/ 了解传统IIFE是如何解决多方依赖的问题
答:IIFE加传参调配

实际上,jQuery等框架应用了revealing的写法。

const iifeModule = ((dependencyModule1, dependencyModule2) => {
  let count = 0;
  const increase = () => ++count;
  const reset = () => {
    count = 0;
  }
  //只返回方法,不需要关心实现的方式
  return {
    increase, reset
  }
})(dependencyModule1, dependencyModule2);
iifeModule.increase();
iifeModule.reset();

3.成熟期

CJS-Commonjs

node.js制定
特征:

  • 通过module+exports区对外暴露接口
  • 通过require来调用其他模块

模块组织方式:
main.js文件

// 引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);

// 处理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
  count = 0;
}
// 做一些跟引入依赖相关事宜……

// 暴露接口部分
exports.increase = increase;
exports.reset = reset;

module.exports = {
  increase, reset
}

模块使用方式:

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

  increase();
  reset();

追问1:实际执行处理

(function (thisValue, exports, require, module) {
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 业务逻辑……
  }).call(thisValue, exports, require, module);

优点:
CommonJs率先在服务端实现了,从框架层面解决依赖,全局变量污染的问题
缺点:
主要针对了服务端的解决方案。对于依赖拉取的处理整合不是那么友好。

新的问题:异步依赖

AMD规范

通过异步加载+允许制定回调函数
经典实现框架是:require.js

新增定义方式:

  // 通过define来定义一个模块,然后require进行加载
  /*
  define
  params: 模块名,依赖模块,工厂方法
   */
  define(id, [depends], callback);
  require([module], callback);

模块定义方式

 define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
    // 业务逻辑
    // 处理部分
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }

    return {
      increase, reset
    }
  })

引入模块:

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

追问2:如果在AMDmodule中想兼容已有代码,怎么办

 define('amdModule', [], require => {
    // 引入部分
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 处理部分
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }
    // 做一些跟引入依赖相关事宜……

    return {
      increase, reset
    }
  })

追问3:AMD中使用revealing

  define('amdModule', [], (require, export, module) => {
    // 引入部分
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 处理部分
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }
    // 做一些跟引入依赖相关事宜……

    export.increase = increase();
    export.reset = reset();
  })

  define('amdModule', [], require => {
    const otherModule = require('amdModule');
    otherModule.increase();
    otherModule.reset();
  })

追问4:兼容AMD&CJS/如何判断CJS和AMD
UMD的出现

(define('amdModule', [], (require, export, module) => {
    // 引入部分
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 处理部分
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }
    // 做一些跟引入依赖相关事宜……

    export.increase = increase();
    export.reset = reset();
  }))(
    // 目标是一次性区分CommonJSorAMD
    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');
    // jquery相关逻辑

    let dependencyModule1 = require('./dependecyModule1');
    // dependencyModule1相关逻辑
  })

优点:
按需加载,依赖就近

依赖于打包,加载逻辑存在于每个模块中,扩大模块体积

追问5:AMD&CMD区别
依赖就近,按需加载。
AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行

ES6模块化 :走向新时代

新增定义:
引入关键字 — import
导出关键字 — export

模块引入、导出和定义的地方

 // 引入区域
  import dependencyModule1 from './dependencyModule1.js';
  import dependencyModule2 from './dependencyModule2.js';

  // 实现代码逻辑
  let count = 0;
  export const increase = () => ++count;
  export const reset = () => {
    count = 0;
  }

  // 导出区域
  export default {
    increase, reset
  }

模板引入的地方

  <script type="module" src="esModule.js"></script>

node中:

import { increase, reset } from './esModule.mjs';
  increase();
  reset();

  import esModule from './esModule.mjs';
  esModule.increase();
  esModule.reset();

追问6:动态模块
考察 export promise
ES11原生解决方案:

 import('./esModule.js').then(dynamicEsModule => {
    dynamicEsModule.increase();
  })

优点(重要性):通过一种最统一的形态整合了js的模块化

缺点(局限性):本质上还是 运行的依赖分析

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

完全体 :
webpack为核心的工程化 + mvvm框架组件化 + 设计模式

最后

以上就是花痴黑米为你收集整理的js模块化的全部内容,希望文章能够帮你解决js模块化所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部