JS模块化

32 阅读6分钟

理解

  • 什么是模块/模块化?
    • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进组合在一起
    • 块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信
      • 全局函数模式:
        • 将不同的功能封装成不同的全局函数
        • global被污染了,很容易命名冲突
           function foo(){}
           function bar(){}
        
      • Namespaces模式:
        • 简单对象封装
        • 减少Global上变量数据、本质是对象,一点不安全
            var MYAPP = {
                function foo(){}
                function bar(){}
            }
            MYAPP.foo();
        
      • IIFE模式
        • 匿名函数自调用(闭包)
        • 函数是JavaScript唯一的LocalScope
            (function(){
                var _private = 'safe now';
                var foo = function(){
                    console.log(_private);
                }
                window.module3 = {
                    foo:foo
                }
            })()
            module3.foo();
        
      • IIFE模式增
        • 引入依赖
        • 这就是现代模块实现的基石
            (function(window,$){
                let msg = 'module4'
                var foo = function(){
                    console.log('foo()',msg);
               }
               window.moudule4 = foo;
               $('body').css(background','red');
            })(window,jQuery)
            module4();
        
  • 为什么要模块化
    • Web sites are turning into Web Apps
    • Code complexity(复杂度) grows as the site gets bigger
    • Highly decoupled(解耦) JS files/modules is wanted
    • Deployment(部署) wants optimized(优化) code in few HTTP calls
  • 模块化好处
    • 避免命名冲突(减少命名空间污染)
    • 更好的分离,按需加载
    • 更高复用性
    • 高可维护性
  • 页面引入加载script
        body
            script(src='zepto.js')
            script(src='fastClick.js')
            script(src='....js')
    
    • 问题
      • 请求过多
      • 依赖模糊
      • 难以维护
    • 解决
    • 这些问题可以通过现代模块化编码和项目构建来解决
    • 首先我们要依赖多个模块,那样就会发送多个请求,导致请求过多
    • 然后就是依赖关系模糊,我们不知道他们的具体依赖关系是什么,也就是说很容易因为依赖关系
    • 以上的现象就导致了这样会很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题

模块化规范

CommonJS

规范

  • 说明
    • wiki.commonjs.org/wiki/Module…
    • 每个文件都可当做一个模块
    • 在服务器端:模块的加载是运行时同步加载的
    • 在浏览器端:模块需要提前编译打包处理
  • 基本语法
    • 暴露模块
      • module.exports = value
      • exports.xxx = value
      • 问题:暴露的模块到底是什么?exports对象
    • 引入模块
      • require(xxx)
        • 第三方模块:xxx为模块名
        • 自定义模块:xxx为模块文件路径

实现

  • 服务器端实现
  • 浏览器端实现
  • 区别Node与Browserify

Node.js模块化教程

  • 下载安装node.js
  • 创建项目结构
    |-modules
        |-module1.js
        |-module2.js
        |-module3.js
    |-app.js
    |-package.json
        {
            "name":"commonJS-node",
            "version":"1.0.0"
        }
  • 下载第三方模块
        npm install uniq --save
    
  • 模块化代码
    • module1.js
      //module.exports = value 暴露一个对象
      module.exports = {
          msg:'module1',
          foo(){
              console.log(this.msg);
          }
      };
    
    • module2.js
      //暴露一个函数   module.exports = function(){}
      module.exports = function(){
          console.log('module2');
      };
    
    • module3.js
      //exports.xxx = value
      exports.foo = function(){
          console.log('foo() module3');
      }
      exports.bar = function(){
          console.log('bar() module3');
      }
      exports.arr = [1,5,6,8,2,3,6,3,11];
    
    • app.js
      //将其他的模块汇集到主模块
      let module1 = require('./modules/module1');
      let module2 = require('./modules/module2');
      let module3 = require('./modules/module3');
      let uniq = require('uniq');
       module1.foo();
       module2();
       module3.foo();
       module3.bar();
      console.log(uniq(module3.arr));
    

Browserify模块化使用教程

    |-js
        |-dist //打包生成文件的目录
        |-src //源码所在的目录
            |-module1.js
            |-module2.js
            |-module3.js
            |-app.js //应用主源文件
    |-index.html
    |-package.json
        {
            "name":"browerify-test",
            "version":"1.0.0"
        }
  • 下载browerify
    • 全局:npm install browerify -g
    • 局部:npm install browserif --save -dev
  • 定义模块代码
    • module1.js
      module.exports = {
          foo(){
              console.log('module1 foo()');
          }
      }
    
    • module2.js
      module.exports = function(){
          console.log('module2()');
      }
    
    • module3.js
      exports.foo = function(){
          console.log('module3 foo()');
      }
      exports.bar = function(){
          console.log('module3 bar()');
      }
    
    • app.js
      let module1 = require('./module1');
      let module2 = require('./module2');
      let module3 = require('./module3');
    
      module1.foo();
      module2();
      module3.foo();
      module3.bar();
    
  • 打包处理js
    • browserify js/src/app.js -o js/dist/bundle.js
  • 页面引入
    <script type="text/javascript" src="./js/dist/bundle.js"></script>

AMD

规范

  • 说明
    • Asynchronous Moudule Definition(异步模块定义)
    • github.com/amdjs/amdjs…
    • 专门用于浏览器端,模块的加载是异步的
  • 基本语法
    • 定义暴露模块
        //没有依赖的模块
        define(function(){
            return 模块;
        })
        //定义有依赖的模块
        define(['module1'],'module2'],function(m1,m2){
            return 模块;
        })
    
    • 引入使用模块
        require(['module1','module2'],function(m1,m2){
            使用m1,m2
        })
    

实现(浏览器端)

NoAMD

    //定义一个没有依赖的模块
    (function(window){
        let name = 'dataService.js';
        function getName(){
            return name;
        }
        window.dataService = {getName}
    })(window)
    //定义一个有依赖的模块
   (function(windw){
       leet msg = 'alerter.js';
       function showMsg(){
           console.log(msg,dataService.getName());
       }
       window.alerter = {showMsg};
   })(window,dataService)
   //页面引入dataService.js alerter.js app.js

Require.js 使用教程

|-js
    |-libs
        |-require.js
    |-modules
        |-alerter.js
        |-dataService.js
    |-main.js
|-index.html
  • 定义require.js的模块代码
  • dadtaService.js
//定义没有依赖的模块
define(function(){
    let name = 'dataService.js';
    function getName(){
        return name;
    }
    //暴露模块
    return {getName};
})
  • alerter.js
//定义有依赖的模块
define(['dataService'],function(dataService){
    let msg = 'alerter.js';
    function showMsg (){
        console.log(msg,dataService.getName());
    }

    return {showMsg}
});
  • main.js
(function(){
    requirejs.config({
        baseUrl:'js/', //基本路径,出发点在根目录下
        paths:{ //配置路径
            dataService:'./modules/dataService',
            alerter:'./modules/alerter',
            jquery:'./libs/jquery-1.10.1',
            angular:'./libs/angular'
        },
        shim:{
            angular:{
                exports:'angular'
            }
        }
    });

    requirejs(['alerter'],function(alerter){
        alerter.showMsg();
    });
})();
  • html
<!--引入require.js 并指定js主文件的入口-->
    <script data-main="js/main.js" src="js/libs/require.js"></script>

CMD

规范

说明

  • Common Module Definition(通用模块定义)
  • github.com/seajs/seajs…
  • 专门用于浏览器端,模块的加载是异步的
  • 模块使用时才会加载执行

基本语法

  • 定义模块
    • 定义没有依赖的模块
    define(function(require,exports,module){
        exports.xxx = value
        module.exports = value
    })
    
    • 定义有依赖的模块
    define(function(require,exports,module){
        //引入依赖模块(同步)
        var module2 = require('./modulel2');
        //引入依赖模块(异步)
        require.async('./module3',function(m3){
        })
        //暴露模块
        exports.xxx = value
    })
    
  • 引入使用模块
define(function(require){
    var m1 = require('./module1');
    var m4 = require('./module4');
    m1.show();
    m4.show();
})

实现(浏览器端)

sea.js简单使用教程

|-js
    |-libs
        |-sea.js
    |-modules
        |-module1.js
        |-module2.js
        |-module3.js
        |-module4.js
        |-main.js
|-index.html
  • 定义sea.js的模块代码
    • module1.js
    //定义没有依赖的模块
    define(function(require,exprots,module){
        let msg = 'module1';
        function foo(){
            return msg;
        }
        module.exports = {foo};
    })
    
    • module2.js
    //定义没有依赖的模块
    define(function(require,exprots,module){
        let msg = 'module2';
        function bar(){
            console.log(msg);
        }
        module.exports = bar;
    })
    
    • module3.js
    //定义没有依赖的模块
    define(function(require,exprots,module){
        let data = 'module3';
        function fun(){
            console.log(daga);
        }
        exports.module3 = {fun};
    })
    
    • module4.js
    define(function(require,exprots,module){
        let msg = 'module4';
        //同步引入
        let module2 = require('./module2');
        module2();
        //异步引入
        require.async('./module3',function(module3){
            module3.module3.fun();
        })
        funtion fun2(){
            console.log(msg);
        }
        exports.fun2 = fun2;
    })
    
    • main.js
    define(function(require){
        let module1 = require('./module1');
        module1.foo();
        let module4 = require('./module4');
        module4.fun2();
    })
    
    • html
    <script type='text/javascript' src = 'js/libs/sea.js'></scritp>
    <script type='text/javascritp'>
        seajs.use('./js/modules/main.js')
    </script>
    

ES6

规范

说明

语法

  • 导出模块
    • exprot
  • 引入模块
    • import

实现(浏览器端)

  • 使用Babe将ES6编译为ES5代码
  • 使用Browerify编译打包js

ES6-babel-Browerify使用教程

  • 定义package.json文件
{
    "name": "es6_babel-browerify",
    "version": "1.0.0"
}
  • 安装balel-cli,babel-preset-es2015和browserify //cli:command line interface
    • npm install babel-cli browserify -g
    • npm install babel-preset-es2015 --save-dev
    • preset 预设(将es6转换成es5的所有插件打包)
  • 定义 .babelrc文件(run control)
{
    "presets":["es2015"]
}
  • 编码
    • js/src/module1.js 分别暴露
    export function foo(){
        console.log('moudle1 foo()');
    }
    export function bar(){
        console.log('bar() moudle1');
    }
    export let arr = [1,2,3,4,5,6];
    
    • js/src/module2.js 统一暴露
    function fun(){
        console.log('fun() moudle2');
    }
    function fun2(){
        console.log('fun2() moudle2');
    }
    export {fun,fun2};
    
    • js/src/module3.js 默认暴露
    //默认暴露,可以暴露 0 任意数据类型,暴露什么数据接受到的就是什么数据
    //export default()=>{
    //    conle.log('我是默认暴露的箭头函数');
    //}
    
    export default {
        msg:'默认暴露';
        foo(){
            console.log(this.msg);
        }
    }
    
    • js/src/main.js
    //引入其他的模块
    //语法:import xxx from '路径';
    import {foo,bar} moudle1 from './module1';
    import {fun,fun2} moudle2 from './module2';    
    import module3 from './module3';
    
  • 编译
    • 使用Babel将ES6编译为ES5代码(但包含CommonJS语法):babel js/src -d js/lib
    • 使用Browserify编译js: browserify js/lib/main.js -o js/lib/bundle.js
  • 页面引入
<script type="text/javascript" src="js/lib/bundle.js"</script>
  • 引入第三方模块(jQuery)
    • 下载jQuery模块
      • npm install jQuery@1 --save
    • 在app.js中引入并使用
    import $ from 'jquery';
    $('bdoy').css('background','green');