Node概述

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生态环境。
  1. Node的服务器是单线程的
    • Node处理请求时是单线程,但在后台拥有一个I/O线程池;
    • Node有专门处理请求的线程,还有专门处理I/O的线程。
  2. JS没有模块的概念,而Node遵循CommonJS规范,将模块划分为:核心模块、文件模块
    • 核心模块:node引擎提供的模块,可以直接引用,不用指定模块路径;
    • 文件模块:第三方模块,文件模块的标识就是文件的路径;
    • require():引入外部模块的函数,返回值就是引入模块的对象;
    • 如果是相对路径,必须以 ./../ 开头,如require("./req.js"),后缀名 .js 可省略;
  3. 模块的作用域:在Node中,每一个JS文件中的JS代码都独立运行在一个函数中,而不是全局作用域
    1. 模块之间不能直接共享数据,即require()返回的对象无法访问模块内的变量/方法;
    2. 在执行的模块中使用: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对象
      • exportsmodule的属性,即module也可以用于导出:module.exports.x = "Hello";
      • __filename 当前模块的绝对路径; __dirname 当前模块所在目录的绝对路径。
  4. exportsmodule.exports的区别
    1. exports只是一个指向module.exports的引用,module.exports才是一个对象;
      exports.a = 10;  # 等同于:module.exports.a = 10
      
    2. 真正具有向外暴露能力的是module.exports,而不是exports,比如exports={ a: 10 } --> exports指向了一个新的对象,不再指向module.exports,也就失去了可以向外暴露的能力;
    3. exports只能通过 exports.x 的方式向外暴露,而module.exports={ a: 10 }可以暴露;
    4. require()得到的其实也是module.exports对象的引用。
  5. 在浏览器端的JS中,全局变量/函数都作为window对象的属性/方法保存的;在node中,也有类似于window作用的全局对象:global
    1. 所谓node环境,也就是ECMAScriptglobalECMAScript标准提供的;
    2. 本质上,浏览器端的window其实就是扩展自ECMAScript中的global
  6. 包:package,由包结构和包描述文件组成,其实就是一个压缩文件,解压还原为目录;
    • 包结构 用于组织包中的各种文件;
    • 包描述文件 描述包的相关信息,以供外部读取分析;
    • 目录文件包括:描述文件(package.json)、可执行二进制文件(bin)、js代码(lib)、doc(文档)、test(单元测试),其中package.json是必需的;
    • package.json是一个JSON格式的文件,不能有任何注释,它是配置文件。

npm

npm Node Package Managernode的包管理器,用于node的第三方模块的发布、安装、依赖等;

  1. 借助npmNode与第三方模块之间形成了一个很好的生态系统;
  2. npm search 包名:搜索模块包;
  3. npm install/i 包名:在当前目录安装模块包;
    1. 执行:npm init --> 当前目录会生成一个package.json
    2. 安装依赖包,如npm install math:math包会在安装到当前目录下;
    3. 在当前目录下会生成一个node_modules目录,存放安装的模块,使用这些模块时,不需要指定模块路径:var math = require("math");
    4. npm i 包名 --save:在项目或模块包的 package.json 中会生成相关模块的依赖信息;
      npm i math --save # package.json -> "dependencies": { "math": "0.0.3" }
      
    5. 依赖信息的作用:将当前目录(即项目)上传到开源站时,并不会上传node_modules目录,因为会影响上传/下载的速度,而且不能保证依赖的模块包一定是最新版本;那么下载并使用该项目后,也就不能直接运行,需要先在该项目的根目录下执行:npm install =>安装依赖
  4. npm install/i 包名 -g:全局模式安装模块包,一般都是一些工具;
    • npm root -g:查看全局安装的根目录;
    • npm list:查看当前目录下已安装的node包;
    • npm info 包名:查看包的所有版本;npm i [email protected]:指定版本安装。
  5. npm update:升级依赖包
  6. npm remove/r 包名:删除模块包,同理,--save也会删除package.json中的依赖信息;
  7. 镜像服务器:npm服务器不在国内,所以npm下载时可能会很慢,镜像服务器的作用就是把npm服务器的数据拷贝一份,通过国内的镜像服务器下载模块包,就会很快;
    1. 淘宝NPM镜像:同步npm服务器数据的频率为 10 分钟,定制的 cnpm 代替默认的 npm 命令;
    2. npm install -g cnpm --registry=https://registry.npm.taobao.org
    3. cnpmnpm的命令一模一样,但下载的模块目录结构可能不同,也是为了避免相互覆盖。
    4. 然而,有时候cnpm会带来诡异的Bug,为了解决下载速度问题,可以设置npm的镜像
      npm install --registry=https://registry.npm.taobao.org
      
  8. require("math")的查找路径:当前目录的node_modules -> 上一级目录的node_modules -> 再上一级目录的node_modules -> ... -> 磁盘根目录 -> 仍没有则报错

package.json

  1. package.json中的一些节点
    • name 包名
    • version 版本,x.x.x
    • main 包的入口主文件
    • scripts 自定义脚本,通过 npm run 脚本名 执行脚本定义的命令
    • dependencies 生产环境下必需的依赖包
    • devDependencies 只在开发环境下使用的依赖包
  2. dependenciesdevDependencies 中的依赖包版本:
    • ^2.3.4 表示在执行 npm install 时,第一位版本号不变,后两位取最新的;
    • ~2.3.4 前两位不变,最后一位取最新;
    • *2.3.4 表示全部取最新的。

开发方向

  1. GUI:Graphical User Interface,图形用户界面,如officevscode、浏览器、播放器...
  2. CLI:Command Line Interface,命令行界面,也称为CUI(字符用户界面)
    1. 相比于GUICLI更节省计算机资源,一般用于服务器环境,如babel、vue-cli、webpack...
    2. Node第三方命令行框架:commander、chalk、inquirer
    3. commander:命令行开发工具
    4. chalk:命令行样式风格控制器
    5. inquirer:交互式命令行工具
  3. Server:提供服务,如Web Server、IM...

JS模块化

模块化规范:CommonJs、AMD、CMD、ES6

  1. CommonJs规范
    1. 在服务器端:模块的加载是运行时同步加载的;
    2. 在浏览器端:模块需要提前编译打包处理,因为浏览器不识别 require() 的引入模块方式;
    3. 暴露模块的两种方式:module.exports = value,exports.xxx = value
    4. 引入模块:let xxx = require('xxx');
    5. 服务器端的实现:Node.js
    6. 浏览器端的实现:Browserify,用于对js模块的提前打包处理,
    7. 在主模块中声明要引入的其他模块,这样只用一个
  2. ES6规范:依赖模块需要编译打包处理
    1. 导出模块:export,引入模块:importVue.js2.0就是基于ES6规范
    2. 浏览器端的实现:Babel、Browserify
    3. Babel:有的浏览器不支持ES6的语法,Babel可以将ES6编译为ES5
    4. Browserify:用于编译打包JS
    5. 常规暴露:export { fun1, fun2 },引入:import {fun1, fun2} from './module'
    6. 默认暴露:可以暴露任意数据类型,而且暴露的数据与引入的数据是一致的;
      # 暴露一个方法
      export default () => { ... }
      # 引入
      import fun from './module'
      
  3. 自动化打包工具:webpack、Grunt、Gulp,用于简化编译打包JS/Sass/Less、压缩、合并等过程

CommonJs

  1. CommonJs规范主要是为了弥补JS没有标准的缺陷,终极目标是提供一个类似Python、RubyJava的标准库,而不只是停留在小脚本程序的阶段;
  2. CommonJs就是模块化的标准,nodeJs就是CommonJs的一种实现;
  3. NodeJs中的模块分为两类:
    1. NodeJs本身提供的核心模块,如http、url、fs,可以直接引入使用;
    2. 自定义模块:依照CommonJs中的规定,实现的第三方模块。

第三方模块的规定

  1. 一个JS文件作为一个模块,通过 exports 或者 module.exports 暴露属性/方法;
  2. 自定义模块的查找过程:
    1. 如果当前目录下没有,则去当前目录下的 node_modules 目录中查找;
      var foo = require('foo')   # ./node_modules/foo.js
      
    2. 如果node_modules目录中没有foo.js,但有foo文件夹,且foo.jsfoo目录中;
      var foo = require('foo/foo.js')  # ./node_modules/foo/foo.js
      
  3. 如果希望 require('foo') 能直接引用 ./node_modules/foo/foo.js
    1. foo目录下执行 npm init --yes,生成package.json文件,--yes表示强制生成;
    2. require('foo')node_modules中查找到foo目录,那么就查找foo/package.json,如果不存在,则提示没有foo.js模块;
    3. 如果package.json存在,则查找main节点,此时的foo目录被视为一个模块,main节点表示模块的入口,也就是真正暴露的模块;
    4. 如果main节点指向 foo.js,则查找foo/foo.js,如果指向 index.js,则require('foo') 实际引用的是foo/index.js
  4. 通过 npm 下载第三方模块时,也会自动下载到 node_modules 目录中,下载的其实也是目录;
    1. npm i md5-node --save--save-dev:都会把md5-node写入package.json中;
    2. --save:同-S,写入到package.json中的 dependencies 中;
    3. --save-dev:同-D,写入到package.json中的 devDependencies 中;
  5. dependenciesdevDependencies 的区别
    1. 正常执行 cnpm install 时,dependenciesdevDependencies中的模块都会下载;
    2. --save:运行时依赖,如vue、react等,没有这些依赖,打包的项目无法运行;
    3. --save-dev:开发时依赖,即开发环境所需的依赖,如构建工具,测试工具等等,打包发布时不需要这些工具,如less-loader、webpack、babel

模块的加载机制

  1. 模块的分类:文件模块、文件夹模块、核心模块
    1. 文件模块其实就是一个JS文件,通过 module.exports 暴露模块的功能;
    2. 文件夹模块可分为 node_modules Foldersglobal Folders
    3. global folders 是全局模块,由node的环境变量NODE_PATH控制所在路径。
  2. 路径加载模式:require('./m3')引入模块时,以 ./、../、/ 开头,表示路径模块加载模式
  3. 非路径加载模式:require('m3')在引入时不指定模块的路径
    1. module.paths是一个数组,保存的是查找此模块的路径列表;
    2. 核心模块是node的内置模块,require()引用时也不需要指定路径;
    3. 但是,如果自定义的模块名与核心模块名相冲突,则默认加载核心模块。
  4. 模块文件的后缀名处理机制:require('./m3') --> m3 -> m3.js -> m3.json -> m3.node
  5. 主模块与子模块
    一个模块直接通过 node xxx.js 运行起来,它就是主模块;通过 require('xxx.js') 加载时,则是子模块;
    主模块与子模块的判断:!module.parent
    1. app.js
      const 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
      }
      
    2. index.js
      const app = require('./app')
      
    3. 执行
      node ./app.js     --> start server...
      node ./index.js   --> export app...
      

你可能感兴趣的:(Node概述)