node笔记——第一节

node学习笔记

  • 1、node常见属性
  • 2、node中的事件环和nextTick
  • 3、node中模块规范
  • 4、node中常用内置模块
  • 5、代码实现require
  • 6、node中模块引入规则
    • (1)核心模块(内置模块)
    • (2)第三方模块
    • (3)文件模块(相对路径)
  • 7、NPM中常用命令
    • (1)npm init
    • (2)npm i
    • (3)本地安装(--save)(--save-dev)
  • 8、npm版本管理
  • 9、scripts配置

1、node常见属性

console.log(global);//全局上可以直接访问的属性,(全局变量)
console.log(__dirname); // 当前文件执行时的目录 是死的 (绝对路径)
console.log(__filename); // 文件自己的绝对路径
console.log(global.process);//process代表进程,可以获得运行时的一些环境和参数
//process
//(1)、platform win32=>window  mac=>darwin 文件执行环境  每个平台找一些用户文件,位置可能不一样
//(2)、cwd current working directory 当前工作目录 可以改变 webpack会自动查找运行webpack的目录下查找webpack.config.js
//(3)、env 执行代码时传入环境 默认读取全局的环境变量
//(4)、argv 执行代码时传入的参数
console.log(process.platform)
// .env文件 
if (process.env.NODE_ENV === 'development') {
    console.log('dev');
} else {
    console.log('prod');
}
// [执行node所在的exe文件,当前执行的文件,..其他参数]
console.log(process.argv); //commander插件 会自己解析用户命令 会根据用户传递的参数来解析 生成对应的功能 开发一个脚手架 , 运行工具都会使用
// process.argv[0];//node 的可执行文件
// process.argv[1];//node 执行的文件是谁

2、node中的事件环和nextTick

	node的eventLoop:浏览器和node事件环的值,在node10版本执行结果一样,本质不一样
	
	区别:浏览器有一个宏任务,一个微任务,node有多个宏任务队列
	默认是从上到下依次执行代码,依次清空每个队列中的回调方法,每调用一个宏任务都会清空微任务,
	timers:存放所有定时器回调【fn,fn,fn】,
	poll:存放异步io操作,node中,基本上所有的异步api的回调,都会在这个阶段处理
	check:存放所有setImmidiate的回调
				本阶段执行已经被 setTimeout()setInterval() 的调度回调函数。
				  ┌───────────────────────────┐
				┌─>│           timers          │ 
				│  └─────────────┬─────────────┘
				|   执行延迟到下一个循环迭代的 I/O 回调。
				│  ┌─────────────┴─────────────┐
				│  │     pending callbacks     │
				│  └─────────────┬─────────────┘
				|   仅系统内部使用。
				│  ┌─────────────┴─────────────┐
				│  │       idle, prepare       │
				│  └─────────────┬─────────────┘      
				|  检索新的I/O事件;执行与 I/O相关的回调  ┌───────────────┐
				│  ┌─────────────┴─────────────┐      │   incoming:   │
				│  │           poll            │<─────┤  connections, │
				│  └─────────────┬─────────────┘      │   data, etc.  │
				│  setImmediate() 回调函数在这里执行。  └───────────────┘
				│  ┌─────────────┴─────────────┐      
				│  │           check           │
				│  └─────────────┬─────────────┘
				|  一些关闭的回调函数
				│  ┌─────────────┴─────────────┐
				└──┤      close callbacks      │  
				   └───────────────────────────┘


1、主栈
2、检测时间有没有到达的定时,有就清空
3、io操作(poll)逐一清空
4、看setImmidiate中是否有内容,如果有内容就清空check阶段,如果没有就阻塞
5、不停的看定时器中有没有到达时间,如果有,则回去继续执行 如果都执行完毕,则关闭

//  nextTick node中自己实现的 不属于node中的EventLoop,优先级比promise更高
setTimeout(()=>{
    console.log(1);
},0);
Promise.resolve().then(()=>{ 
    console.log(2);
});
process.nextTick(()=>{//优先级高于微任务
    console.log(3);
});
console.log(4)
//4,3,2,1

3、node中模块规范

node中模块  一般是es6Module  commonjs规范 两种规范
用webpack 打包后 es6Module -》 commonjs模块 (tree-shaking)
es6Module 是"静态"模块,支持 tree-shaking, 可以在编译的时候进行分析, commonjs 是 "动态"模块  
可以在代码执行的时候引入模块 (无法做tree-shaking),
所以我们的vue模块引入的时候一般用的都是es6Module规范

4、node中常用内置模块

常用:fs path vm
const fs = require('fs');
const path = require('path');
const vm  =require('vm');
let r = fs.readFileSync('./a.js', 'utf8')//同步读取文件,返回的是字符串
fs.existsSync('./a.js')//是否存在文件后缀,返回布尔值
path.extname()//获取文件后缀名
console.log(path.resolve(__dirname, 'a', 'b', 'c')); // 解析绝对路径, 解析默认采用process.cwd(), 如果有路径/ 会回到根目录
console.log(path.join(__dirname,'a', 'b', 'c')); // 仅仅是拼接,不会产生绝对路径,遇到/ 也会拼在一起
console.log(path.relative('a/b/c/1.js','a')); // 根据路径获取相对路径
console.log(path.dirname('a/b/c')); // 取当前文件的父路径 
vm.runInThisContext(`console.log(a)`);//将字符串变成js来执行 类似eval,eval 会受执行环境影响 

5、代码实现require

首先创建a.js 和a.json文件
a.js

var a =100;
console.log(this === module.exports)
module.exports = a

a.json

{
	"name":"demo"
}

req.js

const fs = require('fs');
const path = require('path');
const vm = require('vm');
class Module{
    constructor(id){
        this.id = id
        this.exports={}
    }
    load(){
        let ext = path.extname(this.id); // 获取文件后缀名
        Module._extensions[ext](this);
    }
}
Module._cache = {}
Module._extensions = {
    '.js'(module){
        let script = fs.readFileSync(module.id,'utf8');//同步读取文件
        let templateFn = `(function(exports,module,require,__dirname,__filename){${script}})`;
        let fn = vm.runInThisContext(templateFn);//运行上下文,运行模板字符串
        // let fn1 = (function(exports,module,require,__dirname,__filename){
        //             eval(script)
        //         })
        let exports = module.exports;
        let thisValue = exports; // this = module.exports = exports;
        let filename = module.id;
        let dirname = path.dirname(filename);//取当前文件的父路径
        // 函数的call 的作用 1.改变this指向 2.让函数执行
        fn.call(thisValue,exports,module,req,dirname,filename); // 调用了a模块 module.exports = 100;
    },
    '.json'(module){
        let script = fs.readFileSync(module.id,'utf8');
        module.exports = JSON.parse(script)
    }
}
Module._resolveFilename = function(id){
    let filePath = path.resolve(__dirname,id)
    let isExists = fs.existsSync(filePath);//是否存在文件后缀,返回布尔值
    if(isExists) return filePath;//如果存在,直接返回
    // 如果不存在,for循环尝试添加后缀
    let keys = Object.keys(Module._extensions); // 以后Object的新出的方法 都会放到Reflect上
    for(let i =0; i < keys.length;i++){
       let newPath = filePath + keys[i];
       if(fs.existsSync(newPath)) return newPath
    }
    throw new Error('module not found')
}
function req(filename){
    filename = Module._resolveFilename(filename); // 1.创造一个绝对引用地址,方便后续读取
    let cacheModule = Module._cache[filename]
    if(cacheModule) return cacheModule.exports; // 直接将上次缓存的模块丢给你就ok了
    const module = new Module(filename); // 2.根据路径创造一个模块
    console.log(module)
    Module._cache[filename] = module; // 最终:缓存模块 根据的是文件名来缓存
    module.load(); // 就是让用户给module.exports 赋值
    return module.exports; // 默认是空对象
}
let a = req('./a.js');
a = req('./a.js');
a = req('./a.js');
console.log(a)

6、node中模块引入规则

(1)核心模块(内置模块)

node中自带模块

(2)第三方模块

默认会沿着当前目录向上查找,查找 node_modules 下的同名的文件夹, 根据(package.json 中的main),如果main有指定的入口文件,就找main指定的文件,如果没有就找 index.js , 如果没找到继续向上查找 ,查找上级的node_modules ,如果到根路径还没有找到就报错了

console.log(module.paths); // 查找路径

(3)文件模块(相对路径)

文件模块一般都是引用我们自己的模块,一般引用的都是相对路径,在我们引用的时候会先判断是不是核心模块,或者第三方模块,如果不是的话:
最新:默认先查找同名文件,如果没找到,会尝试查找同名.js和.json文件,如果再没有就会查找同名文件夹(当成了一个包),先查找同名文件夹下有没有package.json,如果没有,那就找index.js,如果还没有就报错了。(老版本有点不一样,这里不考虑)

7、NPM中常用命令

(1)npm init

执行npm init 之后会默认生成一个pakage.json文件

{
  "name": "sf",//是当前包的名字,也就是最终发布的npm官网上包的名字。不能和已有的包重名
  "version": "1.0.0",//版本号
  "description": "",//描述
  "main": "index.js",//就是当前包的入口文件,也就是使用require默认引入的文件
  "scripts": {//可以配置一些执行脚本
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",//作者,账号
  "license": "ISC"//协议许可
}

(2)npm i

全局安装:安装的全局模块都在npm下生成了一个快捷方式,全局安装只能在命令行里用
自己实现一个全局包:

  1. 在新建的文件夹路径下执行npm init ,生成package.json文件
  2. 创建bin目录,新增www文件,名字叫什么无所谓~
    www文件内容
#! /usr/bin/env node // #! 这句表示采用node来执行此文件,同理 shell可以表示 sh
console.log('demo')
  1. 更改package.json文件,添加
"bin": {
	"demo":"./bin/www" // 这里要注意名字和你建立的文件夹相同
},
  • 可以把工具发到npm上,然后全局安装,就可以执行demo命令
  • 测试的话可以执行npm link ,demo 命令就可以执行了

(3)本地安装(–save)(–save-dev)

所谓本地安装的意思就是只在项目中使用,而非在命令行使用,后面加-dev 的话表示只在开发的时候使用,不加的话表示为项目依赖开发上线都需要
安装的时候可以指定版本 比如 npm i jquery@2 表示安装2.X版本,npm i [email protected] 表示安装2.2.0版本

1、package.json文件中版本号的含义:常见

range 含义 示例
^2.2.1 指定的 MAJOR 版本号下, 所有更新的版本 匹配 2.2.3, 2.3.0; 不匹配 1.0.3, 3.0.1
~2.2.1 指定 MAJOR.MINOR 版本号下,所有更新的版本 匹配 2.2.3, 2.2.9 ; 不匹配 2.3.0, 2.4.5
>=2.1 版本号大于或等于 2.1.0 匹配 2.1.2, 3.1
<=2.2 版本号小于或等于 2.2 匹配 1.0.0, 2.2.1, 2.2.11
1.0.0 - 2.0.0 版本号从 1.0.0 (含) 到 2.0.0 (含) 匹配 1.0.0, 1.3.4, 2.0.0

预发版:

  • alpha(α):预览版,或者叫内部测试版;一般不向外部发布,会有很多bug;一般只有测试人员使用。
  • beta(β):测试版,或者叫公开测试版;这个阶段的版本会一直加入新的功能;在alpha版之后推出。
  • c(release candidate):最终测试版本;可能成为最终产品的候选版本,如果未出现问题则可发布成为正式版本。
    2.1.0-beta.1这样声明的版本用户不会立马使用,可以用来做测试使用

2、package.json中常见的依赖方式

  • dependencies 项目依赖
    可以使用npm install -S或 npm install --save保存到依赖中,当发布到npm上时dependencies下的模块会作为依赖,一起被下载!
  • devDependencies 开发依赖
    可以使用npm install -D或 npm install --save-dev保存到依赖中。 当发布到npm上时devDependencies下面的模块就不会自动下载了
  • peerDependencies 同版本依赖
    同等依赖,如果你安装我,那么你最好也安装我对应的依赖,如果未安装会报出警告,对,没错,只有提示作用
  • bundledDependencies 捆绑依赖
    使用npm pack 打包tgz时会将捆绑依赖一同打包
  • optionalDependencies 可选依赖
    如果发现无法安装或无法找到,不会影响npm的安装

本地安装之后会生成package-lock.json文件,而且将安装的模块放到了node_modules下

package-lock.json 的作用是锁定依赖安装结构,保证在任意机器上执行 npm install 都会得到完全相同的 node_modules 结果,因为package-lock.json存储所有安装的信息
如果手动更新了package.json文件,package-lock.json 文件会自动更新,如果版本兼容会采用lock的配置

8、npm版本管理

npm采用了semver规范作为依赖版本管理方案。semver 约定一个包的版本号必须包含3个数字.

MAJOR.MINOR.PATCH 意思是 主版本号.小版本号.修订版本号

  • MAJOR 对应大的版本号迭代,做了不兼容旧版的修改时要更新 MAJOR 版本号
  • MINOR 对应小版本迭代,发生兼容旧版API的修改或功能更新时,更新MINOR版本号
  • PATCH 对应修订版本号,一般针对修复 BUG 的版本号

当我们每次发布包的时候都需要升级版本号

npm version major  # 大版本号加 1,其余版本号归 0
npm version minor  # 小版本号加 1,修订号归 0
npm version patch  # 修订号加 1

把包发到npm上的命令:

npm publish

如果账号没登录的话需要先添加账号

npm addUser

9、scripts配置

在package.json中可以定义自己的脚本通过npm run来执行

"scripts": {
   "hello": "echo hello",
   "build": "webpack"
   "demo":"node demo.js"
}

可以使用 npm run hello执行脚本,也可以使用 npm run build执行node_modules/.bin目录下的webpack文件,也可以执行npm run demo 执行文件夹下的demo.js文件

  • npm run 命令执行时,会把 ./node_modules/.bin/ 目录添加到执行环境的 PATH 变量中,因此如果某个命令行包未全局安装,而只安装在了当前项目的 node_modules 中,通过 npm run 一样可以调用该命令。
  • 执行 npm 脚本时要传入参数,需要在命令后加 – 标明, 如 npm run hello – --port 3000 可以将 --port 参数传给hello 命令
  • npm 提供了 pre 和 post 两种钩子机制,可以定义某个脚本前后的执行脚本,没有定义默认会忽略
"scripts": {
   "prehello":"echo prehello",
   "hello": "echo hello",
   "posthello":"echo posthello"
}

你可能感兴趣的:(nodejs)