请选择 进入手机版 | 继续访问电脑版
设为首页收藏本站

亿仁网

 找回密码
 立即注册

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 5155|回复: 0

Javascript 模块化指北

[复制链接]
  • TA的每日心情
    奋斗
    2019-3-14 22:24
  • 签到天数: 160 天

    [LV.7]常住居民III

    1074

    主题

    1139

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    18046
    发表于 2018-10-21 18:14:43 | 显示全部楼层 |阅读模式

    随着 Web 技术的蓬勃发展和依赖的基础设施日益完善,前端领域逐渐从浏览器扩展至服务端(Node.js),桌面端(PC、Android、iOS),乃至于物联网设备(IoT),其中 JavaScript 承载着这些应用程序的核心部分,随着其规模化和复杂度的成倍增长,其软件工程体系也随之建立起来(协同开发、单元测试、需求和缺陷管理等),模块化编程的需求日益迫切。JavaScript 对模块化编程的支持尚未形成规范,难以堪此重任;一时间,江湖侠士挺身而出,一路披荆斩棘,从刀耕火种过渡到面向未来的模块化方案;

    模块化编程就是通过组合一些__相对独立可复用的模块__来进行功能的实现,其最核心的两部分是__定义模块__和__引入模块__;

    • 定义模块时,每个模块内部的执行逻辑是不被外部感知的,只是导出(暴露)出部分方法和数据;

    • 引入模块时,同步 / 异步去加载待引入的代码,执行并获取到其暴露的方法和数据;


    尽管 JavaScript 语言层面并未提供模块化的解决方案,但利用其可__面向对象__的语言特性,外加__设计模式__加持,能够实现一些简单的模块化的架构;经典的一个案例是利用单例模式模式去实现模块化,可以对模块进行较好的封装,只暴露部分信息给需要使用模块的地方;

    1. // Define a module
    2. var moduleA = (function ($, doc) {
    3.   var methodA = function() {};
    4.   var dataA = {};
    5.   return {
    6.     methodA: methodA,
    7.     dataA: dataA
    8.   };
    9. })(jQuery, document);

    10. // Use a module
    11. var result = moduleA.mehodA();
    复制代码

    直观来看,通过立即执行函数(IIFE)来声明依赖以及导出数据,这与当下的模块化方案并无巨大的差异,可本质上却有千差万别,无法满足的一些重要的特性;

    • 定义模块时,声明的依赖不是强制自动引入的,即在定义该模块之前,必须手动引入依赖的模块代码;
    • 定义模块时,其代码就已经完成执行过程,无法实现按需加载;
    • 跨文件使用模块时,需要将模块挂载到全局变量(window)上;

    AMD 和 CMD 最具代表的两个作品分别对应 require.js 和 sea.js;其主要区别在于依赖声明和依赖加载的时机,其中 require.js 默认在声明时执行, sea.js 推崇懒加载和按需使用;另外值得一提的是,CMD 规范的写法和 CommonJS 极为相近,只需稍作修改,就能在 CommonJS 中使用。参考下面的 Case 更有助于理解;

    1. // AMD
    2. define(['./a','./b'], function (moduleA, moduleB) {
    3.   // 依赖前置
    4.   moduleA.mehodA();
    5.   console.log(moduleB.dataB);
    6.   // 导出数据
    7.   return {};
    8. });

    9. // CMD
    10. define(function (requie, exports, module) {
    11.   // 依赖就近
    12.   var moduleA = require('./a');
    13.   moduleA.mehodA();     

    14.   // 按需加载
    15.   if (needModuleB) {
    16.     var moduleB = requie('./b');
    17.     moduleB.methodB();
    18.   }
    19.   // 导出数据
    20.   exports = {};
    21. });
    复制代码

    CommonJS 作为其中最核心的特性之一,适用于服务端下的场景;历年来的考察和时间的洗礼,以及前端工程化对其的充分支持,CommonJS 被广泛运用于 Node.js 和浏览器;

    1. // Core Module
    2. const cp = require('child_process');
    3. // Npm Module
    4. const axios = require('axios');
    5. // Custom Module
    6. const foo = require('./foo');

    7. module.exports = { axios };
    8. exports.foo = foo;
    复制代码
    规范
    • module (Object): 模块本身
    • exports (*): 模块的导出部分,即暴露出来的内容
    • require (Function): 加载模块的函数,获得目标模块的导出值(基础类型为复制,引用类型为浅拷贝),可以加载内置模块、npm 模块和自定义模块
    实现1、模块定义
    默认任意 .node .js .json 文件都是符合规范的模块;
    2、引入模块
    首先从缓存(require.cache)优先读取模块,如果未命中缓存,则进行路径分析,然后按照不同类型的模块处理:
    • 内置模块,直接从内存加载;
    • 外部模块,首先进行文件寻址定位,然后进行编译和执行,最终得到对应的导出值;
    其中在编译的过程中,Node对获取的JavaScript文件内容进行了头尾包装,结果如下:
    1. (function (exports, require, module, __filename, __dirname) {
    2.     var circle = require('./circle.js');
    3.     console.log('The area of a circle of radius 4 is ' + circle.area(4));
    4. });
    复制代码
    特性总结
    • 同步执行模块声明和引入逻辑,分析一些复杂的依赖引用(如循环依赖)时需注意;
    • 缓存机制,性能更优,同时限制了内存占用;
    • Module 模块可供改造的灵活度高,可以实现一些定制需求(如热更新、任意文件类型模块支持);





    造物之前,必先造人。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|亿仁网 ( 粤ICP备16098737  

    GMT+8, 2020-8-12 04:59 , Processed in 0.161231 second(s), 24 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表