阅读 238

JavaScript模块化的演变

JavaScript模块化的演变

前情回顾:

自执行函数(IIFE):

作用:马上执行这个函数,自执行函数(IIFE),不易读

(function(x){console.log(x);})(3);

易读版本:

(function(x){    return x *x;
})(3);

闭包引申:

回顾:

function create_counter(initial){    var x = initial || 0;  //如果initial没有值,那么采用后面的0
    return {        //对象
        inc:function(){
            x+=1;            return x;
        }
    }
}var c1 = create_counter();console.log(c1.inc());console.log(c1.inc());console.log(c1.inc());var c2 = create_counter(10);console.log(c2.inc());console.log(c2.inc());console.log(c2.inc()); */

箭头函数:

function (x){    return x * x;
}

上述代码等价于下面:

x => x*x;

箭头函数的无参、单参、双参、多参的格式:

//无参()=>3;//单参x =>{    if(x > 0){        return x*x;
    }else{        return -x*x;
    }
}//双参(x,y) => x*x+y*y;//多参(x,y,...rest)=>{

}

this指向的引入以及发展:

this的指向在有无use strict会不同的,我们通过几段不同的代码段引入this以及this指向的发展。

'use strict';//1版-->正常使用var xiaoming = {    name:'小明',    birth:2000,    age:function(){        var y = new Date().getFullYear();        return y - this.birth;
    }
};console.log(xiaoming.age());

//2版:'use strict';/* 
  严格模式:
  xiaoming.age() 可以得到结果,
  getAge()显示报错--> Cannot read property 'birth' of undefined
*/function getAge(){    var y = new Date().getFullYear();    return y - this.birth;
}var xiaoming = {    name:'小明',    birth:2000,    age:getAge
};console.log(xiaoming.age());console.log(getAge());

前两个版本暂时还是没有引出this以及指向,下面看第三个版本,这个版本会有较大的改变,在不在strict中的结果也会不一样。

'use strict';/* 
  严格模式下:会报错,Cannot read property 'birth' of undefined
  非严格模式下:NaN  not a number,浏览器不会爆红,可能指向window
 */var obj = {    birth:2000,    getAge:function(){        function GetAgeFormBirth(){            var y = new Date().getFullYear();            return y - this.birth;
        }        return GetAgeFormBirth();
    }
};console.log(obj.getAge());

这是js的遗留问题,为了解决这个问题,我们可以使用变量保存this的指向,如版本四:

'use strict';var obj = {    birth:2000,    getAge:function(){        //保存thisz指向
        var that = this;        function GetAgeFormBirth(){            var y = new Date().getFullYear();            return y - that.birth;
        }        return GetAgeFormBirth();
    }
};console.log(obj.getAge());

但是这种表述方式比较麻烦,代码量也有些多,我们可以采用箭头函数来实现:

//这是个对象'use strict';var obj = {    birth:2000,    getAge:function(){        var b = this.birth;        var fn = ()=> new Date().getFullYear()-this.birth;        return fn();
    }
};console.log(obj.getAge());

模块化演变:

模块化演变是为了初学者一步一步实现和解决代码的冗余、复用性、命名污染问题。

模块演变1:

缺点已在代码块中标明

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title></head><body>
    <!-- html --》"" -->
    <!-- js --》'' -->
    <!-- 使用内嵌脚本 -->
    <!-- 
        缺点:
        复用性 很低 
        1.缺乏依赖解析
        2.全局命名空间污染的问题
    -->
    <h1>
        the answer is <span id="answer"></span>
    </h1>
    <script>
        function add(a,b){            return a+b;
        }        function reduce(arr,iteratee){            var index=0,length = arr.length,memo = arr[index];
	index+=1;            for(index =0;index < length;index++){
                memo = iteratee(memo,arr[index]);
            }            return memo;
        }        function sum(arr){            return reduce(arr,add);
        }        var values = [1,2,3,4,5,6,7,8,9];        var answer = sum(values);        document.getElementById("answer").innerHTML = answer;    </script></body></html>

模块演变2:

add.js

function add(a,b){    return a+b;
}

reduce.js

function reduce(arr,iteratee){    var index=0,length = arr.length,memo = arr[index];
    index+=1;    for(index =0;index < length;index++){
        memo = iteratee(memo,arr[index]);
    }    return memo;
}

sum.js

function sum(arr){    return reduce(arr,add);
}

main.js

var values = [1,2,3,4,5,6,7,8,9];var answer = sum(values);document.getElementById("answer").innerHTML = answer;

index.html

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title></head><body>
    <h1>
        the answer is <span id="answer"></span>
    </h1>
    <!-- script标签引入JavaScript -->
    <!-- 缺点:
        必须保证js的引用顺序正确
        同样缺乏依赖解析
        同样有命名冲突的问题
    -->
    <script src = "./add.js"></script>
    <script src = "./reduce.js"></script>
    <script src="./sum.js"></script>
    <script src = "./main.js"></script></body></html>

模块演变3:

myApp.js

// 空对象var myApp = {
};

add.js

// 立即执行函数(function (){    // 将add放入myApp中
    myApp.add = function(a,b){        return  a+b;
    }
})();

reduce.js

// 立即执行函数IIFE(function (){    // 将reduce放入myApp中
    myApp.reduce = function(arr,iteratee){        var index=0,length = arr.length,memo = arr[index];
	index+=1;        for(;index < length;index++){
            memo = iteratee(memo,arr[index]);
        }    return memo;
    }
})();

sum.js

// 立即执行函数(function (){    // 将sum放入myApp中
    myApp.sum = function(arr){        return  myApp.reduce(arr,myApp.add);
    }
})();

main.js

/**
 * @description: 
 * @param {*}
 * @return {*}
 *//* (function(){
    var values = [1,2,3,4,5,6,7,8,9];
    var answer = myApp.sum(values);
    document.getElementById("answer").innerHTML = answer;
}); */(function(app){    var values = [1,2,3,4,5,6,7,8,9];    var answer = myApp.sum(values);    document.getElementById("answer").innerHTML = answer;
})(myApp);

index.html

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title></head><body>
    <h1>
        the answer is <span id="answer"></span>
    </h1>

    <script src="./myapp.js"></script>
    <script src="./reducec.js"></script>
    <script src="./sum.js"></script>
    <script src="./add.js"></script>
    <script src="./main.js"></script>
    </body></html>

模块演变4:

采用require.js来实现。

为什么要使用require.js来实现:

在正式开发的项目中,随着js的外部引用文件越来越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

在开发中,有一个网页三秒原则

网页三秒原则:

当用户的在请求一个网站的时候,响应时间超过三秒钟,大部分用户将关闭或者重新刷新网页,用户体验很不爽。

所以require.js就解决上述问题:

  1. 实现js文件的异步加载,避免网页失去响应;

  2. 管理模块之间的依赖性,便于代码的编写和维护。

require.js的AMD格式规范:

// main.jsdefine(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){    // some code here});

define()函数接受的参数。

  1. 第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;

  2. 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

  3. 模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中

  4. 如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,且指明该模块的依赖性。

define()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

以下是模块演变4的实现代码:

index.js

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title></head><body>
    <h1>
        the answer is <span id="answer"></span>
    </h1>
	<!-- 入口点是main -->
	<!-- require加载了main.js的依赖,也会加载它依赖的依赖 -->
	<!-- 带来的问题:
		1. 降低一些性能
	 -->
    <script data-main="main" src = "require.js"></script></body></html>

data-main属性的作用是,指定网页程序的主模块

main.js

define(['sum'],function(sum){		var value = [1,2,3,4,5,6,7,8,9];		var answer = sum(value);		document.getElementById('answer').innerHTML = answer;
})

sum.js

define([    'add',    'reduce'], function(add, reduce) {    var sum = function(arr){        return reduce(arr,add);
    }    return sum;
    
});

reduce.js

define([], function(arr,iteratee){    var reduce = function(arr,iteratee){        var index=0,length = arr.length,memo = arr[index];
	index+=1;        for(;index < length;index++){
            memo = iteratee(memo,arr[index]);
        }        return memo;
    }    return reduce;
});

add.js

define([],function(){    var add = function(a,b){        return a+b;
    }	return add;
})

分析上述代码的执行流程:

  1. 先加载index.html以及require.js文件,找到模块的主入口(main.js)

  2. 加载main.js,由于main中依赖sum.js

  3. 再加载sum.js,sum中依赖add.js以及reduce.js

  4. 再加载add.js以及reduce.js

  5. 最后全部依赖执行完成后,回调得到的结果

执行效果以及文件加载顺序的观察:

image-20210712142033851

模块演变5:

基于commonJS规范以及browserify浏览器端的模块演变

什么是browserify:

browserify是专注于解决按照CommonJS规范书写的模块能够在浏览器中使用问题的构建工具。

CommonJS规范:

  1. 每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见

  2. 每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

下载方式:

npm install -g browserify

代码如下:

index.js

<!DOCTYPE html><html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>
			The Answer is <span id="answer"></span>
		</h1>
		<!--  browserify .\main.js -o bundle.js   打包-->
		<script src="bundle.js"></script>
	</body></html>

注意点:引用的js的文件是与我们打包命令时 -o后面名字对应的。

main.js

var sum = require('./sum');var values = [1,2,3,4,5,6,7,8,9];var answer = sum(values);document.getElementById("answer").innerHTML = answer;

sum.js

var reduce = require('./reduce');var add = require('./add');module.exports = function(arr){	return reduce(arr,add);
};

reduce.js

module.exports = function reduce(arr,iteratee){        var index=0,length = arr.length,memo = arr[index];
	index+=1;        for(;index < length;index++){
            memo = iteratee(memo,arr[index]);
        }        return memo;
    };

add.js

module.exports = function add(a,b){	return a+b;
};

打包方式:

browserify .\main.js -o bundle.js   //window中

效果图:

image-20210712125510595

运行查看加载信息:

image-20210712144048436

可以与require.js的加载方式做对比,理解两者的不同以及相同的地方。

扩展:

require.js VS browserify

  • require.js是模块加载器;browserify是预编译工具

  • require.js遵循的是AMD规范;browserify遵循的是CommonJS规范

  • require.js是在浏览器端运行期分析依赖;browserify在服务器端编译期就进行了依赖分析,通过每个模块的外部接口获取信息

结束:

如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢;

有错误的地方,欢迎在评论指出,作者看到会进行修改。


__EOF__

本文作者YLJ
本文链接:https://www.cnblogs.com/xbhog/p/15001650.html


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐