NodeJs分为四个阶段 第三阶段~
node的模块化
begin enjoy👇

模块化编程

什么是模块化

所有的语言平台,只要其具有以下特征,则我们说它是使用模块编程:

  • 文件作用域
    • 也叫模块作用域,即模块文件具有封装性,文件内容只能在内部使用 而不是在外部访问
  • 加载与导出
    • 使用专门的语法 导出接口列表:export语法、exports对象、module.exports等
    • 使用专门的方法 加载模块文件:require、include、import等。

目前为止,关于前端 我们学过的规范或者规则有:

  • 核心语法ECMAScript支持的模块语法,ES6以后可以使用,但在Node中支持不足
  • 第三方库 requireJS规范,适用于客户端浏览器环境的异步加载机制,让前端具备模块化编程的能力
  • 第三方库 CommonJS规范,适用于服务端环境的模块同步加载机制,也是Node模块加载采用的规范

ES6模块规范

es6新增语法用于模块化编程,使得前端工程的结构更为合理,维护性和扩展性增强,但目前在node中支持不足

export语句

在客户端环境中,文件默认没有作用域可以通过script直接引入。当文件中存在export或者import语法时则被认为是模块文件并且具有封装性,export用来声明开放接口的列表,具体用法如下:

  • 使用export直接导出定义,通过在创建该内容的代码块前面添加export关键字

    //demo.js
    //一个文件中可以存在多个export
    export let foo = "foo";
    export function bar() {
        return "bar";
    };
    
  • export导出接口列表,以{}形式存放接口集合。此处的{}非对象的声明,只是集合语法

    let foo = "foo";
    function bar () {
        return "bar";
    }
    
    export {
        foo,
        bar
    };  //此处不是对象的简洁属性,而是{}集合
        //不能使用{fo : foo, ba : bar}
        //可以使用as给接口设置别名
    
    export {
        foo as fo,
        bar as ba,
    }
    
  • export default用来设置默认接口,这样在加载页面时就无需声明列表,直接接收即可。

    //demo.js
    let foo = "foo";
    function bar() {
        
    }
    
    //将foo作为默认接口导出
    //export default foo;	
    //export default只能出现一次 如果要返回多个接口 可以将它们放入对象中
    export default {
        foo,
        bar,
    } //此处为对象的简洁写法
    
    //加载页面中使用
    import demo from './demo.js';
    console.dir(demo);	//demo为一个对象,包含多个接口
    
    
  • 最后要注意,export语句必须处于模块文件顶层代码中,不能放在块级作用域中。

import 语句

页面中加载模块的script代码段需要声明type类型为module以区别普通的脚本;其次要以服务器地址来请求模块文件,否则会被跨域拦截。es6中使用import导入模块文件并接收接口列表。

  • import {add, minus} from './math.js' 其中相对路径中的./不可省略,.js后缀也不可省略 。

    //math.js
    export function add(x, y) {
        return x + y;
    }
    
    export function minus(x, y) {
        return x - y;
    }
    
    <script>
    	import {add, minu} from "./math.js";
        console.log(add, minu);
    </script>
    
  • import {add, minus as min } from './math.js' 导入时使用as重命名接口名称

    //math.js
    function add(x, y) {
        return x + y;
    }
    
    function minus(x, y) {
        return x - y;
    }
    
    export {
    	add,
        minus
    }
    
    <script>
    	import {add as sum, minus as mus} math from './math.js';
        console.log(sum, mus);
    </script>
    
  • import * as math from './math.js' 将模块成员存入mod对象中使用。

    //math.js
    function add(x, y) {
        return x + y;
    }
    
    function minus(x, y) {
        return x - y;
    }
    
    export {
    	add,
        minus
    }
    
    <script>
    	import * as math from './math.js';
        console.log(math);
    </script>
    
  • import math from './math.js' 使用了export default方式导出的接口

    //math.js
    export default {
        add (x, y) {
            return x + y;
        },
        
        minus (x, y) {
            return x - y;
        }
    }
    
    <script>
    	import math from './math.js';
        console.log(math);
    </script>
    

CMD模块规范

CMD模块规范相对于AMD,得名来自CommonJS。CommonJS是适用于服务端的同步风格的模块加载机制,也是Node采用的规范。在CommonJS模块规范中,一个文件就是一个模块 具有封装性(作用域)。每个文件有一个隐式声明module.exports对象,用来挂载接口。接下来在项目文件中,使用require('模块路径')载入该模块。

exports的使用

  • 在CommonJS中,每个模块文件都有一个隐式声明的module.exports对象以及一个引用它的exports对象,用来挂载接口 - 对象扩展;如果模块文件未设置module.exports或者exports则为一个空对象

    //mode.js
    let foo = "foo";
    
    function bar() {
    
    }
    
    //模块文件具有封装性 - 文件作用域
    console.log("hello world");
    
    //每个模块文件都有一个module.exports对象 用来挂载接口
    // module.exports.foo = foo;
    // module.exports.bar = bar;
    // console.log(module.exports);
    
    //exports引用了module.exports对象 便于使用
    // exports = module.exports;
    console.log(exports == module.exports); //true
    
    //因此可以直接简化导出接口列表的过程为:
    exports.foo = foo;
    exports.bar = bar;
    

    在项目文件中,使用require('模块标识符')载入该模块,并定义变量来接收module.exports的内容:

    //index.js
    const mode  = require('./mode');
    console.log(mode);	//{foo : "hello world", bar : function () {}}
    
  • 除了扩展的方式外,我们还可以重写module.exports对象为一个新值,这样require接收到的即为该值,它主要有以下两点优势:

    • 当模块中 只有一个接口时 (例如 定义类的模块文件),可以直接将该类导出 而不是作为接口对象成员。
    • 当模块中 接口较多时 可以将它们集合到一个对象中 然后作为module.exports的新值。
    //comm.Rectangle.js
    //定义矩形操作类模块
    class Rectangle {
        constructor(width, height) {
            this.w = width;
            this.h = height;
        }
    
        area() {
            return this.w * this.h;
        }
    
        perimter() {
            return 2 * (this.w + this.h);
        }
    }
    
    //重写module.exports对象为该接口内容
    module.exports = Rectangle;   
    
    /*
    * 此处如果使用exports = Rectangle则无效
    * 因为真正能够导出接口的是module.exports
    * exports是因为引用module.exports而能导出接口
    * 现在重置exports将使得其不再具备接口导出的能力
    
    * 同理,module.exports重置后 也会使exports丧失挂载接口的能力
    */
    
    //exports上挂载的一切接口都无效
    exports.a = "hello";
    exports.b = "world";
    
    //index.js
    const Rectangle = require("./comm.Rectangle.js");
    console.log(Rectangle);	
    

    对于module.exports或者exports导出的多接口,可以在项目文件中使用解构赋值的方式直接提取出来:

    //comm.math.js
    let x = 10,
        y = 20;
    
    function add() {
        return x + y;
    }
    
    function minus() {
        return x - y;
    }
    
    module.exports = {
        add,
        minus,
    }
    
    let {add, minus} = require('./comm.math.js');
    console.log(add, minus);
    

require的使用

在一些平台中,存在require()与require_once() 这样两个用于加载模块的函数;其中require()会重复加载被引入的文件(多次执行),而require_once()则在每次加载前验证当前是否已有加载的文件,如果已有则不再加载,也就是说每个文件最多只加载一次;而node中的require也是只加载一次

//mode.php
echo "hello world";

//index.php
require("mode.php");//输出
require("mode.php");//输出

require_once("mode.php");	//不再输出
require_once("mode.php");	//不再输出
//mode.js
console.log("hello world");

//index.js
require("./mode");	//只执行一次
require("./mode");

AMD模块规范

​ requireJS 是AMD风格的模块开发规范,所谓AMD是指异步模块定义规范,它非常适用于客户端(浏览器)环境

​ 模块开发是服务端必备的技术,随着前端发展 大规模的工程化应用也需要模块的支持 以增强项目的可维护性和扩展性。在前端模块化开发中,由于需要远程请求服务端资源 传统的script同步请求 在网络不佳的情况下 容易产生阻塞,因此必须使用异步的方式加载:

<script src="jquery.js" async="true"></script>

​ 如果要以异步方式加载,就要考虑文件和代码之间的依赖关系。之所以很多时候 我们以同步方式引入模块或者库文件,是因为后续代码的执行 必须依赖这个模块文件。现在 如果异步加载 势必要将依赖的代码 作为该模块加载完毕后的回调函数来执行,相当于这样:

<script src="jquery.js" async="true" onload="todo();"></script>
<script>
    function todo() {
    	//所有依赖jquery的代码 必须在此处执行
    	
	}
</script>
  • requireJS很好的解决了上述问题,并更加智能和高效。requireJS中,模块使用define定义,它有两个参数:
    • 一是该模块依赖的其他模块,需要先异步加载进来
    • 另一个是模块的定义代码,作为其他依赖模块都加载完毕后的回调函数

requireJS模块的封装性是通过函数作用域实现的,接下来 需要返回一个公有对象存储接口列表并返回。

//mode.js
//该模块依赖jquery,当jquery加载完毕后 执行回调并接收jquery为$对象
define(['jquery'], function ($) {
    //---模块核心代码---
    let foo = "foo";
    function bar() {};
    
    //---返回公有接口---
    return {
        foo, 
        bar
    } //使用简洁属性 模拟es6 exports导出的接口列表
    
});
  • 定义好的模块和接口,接下来使用require函数引入,在require引入之前 需要使用require.config()配置模块和文件路径的映射 这样才能更好的加载和使用模块。
//index.html中 首先载入require库文件 
<script src="require.js" async="true"></script>
//如果页面中的js作为外链文件 还可以声明data-main属性
<script src="require.js" async="true" data-main="js/index"></script>

//index.js
require.config({
    baseUrl : "./modules",	//模块根目录
    paths : {
        jquery : "./jquery-2.0.2.min",	//可以省略js
        foo : "foo"
    }
});

//依次载入依赖的模块 然后执行页面代码
require(["foo", "jquery"], function (f, $) {
    console.log(f, $);
    
    //页面特效的代码
    
});

node模块系统

node中使用的模块系统包含以下几种:

​ 1) 核心模块:fs、http、url、querystring等

​ 2) 第三方模块:使用npm从外部下载安装的模块,例如 art-template

​ 3) 自定义模块:用户自己定义的模块文件,例如 math.js、mode.js等

而require在引入上述模块时,可以传入两种格式的模块标识:路径形式和包名

require('模块标识符')

  • 当传入了一个路径时(以**./或者../**开头) 表示加载按照路径加载文件 一般用于自定义模块中。

  • 当传入一个模块名时,就是参数中只有名称时 表示加载核心模块或者第三方模块。此时的模块文件是存在于Node核心文件(nodex.exe)中或者使用npm下载的node_modules目录中。

    • require('fs')加载核心模块fs,fs.js模块文件随着node环境被编译到node.exe文件中。这里可以通过源码查看到该文件。@link http://github.com/nodejs

    • require("art-template") 加载第三方模块包,这里的art-template模块是存储于npm下载的包里面

      接下来引入包时 它有一套明确的查找机制:

      1. 首先查找当前目录的node_modules,定位node_modules/包名/libs/package.json包说明文件
      2. 查找配置项main,main用于指定包的入口文件,该文件用于调度包资源 并返回一个接口对象

      同理,我们使用npm安装jquery,查看它的package.json文件 找到main配置的入口文件;再接下来 我们可以自定义包 并使用require加载该包。注意require中传入的是包名。

      1. 如果上述规则不能有效的运行,例如 package.json不存在或者没有main项,或者main指定文件不存在, 此时它还有一个备选项 就是直接找到包中的index.js作为默认入口文件。

      2. 上述条件都不成立时,它会沿着上级目录层层查找node_modules 直到磁盘根目录为止。也就是说存在于项目顶层目录node_modules中的模块 可以在所有的项目文件中使用require(包名)导入。因此 建议将node_modules放置于项目根目录中。

      a
      	node_modules/art-template
      b
      	require("art-template");	//找不到 除非使用路径查找
      	require("../a/node_modules/art-template/index.js"); //可以找到
      

package.json

package.json是包说明文件,可用于项目中 用来添加项目描述以及管理项目中需要依赖的包。package.json文件可以通过npm init自动生成,该命令会以向导的方式提示基本的配置项。

#在控制台执行npm init
d:\Wamp\WWW\node\day3\web>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (web)
version: (1.0.0) 0.0.1
description: 这是一个测试项目                                                     
entry point: (index.js) index.js
test command:
git repository:
keywords:
author:
license:
d:\Wamp\WWW\node\day3\web>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.

package name: (web) web
version: (1.0.0) 0.0.1
description: demo app
entry point: (index.js) inidex.js
test command:
git repository:
keywords:
author: lautin
license: (ISC)
About to write to d:\Wamp\WWW\node\day3\web\package.json:

{
  "name": "web",
  "version": "0.0.1",
  "description": "demo app",
  "main": "inidex.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "lautin",
  "license": "ISC"
}

Is this OK? (yes) yes
//package.json
{
  "name": "web",
  "version": "0.0.1",
  "description": "demo app",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "lautin",
  "license": "ISC"
}

package.json中还有一个比较重要的配置项dependencies用来添加项目所依赖的包名,在安装包时 通过--save参数可以将包写入该配置项,例如:

# 控制台使用npm
> npm install art-template --save

然后package.json中会自动添加一行:

"dependencies": {
    "art-template": "^4.13.2"
 }

当项目中的node_modules丢失时 可以通过npm install一次性找回。npm install会依据package.jsondependencies选项挨个执行安装。因此 我们建议每个项目都添加一个package.json

package-lock.json

npm5+的版本中,无需手动--save,它也会自动保存依赖信息。除此以外还加入了名为package-lock.json的文件。该文件在安装包时自动创建,保存了node_modules中所有包的信息:版本、描述以及下载地址,如此一来当再次安装该包时,它的速度就会显著提升。

lock文件被称为锁文件,主要是锁定包的版本,避免下次重装时以最新版本来下载。例如当前使用的是bootstrap3,当lock中记录了该版本时,下次安装就不会以最新的bootstrap4来下载,而是依然选择bt3。

npm的使用

npm官网

@link www.npmjs.com 专门用来管理第三方包的平台,所有可用npm安装的包 都需要事先提交到该网站。接下来才能在用户代码中 使用npm下载。如果你有兴趣 也可以向该平台提交包。

npm发布

https://www.cnblogs.com/chengxs/p/7651653.html

npm指令

npm是随着node一起安装的,当node安装完成npm也就有了, 并且写入了环境变量中。接下来在cmd窗口 可以直接使用npm来执行命令:

  • npm --version

    • 查看npm版本信息
  • npm help

    • 查看使用帮助信息,命令列表
    • 命令手册 npm install--help
  • npm install npm --global

    • 升级npm
  • npm init

    • 以向导形式 初始化package.json文件
    • npm init -y 则是跳过向导 快速生成
  • npm install

    • 一次性安装 所有depenencies中的依赖包
    • 简写形式 npm i
  • npm install 包名

    • 安装指定的包到node_modules中
    • 简写形式 npm i 包名
  • npm install 包名 --save

    • 安装并将写包写入项目的依赖中
    • 简写形式 npm i 包名 -S
  • npm uninstall 包名

    • 删除一个包,保留依赖项
  • 简写形式 npm un 包名

  • npm uninstall 包名--save

    • 删除一个包以及依赖项
    • 简写形式 npm un 包名 -S

npm配置

  • 使用npm config list查看npm的基本配置信息

    它包括有cli configsuserconfigbuiltin config几级配置,通常修改userConfig的配置。userconfig配置文件在c:/users/admin/.npmrc.文件中,可以直接修改该文件来设置用户配置项

  • 使用npm config list -l查看npm配置的详细信息

  • npm config get key 读取一个配置项的值

  • npm config set key value 设置一个配置项的值

cnpm代理

npm远程服务器存放于国外,即便国内有CDN也会比较慢,因此建议使用时配置国内的镜像地址 。

@link http://npm.taobao.org

这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次 以保证尽量与官方服务同步。

1)全局安装cnpm

> npm install cnpm --global

2)使用cnpm安装包

# 使用cnpm安装art-template
> cnpm install art-template
√ Installed 1 packages
√ Linked 27 latest versions
√ Run 0 scripts
√ All packages installed (30 packages installed from npm registry, used 16s(network 16s), speed 69.44kB/s, json 28(79.46kB), tarball 1.01MB)

如果不想安装cnmp服务 而又想使用其镜像服务器下载包,则可以通过--registry来设置

> npm install art-template --registry=https://registry.npm.taobao.org

或者直接修改npm配置项中下载入口地址:

> npm config set registry https://registry.npm.taobao.org
#查看npm配置项信息
> npm config list
> npm config list -l

posted @ Zycin (非转载 来自个人学习资料整理)