神刀安全网

AMD的一道面试题

AMD的一道面试题

模块化现在应该已经成为了稍微复杂一点前端开发的标配了。在es6中,都已经支持了的模块化。

之前的面试中,一直感觉模块化AMD,CMD没有什么可以问的,不过昨天面试突然想到一个题目:对于一个AMD的模式下

文件 d.js 如下

define(function (require) {  // ... 很多代码  require('a');  // ... 很多代码  require(['b'], function (b) {});  // ... 很多代码  require('c'); }); 

a.js , b.js , c.js 文件分别是什么时候加载的,如何加载的?

题目不难

答案是 a.js 和  c.js 是在加载完 d.js 后就加载。

b.js 是在执行到这一行时异步加载的。

具体分析:

我没有看过 require.js 的源码,我们使用的是 esl.js (也是一个AMD的模块加载器),但是他们的实现原理应该差不多。

我从 esl.js 的角度解读一下:

同步加载,异步加载

首先大家需要知道AMD里面一个同步加载和异步加载的概念。

从概念上面理解,同步就是当我执行到 require('a'); 时,我需要同步的执行 a.js 里面的内容,也就是需要在执行到这句话时 a.js 必须已经加载好了,这样才能到达同步。

而对于 require(['b'], function (b) {}); ,我执行到这一步时,是异步的发出请求,然后异步等待 b.js 的返回+执行。

同步加载的实现原理

我们从概念上面理解的同步加载的原理,现在看看 esl.js 的实践。

这里面需要处理两个核心步骤

  1. 执行到 require('a'); 时, a.js 必须已经加载好了;
  2. a.js 文件里面的所有 require('*') ,也都必须加载好了,保证在执行 a.js 时,所有 a.js 依赖的同步文件都能同步执行;

对于第一步的实现,大概原理是这样的,在加载好了 d.js 后,会正则匹配一次文件里面的同步依赖 require('*'); ,例如匹配出了  a.jsc.js ,然后继续加载 a.jsc.js

对于第二部,其实就是一个递归处理,直到没有下一步的依赖为止。

同步加载另外一种处理方法

上面有一部正则逻辑,可见如果使用这种方式,在执行代码前,js需要全部正则一次所有模块化代码的。这样性能是不是有一个无谓的耗损。

那么我们一般怎么处理了?

大家一般都了解过打包编译,例如在使用 Requirejs 时,线上环境的代码会经过 r.js 处理一次。

那么 d.js 文件应该会处理如下

define(  'path/b',  ['require', 'a', 'b'],  function (require) {  // ... 很多代码  require('a');  // ... 很多代码  require(['b'], function (b) {});  // ... 很多代码  require('c'); }); 

define方法会增加第一个和第二个参数

第一个参数是按照路径生成一个具名id第二个参数是此文件所依赖的同步文件

这时当模块在解析这个 b,js 文件时,发现如果存在第二个参数,就会直接解析所需依赖部分,而省去了正则这一步。

我们正则这一步转换到了打包编译中去分析,这样就省掉了浏览器加载时去正则所有AMD文件这一步。

那么为什么我们不在开发环境中直接使用 ['require', 'a', 'b'] 方式,我理解目的是为了提高开发便捷性,我们不需要再增加一个 require('*') 都在中括号内配置一次,同样删除时也不用去删掉配置。

因为这一步完全可以在编译时处理。

打包编译的延生

不知道大家有没有看过编译后的代码和开发环境代码的区别,对于这个 b.js 文件,编译后应该是:

define(  'path/a',  ['require'],  function (require) {  // ... 很多代码 });   define(  'path/c',  ['require'],  function (require) {  // ... 很多代码 });   define(  'path/b',  ['require', 'a', 'b'],  function (require) {  // ... 很多代码  require('a');  // ... 很多代码  require(['b'], function (b) {});  // ... 很多代码  require('c'); }); 

上面可见, a.jsc.js 这两个文件被合并到了 d.js 中,所有文件都加上了具名id。而且这个id的生成规则是更具路径生成的。

而我们异步加载的 b.js 文件就没有被打包进来。这是因为我们期望 b.js 是懒加载的,当使用时在加载,这样也能达到按需加载的目的。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » AMD的一道面试题

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮