浅析:前端模块化开发规范

  • 早期的模块化解决方案
    • NameSpace:命名空间模式
    • 匿名闭包:IIFE模式
    • 模块模式:IIFE+依赖传参
  • ES6之前的模块化解决方案
    • CommonJS模块化规范
    • AMD模块化规范
    • CMD模块化规范
  • ES6模块化规范
    • 基本语法
    • 定义ES6模块
      • 导出多个对象
      • 导出默认对象
    • 引入ES6模块
    • ES6与Babel、Browserify
      • Babel:JS编译器
      • Babel Preset:@babel/preset-env
      • Browserify
  • ES6模块化:示例demo
    • 项目搭建
    • JQuery库拉取
    • 编写子模块
      • ES6模块:module1.js
      • ES6模块:module2.js
      • ES6模块:module3.js
    • 编写主模块:main.js
    • 搭建babel和Browserify环境
    • ES6语法转换与代码打包合并
    • index.html引入“ES6模块”
    • 项目部署

ES6模块化规范,其设计思想是尽量的静态化,使得编译时就可以确定模块的依赖关系,以及输入和输出的变量。在语言标准的层面上,ES6的模块化标准致力于统一浏览器和服务端的通用模块解决方案。但是,前端模块化开发规范的发展经历了诸多过程,以下是个人对其发展历程和实际应用的一些浅显理解。

早期的模块化解决方案

在最早期,Web开发者为了实现模块化开发,提出了不少思路。我所了解到的有如下几种,

NameSpace:命名空间模式

我们知道,像C#、C++中都有命名空间的概念,可以用来实现变量隔离,防止变量污染或者命名冲突。JS前端开发也参考了这种开发思路,即:将不同功能的函数拆分、封装到若干个NameSpace命名空间中,本质上是借助一个个单独的对象来实现的。
    示例代码如下,

    //1-NameSpace模式var nameSpace_A = {doSleep:function (){},doWork:function (){},};var nameSpace_B = {doAsk:function (){},doAssist:function (){},};

如上代码,即时两个NameSpace命名空间中出现了同名方法,也不会被导致命名冲突,因为它们属于两个不同的对象nameSpace_A和nameSpace_B。但是这种方式并不安全,因为用来表示一个命名空间的Object对象的属性很容易被修改、覆盖。

匿名闭包:IIFE模式

匿名闭包-IIFE模式,是通过立即执行函数实现的。由于立即执行函数中的数据在全局中无法被取到,不能被操作,相对来说更安全。
    如下代码,通过立即执行函数返回了两个接口方法setPath、getPath,实现单独的模块化封装。

  //2-匿名闭包模式var module_1 = (function (){var _path = "no_setting";//标记为私有属性var getPath = function (){return _path;}var setPath = function (path){_path = path;}return {setPath:setPath,getPath:getPath,};})();module_1.setPath("current_path");console.log(module_1.getPath());

模块模式:IIFE+依赖传参

虽然上面的IIFE模式,已经能实现简单的模块化的开发需求。但是在很多开发场景下,要实现模块之间的交互操作,那么就需要为闭包引入外部依赖。具体如何实现呢?
    此时就可以为匿名闭包传递一个参数,将外部的依赖值作为参数传入闭包。举个例子,Jquery脚本库的实现就有这种模式的影子,如下图所示,JQuery源码中提供了一个闭包函数,并将window或者说是当前this对象作为闭包函数的参数进行传递,实现JQuery挂载到全局对象上。
    参考上面这种在外部传参的实现思路,给出如下的代码示例,用于实现模块之间的依赖注入。

 //3-模块模式var paramValue = "paramValue";//待传递参数//通过IIFE+依赖传参方式定义模块var module_plus = (function (param){var _path = param;//标记为私有属性var getPath = function (){return _path;}var setPath = function (path){_path = path;}return {setPath:setPath,getPath:getPath,};})(paramValue);console.log(module_plus.getPath());

ES6之前的模块化解决方案

随着编程技术的发展,在ES6模块化规范出现之前,也出现了一些其它的广为流行的模块化加载方案,主要是:CommonJS规范、AMD异步模块定义规范、CMD通用模块定义规范。
    其中:
        ①AMD和CMD适用于浏览器端的模块化开发;
        ②首位CommonJS规范借助Browserify工具虽然可以应用在浏览器端,但是相较而言,还是更加适用于服务器的模块化开发。
    这些模块化规范,或者需要依赖第三方JS脚本库,例如:AMD需要依赖RequireJS,CMD需要依赖Sea.js;或者需要需要进行预先的编译打包处理,例如:CommonJS虽然在服务器端适用良好,但是为了适应在浏览器端的模块化开发需求,就需要借助Browserify工具进行语法转换,得到前端可用的js脚本。
    关于上述模块化规范的详细介绍、定义与使用方式,可以参看我的另外三篇博文,

CommonJS模块化规范

①模块化:CommonJS规范;

AMD模块化规范

②模块化:AMD规范;

CMD模块化规范

③模块化:CMD规范。

ES6模块化规范

ES6的模块化规范相比之前的所有方案,采用了编译时加载的机制,使得静态分析成为可能(ES6之前的CommonJS、CMD、AMD模块化规范,都是在运行期间的动态加载方式,无法实现在编译期间的“静态优化”)。除此之外,ES6模块还有以下好处:

摘自:《ECMAScript 6 入门-阮一峰》

基本语法

在语法层面,ES6模块化规范做了简化实现,无需像之前CMD、AMD规范中进行的require()、define()、require.config()、require()等繁琐的定义、导入和参数配置。只需要借助export、import两个关键字,就可以实现模块的导出、导入。

[1] 模块导出:export - 导出多个 export default-默认导出
[2] 模块导入:import

定义ES6模块

定义ES6模块,就像定义一个函数一样,示例代码如下,

导出多个对象

export+函数|变量的方式可以从当前模块导出任意类型的数据。

//定义子模块-逐个暴露
export function foo(){console.log("module1")
}export function foo2(){console.log("module1")
}export const arr = [1,2,3,52,0,5];

导出默认对象

export default+函数|变量的方式可以从当前模块导出一个默认对象|类型|函数等。如下代码导出一个默认函数,并且只能导出一个。

//导出默认的子模块
export default function doWork(){console.log("module3");
}

引入ES6模块

ES6模块化规范中,规定使用import关键字引入任何自定义或者第三方模块。示例代码如下,

//主模块
//引入子模块-对象解构赋值-{ES6要求使用解构赋值获取模块中的内容}
import { foo,foo2,arr } from './modelu1';
import { fun,fun2 } from './modelu2';
import doWork from "./modelu3";//引入第三方模块Jquery
import $ from 'jquery';console.log(arr);
foo();
foo2();fun();
fun2();doWork();$('body').css('backgroundColor',"skyblue");

ES6与Babel、Browserify

虽然上面的ES6模块化的语法规则十分简单,但是由于浏览器无法直接识别ES6的模块化语法,导致直接在html页面中引入使用了import、export语法的js脚本文件会报错。如下所示,

    熟悉前端开发的小伙伴可能会立即想到使用WebPack、npm、yarn等前端项目构建工具,以及Vue-cli等等,这些工具固然可以帮助我们搭建本地服务器,并借助一些插件实现语法转换的能力。但是,在不使用这些构建工具的前提下,如果我们硬要在html页面中引入ES6模块,要如何实现呢?
    此时可以借助Babel和Browserify,分两次将ES6模块转换为任意HTML页面都能够在script标签中轻松引入的JavaScript脚本。

Babel:JS编译器

Babel的官网地址在此处,
    Babel 是一个 JavaScript 编译器/工具链,主要用于将ES5更高级别的语法编写出来的代码,转换为向后兼容的JavaScript语法,以使其可以运行在当前和旧版本的浏览器或者其它环境中。

Babel Preset:@babel/preset-env


    在Babel官网的中文文档目录下,可以看到Babel Presets菜单项,我们主要关注@babel/preset-env,先来看一下官网对此的介绍,然后在继续叙述我们想做的事情。
    @babel/preset-env是一款自动管理浏览器版本和babel语法转换插件映射的一款工具。在功能上类似于babel-preset-es2015,但是babel-preset-env 可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为对应的es5代码,而无需其它配置,十分友好。借助@babel/preset-env这个工具,就可以实现前面所说的:将ES6模块转换为ES5模块的工作了。

Browserify

然后我们接着来看Browserify这款工具,它主要是将CommonJS规范的模块中require语法转换为浏览器可以直接识别的语法。
    为什么需要这款工具呢?是因为@babel/preset-env转换过来的ES5代码仍然是遵循CommonJS模块化规范的,直接引入html文件中,依旧会报错。

Browserify官网地址为点击此处可访问。从官网介绍就可以知晓:Browserify 工具可以将CommonJS规范的模块依赖进行打包,从而使得require(‘module’)的语法转换为可以在浏览器端运行的代码。

ES6模块化:示例demo

到目前为止,已经做了不少铺垫性的工作。主要是:介绍ES6模块的定义语法、借助Babel工具将ES6转换为遵循CommonJSES5语法、借助Browserify工具将CommonJS规范的模块转换为Browser浏览器端可识别解析的代码。
    当然,您可能会直接跳转到此处,来查看下面的示例demo如何编写。但是,我仍然建议您在看完demo之后,跳到之前未看的部分,进入其官网将其作进一步的理解。

项目搭建

由于是运行在浏览器端的项目,我们期望双击HTML文件就能够直接看到效果,所以,在此,我们无须像Vue或者React前端项目一样,去搭建本地Web服务器。项目结构如下,

    其中:
        ①js/src:用于存放ES6规范的子模块;
        ②js/dist:用于存放ES6模块打包编译之后的内容;
        ③index.html:主页面html文件;
        ④package.json:用于存放通过npm包管理工具下载的Babel工具版本信息。

JQuery库拉取

实际开发过程中,一般使用JQuery v1.x版本,拉取命令如下,

# @1表示-默认会为我们下载jquery-1版本中最新的jquery库
npm install jquery@1

编写子模块

ES6模块:module1.js

//定义子模块-逐个暴露
export function foo(){console.log("module1")
}export function foo2(){console.log("module1")
}export const arr = [1,2,3,52,0,5];

ES6模块:module2.js

//统一暴露
function fun(){console.log("module2");
}function fun2(){console.log("module2");
}//导出对象
export {fun,fun2};

ES6模块:module3.js

//导出默认的子模块
export default function doWork(){console.log("module3");
}

编写主模块:main.js

//主模块
//引入子模块-对象解构赋值-{ES6要求使用解构赋值获取模块中的内容}
import { foo,foo2,arr } from './modelu1';
import { fun,fun2 } from './modelu2';
import doWork from "./modelu3";
import $ from 'jquery';console.log(arr);
foo();
foo2();fun();
fun2();doWork();$('body').css('backgroundColor',"skyblue");

搭建babel和Browserify环境

命令如下,

# 安装browserify
npm install browserify -g
# 安装babel-cli
npm install babel-cli -g
# 安装babel-preset-env
npm install babel-preset-env --save-dev

安装完毕之后的package.json文件内容如下,

{"name": "ES6_Babel_Browserify","version": "1.0.0","devDependencies": {"babel-preset-env": "^1.7.0"},"dependencies": {"jquery": "^1.12.4"}
}

然后,我们需要编写.babelrc配置文件,以便于使用babel进行语法转换时读取构建目标,

{"presets": ["env"]
}

ES6语法转换与代码打包合并

主要分两步实现:
        ①将ES6子模块转换为ES5语法规则的代码,命令如下,

# 将当前js/src下的ES6模块转换为ES5语法的代码,输出目录为js/dist
babel js/src -d js/dist

②将默认遵循CommonJS规范的ES5代码模块,作进一步转换为浏览器可直接解析的代码,命令如下,

 # 将js/dist下main.js主模块及其依赖的子模块转换合并为bundle.js文件,并输出到js/build目录下browserify js/dist/main.js -o js/build/bundle.js

最终构建完毕的目录结构如下,只需要将bundle.js文件引入到index.html文件中即可。

index.html引入“ES6模块”

此处所说的ES6模块,其实已经不是原始的ES6模块了,因为经过babel、Browserify工具的两次转换,已经被合并到bundle.js一个JavaScript脚本库中了。但是我们整体的开发模式依然是基于ES6的模块化规范的,只是开发完毕之后手动做了语法转换工作。最终效果如下图所示。
    这种过程其实和使用WebPack、Vue-cli等代码构建工具实现自动的转换过程是类似的。所以,接下来要研究的内容,就是弄清楚,Webpack这类工具,是如何操作、如何完成自动转换的过程的。

项目部署

谈及项目部署,可能有些小题大做的意思了,但是,为了保证流程的完整性,还是简单介绍一下。
    此时我们的项目经过两次语法转换,已经可以直接拿来部署了。并且由于没有引入静态资源,因此,只需要简单抽取index.html和bundle.js文件,即可在服务器上部署。
    此处,我们将抽取出来的项目文件:index.html和bundle.js部署到我自己的云服务器上,然后进行访问测试。部署路径如下,

    利用xftp连接服务器,上传文件,将其部署到云服务器,

然后尝试访问,我这里的测试地址是:http://xx.xx.192.113:8089/es6_module/,页面和控制台展示内容如下,部署成功。

浅析:前端模块化开发规范相关推荐

  1. 前端模块化开发规范之AMD(可不是处理器哦!)

    首先强调下,我们这里提到的AMD可不是计算机的处理器哦! 继CommonJS之后,双出现了一种异步加载模块的方法.就是AMD,全称为:Asynchronous module definition. 它 ...

  2. 前端模块化开发学习之gulpbrowserify篇

     随着web应用的发展,前端的比重占得越来越多,编写代码从而也越来越复杂.而通常我们需要将不同功能或者不同模块的代码分开写,最后在html中一起加载,这样做是可以的,但是当你需要进行维护或者是二次开发 ...

  3. web前端模块化开发_真正的模块化Web应用程序:为什么没有开发标准?

    web前端模块化开发 OSGI , SpringSource , Jboss模块 ,J2EE和清单永远不会结束.所有这些技术都向他们的最终用户/开发人员保证了相同的东西,或多或少是Java模块化Web ...

  4. Node.js模块化开发||Node.js中模块化开发规范

    JavaScript开发弊端 a.js b.js JavaScript在使用时存在两大问题,文件依赖和命名冲突. 生活中的模块化开发 软件中的模块化开发 app.j user.一个功能就是一个模块,多 ...

  5. 前端规范 - 前端项目开发规范

    0 前言 好好做业务,提高自己的工程能力 [强制] 1 开启eslint 根据团队的习惯,制定适合自己的rules 比如 no-console no-debugger可以关闭 [强制] 2 新项目使用 ...

  6. Web前端工程师开发规范必须要注意的事

    Web前端工程师开发规范必须要注意的事 规范目的 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范 ...

  7. 前端模块化开发到底是什么?

    文章目录 前言 模块化演变过程 模块化规范的出现 模块化的最佳实践 ES Modules基本特性 ES Modules 导出 ES Modules导入导出的注意事项 ES Module导入用法 ES ...

  8. web前端模块化开发

    定义与由来 定义 模块就是实现特定功能的相互独立的一组方法. 由来背景 随着网站逐渐的开发,嵌入网页的js代码越来越庞大,而网页也越来越像桌面程序,需要一个团队去分工协作,进行管理和测试等等.为了更好 ...

  9. Web前端设计开发规范

    前言 本文主要整理了Web前端设计开发的相关规范,主要由网络收集,希望对大家在Web设计和开发的过程中有一些帮组. 目录 一.界面设计规范细则 二.CSS编写和命名规范 三.JavaScript编程规 ...

最新文章

  1. matlab imcrop 对应python函数_MATLAB车牌识别之7个字符切割浅谈【抽丝剥茧】
  2. 数据结构:单向链表的反转
  3. 微机原理汇编之部分重要知识整合包括:判断解释程序运行(新手有利)并画存储结构,字节变量,寻址方式
  4. linux安装python3.6以后报错处理
  5. cs106a编程方法学作业解答(3)
  6. CVPR2019| 最新CVPR2019论文抢先看!
  7. 通过伪协议解决 父页面与iframe页面通信的问题
  8. VS2019以及MFC的安装(详细)
  9. java重载与重写的区别
  10. 10慕课网《进击Node.js基础(一)》初识promise
  11. rx590 黑苹果 无货_黑苹果配置挑选,教你组装一台苹果机
  12. 吉林大学线性代数知识点及解题方法
  13. html+canvas 星空背景案例
  14. IOS Appstore 预览图尺寸
  15. js移动端rem.js自适应布局代码
  16. Oracle11g R2相比R1的区别
  17. Twitter开发者账号【Twitter开发者文档系列3】——推特标准接口API的请求频率限制说明
  18. 闲鱼x-sign参数
  19. 利用matplotlib绘制圆环图的案例
  20. 微信授权登录代码示例

热门文章

  1. Matlab 绘制函数切线(annotation函数法)
  2. 计算机故障率,电脑五大件故障率统计
  3. SQL 索引的作用(真的是超详细)
  4. 《如何让你爱的人爱上你》——莉尔·朗兹[美]
  5. 万有引力调研报告 ———大学物理第二节课思考题一
  6. VL48-使用Verilog解决多bit MUX同步器—慢时钟域同步到快时钟域(多bit跨时钟域),快时钟同步使能端并通过该使能端控制输出数据
  7. java学习笔记21——JDBC
  8. 2021-08-13——最终项目(待完善)-对比21世纪中美两国在夏季奥运会上的奖(金)牌数量
  9. php 中的字符串转数组函数,php中字符串转数组的函数是什么
  10. 防止SQL注入攻击的一些方法小结