美文网首页
Node概述

Node概述

作者: hellomyshadow | 来源:发表于2019-04-21 10:34 被阅读0次

    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 jquery@1.8.2:指定版本安装。
    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模块的提前打包处理,<script>引入处理后的js
    2. AMD规范:专门用于浏览器端,模块的加载是异步的
      1. CommonJs最初是基于服务器端的Js模块化规范,AMD针对浏览器端出了一套JS模块化规范之后,CommonJs才有了基于浏览器端的实现;
      2. 暴露模块 --> 定义没有依赖的模块:define(function(){ return 模块 })
      3. 暴露模块 --> 定义有依赖的模块:
        define(['module1', 'module2'], function(m1, m2){
           # 回调的形参与依赖的模块相对应
           return 模块
        })
        
      4. 浏览器端的实现:RequireJs,使用时,<script>引入require.js,并指定主模块入口
        <script data-main="js/main.js" src="js/libs/require.js"></script>
        
      5. 在主模块中声明要引入的其他模块,这样只用一个<script>就能引入所有需要的模块
    3. CMD规范:CMD是阿里的JS规范,也是专门用于浏览器端,后来出售给了国外,很少用;
      1. CMD规范其实就是把CommonJsAMD进行结合,定义模块使用AMD,暴露模块使用CommonJS
      2. 浏览器端的实现: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>
        
    4. 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'
        
    5. 自动化打包工具: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概述

          本文链接:https://www.haomeiwen.com/subject/khudgqtx.html