本文共 10825 字,大约阅读时间需要 36 分钟。
本人是个新手,写下博客用于自我复习、自我总结。 如有错误之处,请各位大佬指出。 学习资料来源于:尚硅谷
说在最前面:需要注意的是,代码中涉及到了ES6,出现了一些新特性,也许放在编译器里会报错。(因为我之前用Eclipse,发现把代码写在其中报了错,在idea上就没问题了,因为idea上是可以设置使用版本的,目前没发现Eclipse如何改版本)
当项目功能越来越多,代码量便也会越来越多,后期的维护难度会增大,此时在JS方面就会考虑使用模块化规范去管理。建议学习完模块化规范以后再学习项目构建,以更好的武装自己的技能。
JS模块化:将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),再把它们组合在一起。块的内部数据/实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信。
可以明显看出来,很容易出现命名冲突的问题。
01_全局function模式
module1.js
/** * 全局函数模式: 将不同的功能封装成不同的全局函数 * 问题: Global被污染了, 很容易引起命名冲突 */function foo() { console.log('foo()')}function bar() { console.log('bar()')}
module2.js
function foo() { //与另一个模块中的函数冲突了 console.log('foo()2')}
可以明显看出来,这样数据不安全,外部可以直接修改。
02_namespace模式
module1.js
/** * namespace模式: 简单对象封装 * 作用: 减少了全局变量 * 问题: 不安全(数据不是私有的, 外部可以直接修改) */let myModule = { data: 'hello', foo: function(){ console.log(this.data); }}
现在就保证了安全。
03_IIFE模式
/** * IIFE模式: 匿名函数自调用(闭包) * IIFE : immediately-invoked function expression(立即调用函数表达式) * 作用: 数据是私有的, 外部只能通过暴露的方法操作 * 问题: 如果当前这个模块依赖另一个模块怎么办? */(function (window) { //数据 let data = 'hello' //操作数据的函数 function foo() { //用于暴露有函数 console.log('foo()',data) } //暴露行为 window.myModule = { foo}})(window)
04_IIFE模式增强
module4.js
/** * IIFE模式增强 : 引入依赖 * 这就是现代模块实现的基石 */(function (window, $) { //数据 let data = 'hello' //操作数据的函数 function foo() { //用于暴露有函数 console.log('foo()' ,data) $('body').css('background', 'red') } //暴露行为 window.myModule = { foo}})(window, jQuery)
(在这里建议先学习Node.JS,相关语法在Node.JS中提及,而且学习模块化会有更好的理解。)
说明:
基本语法:
require()
函数来引入一个模块var 变量 = require("模块的标识");
exports
例子: exports.属性 = 属性值;
exports.方法 = 函数;
module.exports
例子: module.exports.属性 = 属性值;
module.exports.方法 = 函数;
module.exports = {};
var fs = require("fs");
var express = require("express");
对于自定义的文件模块,需要通过文件的路径来对模块进行引入。 路径可以是绝对路径,如果是相对路径必须以./或 …/开头。 var router = require("./router");
exports
导出、module.exports
导出,它们之间的用法是有区别的:
helloModule.js
module.exports.name = "孙悟空";module.exports.age = 18;module.exports.sayName = function () { console.log("我是孙悟空");};/* 无法使用这种方式,会报错。但module.exports就不会exports = { name:"猪八戒", age:28, sayName:function () { console.log("我是猪八戒"); }};*/
var hello = require("./helloModule");/* exports 和 module.exports - 通过exports只能使用.的方式来向外暴露内部变量 exports.xxx = xxx - 而module.exports既可以通过.的形式,也可以直接赋值 module.exports.xxx = xxxx module.exports = {} */console.log(hello.name);console.log(hello.age);hello.sayName();
项目结构:
不要忘记,package.json可以用npm init
来创建,建议不要手动创建(毕竟麻烦)。 "name"中不能有大写字母。(保留了简单的信息,毕竟我们不需要上传项目) 在这里我们下载一个第三方模块用来演示:(在当前项目目录下安装)
npm install 包名 --save
:用来安装包,并添加到依赖中 这其中的uniq的用法:(就是用来去除数组中重复的内容)
安装好之后: module1.js//使用module.exports = value向外暴露一个对象module.exports = { msg: 'module1', foo() { console.log(this.msg); }}
module2.js
//使用module.exports = value向外暴露一个函数module.exports = function () { console.log('module2');}
module3.js
//使用exports.xxx = value向外暴露一个对象exports.foo = function () { console.log('module3 foo()')}exports.bar = function () { console.log('module3 bar()')}exports.arr = [2,4,5,2,3,5,1,11];
app.js
/** 1. 定义暴露模块: module.exports = value; exports.xxx = value; 但是exports不能使用exports = {}的方式,但module.exports可以 2. 引入模块: var module = require(模块名或模块路径); */let uniq = require('uniq');//引用模块let module1 = require('./modules/module1');let module2 = require('./modules/module2');let module3 = require('./modules/module3');//使用模块module1.foo();module2();module3.foo();module3.bar();let result = uniq(module3.arr);console.log(result);
输出结果:
(uniq不仅能去掉重复元素,还能根据元素首位数字的大小进行排序)项目结构:
在当前项目目录下安装: (直接save加依赖,加的是’运行依赖’的包,save-dev加依赖,加的是’开发依赖’的包,-g是全局下载包,给计算机用的)安装后:
package.json:{ "name": "commonjs-browserify", "version": "1.0.0", "dependencies": { "uniq": "^1.0.1" }, "devDependencies": { "browserify": "^16.5.1" }}
module1.js
//使用module.exports = value向外暴露一个对象module.exports = { foo() { console.log('module1 foo()') }}
module2.js
//使用module.exports = value向外暴露一个函数module.exports = function () { console.log('module2()')}
module3.js
//使用exports.xxx = value向外暴露一个对象exports.foo = function () { console.log('module3 foo()')}exports.bar = function () { console.log('module3 bar()')}
app.js
//引用模块let module1 = require('./module1')let module2 = require('./module2')let module3 = require('./module3')let uniq = require('uniq')//使用模块module1.foo()module2()module3.foo()module3.bar()console.log(uniq([1, 3, 1, 4, 3]))
在之前已经说过了,浏览器是无法识别require语法的,需要提前编译打包处理,Browserify就是帮我们做这个事情的。
这段指令分成三部分。除去browserify。-o
:output,意味着输出。输出的左侧是需要处理的源文件,输出的右侧是作用后输出的文件位置及名字。(起什么名字都可以,只是bundle是打包的意思) 完成操作后:
index.html
Title
然后打开页面,就可以看到输出结果了:
AMD(Asynchronous Module Definition):异步模块定义,专门用于浏览器端。模块的加载是异步的。AMD规范的出现比CommonJS-Browserify要早。
基本语法:
define(function(){return 模块对象})
define([依赖模块名], function(){return 模块对象})
如:define(['module1','module2'],function(m1,m2){return 模块对象})
require(['module1', 'module2'], function(m1, m2){//使用模块对象})
requirejs(['module1', 'module2'], function(m1, m2){//使用模块对象})
(首先,我们不使用AMD规范,自己来写)
项目结构:dataService.js
//定义一个没有依赖的模块(function (window) { let msg = 'dataService.js'; function getMsg() { return msg; } window.dataService = { getMsg}})(window)
alerter.js
//定义一个有依赖的模块(function (window, dataService) { let msg = 'alerter.js'; function showMsg() { alert(dataService.getMsg() + ', ' + msg); } window.alerter = { showMsg}})(window, dataService)
app.js
(function (alerter) { alerter.showMsg();})(alerter)
test1.html
Modular Demo 1 Modular Demo 1: 未使用AMD(require.js)
很明显的,我们需要通过script
标签,将所有依赖导入才能使用,显然很麻烦。
接下来使用模块化规范👇
在使用之前,需要下载require.js,并引入:
然后将require.js导入项目: js/libs/require.js
项目结构: dataService.js//定义没有依赖的模块define(function () { let name = 'dataService.js'; function getName() { return name; } return { getName}});
alerter.js
//定义有依赖的模块define(['dataService', 'jquery'], function (dataService, $) { let msg = 'alerter.js'; function showMsg() { $('body').css('background', 'gray'); alert(dataService.getName() + ', ' + msg); } return { showMsg}});
main.js
(function () { //配置 requirejs.config({ //基本路径 (出发点在根目录下) baseUrl: 'js/', //映射: 模块标识名: 路径 paths: { //注意:文件千万不要写 .js,规范会自动帮我们加 //自定义模块 alerter: './modules/alerter', dataService: './modules/dataService', //库模块 jquery: './libs/jquery-1.10.1', angular: './libs/angular' }, //暴露angular对象 shim:{ angular:{ exports: 'angular' } } }) //引入模块使用 requirejs(['alerter','angular'], function (alerter,angular) { alerter.showMsg(); console.log(angular); })})()
index2.html
Modular Demo 2
其中需要注意的是,要引入jQuery模块时,一定要使用这个名字:jquery
。
shim
来暴露出angular
对象。 CMD在市面上用的不多。CMD也是专门用于浏览器端,模块的加载也是异步的。模块只在使用时才会加载执行。
基本语法:(像是AMD和CommonJS的合并)
define(function(require, exports, module){ //通过require引入依赖模块 //引入依赖模块(同步) var module2 = require('./module2') //引入依赖模块(异步) require.async('./module3',function(m3){ }) //通过module/exports来暴露模块 exports.xxx = value module.exports = value})
define(function(require){ var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show()})
在使用之前,需要下载sea.js,并引入:
(需要说明的是,因为sea.js好像已经被卖了,网站应该已经点不开了,同时应该也找不到官方下载途径了,所以sea.js听人说已经快属于文物了。而且sea.js是阿里的大佬写的,目前只有阿里少部分在用。不过下载不到sea.js也没关系,其他模块化方法也可行,这里了解即可)
然后将sea.js导入项目: js/libs/sea.js
项目结构: module1.js//定义没有依赖的模块define(function (require, exports, module) { //内部变量数据 let msg = 'module1'; //内部函数 function foo() { return msg; } //向外暴露 module.exports = { foo};});
module2.js
define(function (require, exports, module) { let msg = 'module2'; function bar(){ console.log(msg); } module.exports = bar;});
module3.js
define(function (require, exports, module) { let data = 'module3'; function fun(){ console.log(data); } exports.module3 = { fun};});
module4.js
define(function (require, exports, module) { let msg = 'module4'; //引入依赖模块(同步) let module2 = require('./module2'); module2(); //引入依赖模块(异步) require.async('./module3', function (m3) { m3.module3.fun(); }); function fun2(){ console.log(msg); } exports.fun2 = fun2;})
main.js
define(function (require) { var m1 = require('./module1'); console.log(m1.foo()); var m4 = require('./module4'); m4.fun2();})
index.html
Title
ES6规范很重要,目前使用最多的就是它。而且ES6要比CommonJS和AMD好用的多。
ES6的依赖模块也需要编译打包处理,这是因为有一些浏览器没有ES6的语法,所以需要把它编译成ES5,来让浏览器识别。当然我们之前也提到过,浏览器也不识别require
语法,所以这就需要用到Browserify
。
基本语法:
export
import
实现(浏览器端):使用Babel
将ES6编译为ES5代码,再使用Browserify
编译打包成JS。
项目结构:
①首先定义package.json文件:{ "name": "es6-babel-browserify", "version": "1.0.0"}
②安装:babel-cli
, babel-preset-es2015
和browserify
babel-cli
:command line interface (命令行接口),让我们能使用babel的命令。
babel-preset-es2015
:下载ES6转为ES5的所有插件。 ③定义.babelrc文件
{ "presets": ["es2015"]}
当babel运行的时候,它会先去读.babelrc文件,看看要做什么: "presets": ["es2015"]
。数组中放es2015,它就会转ES6。(也能放其他参数,比如"react"
,它就会转JSX)
(以后可能看到很多rc文件,rc:run control ,即运行时控制文件:运行的时候读这个文件)
④ 定义js文件
module1.js
//暴露模块 分别暴露export function foo() { console.log('module1 foo()');}export let bar = function () { console.log('module1 bar()');}export let arr = [1, 2, 3, 4, 5];
module2.js
//统一暴露function fun1() { console.log('module2 fun1()');}function fun2() { console.log('module2 fun2()');}export { fun1, fun2};
module3.js
//这种方式可以暴露任意数据类型,暴露什么数据接收到的就是什么数据export default { name: 'Tom', setName: function (name) { this.name = name }}
app.js
//引入其他模块import { foo, bar} from './module1'import { arr} from './module1'import { fun1, fun2} from './module2'foo();bar();console.log(arr);fun1();fun2();//上述用法如果出现重名,显然用不了import person from './module3'person.setName('JACK');console.log(person.name);//引入第三方模块(jQuery)import $ from 'jquery'$('body').css('background', 'red');⑤ 编译 ⑥ 页面中引入测试
index.html
Title
这其中就会发现,如果需要修改app.js,在修改之后,每次都需要重新编译,才能执行。
每次都需要反复敲命令。之后的自动化构建工具,就可以帮我们解决这个问题。转载地址:http://qoyki.baihongyu.com/