-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端模块化详解(完整版) #48
Comments
赞一个 |
感谢 |
写的非常好,谢谢分享! |
十分感谢,多多关注我的文章 |
是不是叫ES Module会比较好一点。。。 |
ES6 Module?可以啊 |
跟着代码敲了一遍并做了个小总结😋,难得有这么通俗易懂并且一步步带着敲代码并讲解的关于模块化的文章。 模块化规范的必要性:引入多个<script>出现的问题->请求过多,依赖模糊,难以维护。 CommonJS: AMD: CMD: 借助工具:sea.js ES6模块化: |
就是我对于运行时和编译时这个概念还是不太清晰。 |
很认真的妹子,CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。这句话是阮大神的话,你可以看es6模块,有详细介绍!欢迎加入我们的微信交流群,欢迎加我微信qqlcx55 |
写的很好,有空可以搭建一个hexo站点,做一些分类,可以参考 |
谢谢你的建议,最近打算先弄个vuepress |
作为一枚转型中的前端,之前一直没明白项目中为什么要那样写代码,今天碰巧看到这篇,才知道原来是按照CMD规范,这层窗户纸算是捅破了,可以往里继续进攻了,感谢作者用心分享!~ |
哈哈,有帮助就好,欢迎关注我的公众号:前端工匠 |
请问下, AMD和CMD的输出是值的引用还是值的拷贝呢? |
跟着巧了一遍 对之前不熟悉只是稀里糊涂用的东西 理解加深一步。 |
好文!我们需要这样的文章 |
反复看了两次,这个CMD 与 AMD看清楚与理解了,CMD其实就是AMD 与 commonJS优点的集合的升级版本…… |
你好,"CommonJS模块的加载机制是,输入的是被输出的值的拷贝。"关于这点有些疑问。 // index.js // lib.js 两个文件中的val值可以相互影响,说明输入输出应该指向了同一个对象实例。 |
前言
在JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀,此时在JS方面就会考虑使用模块化规范去管理。
本文内容主要有理解模块化,为什么要模块化,模块化的优缺点以及模块化规范,并且介绍下开发中最流行的CommonJS, AMD, ES6、CMD规范。本文试图站在小白的角度,用通俗易懂的笔调介绍这些枯燥无味的概念,希望诸君阅读后,对模块化编程有个全新的认识和理解!
建议下载本文源代码,自己动手敲一遍,请猛戳GitHub个人博客(全集)
一、模块化的理解
1.什么是模块?
2.模块化的进化过程
这样的写法会暴露所有模块成员,内部状态可以被外部改写。
最后得到的结果:
这就是现代模块实现的基石
上例子通过jquery方法将页面的背景颜色改成红色,所以必须先引入jQuery库,就把这个库当作参数传入。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。
3. 模块化的好处
4. 引入多个
<script>
后出现出现问题首先我们要依赖多个模块,那样就会发送多个请求,导致请求过多
我们不知道他们的具体依赖关系是什么,也就是说很容易因为不了解他们之间的依赖关系导致加载先后顺序出错。
以上两种原因就导致了很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题。
模块化固然有多个好处,然而一个页面需要引入多个js文件,就会出现以上这些问题。而这些问题可以通过模块化规范来解决,下面介绍开发中最流行的commonjs, AMD, ES6, CMD规范。
二、模块化规范
1.CommonJS
(1)概述
Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。
(2)特点
(3)基本语法
module.exports = value
或exports.xxx = value
require(xxx)
,如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径此处我们有个疑问:CommonJS暴露的模块到底是什么? CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
上面代码通过module.exports输出变量x和函数addX。
require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
(4)模块的加载机制
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。这点与ES6模块化有重大差异(下文会介绍),请看下面这个例子:
上面代码输出内部变量counter和改写这个变量的内部方法incCounter。
上面代码说明,counter输出以后,lib.js模块内部的变化就影响不到counter了。这是因为counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
(5)服务器端实现
①下载安装node.js
②创建项目结构
注意:用npm init 自动生成package.json时,package name(包名)不能有中文和大写
③下载第三方模块
npm install uniq --save // 用于数组去重
④定义模块代码
⑤通过node运行app.js
命令行输入
node app.js
,运行JS文件(6)浏览器端实现(借助Browserify)
①创建项目结构
②下载browserify
③定义模块代码(同服务器端)
注意:
index.html
文件要运行在浏览器上,需要借助browserify将app.js
文件打包编译,如果直接在index.html
引入app.js
就会报错!④打包处理js
根目录下运行
browserify js/src/app.js -o js/dist/bundle.js
⑤页面使用引入
在index.html文件中引入
<script type="text/javascript" src="js/dist/bundle.js"></script>
2.AMD
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。此外AMD规范比CommonJS规范在浏览器端实现要来着早。
(1)AMD规范基本语法
定义暴露模块:
引入使用模块:
(2)未使用AMD规范与使用require.js
通过比较两者的实现方法,来说明使用AMD规范的好处。
最后得到如下结果:
这种方式缺点很明显:首先会发送多个请求,其次引入的js文件顺序不能搞错,否则会报错!
RequireJS是一个工具库,主要用于客户端的模块管理。它的模块管理遵守AMD规范,RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
接下来介绍AMD规范在浏览器实现的步骤:
①下载require.js, 并引入
http://www.requirejs.cn/
https://github.com/requirejs/requirejs
然后将require.js导入项目: js/libs/require.js
②创建项目结构
③定义require.js的模块代码
④页面引入require.js模块:
在index.html引入
<script data-main="js/main" src="js/libs/require.js"></script>
**此外在项目中如何引入第三方库?**只需在上面代码的基础稍作修改:
上例是在alerter.js文件中引入jQuery第三方库,main.js文件也要有相应的路径配置。
小结:通过两者的比较,可以得出AMD模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。
3.CMD
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。在 Sea.js 中,所有 JavaScript 模块都遵循 CMD模块定义规范。
(1)CMD规范基本语法
定义暴露模块:
引入使用模块:
(2)sea.js简单使用教程
①下载sea.js, 并引入
然后将sea.js导入项目: js/libs/sea.js
②创建项目结构
③定义sea.js的模块代码
④在index.html中引入
最后得到结果如下:
4.ES6模块化
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
(1)ES6模块化语法
export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
如上例所示,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
(2)ES6 模块与 CommonJS 模块的差异
它们有两个重大差异:
① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
下面重点解释第一个差异,我们还是举上面那个CommonJS模块的加载机制例子:
ES6 模块的运行机制与 CommonJS 不一样。ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
(3) ES6-Babel-Browserify使用教程
简单来说就一句话:使用Babel将ES6编译为ES5代码,使用Browserify编译打包js。
①定义package.json文件
②安装babel-cli, babel-preset-es2015和browserify
③定义.babelrc文件
④定义模块代码
⑤ 编译并在index.html中引入
babel js/src -d js/lib
browserify js/lib/app.js -o js/lib/bundle.js
然后在index.html文件中引入
最后得到如下结果:
此外第三方库(以jQuery为例)如何引入呢?
首先安装依赖
npm install jquery@1
然后在app.js文件中引入
三、总结
后记
花了很长时间(>10h)终于把"JS模块化"讲清楚,自己对模块化的认识又加深了一步,事实上,理解一件事并不难,难的是如何将一件事通俗分享给别人,并让别人也有所收获,一直以来我也是这样要求自己!文章如有错误和不正之处,欢迎指正和批评,同时也希望大家多多支持,我会有更大的创作动力!
参考文章
The text was updated successfully, but these errors were encountered: