Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

模块化的发展史 #22

Open
huangchucai opened this issue Aug 2, 2017 · 0 comments
Open

模块化的发展史 #22

huangchucai opened this issue Aug 2, 2017 · 0 comments

Comments

@huangchucai
Copy link
Owner

世人皆醒,为我独醉,人生何苦如此---sunnyhuang

题目1: 为什么要使用模块化?

  1. 解决命名冲突
  2. 依赖管理
  3. 可以提高代码的可读性
  4. 提高代码的复用性
  5. 避免污染全局变量

题目2:CMD、AMD、CommonJS 规范分别指什么?有哪些应用

不同的运用
gulp和webpack

  • AMD(异步模块定义):由于浏览器加载脚本天生异步,AMD是一个在浏览器模快化开发的规范,但是由于元素的js并没有define()和require(),所以AMD规范运用了requirejs库。

  • ** CMD(通用模板定义)**:也是异步加载模块,它和AMD只是模块定义的方式和模块记载的时机不同

    AMD和CMD的区别以及运用

    1. 都运用于浏览器端,AMD是异步加载资源,而CMD是松散加载资源,根据需求加载。
    2. AMD是全部加载后,然后需求使用,而CMD是按需加载,就近依赖
    3. AMD在加载模块完成后就会执行模块,所有模快都记载完成后,就会进入required的回调函数,执行主逻辑函数,,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。
    4. CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的
    //AMD
    define(['jquery','dailog','carousel'],function(jquery,dailog,Carousel){
      var $=jquery;
      var dailog=dailog;
      var Carousel=Carousel;
    })
    //CMD
    //1. 定义一个模快myModule.js
    define(function(require,exports,module){
      var $=require(jquery);
      $("div").addClass("active");
      exports.add=function(){
        var i=0;
        i++
        return i
      }
    })
    define(function(require,exports,module){
      var sum=require("myModule").add;
      console.log(sum)
    })
  • CommonJS:是服务器端模快的规范,Node.js就是运用这个规范,使用与同步加载资源,一个单独的文件就是一个模快,每一个模快都有一个单独的作用域。

    1. 不需要引用其它的库,node.js自动封装了一个2个对象用于模快的输出和请求
    2. module.exports 模快的唯一出口,我们需要把模快要输出的东西放入该对象。
    3. require() 加载模块使用require方法,该方法读取一个文件并执行,返回文件内部的module.exports对象
    //定义一个a.js文件
    var name="hcc";
    function printName(){
      console.log(name)
    }
    module.exports={
      printName:printName
    }
    
    //在b.js中引用a.js
    var b= require('./a')  //接受a.js的exports的值 
    b.printName()    //输出hcc

requirejs详解

  1. 首页加载require.js的文件

    <script data-main="scripts/main.js" src="scripts/require.js"></script>
    //首先浏览器不会识别data-main属性的地址,

//当require.js加载完成后,发现require.js里有data-main的内容,
//就会回头加载data-main里面的位置资源

2. require.config() 配置文件,配置资源的入口
![事例文件夹配置](http://upload-images.jianshu.io/upload_images/3635292-9976be7feae413a6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

// 1.加载requirejs库(假如下载后的require.js在lib下)

<script scr="js/lib/require.js"></script>

//2.直接在index.html下配置require.js的文件路径

<script> requirejs.config({ baseUrl: "js/lib", paths:{ app: '../app', } }) //加载模快的入口 requirejs(['jquery','convas','app/sub'],function($,convas,sub){ ........ }) </script>
- AMD规范的调用

//1. 没有回调函数,就会直接调用依赖
define([jquery])
//2. 有回调函数,没有依赖
//main.js
define(function(){
var add = function(x, y) {
return x + y;
};
return {
add: add
};
})
//3. 有依赖,需要指明依赖数组
define(['myMoudel','jquery'],function(mymoudel,$){
function foo(){
dosomething() .....
}
return {
foo:foo
}
})


### 项目实际运用
![根目录列表](http://upload-images.jianshu.io/upload_images/3635292-d63146917d5ced4b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
1. 首页引入require.js
`<script data-main="./js/app/main.js"  src="./src/js/require.js"></script>`
**解析:**当浏览器加载到require.js,加载完成后,会去寻找data-main里面的脚本运用到页面中
2. 当解析完成后,会找到相应路经的main.js文件

requirejs.config({
baseUrl: './js/lib', //相对于根路径而已(index.html)
paths: {
'app': '../app' //设置简写的路径
}
});
//加载模快入口
requirejs(['app/index']);
//配置文件main.js的baseUrl是相对于index.html而言
//配置完require.config后,需要加载模快,
//对于没有回调函数,就会直接调用依赖,依赖位置在./js/lib/app/main.js

3. `./js/lib/app/main.js`里面的js文件加载页面的主要功能

define(['jquery','goTop','Carousel','Waterfall'],function( $ ,GoTop,Carousel,Waterfall){
new GoTop($(".goTop")) //回到顶部

Carousel.init($(".carousel"))  //轮播
//
new Waterfall($(".wrap-pic"))  //瀑布流

})

**解析:** 由于我们已经设置了main.js的基本目录,后面所有加载的js文件都是根据baseUrl+paths的路经,所以./js/lib下面的js插件都可以直接运用名字(后缀名js省略)
> 相当于加载jquery.js / goTop.js / Carousel.js / Waterfall.js  并给它传入到相应的回调函数的形参,用来调用相应的方法。

4. 解析define的用法
为什么`define(['jquery','goTop','Carousel','Waterfall'],function(){.....}`之后就会有相应的方法和函数出来呢?

define('id',[],function(){})
id: 模快名字,给一个js文件取一个名字
[] : 模快依赖,就是你这个js文件需要依赖的东西,例如:上面index.js依赖了jquery.js goTop.js.......
function(){}: 回调函数,加载完依赖后需要执行的东西

5. 相应的子js文件的解析
 * goTop.js

//定义goTop.js需要的依赖(jquery),在回调函数中用$使用jquery
define(['jquery'],function($){
function GoTop($ct){
this.$ct=$ct;
this.bind()
this.init()
}
GoTop.prototype.bind=function(){
if($(window).scrollTop()>50){
this.$ct.fadeIn()
this.$ct.on('click',function(){
$(window).scrollTop(0)
})
}
else {
this.$ct.fadeOut()
}
}
GoTop.prototype.init=function(){
var _this=this
$(window).on('scroll',function(){
_this.bind()
})
}

//return 出来
return GoTop

})
// 记住这里一定要,一定要,
//一定要return 出来给其它的js文件引用,
//就像index.js中define引用的goTop
//得到的就是我们return的GoTop构造函数。
//然后就可以new相应的构造函数得到相应的效果

* Waterfall.js

//定义Waterfall.js需要的jquery依赖
define(['jquery'],function($){
function Waterfall($ul) {
this.$ul=$ul;
this.$itemLi =this.$ul.find('.item-li') ;
this.btn=this.$ul.siblings(".item-btn")
this.init();
this.getData();
this.event()
}
Waterfall.prototype.init=function(){

        this.$itemLiWidth = this.$itemLi.outerWidth(true);
        this.arrLength = parseInt(this.$itemLi.parents('.wrap').width() / this.$itemLiWidth)
        this.pageCount= this.arrLength*2;
        this.curPage=1;
        this.dataIsArrive=false
        this.arr=[];
        //初始化数组
        for(var i=0;i<this.arrLength;i++){
            this.arr.push(0)
        }
    }
    Waterfall.prototype.event=function(){
        var _this=this;
        if(!this.dataIsArrive){
            this.btn.on('click',function(){
                _this.getData()
                _this.dataIsArrive=true
            })
        }
    }

    Waterfall.prototype.show = function ($node) {
        var top = $node.offset().top;
        var scr = $(window).scrollTop();
        var winHeight = $(window).height()
        if (top < scr + winHeight) {
            return true
        }
        else return false
    }
    Waterfall.prototype.getData = function () {
        var _this=this;
        if (!this.dataIsArrive) {
            $.ajax({
                method: "GET",
                url: "http://platform.sina.com.cn/slide/album_tech",
                dataType: "jsonp",
                jsonp: "jsoncallback",
                // http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4
                data: {
                    app_key: "1271687855",
                    num: this.pageCount,
                    page: this.curPage
                }
            }).done(function(res){
                // console.log(res.data)
                _this.curPage++
                _this.place(res.data)  //得到10条数据

                _this.dataIsArrive=false
            })
        }
        dataIsArrive=true
    }
    Waterfall.prototype.place=function(items){
        var _this=this
        this.$tpls=this.create(items);
        // console.log(this.$tpls)
        // console.log(this.$tpls.html())
        // console.log()
        $.each(this.$tpls,function(index,ele){
            var $node=$(ele);
            $node.find("img").on('load',function(){
                _this.$ul.append($node);
                _this.waterfall($node)                })
        })
    }
    Waterfall.prototype.waterfall=function($node){
        var idx=0,min=this.arr[idx]
        for(var i=0;i<this.arr.length;i++){
            if(this.arr[i]<min){
                idx=i
                min=this.arr[i]
            }
        }
        $node.css({
            top: min,
            left: idx*this.$itemLiWidth
        })
        // console.log($node.outerWidth(true))
        // console.log($node.outerHeight(true))
        this.arr[idx]=$node.outerHeight(true)+this.arr[idx]
        this.$ul.height(Math.max.apply(null,this.arr))
    }
    Waterfall.prototype.create=function(nodes){
        var tpls='';
        for(var i=0;i<nodes.length;i++){
            tpls+="<li class='item-li'>";
            tpls+="<a href="+nodes[i].url+">";
            tpls+="![](+nodes[i].img_url+)";
            tpls+="</a>"
            tpls+="<h4 class='title'>"+nodes[i].short_name+"</h4>"
            tpls+="<p class='desp'>"+nodes[i].short_intro+"</p>"
            tpls+="</li>"
        }
        return $(tpls)
    }
return Waterfall

//返回Waterfall构造函数给其它的文件使用
})

6. 打包所有的js文件,减少服务器请求

//全局安装requirejs打包js
npm install -g requirejs

7. 配置相应的路径文件打包
我们需要给r.js一个配置文件来打包所有的js文件,并且配置文件baseUrl找到我们requirejs.config配置的baseUrl
例如:build.js文件

({
// 这里的baseUrl和我们之前设置的入口文件main.js的baseUrl的区别
baseUrl:'../../js/lib', //以自己的位置为准 找到和requirejs.config的baseUrl的位置一样的文件夹
paths: {
app: '../app'
},
name: "app/main", //baseUrl+paths定位到main.js
out: "../dist/merge.js" //解析main.js并转换存放在当前文件的上级目录的dist文件中
})

8. 切换到build.js的文件夹下运行打包

r.js -o build.js //打包js文件

![打包减少请求](http://upload-images.jianshu.io/upload_images/3635292-b7904d56fe145165.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [requirejs官网](http://www.requirejs.cn/)
  ​
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant