这是我参与「第四届青训营 」笔记创作活动的的第12天
模块化Node相关知识铺垫
1. Node是什么?
是一个js运行环境
2.Node与npm是什么关系?
我们在开发时,经常会用到很多别人已经写好的代码或第三方库,我们一般会这样操作:搜索,下载,解压,引入。
如果每次都这么做势必很麻烦,于是Node.js的设计者打造了一个包管理器:npm,一些第三方库的作者把代码放在npm上。
当我们想要使用时,直接通过npm命令去安装即可,不用去理会应该下载什么?放在哪里?一切都处理好。
而且如果我们要用的模块A,而模块A又依赖模块B,模块B又依赖模块C和D,那么npm会根据依赖关系,把所有依赖的包都下载下来并且管理起来。
1.npm全称:Node package manager(Node包管理器),安装了Node就自动安装好了npm。 2.包(库、项目):电脑上的一个普通文件夹,包含了package.json,就变成了一个符合npm规范的包。 3.使用命令:
npm init把一个普通的文件夹变成一个包,即自动生成package.json
查看自己node的版本命令:node -v 查看自己npm的版本命令:npm -v
【版本要求】:
- node版本:最低是10
- npm版本:最低是6
- 更改源
npm config set registry http://registry.npm.taobao.org/
3.包名的要求:
不能有中文 不能有大写字母 不能与npm上已经存在的包重名,例如:(axios、jquery、less等)
4.安装一个包的命令
npm install xxxx
简写:npm i xxxx
npm install下载对应package.json文件中所有依赖的包。
模块化进化史
JS模块化
-
什么是模块?
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
- 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
-
一个模块的组成
- 数据--->内部的属性
- 操作数据的行为--->内部的函数
-
模块化
- 编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
模块化进化史
1. 全局function模式
-
module1.js
//数据 let data = 'atguigu.com' //操作数据的函数 function foo() { console.log(`foo() ${data}`) } function bar() { console.log(`bar() ${data}`) } -
module2.js
let data2 = 'other data'; function foo() { //这里与另一个模块中的函数冲突了 console.log(`foo() ${data2}`) } -
test.html
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript"> let data = "我是修改后的数据" foo() bar() </script> -
说明:
- 全局函数模式: 将不同的功能封装成不同的全局函数
- 问题: Global被污染了, 很容易引起命名冲突
2. namespace模式
-
module1.js
let myModule = { data: 'module1 atguigu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } } -
module2.js
let myModule2 = { data: 'module2 atguigu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } } -
test.html
<script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module22.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() myModule2.foo() myModule2.bar() //可以直接修改模块内部的数据 myModule.data = 'other data' myModule.foo() </script> -
说明
- namespace模式: 简单对象封装
- 作用: 减少了全局变量
- 问题: 依然可以修改模块内部代码,不安全
3. IIFE模式(闭包)
-
module1.js
(function (window) { //数据 let data = 'atguigu.com' //操作数据的函数 function foo() { //向外暴露的内部私有函数 console.log(`foo() ${data}`) } function bar() {//向外暴露的内部私有函数 console.log(`bar() ${data}`) otherFun() //内部调用 } function otherFun() { //未暴露的内部私有函数 console.log('otherFun()') } //暴露行为 window.myModule = {foo, bar} })(window) -
test.html
<script type="text/javascript" src="module3.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() //myModule.otherFun() //报错:myModule.otherFun is not a function console.log(myModule.data) //undefined 不能访问模块内部数据 myModule.data = 'xxxx' //并不是修改的模块内部的data myModule.foo() //未受影响 </script> -
说明:
- IIFE模式: 匿名函数自调用(闭包)
- IIFE : immediately-invoked function expression(立即调用函数表达式)
- 作用: 数据是私有的, 外部只能通过暴露的方法操作
- 问题: 如果当前这个模块依赖另一个模块怎么办?
4. IIFE模式增强
-
引入jquery到项目中
-
module4.js
(function (window, $) { //数据 let data = 'atguigu.com' //操作数据的函数 function foo() { //用于暴露有函数 console.log(`foo() ${data}`) $('body').css('background', 'red') } function bar() {//用于暴露有函数 console.log(`bar() ${data}`) otherFun() //内部调用 } function otherFun() { //内部私有的函数 console.log('otherFun()') } //暴露行为 window.myModule = {foo, bar} })(window, jQuery) -
test4.html
<script type="text/javascript" src="jquery-1.10.1.js"></script> <script type="text/javascript" src="module4.js"></script> <script type="text/javascript"> myModule.foo() </script> -
说明
- IIFE模式增强 : 引入依赖
- 这就是现代模块实现的基石
5. 页面加载多个js的问题
-
页面:
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module3.js"></script> <script type="text/javascript" src="module4.js"></script> <script type="text/javascript" src="module5.js"></script> <script type="text/javascript" src="module6.js"></script> <script type="text/javascript" src="module7.js"></script> <script type="text/javascript" src="module8.js"></script> <script type="text/javascript" src="module9.js"></script> <script type="text/javascript" src="module10.js"></script> <script type="text/javascript" src="module11.js"></script> <script type="text/javascript" src="module12.js"></script> -
说明
-
一个页面需要引入多个js文件
-
问题:
- 请求过多
- 依赖模糊
- 难以维护
-
这些问题可以通过现代模块化编码和项目构建来解决
CommonJS服务端模块化教程(Node.js模块化教程)
-
0. 基本语法
CommonJS是唯一的双端模块化规范
在服务器端:模块的加载是运行时同步加载的
在浏览器端:模块需要提前编译打包处理(异步加载)
暴露模块:
- module.exports = value
- exports.xxx = value
引入模块:
- require(xxx)
- 若引入的是第三方模块,则xxx为文件名
- 若引入的是自定义模块,则xxx为模块文件路径,必须写'./'
1. 安装Node.js
2. 创建项目结构
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.json
{
"name": "test-0719",
"version": "1.0.0"
}
3. 模块化编码:
-
module1.js
module.exports = { data:'module1', foo(){ console.log('foo()------',this.data); }, bar(){ console.log('bar()------',this.data); } } -
module2.js
module.exports = function () { console.log('module2'); } -
module3.js
exports.foo = function () { console.log('foo() module3'); } exports.bar = function () { console.log('bar() module3'); } -
下载第三方模块uniq:打开左下角的Terminal,cd到02_CommonJS-Node路径,输入命令:
npm install uniq -
app.js
let module1 = require('./modules/module1') let module2 = require('./modules/module2') let module3 = require('./modules/module3') let a = require('uniq') module1.foo() module1.bar() module2() module3.foo() module3.bar() let arr = [1,11,2,2,2,5,5,5,3,4,6,6,9,7,8] console.log(a(arr));
4. 在node环境下运行app.js的两种方法(任选其一):
- 第一种方法:用命令启动:
node app.js - 第二种方法:用工具启动: 右键 --> Run 'xxxxx.js'
5. CommonJs模块化内置关系
暴露的本质是exports对象,更准确的说是module.exports所指向的那个对象。
注意:在CommonJs模块规范中,module.exports和exports.xxx不能混用,如果混用,以module.exports为主。
require.js使用教程
0. 基本语法
AMD:Asynchronous Module Definition 异步模块定义
暴露模块:
- 定义没有依赖的模块:define(function() {return 模块})
- 定义有依赖的模块:define(['module1','module2'], function(m1, m2) {return 模块})
引入模块:
- 入口js配置:requirejs.config({
// 项目中所有要用到的模块,都要在这里注册
path: {
}
requirejs(['module1','module2'], function(m1, m2) {使用m1,m2})
})
- 页面引入使用:<script data-main="js/main.js" src="js/libs/require.js"></script>
1. 下载require.js
- 官网: www.requirejs.cn/
- github : github.com/requirejs/r…
- 将require.js导入项目: js/libs/require.js
2. 创建项目结构
|-js
|-libs
|-require.js
|-modules
|-loger.js
|-dataService.js
|-main.js
|-index.html
3. 定义require.js的模块代码
-
dataService.js(没有依赖)
define(function () { let msg = 'atguigu.com' function getMsg() { return msg.toUpperCase() } return {getMsg} }) -
loger.js (有依赖)
define(['dataService', 'jquery'], function (dataService, $) { let name = 'Tom2' function showMsg() { $('body').css('background', 'gray') console.log(dataService.getMsg() + ', ' + name) } return {showMsg} })
4. 应用主(入口)js: main.js
requirejs.config({
//模块标识名与模块路径映射
paths: {
"loger": "modules/loger",
"dataService": "modules/dataService",
}
})
//引入使用模块
requirejs( ['loger'], function(loger) {
loger.showMsg()
})
5. 页面使用模块:
<script data-main="js/main.js" src="js/libs/require.js"></script>
6. 使用第三方基于require.js的框架(jquery)
-
将jquery的库文件导入到项目:
- js/libs/jquery-1.10.1.js
-
在main.js中配置jquery路径
paths: { 'jquery': 'libs/jquery-1.10.1' } -
在loger.js中使用jquery
define(['dataService','jquery'],function (dataService,$) { function showMsg() { console.log(dataService.getData()); $('body').css('background','skyblue') } return {showMsg} }) -
备注:define中的要写jquery不可以写jQuery,因为jQuery源码已经对AMD模块化进行了适配,已经定义好了"jquery"
sea.js简单使用教程
1. 下载sea.js, 并引入
- 官网: seajs.org/
- github : github.com/seajs/seajs
- 将sea.js导入项目: js/libs/sea.js
2. 创建项目结构
|-js
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html
3. 定义sea.js的模块代码
-
module1.js (没有依赖)
define(function (require,exports,module) { var name = 'module1'; function fun1() { console.log(name); } //暴露模块 exports.showName = fun1 }); -
module2.js (没有依赖)
define(function (require,exports,module) { var name = 'module2'; function fun2() { console.log(name); } //暴露模块 module.exports = fun2 }); -
module3.js (没有依赖)
define(function (require,exports,module) { var name = 'module3'; function fun3() { console.log(name); } //暴露模块 module.exports = fun3 }); -
module4.js (有依赖)
//module4依赖于module2,module3 define(function (require,exports,module) { var name = 'module4'; function fun4() { console.log(name); } //同步引入module2 let module2 = require('./module2') module2() //异步引入module3 require.async('./module3',function (m3) { m3.foo() }) //暴露模块 module.exports = fun4 }); -
main.js : 主(入口)模块
define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1() m4() })
4. index.html
<!--
使用seajs:
1. 引入sea.js库
2. 如何定义导出模块 :
define()
exports
module.exports
3. 如何依赖模块:
require()
4. 如何使用模块:
seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>
5.思考:为什么运行后输出结果如下?
module2 // module2同步引入,不执行完不会继续执行后续代码
module1
module4
module3 // module3异步引入,要等回调执行完再执行
ES6-Babel-Browserify模块化教程
0. ES6模块化语法
暴露模块:
- 分别暴露:export 暴露内容
- 统一暴露:export {xxx, yyy}
- 默认暴露:export default 暴露内容
引入模块:
- 默认暴露的引入:import xxx from './xxx'
- 几乎所有的第三方模块都用默认暴露的方式
- 分别暴露和统一暴露的引入:import {xxx, yyy} from './xxx'
- 必须加{}
1. 创建项目结构
|-js
|-src
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-index.html
|-package.json
{
"name" : "es6-modular-0719",
"version" : "1.0.0"
}
2. 安装babel-cli, babel-preset-es2015和browserify:
babel的两大作用:1. es6 -> es5 ; 2. jsx -> js
浏览器不认识ES6对象结构赋值语法,故安装babel。
第一步,全局安装:npm install babel-cli browserify -g
第二步,局部安装:npm install babel-preset-es2015
备注:若全局已经安装过browserify,可以在第一步中去除browserify
3. 创建.babelrc文件(给babel指定具体的任务),内容如下:
{
"presets": ["es2015"]
}
4. 编码
-
js/src/module1.js
//分别暴露 export function foo() { console.log('module1 foo()'); } export function bar() { console.log('module1 bar()'); } export const DATA_ARR = [1, 3, 5, 1] -
js/src/module2.js
//统一暴露 let data = 'module2 data' function fun1() { console.log('module2 fun1() ' + data); } function fun2() { console.log('module2 fun2() ' + data); } export {fun1, fun2} -
js/src/module3.js
//默认暴露 export default { name: 'Tom', setName: function (name) { this.name = name } } -
下载jQuery模块:
npm install jquery --save -
js/src/app.js
import {foo, bar} from './module1' import {DATA_ARR} from './module1' import {fun1, fun2} from './module2' import person from './module3' import $ from 'jquery' $('body').css('background', 'red') foo() bar() console.log(DATA_ARR); fun1() fun2() person.setName('JACK') console.log(person.name);
5. 编译源代码(确保已进入06_ES6_Babel_Browserify所在文件夹)
- 第一步:使用Babel将ES6编译为ES5代码 命令为:
babel js/src -d js/build - 第二步:使用Browserify编译js上一步生成的js 命令为:
browserify js/build/app.js -o js/build/build.js - 备注:第一步操作后Babel将es6的模块化语法,转换成了CommonJS模块化语法(浏览器不识别),所以需要第二步用Browserify再次编译。
6. 页面中引入测试
<script type="text/javascript" src="js/build/build.js"></script>