模块化

作者: jrg_memo | 来源:发表于2017-03-17 22:42 被阅读24次

    *解析

    模块是可以实现特定功能的一组代码。

    由于JS不能handle超大规模代码,所以要借鉴其它语言处理这种问题的方式:
    把逻辑相关的代码组织到同一个区域,区域内部相对独立和外界互不干扰;
    外部使用时直接引用对应的区域即可。

    模拟类似的功能,用来隔离、组织复杂的JavaScript代码,我们称为模块化。

    *作用

    1 解决命名冲突
    2 解决互相依赖
    3 提高代码的可读性
    4 提高代码的复用性

    *规范

    **CommonJS

    解析:
    服务器端使用的模块规范 例如 Node.js

    使用:
    1 定义模块 一个文件 = 一个模块 = 一个单独的作用域
    2 模块输出: 唯一出口module.exports对象,存放输出内容
    3 加载模块: 使用require方法读取文件并执行,返回ta内部的module.exports对象

    举例:                      
      
    Model-1.js                    1 定义模块
        var name = 'jrg';           
        function a(){...}             name是模块的内部变量,只有文件内的函数可以使用
        function b(){...}                            
        module.exports={          2 模块输出 (导出接口)
            a:a,
            b:b
        }
    
    Model-2.js                        *新模块 = 新文件
        function c(){...}
        module.exports = c
    
    
    main.js                       3 加载模块                              
        var xx = require('./Model-1');
        var dd = require('./Model-2');  使用哪个模块再引入                        
        xx.a()                      
        dd.c()                          然后就可以调用模块提供的方法
    

    **问题

    Q CommonJS 规范在服务器端这么方便,那么浏览器端可以用吗?
    A NO!
    R 服务器端的文件在本地 => 同步加载;浏览器端的文件在远程服务器 =>异步加载
    ····所以CommonJS模块在浏览器环境中无法正常加载。
    ····浏览器端需要另外的规范来封装模块 => AMD & CMD

    **AMD

    解析:
    浏览器端使用的模块规范之一 Asynchronous Module Definition异步模块定义

    define函数,定义模块-------------------------------------------------- 
    
    语法: 
        define(id?, dependencies?, factory)
    
    参数:
        id:模块名;如果没有参数,默认为文件名。
        dependencies:一个数组;当前模块 [依赖的模块名称集合]。
        factory:工厂函数;模块初始化要执行的函数(只执行一次) or 对象(模块的输出值)。
    
    
     require函数,加载模块-------------------------------------------------
    
     语法:  require([dependencies], function(){});
    
     参数:  [dependencies]是一个数组,表示所依赖的模块;
             function(){} 是一个回调函数,当前面指定的模块都加载成功后,以参数形式传入该函数,
                          然后函数内部就可以使用这些模块了。
    
     
     举例----------------------------------------------------------------------------
    
     myModule1.js    定义模块 
         define(['dependency'], function(){      
             function a(){...} // 使用面向对象方式封装好的组件
             return a,        //  留出接口
         });
    
     main.js      加载模块  
         require(['myModule1','myModule2'], function (myModule1,myModule2){    
              myModule1.a();      // 调用组件
         });
    

    **CMD

    解析:
    国内发展出来的模块规范Common Module Definition通用模块定义

    define函数,定义模块---------------------------------------------------------
    
    语法:               
        define(function(require, exports, module){ });
    
    参数:    
       require 是一个方法,获取其他模块的接口
       exports 是一个对象,向外提供模块接口
       module 是一个对象,存储了一些属性和方法
    
    举例--------------------------------------------------------------------------
    
     ModuleA.js     定义模块 
       define(function(require, exports, module) {    function的三个参数固定不变 
         var a = require('a.js')                       函数内部,用到谁就引入谁
         a();                                          写法类似commonJS
         ModuleA.exprots = xx;
       });
    
    main.js       加载模块  
       seajs.use(['ModuleA.js'], function(ModuleA){ ... });
    

    模块化工具:
    ▶由于不是JavaScript原生支持,
    使用AMD需要用到对应的库函数RequireJS
    使用CMD需要用到对应的库函数SeaJS
    其实AMDCMD分别 是RequireJSSeaJS 在推广过程中对模块定义的规范化产出;
    同样倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。

    AMDCMD的区别?
    ** 1** 关于依赖的模块,加载都是异步,执行处理不同
    AMD:推崇依赖前置,提前执行(依赖先执行)
    CMD:推崇依赖就近,延迟执行(运行到需加载,根据顺序执行)
    ** 2** 关于API
    AMD:API根据使用范围有区别,但使用同一个api接口
    CMD:每个API的职责单一

    **requireJS

    requireJS主要解决两个问题:
    1 实现js文件的异步加载,避免网页失去响应;
    2 管理模块之间的依赖性,便于代码的编写和维护。

    如果
       不使用`requireJS`就要单独加载每个js文件,像这样:
       <script src="1.js"></script>
       <script src="2.js"></script>
       <script src="3.js"></script>
     缺点
       1 加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;
       2 由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块必须放到最后,
         当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
    

    **总结

    至此,已经了解模块规范,可以写出符合规范的 模块文件了!
    以AMD为例,梳理一下使用过程:

    1 下载require.js文件,放入根目录
    2 页面引入requireJS库<script src="http://require.js"></script> => 主文件
    3 页面引入主文件 <script src="http://main.js"></script> => 加载模块
    4 主文件 => 已放入根目录的模块文件
    Tip:
    主文件=>加载模块=>require函数=>语法require([dependencies], function(){})
    模块文件=>定义模块=>define函数=>语法define([dependencies], factory)

    **r.js

    这是一个打包工具

    作用
    把所有的模块和框架js文件压缩到同一个js文件里,
    Before: 引入requireJS库=>引入主文件=>查找模块
    After : 引入requireJS库=>查找模块

    使用
    1 下载r.js文件,放入根目录
    2 配置压缩文件build.js
    3 打开node,进入目标DEMO-AMD
    4 执行命令行node r.js -o build.js,会自动生成压缩文件
    5 将页面上的主文件地址main.js替换为压缩后的文件index.merge.min.js

    *应用

    Demo

    ▼DEMO-AMD   
       ▶index.html   
       ▶CSS              
       ▼JS
         ▼lib            框架&库目录
           ▷require.js
           ▷r.js
           ▷jquery.js
            ......
         ▼app            组件目录
           ▷gotop.js
            ......
         ▶main.js       主文件
         ▶build.js      压缩配置文件
    
         ▼dist          输出目录
           ▷index.merge.min.js   
    
    
       ■ index.html
        页面加载 <script src="http://js/lib/require.js" data-main="js/main.js"></script>
        □ src       先加载库 => 执行require.js => 解析后指向主文件  
        □ data-main 自定义属性 => 执行main.js  =>  主文件里是所有模块的入口
    
    
       ■ main.js                主文件 =>
         requirejs.config({              □ 配置路径,方便查找
             baseUrl: "./js/app",             默认文件夹(加载时写直接写里面的模块名)
             paths: {                         特殊文件/夹(相对于baseUrl)
                 'jquery': '../lib/jquery'        指向模块  (加载时写模块名)
                 'lib':'../lib'                   指向文件夹(加载时写lib下模块名)
             }                                  
         });
         requirejs(["GoTop","lib/xx",...],
         function(GoTop,$,...) {         □ 加载([依赖模块],工厂函数)
             new GoTop();                          模块加载完毕,作为参数传入工厂函数
             ......                                 执行工厂函数
         });
    
        ● 这里的baseUrl是main.js相对于index.html=> 从index出发找到app
        ● requirejs(["xxx"]) => 如果没有工厂函数,直接加载并执行文件
    
    
       ■ build.js             压缩配置文件 =>  
         ({                                □ 配置路径   
             baseUrl: ".",                       两个baseUrl指向要保持一致(都指向app)  
             paths: {
                 'jquery': './lib/jquery',
                 'GoTop':'./com/GoTop',
                 ......
             },
             name: "main",                       配置入口
             out: "./dist/index.merge.min.js"    配置出口(自定义)
         });
    
        ● 这里的baseUrl是build.js相对于自身的,=> 从build 出发找到 app
    

    参考 详解JavaScript模块化开发

    但是!程序员永远有更便捷的方法 => webpack

    相关文章

      网友评论

          本文标题:模块化

          本文链接:https://www.haomeiwen.com/subject/nahynttx.html