requirejs优势(使用场景)
- 防止js加载阻塞页面渲染(async属性)
- 避免命名冲突以及为避免冲突所作的若干挣扎…,甚至还有一种情况是:不同版本的依赖库,如jquery,可以通过赋以不同的命名加以区分。
- 更好的依赖处理,解决:文件依赖导致需要小心翼翼的放置各js,以确保它们的加载顺序。
教程 - 各模块专注于单一的功能,仅暴露出必要的部分
- 改善性能问题。按需加载,减少不必要的加载。也可以将很少更新的合并成一个文件,用缓存,经常变动的独立成模块,互不影响。
实战演练
define跟require的区别在于一个是用于定义模块的,一个是使用已定义的模块的。
以下demo是可以运行的,但是放到codepen中,require存在问题,待解决,此处仅供代码参考。
See the Pen requirejsDemo by lu (@luchen) on CodePen.
fis中的mod.js里面的require
实习的过程中最痛心的就是遗留代码的加功能点…之前的fe是用的旧版本的fis中的mod.js,定义了citylist组件,导致页面上要用到这一部分就得使用mod中的require。
然后比较神奇的有这么几点
require函数的大体思想就是,定义一个全局对象,然后把引入的js文件作为一个一个的key,value存进去,每次进require函数,就去找这个对象,如果没有,就添加,如果有,就更新,保证全局唯一,某种程度上避免了不同模块是不同的fe写的,然后重复引用js的问题。但是比较坑爹的是,需要手动加script标签引入…如此反人类…当然也提供了require.async来异步引入,这个函数中它是可以createScript的,也就是说,你只要提供url,它会在head中 append script元素。
还有一点注意的是,require做了部分兼容,就是如果传给他的是一个数组对象,那么它会调用require.async。
关于类比require中的path,mod里面也有resourceMap进行处理,
|
|
eg.
并不是我们通常意义上理解的模块、同步、引入。= =
demo如图。
其中,heatform的文件映射见上
|
|
AMD,CMD,UMD概览
一般 require 用于处理页面首屏所需要的模块,require.async 用于处理首屏外的按需模块。
三者代码示例
AMD:
见上
CMD:
|
|
此处需要注意exports是module.exports的一个引用,如果直接使用
暴露模块的输出的话,是无效的。因为此时相当于exports的引用对象更改了,两者并不指向同一个对象。module.exports指向的才是。
UMD:
|
|
函数有两个参数,第一个参数是当前运行时环境,第二个参数是模块的定义体。在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境( window )。
从模块加载流程看amd,cmd区别
总结来说,cmd是异步加载、延迟且同步执行、依赖就近
amd是异步加载(下载)、异步执行、依赖前置。
(1)示例
在BEGIN1位置处a、b模块都需要被执行一次。CMD中BEGIN 2处a、b都没有被执行,在END处,a、b只有一个被实际执行过。这就是cmd所说的延迟加载。
(2)大致流程
以requirejs为例,其实amd,cmd的流程基本一样,区别在于后面的执行方面。
requirejs:用registry({id:module})来维持一个全局的模块资源表,保证不重复,每次根据id来查找,有则返回,无则去new Module。new Module的时候会触发Module.init函数,依次进行createScript,loading,将模块的依赖加入到依赖数组里,触发自己的completeLoad事件,在该事件中,依次去get依赖的Module,(在getModule时又会回到上面的步骤,有则返回,无则new Module).再接下来会做 checkLoaded,其中每隔50ms去checkoutLoadTimeoutId,因为模块是异步加载的,所以用这个来保证加载结束。 define函数中进行了兼容,包括无id,无依赖的,commonjs写法的(这种情况下同seajs一样,factory.toString()后正则匹配出依赖项)等多种情况。
seajs:
异步下载脚本模块,下载后,浏览器会自动执行 define(fn),define 方法会保存 fn 和提取该模块的依赖模块(利用 fn.toString),按照上面的方法,把依赖模块都下载好
接着根据依赖关系,依次执行各个模块的 fn。
(3)区别/相同点
- 同:amd,cmd都是通过设置createElement(script),且设置async属性=true,因此它们都是async,异步下载的。
- 异1(执行时刻):amd是并行加载后就执行,而cmd是as lazy as possible,先并行加载,但是直到require使用时才会执行。因此相对会耗费了时间。
举个栗子,amd中,define(id,[],factory),[]指示的依赖数组就相当于cmd里面的那个require,那个require就是执行~ - 异2(执行顺序):amd是异步执行(执行顺序不定),而cmd是同步执行(当然也可以设置require.async去异步执行)
注:
amd,cmd都是基于commonjs的,commonjs服务于服务端,cmd和amd作用于浏览器端,因为cmd中的require是同步执行,需要执行完才能执行下面的代码,对于浏览器端来说,是一个很大的性能问题,因为模块在服务器端,完全拼网速。
“ 因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。” –阮一峰
(4)补充
requirejs性能好点的原因:
- 解析依赖时直接就可以知道,不需要像seajs一样 function.to String()后正则匹配require后的依赖
- 异步加载执行,而cmd同步执行耗时
参考amd,cmd
cmd 中require的加载与执行的关系?require的时候实际上是已经加载好了,去执行 exec()
补充知识:
- 浏览器通常解析script的时候是同步下载、同步阻塞执行。
除非手动设置了async,defer等属性
具体总结来说
defer:
异步下载、最后(document被解析之后)执行,仍然在DOMContentLoaded之前async:
异步下载后立刻异步执行(执行时可能页面还在解析,不block parse,顺序不定)
且在window的load事件之前执行不设置:
同步下载、同步执行,在页面继续解析之前,因此会阻塞
2,动态创建script标签并插入==设置为async
references
- Asynchronous and deferred JavaScript execution explained
- script的defer和async
- 以代码爱好者角度来看AMD与CMD
- seajs 源码解读
- Deep dive into the murky waters of script loading 主要涉及到异步加载顺序执行的方法
to think:
- es6中的module机制以及webpack的处理