JS模块化学习笔记

JS模块化学习笔记

JS模块化

1 模块化进化史教程

全局function模式
  • module1.js

    //数据
    let data = 'atguigu.com'
    
    //操作数据的函数
    function foo() {
      console.log(`foo() ${data}`)
    }
    function bar() {
      console.log(`bar() ${data}`)
    }
    
  • module2.js

    let data2 = 'other data'
    
    function foo() {  //与另一个模块中的函数冲突了
      console.log(`foo() ${data2}`)
    }
    
  • test1.html

    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript">
    
      let data = "修改后的数据"
      foo()
      bar()
    </script>
    
  • 说明:

    • 全局函数模式: 将不同的功能封装成不同的全局函数
    • 问题: Global被污染了, 很容易引起命名冲突
namespace模式
  • module1.js

    let myModule = {
      data: 'atguigu.com',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    
  • module2.js

    let myModule2 = {
      data: 'atguigu.com2222',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    
  • test2.html

    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module22.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
    
      myModule2.foo()
      myModule2.bar()
    
      myModule.data = 'other data' //能直接修改模块内部的数据
      myModule.foo()
    
    </script>
    
  • 说明

    • namespace模式: 简单对象封装
    • 作用: 减少了全局变量
    • 问题: 不安全
IIFE模式
  • module3.js

    (function (window) {
      //数据
      let data = 'atguigu.com'
    
      //操作数据的函数
      function foo() { //用于暴露有函数
        console.log(`foo() ${data}`)
      }
    
      function bar() {//用于暴露有函数
        console.log(`bar() ${data}`)
        otherFun() //内部调用
      }
    
      function otherFun() { //内部私有的函数
        console.log('otherFun()')
      }
    
      //暴露行为
      window.myModule = {foo, bar}
    })(window)
    
  • test3.html

    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript">
      myModule.foo()
      myModule.bar()
      //myModule.otherFun()  //myModule.otherFun is not a function
      console.log(myModule.data) //undefined 不能访问模块内部数据
      myModule.data = 'xxxx' //不是修改的模块内部的data
      myModule.foo() //没有改变
    
    </script>
    
  • 说明:

    • IIFE模式: 匿名函数自调用(闭包)
    • IIFE : immediately-invoked function expression(立即调用函数表达式)
    • 作用: 数据是私有的, 外部只能通过暴露的方法操作
    • 问题: 如果当前这个模块依赖另一个模块怎么办?
IIFE模式增强
  • 引入jquery到项目中

  • module4.js

    (function (window, $) {
      //数据
      let data = 'atguigu.com'
    
      //操作数据的函数
      function foo() { //用于暴露有函数
        console.log(`foo() ${data}`)
        $('body').css('background', 'red')
      }
    
      function bar() {//用于暴露有函数
        console.log(`bar() ${data}`)
        otherFun() //内部调用
      }
    
      function otherFun() { //内部私有的函数
        console.log('otherFun()')
      }
    
      //暴露行为
      window.myModule = {foo, bar}
    })(window, jQuery)
    
  • test4.html

    <script type="text/javascript" src="jquery-1.10.1.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    <script type="text/javascript">
      myModule.foo()
    </script>
    
  • 说明

    • IIFE模式增强 : 引入依赖
    • 这就是现代模块实现的基石
页面加载多个js的问题
  • 页面:

    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    
  • 说明

    • 一个页面需要引入多个js文件
    • 问题:
      • 请求过多
      • 依赖模糊
      • 难以维护
    • 这些问题可以通过现代模块化编码和项目构建来解决

2 CommonJS_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 = {
      foo() {
        console.log('moudle1 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

    /**
      1. 定义暴露模块:
        module.exports = value;
        exports.xxx = value;
      2. 引入模块:
        var module = require(模块名或模块路径);
     */
    "use strict";
    //引用模块
    let module1 = require('./modules/module1')
    let module2 = require('./modules/module2')
    let module3 = require('./modules/module3')
    
    let uniq = require('uniq')
    let fs = require('fs')
    
    //使用模块
    module1.foo()
    module2()
    module3.foo()
    module3.bar()
    
    console.log(uniq([1, 3, 1, 4, 3]))
    
    fs.readFile('app.js', function (error, data) {
      console.log(data.toString())
    })
    
通过node运行app.js
  • 命令: node app.js
  • 工具: 右键–>运行

3 CommonJS_Browserify模块化使用教程

把commonJS应用到浏览器端 index.html 中

创建项目结构
|-js
|-dist //打包生成文件的目录
|-src //源码所在的目录
  |-module1.js
  |-module2.js
  |-module3.js
  |-app.js //应用主源文件
|-index.html
|-package.json
{
  "name": "browserify-test",
  "version": "1.0.0"
}
下载browserify
  •   * 全局: npm install browserify -g
      * 局部: npm install browserify --save-dev
    
定义模块代码
  • module1.js

    module.exports = {
      foo() {
        console.log('moudle1 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 (应用的主js)

    //引用模块
    let module1 = require('./module1')
    let module2 = require('./module2')
    let module3 = require('./module3')
    
    let uniq = require('uniq')
    
    //使用模块
    module1.foo()
    module2()
    module3.foo()
    module3.bar()
    
    console.log(uniq([1, 3, 1, 4, 3]))
    
  • 打包处理js:

    browserify js/src/app.js -o js/dist/bundle.js  // -o(output) 输出左边的是目标文件,右边是打包好的输出文件
    
  • 页面使用引入:

    <script type="text/javascript" src="js/dist/bundle.js"></script> 
    

4 AMD_require.js使用教程

**AMD(asynchronous module definition)**异步模块定义

​ 专门用于浏览器端,模块的加载是异步

下载require.js, 并引入
  • 官网: http://www.requirejs.cn/
  • github : https://github.com/requirejs/requirejs
  • 将require.js导入项目: js/libs/require.js
创建项目结构
|-js
|-libs
  |-require.js
|-modules
  |-alerter.js
  |-dataService.js
|-main.js
|-index.html
定义require.js的模块代码
  • dataService.js

    define(function () {
      let msg = 'atguigu.com'
    
      function getMsg() {
        return msg.toUpperCase()
      }
    
      return {getMsg}
    })
    
  • alerter.js

    define(['dataService', 'jquery'], function (dataService, $) {
      let name = 'Tom2'
    
      function showMsg() {
        $('body').css('background', 'gray')
        alert(dataService.getMsg() + ', ' + name)
      }
    
      return {showMsg}
    })
    
应用主(入口)js: main.js
(function () {
//配置
require.config({
  //基本路径 出发点在根目录下
  baseUrl: "js/",
  //模块标识名与模块路径映射  不加.js,默认会加一个.js后缀
  paths: {
    "alerter": "modules/alerter",
    "dataService": "modules/dataService",
  }
})
//引入使用模块
require( ['alerter'], function(alerter) {
  alerter.showMsg()
})
})()
页面使用模块:
<script data-main="js/main" src="js/libs/require.js"></script>

使用第三方基于require.js的框架(jquery)
  • jquery的库文件导入到项目:

    • js/libs/jquery-1.10.1.js
  • 在main.js中配置jquery路径

    paths: {
              'jquery': 'libs/jquery-1.10.1'
          }
    
  • 在alerter.js中使用jquery // jquery在amd规范中会自己创建一个jquery模块名字,所以这里要用小写

    define(['dataService', 'jquery'], function (dataService, $) {
        var name = 'xfzhang'
        function showMsg() {
            $('body').css({background : 'red'})
            alert(name + ' '+dataService.getMsg())
        }
        return {showMsg}
    })
    

使用第三方不基于require.js的框架(angular/angular-messages)
  • 将angular.js和angular-messages.js导入项目

    • js/libs/angular.js
    • js/libs/angular-messages.js
  • 在main.js中配置

    (function () {
      require.config({
        //基本路径
        baseUrl: "js/",
        //模块标识名与模块路径映射
        paths: {
          //第三方库
          'jquery' : 'libs/jquery-1.10.1',
          'angular' : 'libs/angular',
          'angular-messages' : 'libs/angular-messages',
          //自定义模块
          "alerter": "modules/alerter",
          "dataService": "modules/dataService"
        },
        /*
         配置不兼容AMD的模块
         exports : 指定导出的模块名
         deps  : 指定所有依赖的模块的数组
         */
        shim: {
          'angular' : {
            exports : 'angular'
          },
          'angular-messages' : {
            exports : 'angular-messages',
            deps : ['angular']
          }
        }
      })
      //引入使用模块
      require( ['alerter', 'angular', 'angular-messages'], function(alerter, angular) {
        alerter.showMsg()
        angular.module('myApp', ['ngMessages'])
        angular.bootstrap(document,["myApp"])
      })
    })()
    
  • 页面:

    <form name="myForm">
      用户名: <input type="text" name="username" ng-model="username" ng-required="true">
      <div style="color: red;" ng-show="myForm.username.$dirty&&myForm.username.$invalid">用户名是必须的</div>
    </form>
    

5 CMD_SeaJS简单使用教程

下载sea.js, 并引入
  • 官网: http://seajs.org/
  • github : https://github.com/seajs/seajs
  • 将sea.js导入项目: js/libs/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, exports, module) {
      //内部变量数据
      var data = 'atguigu.com'
      //内部函数
      function show() {
        console.log('module1 show() ' + data)
      }
    
      //向外暴露
      exports.show = show
    })
    
  • module2.js

    define(function (require, exports, module) {
      module.exports = {
        msg: 'I Will Back'
      }
    })
    
  • module3.js

    define(function (require, exports, module) {
      const API_KEY = 'abc123'
      exports.API_KEY = API_KEY
    })
    
  • module4.js

    define(function (require, exports, module) {
      //引入依赖模块(同步)
      var module2 = require('./module2')
    
      function show() {
        console.log('module4 show() ' + module2.msg)
      }
    
      exports.show = show
      //引入依赖模块(异步)
      require.async('./module3', function (m3) {
        console.log('异步引入依赖模块3  ' + m3.API_KEY)
      })
    })
    
  • main.js : 主(入口)模块

    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
    
index.html:
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
  define()
  exports
  module.exports
3. 如何依赖模块:
  require()
4. 如何使用模块:
  seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>

6 ES6-Babel-Browserify使用教程

转换ES6/ES5语法

定义package.json文件
{
"name" : "es6-babel-browserify",
"version" : "1.0.0"
}
安装babel-cli, babel-preset-es2015和browserify
  • npm install babel-cli browserify -g
    • npm install babel-preset-es2015 --save-dev
定义.babelrc文件
{
 "presets": ["es2015"]
  }
编码
  • js/src/module1.js

    export function foo() {
      console.log('module1 foo()');
    }
    export let bar = function () {
      console.log('module1 bar()');
    }
    export const DATA_ARR = [1, 3, 5, 1]
    
  • js/src/module2.js

    let data = 'module2 data'
    
    function fun1() {
      console.log('module2 fun1() ' + data);
    }
    
    function fun2() {
      console.log('module2 fun2() ' + data);
    }
    
    export {fun1, fun2}
    
  • js/src/module3.js

    export default {
      name: 'Tom',
      setName: function (name) {
        this.name = name
      }
    }
    
  • js/src/app.js

    import {foo, bar} from './module1'
    import {DATA_ARR} from './module1'
    import {fun1, fun2} from './module2'
    import person from './module3'
    
    import $ from 'jquery'
    
    $('body').css('background', 'red')
    
    foo()
    bar()
    console.log(DATA_ARR);
    fun1()
    fun2()
    
    person.setName('JACK')
    console.log(person.name);
    
编译
  • 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel js/src -d js/lib
  • 使用Browserify编译js : browserify js/lib/app.js -o js/lib/bundle.js
页面中引入测试
<script type="text/javascript" src="js/lib/bundle.js"></script>
引入第三方模块(jQuery)

​ 1). 下载jQuery模块:

​ npm install jquery@1 --save
​ 2). 在app.js中引入并使用

import $ from 'jquery'
$('body').css('background', 'red')

7 总结

  • 模块化的理解
  • 什么是模块?
    • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
    • 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
  • 一个模块的组成
    • 数据—>内部的属性
    • 操作数据的行为—>内部的函数
  • 模块化
    • 编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
  • 模块化的进化过程
    • 全局function模式 :
      • 编码: 全局变量/函数
      • 问题: 污染全局命名空间, 容易引起命名冲突/数据不安全
    • namespace模式 :
      • 编码: 将数据/行为封装到对象中
      • 解决: 命名冲突(减少了全局变量)
      • 问题: 数据不安全(外部可以直接修改模块内部的数据)
    • IIFE模式/增强
      • IIFE : 立即调用函数表达式—>匿名函数自调用

      • 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口

      • 引入依赖: 通过函数形参来引入依赖模块

        (function(window, module2){
          var data = 'atguigu.com'
          function foo() {
             module2.xxx()
             console.log('foo()'+data)
          }
          function bar() {
             console.log('bar()'+data)
          }
          
          window.module = {foo}
        })(window, module2)
        
  • 模块化规范
    • CommonJS
      • Node.js : 服务器端

      • Browserify : 浏览器端 也称为js的打包工具

      • 基本语法:

        • 定义暴露模块 : exports

          exports.xxx = value
          module.exports = value
          

          引入模块 : require

          var module = require('模块名/模块相对路径')
          
      • 引入模块发生在什么时候?

        • Node : 运行时, 动态同步引入
        • Browserify : 在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了),
          运行的是打包生成的js, 运行时不存在需要再从远程引入依赖模块
    • AMD : 浏览器端
      • require.js

      • 基本语法

        • 定义暴露模块: define([依赖模块名], function(){return 模块对象})

        • 引入模块: require([‘模块1’, ‘模块2’, ‘模块3’], function(m1, m2){//使用模块对象})

        • 配置:

          require.config({
            //基本路径
            baseUrl : 'js/',
            //标识名称与路径的映射
            paths : {
              '模块1' : 'modules/模块1',
              '模块2' : 'modules/模块2',
              'angular' : 'libs/angular',
              'angular-messages' : 'libs/angular-messages'
            },
            //非AMD的模块
            shim : {
              'angular' : {
                  exports : 'angular'
              },
              'angular-messages' : {
                  exports : 'angular-messages',
                  deps : ['angular']
              }
            }
          })
          
    • CMD : 浏览器端
      • sea.js

      • 基本语法

        • 定义暴露模块:

          define(function(require, module, exports){
            通过require引入依赖模块
            通过module/exports来暴露模块
            exports.xxx = value
          })
          
        • 使用模块seajs.use([‘模块1’, ‘模块2’])

    • ES6
      • ES6内置了模块化的实现

      • 基本语法

        • 定义暴露模块 : export

          • 暴露一个对象:

            export default 对象
            
          • 暴露多个:

            export var xxx = value1
            export let yyy = value2
            
            var xxx = value1
            let yyy = value2
            export {xxx, yyy}
            
        • 引入使用模块 : import

          • default模块:

            import xxx  from '模块路径/模块名'
            
          • 其它模块

            import {xxx, yyy} from '模块路径/模块名'
            import * as module1 from '模块路径/模块名'
            
      • 问题: 所有浏览器还不能直接识别ES6模块化的语法

      • 解决:

        • 使用Babel将ES6—>ES5(使用了CommonJS) ----浏览器还不能直接支行
        • 使用Browserify—>打包处理----浏览器可以运行

tion(require, module, exports){
通过require引入依赖模块
通过module/exports来暴露模块
exports.xxx = value
})
```

  * 使用模块seajs.use(['模块1', '模块2'])
  • ES6
    • ES6内置了模块化的实现

    • 基本语法

      • 定义暴露模块 : export

        • 暴露一个对象:

          export default 对象
          
        • 暴露多个:

          export var xxx = value1
          export let yyy = value2
          
          var xxx = value1
          let yyy = value2
          export {xxx, yyy}
          
      • 引入使用模块 : import

        • default模块:

          import xxx  from '模块路径/模块名'
          
        • 其它模块

          import {xxx, yyy} from '模块路径/模块名'
          import * as module1 from '模块路径/模块名'
          
    • 问题: 所有浏览器还不能直接识别ES6模块化的语法

    • 解决:

      • 使用Babel将ES6—>ES5(使用了CommonJS) ----浏览器还不能直接支行
      • 使用Browserify—>打包处理----浏览器可以运行
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值