• 操刀 requirejs,自己动手写一个


    这篇文章讲的是,我怎么去写一个 requirejs 。

    去 github 上fork一下,顺便star~

    requirejs,众所周知,是一个非常出名的js模块化工具,可以让你使用模块化的方式组织代码,并异步加载你所需要的部分。balabala 等等好处不计其数。

    之所以写这篇文章,是做一个总结。目前打算动一动,换一份工作。感谢 一线码农 大大帮忙推了携程,得到了面试的机会。

    面试的时候,聊着聊着感觉问题都问在了自己的“点”上,应答都挺顺利,于是就慢慢膨胀了。在说到模块化的时候,我脑子一抽,凭着感觉说了一下requirejs实现的大概步骤,充满了表现欲望,废话一堆。侥幸不可能当场让我写一遍,算是过了,事后尝试了一下,在这里跟大家分享一下我的实现。

    上面是我划分的项目结构:

    1. tool,工具模块,存放便捷方法,很多地方需要用到。
    2. async,异步处理模块,主要实现了 promisedeferred 。逻辑上的异步。
    3. requirejs -> loader ,amd加载器,处理模块的依赖和异步加载。物理上的异步。

    因为对于异步流程控制方面,研究过一段时间,所以这里第一时间想到的就是 promise ,如果用这个来做,所有的模块放入字典,路径做key,promise做value,所有依赖都结束之后,才进行下一步操作。 不用管复杂的依赖关系,把逻辑尽量简单化:

    1. 首先有一个字典,存放所有的模块。key放地址,value放promise,promise在模块加载完毕的时候resolve。
    2. 如果依赖某个模块,先根据路径从字典找key,存在就用该promise,不存在就去加载该模块并放入字典,并使用该模块的promise。
    3. 所有的模块,我只用它的 promise ,在它的回调中写我的后续操作。它的resolve应该单独抽离出来,属于异步加载方面。

    大致思路有了,当然实际写的时候肯定困难重重,不过没关系,遇到问题再去解决。

    考虑到代码的简易性,以及我的个人习惯。我打算用类似于 jquery 的 $.Deferred() 和它的promise,与es6的promise有一定的出入。这样代码书写更简易,并且逻辑上更清晰,es6的promise用起来确实稍显麻烦。我需要的是一个 pub/sub 模式,一个地方触发,多个回调执行的并行方式,es6的promise,需要在then中一次次返回,并且resolve起来也不方便,最最主要的是需要 polyfill 一下,而我想自己写,写我熟悉且喜欢的代码 。

    回调模块 callbacks,熟悉jquery的朋友接下来可能会觉得使用方式很熟悉,没错,我受jq的影响算是比较深的。以前在学习jq源码的时候,就觉得这个很好用,你可以从我的代码里面看到jq的影子 :

     1 import _ from '../tool/tool';
     2 
     3 /**
     4  * 基础回调模块
     5  * 
     6  * @export
     7  * @returns callbacks
     8  */
     9 export default function () {
    10     let list = [],
    11         _args = (arguments[0] || '').split(' '),           // 参数数组
    12         fireState = 0,                                     // 触发状态  0-未触发过 1-触发中  2-触发完毕
    13         stopOnFalse = ~_args.indexOf('stopOnFalse'),       // stopOnFalse - 如果返回false就停止
    14         once = ~_args.indexOf('once'),                     // once - 只执行一次,即执行完毕就清空
    15         memory = ~_args.indexOf('memory') ? [] : null,     // memory - 保持状态
    16         fireArgs = [];                                     // fire 参数
    17 
    18     /**
    19      * 添加回调函数
    20      * 
    21      * @param {any} cb
    22      * @returns callbacks
    23      */
    24     function add(cb) {
    25         if (memory && fireState == 2) {  // 如果是memory模式,并且已经触发过
    26             cb.apply(null, fireArgs);
    27         }
    28 
    29         if (disabled()) return this;      // 如果被disabled
    30 
    31         list.push(cb);
    32         return this;
    33     }
    34 
    35     /**
    36      * 触发
    37      * 
    38      * @param {any} 任意参数
    39      * @returns callbacks
    40      */
    41     function fire() {
    42         if (disabled()) return this; // 如果被禁用
    43 
    44         fireArgs = _.makeArray(arguments); // 保存 fire 参数
    45 
    46         fireState = 1; // 触发中 
    47 
    48         _.each(list, (index, cb) => { // 依次触发回调
    49             if (cb.apply(null, fireArgs) === false && stopOnFalse) { // stopOnFalse 模式下,遇到false会停止触发
    50                 return false;
    51             }
    52         });
    53 
    54         fireState = 2; // 触发结束
    55 
    56         if (once) disable(); // 一次性列表
    57 
    58         return this;
    59     }
    60 
    61     function disable() {    // 禁止
    62         list = undefined;
    63         return this;
    64     }
    65 
    66     function disabled() {  // 获取是否被禁止
    67         return !list;
    68     }
    69 
    70     return {
    71         add: add,
    72         fire: fire,
    73         disable: disable,
    74         disabled: disabled
    75     };
    76 }
    View Code

    这是一个工厂方法,每次所需的对象由该方法生成,用闭包来隐藏局部变量,私有方法。而最后暴露(发布)出来的对象,用 pub/sub 模式,提供了 订阅触发禁用查看禁用 4个方法。 这里要说的是 ,提供了3个参数:stopOnFalseoncememory。触发的时候,按照订阅顺序依次触发,如果是 stopOnFalse 模式,当某个订阅的函数,返回是 false 的时候,停止整个触发过程。 如果是 once ,表示每个函数只能执行一次,在执行过后,会被移除队列。而 memory 状态下,在 callback 触发后,会被保持状态,之后添加的方法,添加后会直接执行。

    这三种模式,传参的时候直接传入字符串,可以随意组合,用空格分开,比如:callbacks('once memory')

    该模块用于整个项目中,处理所有的回调。使用方式类似于jquery的:$.Callbacks(...)

    写到这里,就已经结束了。本文讲了对于requirejs,我的实现思路,列举了可能遇到的问题,及我的解决方式。希望能给大家的学习提供点帮助。

    去 github 上fork一下,顺便star~

    上面是github的地址,求star啊,作为一个虚荣的人,我对这个很看重的,哈哈,也就这点追求了。再次感激 一线码农 大哥的推荐,还有 linkFly 的经验指导。

  • 相关阅读:
    福建90后大学生陈贺的果园梦
    95后女孩穿旗袍走红,老手艺在淘宝迎来不一样的改变
    动态创建完整功能的图片元素
    jquery 插入dom
    它会选择相对于span的所有div父元素
    表单过滤器之checkbox
    匹配所有的最后一个子元素
    docker 常用指令
    splice操作
    当《连线》说“第一台值得买的智能电视终于出现”时,你信不信?
  • 原文地址:https://www.cnblogs.com/lianmin/p/my_requirejs.html
Copyright © 2020-2023  润新知