NodeJs
Node.js采用C++语言编写而成,它不是Javascript应用,而是一个Javascript的运行环境,据Node.js创始人Ryan Dahl回忆,他最初希望采用Ruby来写Node.js,但是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试采用V8引擎,所以选择了C++语言。
Node.js支持的系统包括*nux、Windows,这意味着程序员可以编写系统级或服务器端的Javascript代码,交给Node.js来解释执行。
Node.js 一个能够在服务器端运行JavaScript的开源代码、跨平台JavaScript运行环境;
- 采用
Google开发的V8引擎运行JS代码,使用事件驱动、非阻塞、异步I/O模型等技术提高性能,可优化应用程序的传输量和规模; -
Node大部分基本模块都是采用JS编写的,由一个高性能的Web服务器形成一个Node生态环境。
-
Node的服务器是单线程的-
Node处理请求时是单线程,但在后台拥有一个I/O线程池; -
Node有专门处理请求的线程,还有专门处理I/O的线程。
-
-
JS没有模块的概念,而Node遵循CommonJS规范,将模块划分为:核心模块、文件模块- 核心模块:
node引擎提供的模块,可以直接引用,不用指定模块路径; - 文件模块:第三方模块,文件模块的标识就是文件的路径;
-
require():引入外部模块的函数,返回值就是引入模块的对象; - 如果是相对路径,必须以
./或../开头,如require("./req.js"),后缀名.js可省略;
- 核心模块:
- 模块的作用域:在
Node中,每一个JS文件中的JS代码都独立运行在一个函数中,而不是全局作用域- 模块之间不能直接共享数据,即
require()返回的对象无法访问模块内的变量/方法; - 在执行的模块中使用:
console.log(arguments.callee + "")会发现,当前正在执行的模块代码被node放在了一个函数中:function (exports, require, module, __filename, __dirname) { //模块的JS代码 }-
exports:用于将变量/函数暴露到外部的对象,每个模块文件中都有一个exports对象; -
require:用来引入外部模块的函数,只要是作为exports的属性/方法,该函数返回的对象都可以直接访问:req.js:exports.x = "Hello"; exports.fn = function(){} - 其他模块中引用req.js模块:
var r = require("./req"); # r: {x: 'Hello', fn: [Function]} r.fn(); # 执行模块中的方法 r.x; # 获取模块中的变量 -
module每个模块都有一个内置的对象属性module,包含了当前模块所拥有的一些信息; -
module的属性信息:id(模块的唯一标识)、filename(文件的路径名)、loaded、children、paths、exports、parent、require() --> require()也来自module对象 -
exports是module的属性,即module也可以用于导出:module.exports.x = "Hello"; -
__filename当前模块的绝对路径;__dirname当前模块所在目录的绝对路径。
-
- 模块之间不能直接共享数据,即
-
exports与module.exports的区别-
exports只是一个指向module.exports的引用,module.exports才是一个对象;exports.a = 10; # 等同于:module.exports.a = 10 - 真正具有向外暴露能力的是
module.exports,而不是exports,比如exports={ a: 10 } --> exports指向了一个新的对象,不再指向module.exports,也就失去了可以向外暴露的能力; -
exports只能通过exports.x的方式向外暴露,而module.exports={ a: 10 }可以暴露; -
require()得到的其实也是module.exports对象的引用。
-
- 在浏览器端的
JS中,全局变量/函数都作为window对象的属性/方法保存的;在node中,也有类似于window作用的全局对象:global- 所谓
node环境,也就是ECMAScript,global是ECMAScript标准提供的; - 本质上,浏览器端的window其实就是扩展自
ECMAScript中的global
- 所谓
- 包:
package,由包结构和包描述文件组成,其实就是一个压缩文件,解压还原为目录;- 包结构 用于组织包中的各种文件;
- 包描述文件 描述包的相关信息,以供外部读取分析;
- 目录文件包括:描述文件(
package.json)、可执行二进制文件(bin)、js代码(lib)、doc(文档)、test(单元测试),其中package.json是必需的; -
package.json是一个JSON格式的文件,不能有任何注释,它是配置文件。
npm
npm Node Package Manager,node的包管理器,用于node的第三方模块的发布、安装、依赖等;
- 借助
npm,Node与第三方模块之间形成了一个很好的生态系统; -
npm search包名:搜索模块包; -
npm install/i包名:在当前目录安装模块包;- 执行:
npm init --> 当前目录会生成一个package.json - 安装依赖包,如
npm install math:math包会在安装到当前目录下; - 在当前目录下会生成一个
node_modules目录,存放安装的模块,使用这些模块时,不需要指定模块路径:var math = require("math"); -
npm i 包名 --save:在项目或模块包的package.json中会生成相关模块的依赖信息;npm i math --save # package.json -> "dependencies": { "math": "0.0.3" } - 依赖信息的作用:将当前目录(即项目)上传到开源站时,并不会上传
node_modules目录,因为会影响上传/下载的速度,而且不能保证依赖的模块包一定是最新版本;那么下载并使用该项目后,也就不能直接运行,需要先在该项目的根目录下执行:npm install =>安装依赖
- 执行:
-
npm install/i 包名 -g:全局模式安装模块包,一般都是一些工具;-
npm root -g:查看全局安装的根目录; -
npm list:查看当前目录下已安装的node包; -
npm info 包名:查看包的所有版本;npm i jquery@1.8.2:指定版本安装。
-
-
npm update:升级依赖包 -
npm remove/r 包名:删除模块包,同理,--save也会删除package.json中的依赖信息; - 镜像服务器:
npm服务器不在国内,所以npm下载时可能会很慢,镜像服务器的作用就是把npm服务器的数据拷贝一份,通过国内的镜像服务器下载模块包,就会很快;- 淘宝
NPM镜像:同步npm服务器数据的频率为10分钟,定制的cnpm代替默认的npm命令; npm install -g cnpm --registry=https://registry.npm.taobao.org-
cnpm与npm的命令一模一样,但下载的模块目录结构可能不同,也是为了避免相互覆盖。 - 然而,有时候cnpm会带来诡异的Bug,为了解决下载速度问题,可以设置
npm的镜像npm install --registry=https://registry.npm.taobao.org
- 淘宝
-
require("math")的查找路径:当前目录的node_modules -> 上一级目录的node_modules -> 再上一级目录的node_modules -> ... -> 磁盘根目录 -> 仍没有则报错
package.json
-
package.json中的一些节点-
name包名 -
version版本,x.x.x -
main包的入口主文件 -
scripts自定义脚本,通过 npm run 脚本名 执行脚本定义的命令 -
dependencies生产环境下必需的依赖包 -
devDependencies只在开发环境下使用的依赖包
-
-
dependencies与devDependencies中的依赖包版本:-
^2.3.4表示在执行npm install时,第一位版本号不变,后两位取最新的; -
~2.3.4前两位不变,最后一位取最新; -
*2.3.4表示全部取最新的。
-
开发方向
-
GUI:Graphical User Interface,图形用户界面,如office、vscode、浏览器、播放器... -
CLI:Command Line Interface,命令行界面,也称为CUI(字符用户界面)- 相比于
GUI,CLI更节省计算机资源,一般用于服务器环境,如babel、vue-cli、webpack... -
Node第三方命令行框架:commander、chalk、inquirer -
commander:命令行开发工具 -
chalk:命令行样式风格控制器 -
inquirer:交互式命令行工具
- 相比于
-
Server:提供服务,如Web Server、IM...
JS模块化
模块化规范:CommonJs、AMD、CMD、ES6
- CommonJs规范
- 在服务器端:模块的加载是运行时同步加载的;
- 在浏览器端:模块需要提前编译打包处理,因为浏览器不识别
require()的引入模块方式; - 暴露模块的两种方式:
module.exports = value,exports.xxx = value - 引入模块:
let xxx = require('xxx'); - 服务器端的实现:
Node.js - 浏览器端的实现:
Browserify,用于对js模块的提前打包处理,<script>引入处理后的js
-
AMD规范:专门用于浏览器端,模块的加载是异步的-
CommonJs最初是基于服务器端的Js模块化规范,AMD针对浏览器端出了一套JS模块化规范之后,CommonJs才有了基于浏览器端的实现; - 暴露模块
-->定义没有依赖的模块:define(function(){ return 模块 }) - 暴露模块
-->定义有依赖的模块:define(['module1', 'module2'], function(m1, m2){ # 回调的形参与依赖的模块相对应 return 模块 }) - 浏览器端的实现:
RequireJs,使用时,<script>引入require.js,并指定主模块入口<script data-main="js/main.js" src="js/libs/require.js"></script> - 在主模块中声明要引入的其他模块,这样只用一个
<script>就能引入所有需要的模块
-
-
CMD规范:CMD是阿里的JS规范,也是专门用于浏览器端,后来出售给了国外,很少用;-
CMD规范其实就是把CommonJs和AMD进行结合,定义模块使用AMD,暴露模块使用CommonJS; - 浏览器端的实现:
SeaJs,使用时,一个<script>引入sea.js,一个<script>引入主模块;<script type="text/javascript" src="js/libs/sea.js"></script> <script type="text/javascript"> seajs.use('./js/modules/main') </script>
-
-
ES6规范:依赖模块需要编译打包处理- 导出模块:
export,引入模块:import,Vue.js2.0就是基于ES6规范 - 浏览器端的实现:
Babel、Browserify -
Babel:有的浏览器不支持ES6的语法,Babel可以将ES6编译为ES5 -
Browserify:用于编译打包JS - 常规暴露:
export { fun1, fun2 },引入:import {fun1, fun2} from './module' - 默认暴露:可以暴露任意数据类型,而且暴露的数据与引入的数据是一致的;
# 暴露一个方法 export default () => { ... } # 引入 import fun from './module'
- 导出模块:
- 自动化打包工具:
webpack、Grunt、Gulp,用于简化编译打包JS/Sass/Less、压缩、合并等过程
CommonJs
-
CommonJs规范主要是为了弥补JS没有标准的缺陷,终极目标是提供一个类似Python、Ruby和Java的标准库,而不只是停留在小脚本程序的阶段; -
CommonJs就是模块化的标准,nodeJs就是CommonJs的一种实现; -
NodeJs中的模块分为两类:-
NodeJs本身提供的核心模块,如http、url、fs,可以直接引入使用; - 自定义模块:依照
CommonJs中的规定,实现的第三方模块。
-
第三方模块的规定
- 一个
JS文件作为一个模块,通过exports或者module.exports暴露属性/方法; - 自定义模块的查找过程:
- 如果当前目录下没有,则去当前目录下的
node_modules目录中查找;var foo = require('foo') # ./node_modules/foo.js - 如果
node_modules目录中没有foo.js,但有foo文件夹,且foo.js在foo目录中;var foo = require('foo/foo.js') # ./node_modules/foo/foo.js
- 如果当前目录下没有,则去当前目录下的
- 如果希望
require('foo')能直接引用./node_modules/foo/foo.js- 在
foo目录下执行npm init --yes,生成package.json文件,--yes表示强制生成; -
require('foo')在node_modules中查找到foo目录,那么就查找foo/package.json,如果不存在,则提示没有foo.js模块; - 如果
package.json存在,则查找main节点,此时的foo目录被视为一个模块,main节点表示模块的入口,也就是真正暴露的模块; - 如果
main节点指向foo.js,则查找foo/foo.js,如果指向index.js,则require('foo')实际引用的是foo/index.js
- 在
- 通过
npm下载第三方模块时,也会自动下载到node_modules目录中,下载的其实也是目录;-
npm i md5-node --save与--save-dev:都会把md5-node写入package.json中; -
--save:同-S,写入到package.json中的dependencies中; -
--save-dev:同-D,写入到package.json中的devDependencies中;
-
-
dependencies与devDependencies的区别- 正常执行
cnpm install时,dependencies和devDependencies中的模块都会下载; -
--save:运行时依赖,如vue、react等,没有这些依赖,打包的项目无法运行; -
--save-dev:开发时依赖,即开发环境所需的依赖,如构建工具,测试工具等等,打包发布时不需要这些工具,如less-loader、webpack、babel
- 正常执行
模块的加载机制
- 模块的分类:文件模块、文件夹模块、核心模块
- 文件模块其实就是一个
JS文件,通过module.exports暴露模块的功能; - 文件夹模块可分为
node_modules Folders和global Folders -
global folders是全局模块,由node的环境变量NODE_PATH控制所在路径。
- 文件模块其实就是一个
- 路径加载模式:
require('./m3')引入模块时,以./、../、/开头,表示路径模块加载模式 - 非路径加载模式:
require('m3')在引入时不指定模块的路径-
module.paths是一个数组,保存的是查找此模块的路径列表; - 核心模块是
node的内置模块,require()引用时也不需要指定路径; - 但是,如果自定义的模块名与核心模块名相冲突,则默认加载核心模块。
-
- 模块文件的后缀名处理机制:
require('./m3') --> m3 -> m3.js -> m3.json -> m3.node - 主模块与子模块
一个模块直接通过node xxx.js运行起来,它就是主模块;通过require('xxx.js')加载时,则是子模块;
主模块与子模块的判断:!module.parent-
app.jsconst Koa = require('koa') const app = new Koa() //... if (!module.parent) { // 作为主模块直接运行 console.log('start server...') app.listen(3000) } else { // 作为子模块导出 console.log('export app...') module.exports = app } -
index.jsconst app = require('./app') - 执行
node ./app.js --> start server... node ./index.js --> export app...
-