javascript模块化【RequireJS 】

1、模块化的由来

在没有模块化思想以前,咱们老是将大量的逻辑代码写在一块儿,这样的代码杂乱无章,没有条理性,不便于维护,不利用复用。而且不少代码重复,逻辑重复。甚至形成全局变量污染,也不方便保护私有数据。
为了解决上面的问题,模块化的编程思想应运而生。
模块化的基本思想就是:==闭包自调用函数==
对闭包了解不够的同窗,请先查看《 JS闭包全面解析》一文。html


2、模块规范

想要了解模块化就要先知道JS中3个模块规范。
JS中的模块规范(CommonJS,AMD,CMD),若是你听过模块化这个东西,那么你就应该听过或CommonJS、AMD、CMD这些规范,我也听过,但以前也真的是听听而已。直到最近项目中使用到了才有了必定的理解, 如今就看看吧,这些规范究竟是啥东西,怎么用的。(本文对CommonJS及CMD作一个大概的说明,对AMD中RequireJS作较为全面的讲解)前端

1.CommonJS

CommonJS API定义不少普通应用程序(主要指非浏览器的应用)使用的API,也是Node中使用的模块化解决方案。vue

2.CMD

CMD:common module define,CMD实际上是阿里一位大神编写的seajs中提出的模块化解决方案。
==其实CMD能够当作是CommonJS的前端实现==。
在前几年很是火,不过随着前端框架的崛起,vue、react、angular都集成了各自的模块化。而且es六、webpack等都提供了模块化的解决方案。使得seajs出场机会愈来愈少,做者也中止了更新。seajs也渐渐退出了历史的舞台。react

3.AMD

AMD:async module define:异步模块定义。
AMD其实就是requireJS实现的模块化解决方案,下面我会着重的介绍AMD规范中的requireJS。
连接>>>RequireJS中文网jquery


3、RequireJS

1.基本用法

例如在一个电商网站中,购物车和商品的逻辑会在须要场景应用,因此咱们就能够将二者抽出做为模块开发,使用的时候直接引用,直接上代码。
首先咱们先建立一个cart.js文件webpack

define([],function(){
    console.log('cart模块');
})

而后建立一个product.js文件es6

define([],function(){
    console.log('product模块');
})

而后在首页index.html中调用模块web

<!-- 首先在官网下载requirejs源文件,经过script标签导入 -->
<script src="../js/require.js"></script>
<script>
// 将以前定义好的cart和product模块导入首页模块中
require(["cart","product"],function(){
    console.log('这里是首页模块');
})
</script>
2.动态加载模块&模块返回值

仍是上面的栗子,再加一些代码
cart.js编程

define([],function(){
    // 将函数做为模块的返回值
    return function(){
        console.log('购物车模块初始化');
    }
})

product.jsbootstrap

define([],function(){
    // 模块不只能够返回函数,也可返回对象
    return {
        init() {
            console.log('商品模块初始化');
        }
    }
})

index.html

// 这里咱们给require的回调函数添加形参,接收前面对应模块的返回值,要与数组顺序一致
require(['cart','product'],function(cart,product) {
    // 这里咱们不想一进入就加载cart和product模块,而是等点击按钮再去加载模块
    // 这里也能够理解成按需加载模块
    var btn = document.getElementById('btn1');
    btn.onclick(function(){
        // 在按钮的点击事件中去加载模块
        cart();
        product.init();
    })
})

==注意==:

  • 回调函数中的形参必定要与,数组中导入模块的顺序一致
  • 没有返回值的模块尽可能放到最后导入(数组最后),固然es6中能够写成param1,,,param2
  • 大部分模块都是按需加载的
3.入口文件

通常将模块的入口也定义在一个单独的js文件中,如main.js。
这样引用入口文件处就能够简写为:

<script data-main="./main" src="../js/require.js"></script>
4.入口文件配置---path

经过在入口文件中的一些配置可让咱们在使用模块时更加便捷。
如咱们想在模块中使用jQuery,咱们要这样写

define([jquery-3.3.1],function($){
})

这里可能有人不理解为何每一个模块引用jq,都要引用jq模块。由于:

  • 防止全局变量污染(zepto:$)
  • 使用amd方式在每一个模块导入一下,$就是一个局部变量

设想一下,若是有几十个模块,每一个模块都这样引入jq,若是jq的文件目录发生改变亦或是jq版本改变,那将会是一个很是大的工程。
这时咱们就能够利用path来解决这个问题
main.js

require.config({
    path:{
        jquery:"lib/jquery-3.3.1", // 文件
        bootstrap:"assets/bootstrap/js/bootstrap.min", // 文件
        service:"../service" // 文件夹
    }
})

固然,不是全部的模块都须要配置在这里的,通常来讲经常使用的模块、文件夹才须要配置。
这样当须要用到jq的时候,只须要导入入口文件中配置好的jquery便可,后续的任何修改,每一个引用的模块都会同步。

// 指定文件的能够直接导入文件,指定文件夹的能够经过配置的文件夹目录找到对应文件
define(["jquery","service/xxxxService","bootstrap"],function($,xxxxService){
})

为何jq能够像咱们编写的其余模块同样被导入使用,是由于jq中已经注册了amd模块。虽然说jq的设计并非像咱们写的amd模块那样,可是在内部已经作了兼容,许多第三方库都是这么兼容amd的,经过源码能够看到:

define([],function() {
    // 定义一个模块,将jq对象返回,这样咱们在导入模块后拿到的参数$就是这个jq对象
    return jQuery;
})
5.入口文件配置---baseUrl

一个模块化的项目目录都会比较复杂,如建立两个js文件,cart.js、cartDetail.js。存放的目录为~/js/cart/中,那么想要在cart模块中倒入cartDetail模块就要这样去写:
cart.js

define(["js/cart/cartDetail"],function(cartDetail){
})

虽然两个模块同处一个文件夹中,可是模块的导入是根据入口文件所在的路径去查找的,若是入口文件放在根路径下,那么导入模块的路径也是根路径。
利用baseUrl简化路径查找:
main.js

require.config({
    baseUrl:"js/"
})

改造后咱们再导入模块能够这样去写:
cart.js

define(["cart/cartDetail"],function(cartDetail){
})

==注意==:path里面的配置也是相对于baseUrl的

6.requirejs中的循环依赖

场景:a模块依赖b模块,可是b模块也须要a模块,若是按常理去写会形成循环依赖,致使报错。

  • 这时咱们要在b模块中添加require模块的依赖,而后再添加a的依赖
  • ==可是必定不要去经过回调函数形参的形式获取返回值。==
  • 在须要执行a模块代码的时候经过require调用。
define(["require","a"],function(require){
    require("a")();
})

另外还须要==注意==的一点是:一个模块被不一样模块引用若干次,可是他们获取到的都是该模块同一个引用(闭包数据共享),模块代码不会从新执行,节省性能。

7.检测第三方库是否支持AMD规范

这个方式也是jQuery中使用的。

if ( typeof define === "function" && define.amd ) {
        define([], function() {
            return jQuery;
        } );
    }

4、总结

学习模块化,重要的不是学习具体的实现,而是学习一种思想。只有真正的领悟了模块化的思想才能把模块化更好的应用到开发中,而且在使用其余框架时才能更加驾轻就熟。

相关文章
相关标签/搜索