Javascript——模块化开发

  • 了解模块化开发
    • 模块化开发是指将一类方法封装在一个 JS文件 中,每一个不同的功能类的 JS文件 就是一个模块,然后通过引入不同功能的文件相互配合,实现目标效果
    • 引入多个文件出现的问题
      1. 文件引入顺序,被依赖文件必须在使用被依赖文件内方法的文件前面
      2. 当多个JS文件时,产生文件依赖关系不清晰问题
      3. JS 文件内部全局变量污染
  • 模块化开发发展史
  • 没有模块化开发阶段
    1. 按照依赖顺序进行进入JS文件,最后引入整合文件
    2. 后期无法进行维护代码,依赖关系不清晰
    3. 全局变量污染,模块文件中的全局变量可以在其他模块或整合文件中直接进行使用,其他文件也无法定义相同的变量名
    // 模块文件a// 模块文件b // 文件内部可能使用了 a 文件内部方法,所以需要在 a 文件后面引入// 功能整合文件main // 文件是整合文件,使用了 a b 文件内的方法,所以 a b 需要引入在 main 文件前面// a.js var num = 100; function af1(){ console.log('a文件内的af1方法') } function af2(){ console.log('a文件内的af2方法') }// b.js function bf1(){ af2(); console.log('使用了a文件内的af2方法') } function bf2(){ console.log('a文件内的bf2方法') }// main.js console.log(num) // num 是一个全局变量,所以在main文件内可以使用 af1(); bf1();

  • IIFE伪模块化开发阶段(非官方)
    原理:在JS文件中书写一个自执行函数,然后将方法写在自执行函数体内,但方法是自执行函数内的局部变量,外界无法使用,所以需要将外界需要的方法挂载在全局上
    解决的问题:
    1. 通过自执行函数的参数可知道依赖了哪些模块
    2. 在自执行函数中定义的变量属于局部变量,没有全局变量污染问题
    产生的问题:
    1. 文件引入顺序不能动
    2. 虽然知道依赖了哪些模块,但是不知道模块是哪个文件的
    // 模块文件a// 模块文件b // 文件内部可能使用了 a 文件内部方法,所以需要在 a 文件后面引入// main 文件是整合文件,使用了 a b 文件内的方法,所以 a b 需要引入在 main 文件前面// 模块文件a.js (function(){ // 自执行函数执行结束后,变量自动销毁 var num = 100; function af1(){ console.log('a文件内的af1方法') } function af2(){ console.log('a文件内的af2方法') }// 将变量和方法挂载在全局上,其他文件可进行使用 window.madA={ af1, af2, num } })()// 模块文件b.js (function(madA){ // 这里的参数接收的是自执行函数参过来的参数 function bf1(){ madA.af2(); console.log('使用了a文件内的af2方法') } function bf2(){ console.log('a文件内的bf2方法') } // 将其他文件需要用到的方法挂在在全局上 window.madB={ bf1, bf2 } // 传入其他模块文件挂载在全局上的对象名,在函数内进行使用内部变量和方法 })(madA)// 模块文件main.js (function(madA,madA){ console.log(madA.num) // num 是其他文件挂载在全局上的变量,所以在main文件内可以使用 madA.af1(); madB.bf1(); })(madA,madB)

  • commonJS模块化开发标准
    CommmonJS用于node端模块化开发(只能用于后端)
    模块化开发规范:
    1. 通过module.exports或exports来暴露模块
    2. 通过require来加载模块
    CommmonJS的特点:
    1. 同步加载,一般用于node端
    2. 一次加载,多次使用。对同一个模块而言,只会运行一次,多次使用时会从缓存中获取
    3. 模块加载的顺序,按照它出现的顺序
    4. 代码都运行在模块作用域,不会造成全局污染
    //a.js文件 var math = { add:function(a,b){ return a+b; } } // module是代表当前模块,通常通过module.exports来暴露模块 module.exports = math//b.js // 引入依赖模块 var math = require('./a.js')//模块依赖,通过require导入模块 console.log(math.add(1,2))

  • AMD模块化标准(非官方)阶段——前置依赖
    由社区发起,没有关键字,并且需要引入第三方文件require.js使用
    AMD模块化规范:
    1. 通过define()方法定义模块
    2. 通过require()方法加载模块
    3. 通过define(['依赖文件地址'···],function(接收依赖文件返回值){}),在模块文件中导入另一个模块文件
    解决的问题:
    1. 文件依赖清晰
    2. 解决全局变量污染问题
    【Javascript——模块化开发】产生的问题:
    前置依赖:文件会在打开页面时加载,首屏加载时间长,后期流畅
    // 引入第三方文件// 定义一个模块文件 define(function() { var helloInLang = { en: 'Hello world!', es: '?Hola mundo!', ru: 'Привет мир!' }; // 暴露内容 return { sayHello: function (lang) { return helloInLang[lang]; } }; }); // 引入一个模块 define(['./lib/greeting'], function(greeting) { // 这个形参接收的就是模块返回的方法或内容 var phrase = greeting.sayHello('en'); }); // 在整合文件中使用其他模块文件中的变量和方法 require(['./modules/module1'], function (module1) { module1.sayHello('hello') })

  • CMD通用模块化标准(非官方)——即时依赖
    在社区中,由淘宝“玉伯”开发的CMD模块化标准,依赖第三方文件使用
    定义模块:define(function(require, exports, module){})
    1. require()参数用于导入其他文件
    2. exports参数用于将文件导出内容
    解决的问题:解决前置依赖问题,加载时按需加载,也提供了依赖前置的接口
    存在的问题:即时依赖,首屏加载快,操作不够流畅
    // 引入第三方文件// 定义一个模块文件 define(function(require, exports, module) { // 定义方法 function fn(){ console.log("CMD") }// 在模块文件中导入其他模块 var mod_A = require('依赖文件地址'); // 导出内容 module.exports = {fn} })// 整合文件 define(function(require, exports, module) { seajs.use(['依赖文件地址'], function(modA){调用方法等}) });

  • ES6(ES2015)模块化标准
    ES6 语法中自带一个模块化标准,提供了关键字,需要在 标签添加 type=“module”属性,并且只有在服务器上打开才有效
    导出语法:
    1. export default 导出内容
    2. export var 变量名=值
    导入语法:
    1. import 变量 from '模块文件地址'
    2. import {接收变量} from ‘模块文件地址’
    3. 解决前置依赖问题语法:import('模块文件地址').then(function(形参){})
    解决的问题:不需要依赖第三方文件,每个文件都可以是模块文件,或整合文件
    存在的问题:需要兼容处理
    //定义一个模块 export default { // 以对象或者数组到处 function fn (lang) { return helloInLang[lang]; } }; // 引入一个模块 import fn from "模块文件";

    推荐阅读