zoukankan      html  css  js  c++  java
  • 看完我的笔记不懂也会懂----javascript模块化

    JavaScript模块化

    模块化引子

    1. 什么是模块,什么是模块化

      1. 模块其实就是一个个有特定功能的js文件
      2. 模块化就是将一个复杂的js程序依据一定的规范分割成数个模块(js文件)(数个模块组合在一起就是一整个功能齐全程序)
    2. 为什么要模块化
      数据更安全,只向外暴露接口(黑盒)

    3. 模块化的好处

      1. 当项目代码量大时,便于管理
      2. 有效的减轻全局NameSpace的污染(命名冲突)
      3. 模块功能明确,可以按需部署、加载
      4. 更高的复用性与可维护性
    4. 模块化缺点

      1. 页面引入加载script,这就导致了相比原来需要发的网络请求增加
      2. 模块彼此之间相互依赖,容易导致依赖模糊
      3. 模块彼此依赖导致耦合度增加, 变得更加难以维护

    模块化的历史进化

    1. 全局Function模式

      //全局函数模式: 将不同功能封装成不同的全局函数
      
      /* 
          1.  数据直接暴露 
          2.  容易造成全局的命名空间污染
       */
      let str = '全局函数模式'
      
      function foo(){
          console.log('foo()' + str)
      }
      
      function bar(){
          console.log('bar()' + str)
      }
      
      foo()
      bar()
      
    2. nameSpace模式

      //namespace模式:   使用对象对函数(方法、API)进行简单的封装
      
      /*作用是:   
              保障了数据的安全性(相对全局中不容易被修改)
              降低了全局命名空间的污染程度
      */
      
      let manufacture = {
          str: '飞机制造成功',
          madePlane(){
              console.log(this.str)
          }
      }
      
      manufacture.madePlane()
      
    3. IIFE(匿名函数自调用)

      //IIFE: immeiately-invoked function expression
      
      //匿名函数自调用(闭包)
      
      (function foo(){
          let str = 'closure !!'
          function foo(){
              console.log(`str==>${str}`)
          }
          
          //window.foo = foo    //向外暴露但是又容易造成命名空间的污染
      
          //改进
          window.module = {
              foo: foo
          }
      
          //进一步改进    es6对象属性简写
      })()
      
    4. IIFE模式增强 (这就是JS模块化的原理)

          /* 
          IIFE模式增强:   引入依赖
          这就是现在模块实现的基石
      */
      
      (function(w,$){
          let str = 'IIFE模式增强'
          function foo(){
              console.log(str)
              //用于测试将网页变背景色
          $('body').css(
              {'background-color': 'red'}
              )
          }
          
          //向外暴露
          w.module = foo
      })(window,jQuery)
      
    5. Load Script模式

      <script src="./script1.js"></script>
      <script src="./script2.js"></script>
      <script src="./script3.js"></script>
      

      一个页面中需要引入多个js文件造成的问题:

      1. 请求过多
      2. 依赖模糊
      3. 难以维护

    模块化规范----commonJS

    commonJS是Node.js服务器端使用的JS模块化规范

    commonJS的特性:

    • 其中的每一个js文件都可当做一个模块
    • 在服务器端,模块的加载是在运行时同步加载的
    • 在浏览器端,由于浏览器不认识require()的语法,所以模块需要提前编译打包处理

    commonJS语法之暴露模块

    exports.key = value
    module.exports = value
    /* 
        注意: 
            module.exports的方式暴露模块,只能够暴露最后一次module.exports出的对象,因为后面暴露的对象会覆盖前面的
    */
    

    commonJS向外暴露的都是exports这个对象,为什么这么说呢?

    学过node.js的同学应该都知道exports默认指向的是一个object对象

    module.exports = value相当于改变exports的指向,暴露的仍然是exports

    exports.key就更不用说了,只是单纯的向exports中添加属性然后暴露出去

    commonJS语法之引入模块

    // 当引入的是第三方模块, 直接写模块名
    const $ = require('Jquery')
    
    // 当引入的是自定义模块时, 需要书写完整的路径名
    const HeaderComponent = require('./desktop/HeaderComponent.vue')
    

    node端使用commonJS示例

    分别定义三个module

    // 暴露模块
    module.exports = {
        name: '我是module1',
        printInfo () {
            console.log(this.name)
        }
    }
    
    // 暴露模块
    module.exports = function () {
        console.log('我是模块module2')
    }
    
    // 引入uniq模块用于测试
    const uniq = require('uniq');
    
    // 暴露模块
    exports.sayHello = function () {
        console.log('hello')
    }
    
    exports.sayName = function () {
        console.log('Fitz')
    }
    
    
    exports.testUniq = function () {
        arr = [1,1,3,4,4,5,3,5,6,4,5,4,4,5,8]
        console.log(uniq(arr))
    }
    

    然后在主文件中引入所有需要用的模块

    // 引入所有模块
    const module1 = require('./modules/module1')
    const module2 = require('./modules/module2')
    const module3 = require('./modules/module3')
    
    console.log(module1.name)
    module1.printInfo()
    
    module2()
    
    module3.sayName()
    module3.sayHello()
    module3.testUniq()
    

    browser端使用commonJS示例

    需要用到的编译工具: brorserify

    npm i -D browserify@14.5.0
    

    分别定义三个module

    // 暴露模块
    module.exports = {
        name: '我是module1',
        printInfo () {
            console.log(this.name)
        }
    }
    
    // 暴露模块
    module.exports = function () {
        console.log('我是模块module2')
    }
    
    // 暴露模块
    exports.sayHello = function () {
        console.log('hello')
    }
    
    exports.sayName = function () {
        console.log('Fitz')
    }
    

    然后在主文件中引入所有需要用的模块

    // 引入所有模块
    const module1 = require('./modules/module1')
    const module2 = require('./modules/module2')
    const module3 = require('./modules/module3')
    
    console.log(module1.name)
    module1.printInfo()
    
    module2()
    
    module3.sayName()
    module3.sayHello()
    

    在html页面引用主文件,也就是在浏览器端使用commonJS,需要先使用browserify来进行编译

    browserify 主模块路径 -o 编译输出路径
    

    模块化规范----AMD

    AMD(Asynchronous Module Definition)规范是专门用于浏览器端的模块加载,它的加载过程是异步的,这样就有效避免了阻塞浏览器进行其他工作

    AMD模块化规范依赖的是RequireJS这个工具

    npm i -D require@2.3.6
    

    AMD语法之暴露模块

    // 暴露没有依赖的模块
    define(function () {
        // do something
    
        return 模块
    })
    
    // 暴露有依赖的模块
    define(
        ['依赖1','这里名字任意去但是要与主模块配置相同'],
        function (m1, m2) {
            // do something
    
            return 模块
        }
    )
    

    AMD语法之引入模块

    // 配置模块依赖  这是必须的
    requirejs.config({
        baseUrl: '基础路径',
        paths: {
            '依赖1': '路径',  // 注意路径最后不需要加文件后缀名
            '这里名字任意去但是要与主模块配置相同': '路径'
        }
    })
    
    requirejs(
        ['依赖'],
        function (m1) {
            // do something
        }
    )
    

    对于像Jquery这些支持requireJS的库的模块引入

    requirejs.config({
        baseUrl: '基础路径',
        paths: {
            jquery: '路径'  // key必须是小写的jquery
        }
        /* 
            原因是,jquery在源码最后显示指定了requirejs模块名
            define('jquery', [], function(){return JQuery})
    
            define的第一个参数用于显示指定requireJS模块名
            指定后在config中必须使用这个名字,不能自定义
        */
    })
    
    requirejs(
        ['jquery'],
        function ($) {
            // do something
        }
    )
    

    对于不支持requireJS规范的库,如angular

    requirejs.config({
        baseUrl: '基础路径',
        paths: {
            angular: '库的路径'
        },
        shim: {
            angular: {
                exports: 'angular' // 这里才是依赖的名字
            }
        }
        
    })
    
    requirejs(
        ['angular'],
        function (angular) {
            // do something
        }
    )
    

    requireJS在浏览器端使用

    <script data-main="主模块路径" src="requireJS库所在路径"></script>
    

    模块化规范----CMD

    CMD规范是阿里巴巴公司内部的一种JS模块化规范,现已经出售转让(官网已无法访问)。而且在日常开发中使用极少,在这仅作为介绍使用

    CMD规范同样用于浏览器端,模块的加载也是异步的,在模块使用的时候才会加载执行

    CMD语法之暴露模块

    // 定义没有依赖的模块
    define(function (require, exports, module) {
        exports.xxx = yyy
        module.exports = {}
    })
    
    // 定义有依赖的模块
    define(function (require, exports, module) {
        // 同步引入依赖
        const necessary1 = require('模块')
        // 异步引入依赖
        require.async('模块', function (m) {
            // do something
        })
        exports.xxx = yyy
        module.exports = {}
    })
    

    CMD语法之引入模块

    define(function (require, exports, module) {
        const m1 = require('模块1')
        const m2 = require('模块2')
        // do something
    })
    

    CMD规范使用示例

    定义两个模块

    // 使用CMD规范, 创建没有依赖的模块
    define(function(require, exports, module){
        exports.sayName = function () {
            console.log('Fitz')
        }
    })
    
    // 这个模块依赖于module1.js
    
    // 使用CMD规范, 创建有依赖的模块
    define(function(require, exports, module) {
        const m1 = require('./module1.js')
        m1.sayName()
        function sayHello () {
            console.log('hello')
        }
        module.exports = {
            sayHello
        }
    })
    

    在主模块中引入其他模块

    // IIFE是非必须的,使用只是能确保内部数据的安全
    (function () {
        define(function (require) {
            const m2 = require('./modules/module2.js')
            m2.sayHello()
        })
    })()
    

    浏览器端引入sea.js并使用定义好的模块

    <body>
        <script src="./libs/sea.js"></script>
        <script>
            seajs.use('./index.js') // 使用模块
        </script>
    </body>
    

    模块化规范----ES6规范

    ES6规范是现在模块化中运用最多、最广泛的一种规范,其语法较为简单,需要我们重点掌握

    ES6模块化同样需要对代码进行编译打包处理,运用到的工具有Babel和Browserify

    npm i -D babel-preset-es2015@6.24.1
    npm i -D browserify@14.5.0 babel
    

    需要为babel添加转换语法相关的配置文件.babelrc

    {
        "presets": ["es2015"]
    }
    

    ES6语法之暴露模块

    // 常规暴露之 分别暴露
    export var a = 123
    export const b = {age: 21}
    export function () {
        // do something
    }
    
    // 常规暴露之 统一暴露
    function func1 () {}
    function func2 () {}
    function func3 () {}
    export {
        func1,
        func2,
        func3
    }
    
    
    // 默认暴露: 本质上就是常规暴露, 只不过暴露的是一个变量名叫default的接口对象
    export default a = 123
    export default function () {}
    export default {}
    

    ES6语法之引入模块

    // 引入第三方模块
    import identitify from '包名'
    
    // 引入自定义模块
    import moduleName from '模块路径'
    

    ES6引入模块还有一种通用的方法,能够适用默认暴露、统一暴露、分别暴露

    // 通用方法引入模块
    import * as 自定义模块名 from '模块路径'    // 一定要取别名
    
    console.log(自定义模块名)
    /* 
        是一个对象,包含模块内所有向外暴露的接口
        其中默认暴露default会作为这个对象的一个属性
    
        [module] {
            接口1: 接口内容,
            接口2: 接口内容,
            default: 默认暴露的内容
        }
    */
    

    ES6规范使用示例

    创建三个模块分别运用三种暴露方法

    // 暴露模块之    分别暴露
    
    export function foo () {
        console.log('foo()')
    }
    
    export function bar () {
        console.log('bar()')
    }
    
    export function baz () {
        console.log('baz()')
    }
    
    export const name = 'module1.js'
    
    // 暴露模块之   统一暴露
    
    function func1 () {
        console.log('func1()')
    }
    
    function func2 () {
        console.log('func2()')
    }
    
    function func3 () {
        console.log('func3()')
    }
    
    export {
        func1,
        func2,
        func3
    }
    
    // 暴露模块之   默认暴露
    
    // export default只能使用一次, 因为其本质就是向外暴露一个变量名为default的接口
    export default function () {
        console.log('使用默认暴露')
    }
    
    // 后续可以进行使用常规暴露
    export function foo () {
        console.log('module3 foo()')
    }
    

    在主模块中引入所有的模块

    // 使用import语法引入模块
    import {foo} from './modules/module1'
    import {func1} from './modules/module2'
    
    // 可以为模块内的对象改名
    import {baz as baby} from './modules/module1'
    
    // 引入默认暴露的模块
    import md3 from './modules/module3'
    
    // 如果不同模块中有同名对象,必须为对象改名,否则后面引入的同名对象会覆盖前面引入的
    import {foo as md3Foo} from './modules/module3'
    
    foo()       // 'foo()'
    func1()     // 'func1()'
    baby()      // 'baz()'
    md3()       // '使用默认暴露'
    md3Foo()    // 'module3 foo()'
    

    编译相关语法:

    # 使用Babel将ES6编译转换成ES5的代码
    babel 源文件 -d 目标路径
    
    # 由于转换后会包含CommonJS的语法,所以需要用browserify进一步转换才能被浏览器使用
    browserify 源文件 -o 目标路径
    
  • 相关阅读:
    sql经典语句大全
    经典SQL语句大全
    Bat命令学习
    [Microsoft][ODBC 驱动程序管理器] 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    配置WebSite的IIS时遇到的问题与解决方法
    数据库SQL优化大总结之 百万级数据库优化方案
    数据库索引以及优化
    搭建android开发环境
    SQL2008根据日志恢复
    WebService处理大数据量数据
  • 原文地址:https://www.cnblogs.com/fitzlovecode/p/learn_jsModular.html
Copyright © 2011-2022 走看看