0
点赞
收藏
分享

微信扫一扫

笔记 - JS模块化

何以至千里 2022-01-15 阅读 27

笔记 - JS模块化

历史背景

JS本身简单的页面设计:页面动画 + 表单提交
并无模块化 or 命名空间概念
随着前端页面日益复杂,之前的方式不能满足需求
所以,JS的模块化需求日益增加

发展历程

幼年期:无模块化

  1. 页面需要不同功能的 js - 动画、表单、格式化
  2. 多种js文件被分在不同的文件中
  3. 不同的文件由被同一个模板引用
	<script src="jquery.js"></script>
	<script src="main.js"></script>
	<script src="dep1.js"></script>

优势:文件分离可以起到模块复用和较好维护的作用。
劣势:会污染全局作用域 => 不利于大型项目的开发和多人团队的协作。

成长型:模块化的雏形 - IIFE (语法侧的优化)

主要是增加了对作用域的把控
例子:

	// 普通做法 - 污染全局作用域
	let count = 0;
	const increase = () => ++count;
	const reset = () => { count = 0; }
	
	increase();
	reset();

	// 利用 IIFE
	const iifeModule = (() => {
		let count = 0;

		return {
			increase: () => ++count,
			reset: () => {
				count = 0
			}
		}
	})();

	iifeModule.increase();
	iifeModule.reset();

	// 对上面 IIFE 进行优化 - 注入其他依赖
	const iifeModule = ((dependencyModule1, dependencyModule2) => {
		let count = 0;

		return {
			increase: () => ++count,
			reset: () => {
				count = 0
			}
		}
	})(dependencyModule1, dependencyModule2);

	iifeModule.increase();
	iifeModule.reset();

	// 模仿早期jquery的依赖处理以及模块加载
	const iifeModule = ((dependencyModule1, dependencyModule2) => {
	  	let count = 0;
	  	const increase = () => ++count;
	  	const reset = () => {
	    	count = 0;
	  	}
	
		// 其实是使用了揭示模式
	  	return {
	    	increase, reset
	  	}
	})(dependencyModule1, dependencyModule2);
	
	iifeModule.increase();
	iifeModule.reset();

成熟期:CJS - CommonJs

特征:

  • 通过 module + exports 去对外暴露接口
  • 通过 require 来调用其他模块
	// main.js 文件
	const dependencyModule1 = require('./dependencyModule1');
	const dependencyModule2 = require('./dependencyModule2');

	// 处理方式
	let count = 0;
	const increase = () => ++count;
	const reset = () => {
		count = 0;
	}
	// 做一些跟引入依赖相关事宜...

	// 暴露接口
	// 1
	exports.increase = increase;
	exports.reset = reset;
	// 2
	module.exports = {
		increase,reset
	}
	const { increase, reset } = require('./main.js');

	increase();
	reset();
	(function(thisValue, exports, require, module) {
		const dependencyModule1 = require('./dependencyModule1');
		const dependencyModule2 = require('./dependencyModule2');

		// 业务逻辑...
	}).call(thisValue, exports, require, module)
  • 优点:CommonJs率先在服务端实现了,从框架层面解决了依赖、全局变量污染的问题
  • 缺点:主要针对了服务端的解决方案,对异步拉取依赖的处理整合不那么友好

AMD规范

新增定义方式:

	// 通过define来定义一个模块,然后require进行加载
	/*
	* define
	* params: 模块名,依赖模块,工厂方法
	**/
	define(id, [depends], callback);
	require([module], callback);

模块定义方式:

	define('amdModule', [dependencyModule1, dependencyModule2], (dependencyModule1, 
	dependencyModule2) => {
		// 业务逻辑
		// 处理部分
		let count = 0;
		const increase = () => ++count;
		const reset = () => {
			count = 0;
		}

		return {
			increase, reset
		}
	})

引入模块:

	require(['amdModule'], amdModule => {
		amdModule.increase();
	})
	define('amdModule', [], require => {
		// 引入部分
	    const dependencyModule1 = require(./dependencyModule1);
	    const dependencyModule2 = require(./dependencyModule2);
	
	    // 处理部分
	    let count = 0;
	    const increase = () => ++count;
	    const reset = () => {
	      count = 0;
	    }
	    // 做一些跟引入依赖相关事宜……
	
	    return {
	      increase, reset
	    }
	})
	define('amdModule', [], (require, export, module) => {
    // 引入部分
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // 处理部分
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }
    // 做一些跟引入依赖相关事宜……

    export.increase = increase();
    export.reset = reset();
  })

  define('amdModule', [], require => {
    const otherModule = require('amdModule');
    otherModule.increase();
    otherModule.reset();
  })
	(define('amdModule', [], (require, export, module) => {
	    // 引入部分
	    const dependencyModule1 = require(./dependencyModule1);
	    const dependencyModule2 = require(./dependencyModule2);
	
	    // 处理部分
	    let count = 0;
	    const increase = () => ++count;
	    const reset = () => {
	      count = 0;
	    }
	    // 做一些跟引入依赖相关事宜……
	
	    export.increase = increase();
	    export.reset = reset();
  	}))(
	    // 目标是一次性区分CommonJSorAMD
	    typeof module === "object"
	    && module.exports
	    && typeof define !== "function"
	      ? // 是 CJS
	        factory => module.exports = factory(require, exports, module)
	      : // 是AMD
	        define
	  )
  • 优点:适合在浏览器中加载异步模块,可以并行加载多个模块
  • 缺点:会有引入成本,不能按需加载

CMD规范

	define('module', (require, exports, module) => {
	    let $ = require('jquery');
	    // jquery相关逻辑
	
	    let dependencyModule1 = require('./dependecyModule1');
	    // dependencyModule1相关逻辑
	  })
  • 优点:按需加载,依赖就近
  • 缺点:依赖于打包,加载逻辑存在于每个模块中,扩大模块体积

ES6模块化

	// 引入区域
	import dependencyModule1 from './dependencyModule1.js'
	import dependencyModule2 from './dependencyModule2.js'

	// 实现代码逻辑
	let count = 0;
	export const increase = () => ++count;
	export const reset = () => {
	    count = 0;
	}

	// 导出区域
	export default {
		increase,reset
	}

	// 模块引入
	<script type="module" src="esModule.js"></script>

ES11原生解决方案:

  import('./esModule.js').then(dynamicEsModule => {
    dynamicEsModule.increase();
  })
  • 优点(重要性):通过一种最统一的形态整合了js的模块
  • 缺点(局限性):本质上还是运行时的依赖分析

(重要)解决模块化的新思路 - 前端工程化

背景

	<!doctype html>
	    <script src="main.js"></script>
	    <script>
	      // 给构建工具一个标识位
	      require.config(__FRAME_CONFIG__);
	    </script>
	    <script>
	      require(['a', 'e'], () => {
	        // 业务处理
	      })
	    </script>
	  </html>
  define('a', () => {
    let b = require('b');
    let c = require('c');

    export.run = () {
      // run
    }
  })

工程化实现

  {
    a: ['b', 'c'],
    b: ['d'],
    e: []
  }
  <!doctype html>
    <script src="main.js"></script>
    <script>
      // 构建工具生成数据
      require.config({
        "deps": {
          a: ['b', 'c'],
          b: ['d'],
          e: []
        }
      })
    </script>
    <script>
      require(['a', 'e'], () => {
        // 业务处理
      })
    </script>
  </html>
  define('a', ['b', 'c'], () => {
    // 执行代码
    export.run = () => {}
  })
  • 优点:
  1. 构建时生成配置,运行时执行
  2. 最终转化成执行处理依赖
  3. 可以拓展

完全体 webpack为核心的工程化 + mvvm 框架组件化 + 设计模式

举报

相关推荐

0 条评论