AMD的一道面试题

图片描述

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

以前的面试中,一直感受模块化AMD,CMD没有什么能够问的,不过昨天面试忽然想到一个题目:
对于一个AMD的模式下git

文件d.js以下es6

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

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

题目不难面试

答案是a.jsc.js 是在加载完d.js后就加载。
b.js是在执行到这一行时异步加载的。浏览器

具体分析:异步

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

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

同步加载,异步加载

首先你们须要知道AMD里面一个同步加载和异步加载的概念。ui

从概念上面理解,同步就是当我执行到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('*')都在中括号内配置一次,一样删除时也不用去删掉配置。

由于这一步彻底能够在编译时处理。

打包编译的延生

不知道你们有没有看过编译后的代码和开发环境代码的区别,对于这个d.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是懒加载的,当使用时在加载,这样也能达到按需加载的目的。

公众号

图片描述

博客地址

http://tangguangyao.github.io/

相关文章
相关标签/搜索