博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS模块化 介绍(CommonJS、AMD-RequireJS、CMD-SeaJS、ES6_Babel_Browserify)
阅读量:3965 次
发布时间:2019-05-24

本文共 10825 字,大约阅读时间需要 36 分钟。

JS模块化


本人是个新手,写下博客用于自我复习、自我总结。

如有错误之处,请各位大佬指出。
学习资料来源于:尚硅谷


说在最前面:需要注意的是,代码中涉及到了ES6,出现了一些新特性,也许放在编译器里会报错。(因为我之前用Eclipse,发现把代码写在其中报了错,在idea上就没问题了,因为idea上是可以设置使用版本的,目前没发现Eclipse如何改版本)

JS模块化介绍

当项目功能越来越多,代码量便也会越来越多,后期的维护难度会增大,此时在JS方面就会考虑使用模块化规范去管理。建议学习完模块化规范以后再学习项目构建,以更好的武装自己的技能。

JS模块化:将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),再把它们组合在一起。块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信。


模块化的进化史

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


为什么要模块化?

在这里插入图片描述

减少复杂程度和它们之间的关联性,部署更方便。


模块化的好处

在这里插入图片描述

同时,随之而来的就是引入了多个文件:
(ES5中没有原生支持模块化,我们只能通过script标签引入js文件来实现模块化)
在这里插入图片描述
即,请求过多、依赖模糊、难以维护。


模块化进化史的详细说明

01_全局function模式

可以明显看出来,很容易出现命名冲突的问题。

  
01_全局function模式

module1.js

/** * 全局函数模式: 将不同的功能封装成不同的全局函数 * 问题: Global被污染了, 很容易引起命名冲突 */function foo() {
console.log('foo()')}function bar() {
console.log('bar()')}

module2.js

function foo() {
//与另一个模块中的函数冲突了 console.log('foo()2')}

02_namespace模式

可以明显看出来,这样数据不安全,外部可以直接修改。

  
02_namespace模式

module1.js

/** * namespace模式: 简单对象封装 * 作用: 减少了全局变量 * 问题: 不安全(数据不是私有的, 外部可以直接修改) */let myModule = {
data: 'hello', foo: function(){
console.log(this.data); }}

03_IIFE模式: 匿名函数自调用

现在就保证了安全。

03_IIFE模式
/** * IIFE模式: 匿名函数自调用(闭包) * IIFE : immediately-invoked function expression(立即调用函数表达式) * 作用: 数据是私有的, 外部只能通过暴露的方法操作 * 问题: 如果当前这个模块依赖另一个模块怎么办? */(function (window) {
//数据 let data = 'hello' //操作数据的函数 function foo() {
//用于暴露有函数 console.log('foo()',data) } //暴露行为 window.myModule = {
foo}})(window)

04_IIFE模式增强

  
04_IIFE模式增强

module4.js

/** * IIFE模式增强 : 引入依赖 * 这就是现代模块实现的基石 */(function (window, $) {
//数据 let data = 'hello' //操作数据的函数 function foo() {
//用于暴露有函数 console.log('foo()' ,data) $('body').css('background', 'red') } //暴露行为 window.myModule = {
foo}})(window, jQuery)

CommonJS ※

(在这里建议先学习Node.JS,相关语法在Node.JS中提及,而且学习模块化会有更好的理解。)

说明:

  • 每个文件都可当作一个模块
  • 在服务器端:模块的加载是运行时同步加载的(不会出现什么问题)
  • 在浏览器端:模块需要提前编译打包处理(用户等待的时间可能会很长。需要提前编译是因为浏览器不知道require语法)
  • CommonJS-Node就是用来服务器端实现
    CommonJS-Browserify就是用来浏览器端实现。它也称为CommonJS的浏览器端的打包工具。(但其实现在也不能这么说,因为ES6中也用到Browserify了)

基本语法:

  • 模块的引用
    • 使用 require()函数来引入一个模块
    • 例子:var 变量 = require("模块的标识");
  • 模块的定义
    • 在node中一个js文件就是一个模块
    • 默认情况下在js文件中编写的内容,都是运行在一个独立的函数中,
      外部的模块无法访问
    • 导出变量和函数
      • 使用 exports
        例子:
        exports.属性 = 属性值;
        exports.方法 = 函数;
      • 使用module.exports
        例子:
        module.exports.属性 = 属性值;
        module.exports.方法 = 函数;
        module.exports = {};
  • 模块的标识
    • 模块的标识就是模块的名字或路径。node是通过模块的标识来寻找模块的。
      对于核心模块(npm中下载的模块),直接使用模块的名字就可对其进行引入。
      var fs = require("fs");
      var express = require("express");
      对于自定义的文件模块,需要通过文件的路径来对模块进行引入。
      路径可以是绝对路径,如果是相对路径必须以./或 …/开头。
      var router = require("./router");

exports 导出、module.exports导出,它们之间的用法是有区别的:

helloModule.js

module.exports.name = "孙悟空";module.exports.age = 18;module.exports.sayName = function () {
console.log("我是孙悟空");};/* 无法使用这种方式,会报错。但module.exports就不会exports = { name:"猪八戒", age:28, sayName:function () { console.log("我是猪八戒"); }};*/
var hello = require("./helloModule");/*	exports 和 module.exports		- 通过exports只能使用.的方式来向外暴露内部变量			exports.xxx = xxx		- 而module.exports既可以通过.的形式,也可以直接赋值			module.exports.xxx = xxxx			module.exports = {} */console.log(hello.name);console.log(hello.age);hello.sayName();

CommonJS-Node ※

项目结构:

在这里插入图片描述

不要忘记,package.json可以用npm init来创建,建议不要手动创建(毕竟麻烦)。
在这里插入图片描述
"name"中不能有大写字母。(保留了简单的信息,毕竟我们不需要上传项目)

在这里我们下载一个第三方模块用来演示:(在当前项目目录下安装)

在这里插入图片描述
npm install 包名 --save:用来安装包,并添加到依赖中

这其中的uniq的用法:(就是用来去除数组中重复的内容)

在这里插入图片描述
安装好之后:
在这里插入图片描述
在这里插入图片描述
module1.js

//使用module.exports = value向外暴露一个对象module.exports = {
msg: 'module1', foo() {
console.log(this.msg); }}

module2.js

//使用module.exports = value向外暴露一个函数module.exports = function () {
console.log('module2');}

module3.js

//使用exports.xxx = value向外暴露一个对象exports.foo = function () {
console.log('module3 foo()')}exports.bar = function () {
console.log('module3 bar()')}exports.arr = [2,4,5,2,3,5,1,11];

app.js

/**  1. 定义暴露模块:    module.exports = value;    exports.xxx = value;    但是exports不能使用exports = {}的方式,但module.exports可以  2. 引入模块:    var module = require(模块名或模块路径); */let uniq = require('uniq');//引用模块let module1 = require('./modules/module1');let module2 = require('./modules/module2');let module3 = require('./modules/module3');//使用模块module1.foo();module2();module3.foo();module3.bar();let result = uniq(module3.arr);console.log(result);

输出结果:

在这里插入图片描述
(uniq不仅能去掉重复元素,还能根据元素首位数字的大小进行排序)


CommonJS-Browserify ※

项目结构:

在这里插入图片描述
在当前项目目录下安装:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
(直接save加依赖,加的是’运行依赖’的包,save-dev加依赖,加的是’开发依赖’的包,-g是全局下载包,给计算机用的)

安装后:

在这里插入图片描述
package.json:

{
"name": "commonjs-browserify", "version": "1.0.0", "dependencies": {
"uniq": "^1.0.1" }, "devDependencies": {
"browserify": "^16.5.1" }}

module1.js

//使用module.exports = value向外暴露一个对象module.exports = {
foo() {
console.log('module1 foo()') }}

module2.js

//使用module.exports = value向外暴露一个函数module.exports = function () {
console.log('module2()')}

module3.js

//使用exports.xxx = value向外暴露一个对象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')let uniq = require('uniq')//使用模块module1.foo()module2()module3.foo()module3.bar()console.log(uniq([1, 3, 1, 4, 3]))

在之前已经说过了,浏览器是无法识别require语法的,需要提前编译打包处理,Browserify就是帮我们做这个事情的。

在这里插入图片描述

这段指令分成三部分。除去browserify。-o:output,意味着输出。输出的左侧是需要处理的源文件,输出的右侧是作用后输出的文件位置及名字。(起什么名字都可以,只是bundle是打包的意思)

完成操作后:

index.html

    
Title

然后打开页面,就可以看到输出结果了:

在这里插入图片描述


AMD-RequireJS ※

AMD(Asynchronous Module Definition):异步模块定义,专门用于浏览器端。模块的加载是异步的。AMD规范的出现比CommonJS-Browserify要早。

基本语法

  • 定义暴露模块:
    • 没有依赖的模块:define(function(){return 模块对象})
    • 有依赖的模块:define([依赖模块名], function(){return 模块对象})
      如:define(['module1','module2'],function(m1,m2){return 模块对象})
  • 引入使用模块:
    require(['module1', 'module2'], function(m1, m2){//使用模块对象})
    requirejs(['module1', 'module2'], function(m1, m2){//使用模块对象})

演示:未使用AMD

(首先,我们不使用AMD规范,自己来写)

项目结构:
在这里插入图片描述

dataService.js

//定义一个没有依赖的模块(function (window) {
let msg = 'dataService.js'; function getMsg() {
return msg; } window.dataService = {
getMsg}})(window)

alerter.js

//定义一个有依赖的模块(function (window, dataService) {
let msg = 'alerter.js'; function showMsg() {
alert(dataService.getMsg() + ', ' + msg); } window.alerter = {
showMsg}})(window, dataService)

app.js

(function (alerter) {
alerter.showMsg();})(alerter)

test1.html

  Modular Demo 1

Modular Demo 1: 未使用AMD(require.js)

很明显的,我们需要通过script标签,将所有依赖导入才能使用,显然很麻烦。

接下来使用模块化规范👇


RequireJS ※

在使用之前,需要下载require.js,并引入:

  • 官网下载:
  • github下载 :

然后将require.js导入项目: js/libs/require.js

项目结构:
在这里插入图片描述
dataService.js

//定义没有依赖的模块define(function () {
let name = 'dataService.js'; function getName() {
return name; } return {
getName}});

alerter.js

//定义有依赖的模块define(['dataService', 'jquery'], function (dataService, $) {
let msg = 'alerter.js'; function showMsg() {
$('body').css('background', 'gray'); alert(dataService.getName() + ', ' + msg); } return {
showMsg}});

main.js

(function () {
//配置 requirejs.config({
//基本路径 (出发点在根目录下) baseUrl: 'js/', //映射: 模块标识名: 路径 paths: {
//注意:文件千万不要写 .js,规范会自动帮我们加 //自定义模块 alerter: './modules/alerter', dataService: './modules/dataService', //库模块 jquery: './libs/jquery-1.10.1', angular: './libs/angular' }, //暴露angular对象 shim:{
angular:{
exports: 'angular' } } }) //引入模块使用 requirejs(['alerter','angular'], function (alerter,angular) {
alerter.showMsg(); console.log(angular); })})()

index2.html

    Modular Demo 2     

其中需要注意的是,要引入jQuery模块时,一定要使用这个名字:jquery

因为jQuery的源码中写到了:
在这里插入图片描述
但是并不是所有的第三方库都支持AMD。比如:AngularJS就不支持,就还需要用shim来暴露出angular对象。


CMD-SeaJS

CMD在市面上用的不多。CMD也是专门用于浏览器端,模块的加载也是异步的。模块只在使用时才会加载执行。

基本语法:(像是AMD和CommonJS的合并)

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

在使用之前,需要下载sea.js,并引入:

  • 官网下载:
  • github下载 :

(需要说明的是,因为sea.js好像已经被卖了,网站应该已经点不开了,同时应该也找不到官方下载途径了,所以sea.js听人说已经快属于文物了。而且sea.js是阿里的大佬写的,目前只有阿里少部分在用。不过下载不到sea.js也没关系,其他模块化方法也可行,这里了解即可)

然后将sea.js导入项目: js/libs/sea.js

项目结构:
在这里插入图片描述
module1.js

//定义没有依赖的模块define(function (require, exports, module) {
//内部变量数据 let msg = 'module1'; //内部函数 function foo() {
return msg; } //向外暴露 module.exports = {
foo};});

module2.js

define(function (require, exports, module) {
let msg = 'module2'; function bar(){
console.log(msg); } module.exports = bar;});

module3.js

define(function (require, exports, module) {
let data = 'module3'; function fun(){
console.log(data); } exports.module3 = {
fun};});

module4.js

define(function (require, exports, module) {
let msg = 'module4'; //引入依赖模块(同步) let module2 = require('./module2'); module2(); //引入依赖模块(异步) require.async('./module3', function (m3) {
m3.module3.fun(); }); function fun2(){
console.log(msg); } exports.fun2 = fun2;})

main.js

define(function (require) {
var m1 = require('./module1'); console.log(m1.foo()); var m4 = require('./module4'); m4.fun2();})

index.html

  
Title

ES6_Babel_Browserify ※※

ES6规范很重要,目前使用最多的就是它。而且ES6要比CommonJS和AMD好用的多。

ES6的依赖模块也需要编译打包处理,这是因为有一些浏览器没有ES6的语法,所以需要把它编译成ES5,来让浏览器识别。当然我们之前也提到过,浏览器也不识别require语法,所以这就需要用到Browserify

基本语法

  • 导出模块:export
  • 引入模块:import

实现(浏览器端):使用Babel将ES6编译为ES5代码,再使用Browserify编译打包成JS。


项目结构:

在这里插入图片描述
①首先定义package.json文件:

{
"name": "es6-babel-browserify", "version": "1.0.0"}

②安装babel-cli, babel-preset-es2015browserify

babel-cli:command line interface (命令行接口),让我们能使用babel的命令。

babel-preset-es2015:下载ES6转为ES5的所有插件。

在这里插入图片描述

③定义.babelrc文件

{  "presets": ["es2015"]}

当babel运行的时候,它会先去读.babelrc文件,看看要做什么: "presets": ["es2015"]。数组中放es2015,它就会转ES6。(也能放其他参数,比如"react",它就会转JSX)

(以后可能看到很多rc文件,rc:run control ,即运行时控制文件:运行的时候读这个文件)

④ 定义js文件

module1.js

//暴露模块 分别暴露export function foo() {
console.log('module1 foo()');}export let bar = function () {
console.log('module1 bar()');}export let arr = [1, 2, 3, 4, 5];

module2.js

//统一暴露function fun1() {
console.log('module2 fun1()');}function fun2() {
console.log('module2 fun2()');}export {
fun1, fun2};

module3.js

//这种方式可以暴露任意数据类型,暴露什么数据接收到的就是什么数据export default {
name: 'Tom', setName: function (name) {
this.name = name }}

app.js

//引入其他模块import {
foo, bar} from './module1'import {
arr} from './module1'import {
fun1, fun2} from './module2'foo();bar();console.log(arr);fun1();fun2();//上述用法如果出现重名,显然用不了import person from './module3'person.setName('JACK');console.log(person.name);//引入第三方模块(jQuery)import $ from 'jquery'$('body').css('background', 'red');

在这里插入图片描述

⑤ 编译
在这里插入图片描述
⑥ 页面中引入测试

index.html

    
Title

这其中就会发现,如果需要修改app.js,在修改之后,每次都需要重新编译,才能执行。

每次都需要反复敲命令。之后的自动化构建工具,就可以帮我们解决这个问题。

转载地址:http://qoyki.baihongyu.com/

你可能感兴趣的文章
快速由PCI迁移到PCIe
查看>>
CCD和CMOS图像传感器的快门
查看>>
视频跟踪算法
查看>>
图像处理技术在视频监视中的应用
查看>>
DM8168 HDVPSS中的显示输出
查看>>
光电系统中的视频处理技术
查看>>
NRZ NRZI及扰码等串行编码技术的基本概念
查看>>
ADV7604介绍(一)
查看>>
无人机光电系统图像处理模块
查看>>
VP6802高清视频处理模块
查看>>
VP6802S01高清视频输入模块
查看>>
VP6803高清视频处理模块
查看>>
CAN总线基础知识(一)
查看>>
CAN总线基础知识(二)
查看>>
DM8148的电源和地(二)
查看>>
基于陀螺进行运动检测的电子稳像方案
查看>>
数字视频基础(一)
查看>>
AM5728概述(1)
查看>>
AM5728概述(4)
查看>>
AM5728概述(6)
查看>>