资讯专栏INFORMATION COLUMN

AMD的一道面试题

xiaoqibTn / 3458人阅读

摘要:之前的面试中,一直感觉模块化没有什么可以问的,不过昨天面试突然想到一个题目对于一个的模式下文件如下很多代码很多代码很多代码文件分别是什么时候加载的,如何加载的题目不难答案是和是在加载完后就加载。

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

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

文件d.js如下

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

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

题目不难

答案是a.jsc.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的实践。
这里面需要处理两个核心步骤

执行到require("a");时,a.js必须已经加载好了;

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/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78486.html

相关文章

  • 你不知道h5

    摘要:目前,常用的模块规范主要有两种和。拦截全局请求一直接引入脚本拦截需要的回调或函数。深刻知道一个良好的命名规范的重要性,同时在项目中也会遇到一些命名的瓶颈。 基于 Three.js 的超快的 3D 开发框架:Whitestorm.js Whitestorm.js 是一款基于 Three.js 超快的 Web 应用 3D 开发框架。它为普通的 Three.js 任务提供封装、使搭建环境、...

    li21 评论0 收藏0
  • 你不知道h5

    摘要:目前,常用的模块规范主要有两种和。拦截全局请求一直接引入脚本拦截需要的回调或函数。深刻知道一个良好的命名规范的重要性,同时在项目中也会遇到一些命名的瓶颈。 基于 Three.js 的超快的 3D 开发框架:Whitestorm.js Whitestorm.js 是一款基于 Three.js 超快的 Web 应用 3D 开发框架。它为普通的 Three.js 任务提供封装、使搭建环境、...

    IntMain 评论0 收藏0
  • 一道JS面试引发血案

    摘要:项目组长给我看了一道面试别人的面试题。打铁趁热,再来一道题来加深下理解。作者以乐之名本文原创,有不当的地方欢迎指出。 showImg(https://segmentfault.com/img/bVbur0z?w=600&h=400); 刚入职新公司,属于公司萌新一枚,一天下午对着屏幕看代码架构时。BI项目组长给我看了一道面试别人的JS面试题。 虽然答对了,但把理由说错了,照样不及格。 ...

    fantix 评论0 收藏0
  • 一道多线程面试引起自我救赎

    摘要:重温一个面试题内容数组内容为数组内容为个英文字母,使用两个线程分别输入两个数组,打印内容为这样的规律提取一下核心内容,去除次要内容两个线程需要交替执行,打印数字的线程需要先执行,数组打印完毕后线程需要结束。 一道多线程面试题引起的自我救赎 近日去一个知名互联网企业参加面试,之前准备多多信心满满,但是面试一开始就是一道不起眼的编程题 数组A内容为 1,2,3,4...52 ,数组B内容...

    BaronZhang 评论0 收藏0
  • 前端面试 - 收藏集 - 掘金

    摘要:一基础接口的意义百度规范扩展回调抽象类的意义我的前端面试经历百度前端掘金博主就读于电子科技大学,大三狗一枚面试是个漫长的过程,从海投到收获电话面试,一面二面三面,一个步骤出错那么后面就宣告终结。 一道常被人轻视的前端 JS 面试题 - 前端 - 掘金 目录前言第一问第二问变量声明提升函数表达式第三问第四问第五问第六问构造函数的返回值第七问最后前言 年前刚刚离职了,分享下我曾经出过的一道...

    lpjustdoit 评论0 收藏0

发表评论

0条评论

xiaoqibTn

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<