浅析:前端模块化开发规范
浅析:前端模块化开发规范
- 早期的模块化解决方案
- 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/,页面和控制台展示内容如下,部署成功。
浅析:前端模块化开发规范相关推荐
- 前端模块化开发规范之AMD(可不是处理器哦!)
首先强调下,我们这里提到的AMD可不是计算机的处理器哦! 继CommonJS之后,双出现了一种异步加载模块的方法.就是AMD,全称为:Asynchronous module definition. 它 ...
- 前端模块化开发学习之gulpbrowserify篇
随着web应用的发展,前端的比重占得越来越多,编写代码从而也越来越复杂.而通常我们需要将不同功能或者不同模块的代码分开写,最后在html中一起加载,这样做是可以的,但是当你需要进行维护或者是二次开发 ...
- web前端模块化开发_真正的模块化Web应用程序:为什么没有开发标准?
web前端模块化开发 OSGI , SpringSource , Jboss模块 ,J2EE和清单永远不会结束.所有这些技术都向他们的最终用户/开发人员保证了相同的东西,或多或少是Java模块化Web ...
- Node.js模块化开发||Node.js中模块化开发规范
JavaScript开发弊端 a.js b.js JavaScript在使用时存在两大问题,文件依赖和命名冲突. 生活中的模块化开发 软件中的模块化开发 app.j user.一个功能就是一个模块,多 ...
- 前端规范 - 前端项目开发规范
0 前言 好好做业务,提高自己的工程能力 [强制] 1 开启eslint 根据团队的习惯,制定适合自己的rules 比如 no-console no-debugger可以关闭 [强制] 2 新项目使用 ...
- Web前端工程师开发规范必须要注意的事
Web前端工程师开发规范必须要注意的事 规范目的 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范 ...
- 前端模块化开发到底是什么?
文章目录 前言 模块化演变过程 模块化规范的出现 模块化的最佳实践 ES Modules基本特性 ES Modules 导出 ES Modules导入导出的注意事项 ES Module导入用法 ES ...
- web前端模块化开发
定义与由来 定义 模块就是实现特定功能的相互独立的一组方法. 由来背景 随着网站逐渐的开发,嵌入网页的js代码越来越庞大,而网页也越来越像桌面程序,需要一个团队去分工协作,进行管理和测试等等.为了更好 ...
- Web前端设计开发规范
前言 本文主要整理了Web前端设计开发的相关规范,主要由网络收集,希望对大家在Web设计和开发的过程中有一些帮组. 目录 一.界面设计规范细则 二.CSS编写和命名规范 三.JavaScript编程规 ...
最新文章
- matlab imcrop 对应python函数_MATLAB车牌识别之7个字符切割浅谈【抽丝剥茧】
- 数据结构:单向链表的反转
- 微机原理汇编之部分重要知识整合包括:判断解释程序运行(新手有利)并画存储结构,字节变量,寻址方式
- linux安装python3.6以后报错处理
- cs106a编程方法学作业解答(3)
- CVPR2019| 最新CVPR2019论文抢先看!
- 通过伪协议解决 父页面与iframe页面通信的问题
- VS2019以及MFC的安装(详细)
- java重载与重写的区别
- 10慕课网《进击Node.js基础(一)》初识promise
- rx590 黑苹果 无货_黑苹果配置挑选,教你组装一台苹果机
- 吉林大学线性代数知识点及解题方法
- html+canvas 星空背景案例
- IOS Appstore 预览图尺寸
- js移动端rem.js自适应布局代码
- Oracle11g R2相比R1的区别
- Twitter开发者账号【Twitter开发者文档系列3】——推特标准接口API的请求频率限制说明
- 闲鱼x-sign参数
- 利用matplotlib绘制圆环图的案例
- 微信授权登录代码示例
热门文章
- Matlab 绘制函数切线(annotation函数法)
- 计算机故障率,电脑五大件故障率统计
- SQL 索引的作用(真的是超详细)
- 《如何让你爱的人爱上你》——莉尔·朗兹[美]
- 万有引力调研报告 ———大学物理第二节课思考题一
- VL48-使用Verilog解决多bit MUX同步器—慢时钟域同步到快时钟域(多bit跨时钟域),快时钟同步使能端并通过该使能端控制输出数据
- java学习笔记21——JDBC
- 2021-08-13——最终项目(待完善)-对比21世纪中美两国在夏季奥运会上的奖(金)牌数量
- php 中的字符串转数组函数,php中字符串转数组的函数是什么
- 防止SQL注入攻击的一些方法小结