青训营node.js基础-特点-模块化commonjs&esm-npm包管理(代码片段)

YK菌 YK菌     2023-01-09     464

关键词:

起步

Node.js 是一个基于 Google V8引擎的、跨平台的JavaScript运行环境(不是一门编程语言)

安装与运行

https://nodejs.org/zh-cn/

建议使用LTS版本,一键傻瓜式下载安装

简单写一个Node.js的程序

index.js用来读取文件package.json文件

const  readFile  = require('fs');

    readFile('./package.json', encoding: 'utf-8',(err, data)=> 
    if(err)
        throw err;
    
    console.log(data)
);

运行程序

node index.js

版本管理

node的版本那么多,
在同一个设备上如何快速切换Node.js 版本?

可以使用版本管理工具

  • n: 一个npm 全局的开源包,是依赖npm 来全局安装、使用的
  • fnm: 快速简单,兼容性支持 .node-version和.nvmrc文件
  • nvm: 独立的软件包,Node Version Manager

nvm用的比较多(是macOS平台的软件)
Windows可以下载nvm-windows

详细教程可见https://www.runoob.com/w3cnote/nvm-manager-node-versions.html

特点

异步I/O

当Node.js执行I/O操作时,会在响应返回并恢复操作,而不是阻塞线程并浪费CPU(循环等待)

这里的I/O不仅指文件的读写,也包括对网络的请求,对数据库的操作等

代码编写顺序与执行顺序无关,看下面这例子

const  readFile  = require("fs");
readFile("./node基础/package.json",  encoding: "utf-8" , (err, data) => 
  if (err) 
    throw err;
  
  console.log(data);
);

console.log('我是在最下面编写的代码')

单线程

Node.js 保持了JavaScript 在浏览器中单线程的特点

优点:

  • 不用处处在意状态同步问题,不会发生死锁
  • 没有线程上下文切换带来的性能开销

缺点:(解决方案会在下一篇博文中说明)

  • 无法利用多核CPU
  • 错误会引起整个应用退出,健壮性不足
  • 大量计算占用导致CPU,无法继续执

多进程的浏览器

浏览器是多进程,JS引擎单线程

  • Browser 进程:浏览器主进程,只有一个

  • 插件进程:插件使用时才创建

  • GPU 进程:最多一个用于3D 绘制

  • 渲染进程:页面渲染、JS执行、事件处理

    • GUI 渲染线程
    • JS 引擎线程 (V8)
    • 事件触发线程
    • 定时器触发线程
    • 异步请求线程

跨平台

兼容 Windows 和 *nix 平台,主要得益于在操作系统与 Node 上层模块系统之间构建了一层平台架构

底层都是C/C++写的,应用层才是JavaScript写的

应用场景

Node.js在大部分领域都占有一席之地,尤其是I/O密集型的任务(而非计算密集型任务)

  • Web应用:Express / Koa
  • 前端构建:Webpack
  • GUI 客户端软件:VSCode / 网易云音乐
  • 其它:实时通讯、爬虫、CLI 等…

模块化机制

  1. What 何为模块化?

根据功能或业务将一个大程序拆分成互相依赖的小文件,再用简单的方式拼装起来

  1. Why 为什么模块化?无模块化问题
  • 所有 script 标签必须保证顺序正确,否则会依赖报错
  • 全局变量存在命名冲突,占用内存无法被回收
  • IIFE/namespace 会导致代码可读性低等诸多问题

CommonJS 规范

Node.js 支持CommonJS 模块规范,采用同步机制加载模块

greeting.js导出模块

const prefix = "hello";
const sayHi = function () 
  console.log(`$prefix world`);
;

module.exports = 
  sayHi,
;

index.js导入模块

const  sayHi  = require("./greeting");
sayHi();

机制

我们来研究一下模块化的机制

导出模块的时候不仅可以这样

module.exports = 
  sayHi,
;

也可以这样

exports.sayHi = sayHi

为什么可以这样呢?
在node中exports和module.exports指向的是同一块内存引用

exports = module.exports =

CommonJS 中 exportsrequiremodule__filename__dirname 变量 到底是什么

我们在暴露我们的模块的时候node会在外面包一层函数,会注入一些模块变量,所以我们可以在模块中直接使用

我们将刚刚的sayHi函数改成一个箭头函数,输出arguments(因为箭头函数没有argumengts,所以会向外查找)

const sayHi = () => 
  console.log(arguments);
;

exports.sayHi = sayHi;

这五个参数分别对应着exportsrequiremodule__filename__dirname

所以说Node在外面包裹的一个函数就是这样就是这样

function(exports,require,module,__filename,__dirname)
    const m = 1;
    moudule.exports.m = m;

加载方式

  1. 加载内置模块
require('fs')
  1. 加载相对、绝对路径的文件模块
// 绝对
require('/User/.../file.js')
// 相对
require('./file.js')
  1. 加载npm包
require('lodash')

npm 包查找原则

require('lodash')

  1. 当前目录 node_modules
  2. 如果没有,父级目录的 node_modules
  3. 如果没有,沿着路径向上递归,直到根目录下 node_modules
  4. 找到之后会加载package.json main 指向的文件,如果没有package.json 则依次查找index.js、index.json、index.node

npm 缓存

require.cache 中缓存着加载过的模块,缓存的原因:同步加载

  1. 文件模块查找耗时,如果每次require 都需要重新遍历查找,性能会比较差;

  2. 在实际开发中,模块可能包含副作用代码

有缓存

const mod1 = require("./foo");
const mod2 = require("./foo");
console.log(mod1 === mod2); // true

无缓存

function requireUnChched(module) 
  delete require.cache[require.resolve(module)];
  return require(module);

const mod3 = requireUnChched("./foo");
console.log(mod1 === mod3); // false

其他模块化规范

  • AMD 是RequireJS 在推广过程中规范化产出,异步加载,推崇依赖前置;
  • CMD 是SeaJS 在推广过程中规范化产出,异步加载,推崇就近依赖;
  • UMD (Universal Module Definition) 规范,兼容AMD 和CommonJS 模式
  • ES Modules (ESM),语言层面的模块化规范,与环境无关,可借助babel 编译

ES Modules (ESM)

  • ESM 是在ES6 语言层面提出的一种模块化标准;
  • ESM中主要有importexport两个关键词,不能console打印两个关键词
  • 可以实现Tree Shaking

在node环境中如何使用ESM呢?将文件名后缀改成.mjs即可

不想改文件名后缀,也可以在项目的package.json中加一个type=module *.js

还可以使用babel编译转成ES5后使用(浏览器、node)

CommonJS VS ESM

  • CommonJS 模块输出的是一个值的拷贝;ESM 模块输出的是值的引用
  • CommonJS 模块是运行时加载;ESM 模块是编译时输出(提前加载)
  • 可以混用,但是不建议( import commonjs || import 中require)

同样的代码在使用不同的模块化机制输出可能不用,我们看下面的例子

CommonJS:值的拷贝

lib.js

let counter = 3
function addCounter() 
  counter++

module.exports = 
  counter,
  addCounter

main.js

const  counter, addCounter  = require("./lib");

console.log(counter); // 3
addCounter();
console.log(counter); // 3

ESM:值的引用

lib.mjs

export let counter = 3
export function addCounter() 
  counter++

mian.mjs

import  counter, addCounter  from "./lib.mjs";

console.log(counter); // 3 
addCounter();
console.log(counter); // 4

如果上面的counter定义为一个对象,比如

let countet =  number: 3

此时,两种模块化的结果就是一样的

常用模块介绍

更多内容请见官网 https://nodejs.org/en/

文件操作模块

路径模块

os模块

包管理机制

npm

NPM 是Node.js 中的包管理器,提供了安装、删除等其它命令来管理包

常用命令

npm init
npm config
npm run [cmd]
npm install [pkg]
npm uninstall [pkg]
npm update [pkg]
npm info [pkg]
npm publish

package.json

  • name 包名称
  • version 版本号
  • main 入口文件
  • scripts 执行脚本
  • dependencies 线上依赖
  • devDependencies 开发依赖
  • repository 代码托管地址

更多 https://docs.npmjs.com/cli/v7/configuring-npm/package-json

版本命名规范

  • 1.0.0 Must match version exactly
  • >1.0.0 Must be greater than version
  • >=1.0.0 etc
  • ~1.2.0 “Approximately equivalent to version” See semver
  • ^1.2.0 “Compagble with version” See semver
  • 1.2.x 1.2.0, 1.2.1, etc., but not 1.3.0
  • * Matches any version

npm semver check https://semver.npmjs.com/

第三方依赖

  • hip://… See ‘URLs as Dependencies’ below
  • git… See ‘Git URLs as Dependencies’ below
  • user/repo See ‘GitHub URLs’ below
  • path/path/path See Local Paths below

依赖

  • dependencies 业务依赖,应用发布后正常执行所需要的包
  • devDependencies 开发依赖,只用于开发环境
  • peerDependencies 同等依赖,比如一个webpack 插件依赖特定版本的webpack
  • bundledDependencies 打包依赖(npm run pack),必须已经在devDep 或者dep声明过
  • optionalDependencies 可选依赖

私有npm

如果不想公开一些包,可以用私有npm

  • 镜像公司内部私有npm
  • 镜像设置 npm config set registry=hips://bnpm.byted.org

其他

早期的包管理机制问题比较多

旧包管理机制的一些特性

  • 并行安装
  • 扁平管理
  • 锁文件(lockfile)
  • 缓存优化

新的解决方案

  • npm7 | yarn => lock/扁平/缓存…
  • pnpm => monorepo/硬、符号链接/安全性高…

参考

青训营node.js基础-web应用开发-开发调试-线上部署(代码片段)

文章目录Web应用开发HTTP模块Koa介绍中间件常用中间件基于Koa的前端框架调试断点调试日志调试线上部署利用多核CPU进程守护复杂计算前端开发与后端开发对比前几天学了一些Node.js的基础,今天来学习Web应用开发,在开发... 查看详情

青训营node.js基础-异步编程四种解决方案(代码片段)

文章目录异步编程概述异步编程解决方案CallbackPromiseCallback转为PromiseawaitEvent参考有异步I/O,必有异步编程!今天来学习Node.js里的异步编程!异步编程概述曾经的单线程模型在同步I/O的影响下,由于I/O调用缓慢ÿ... 查看详情

青训营node.js基础-异步编程四种解决方案(代码片段)

文章目录异步编程概述异步编程解决方案CallbackPromiseCallback转为PromiseawaitEvent参考有异步I/O,必有异步编程!今天来学习Node.js里的异步编程!异步编程概述曾经的单线程模型在同步I/O的影响下,由于I/O调用缓慢ÿ... 查看详情

青训营html基础-语义化标签-浏览器渲染过程-笔记及拓展(代码片段)

...)博主还是很久之前学习的,这次趁着字节跳动青训营的活动,就再学习一遍HTML。一小时的课程,巩固了之前的一些知识,也学到了很多新知识。我把这次课程的内容与我这一年来学习前端的经验相结合,... 查看详情

go语言上手|青训营笔记(代码片段)

这是我参与「第三届青训营-后端场」笔记创作活动的的第一篇笔记。文章目录语法速览基础语法第一:类型第二:内置库部分json库的使用时间库的使用字符串和数字互转os相关信息实战项目猜谜游戏(pass,过于... 查看详情

客户端容器|青训营笔记(代码片段)

浏览器架构浏览器架构演进单进程架构:所有模块运行在同一个进程里,包含网络、插件、JavaScript运行环境等多进程架构(现代浏览器的常用架构):主进程、网络进程、渲染进程、GPU进程、插件进程浏览器架构浏览器架构演进单... 查看详情

字节跳动青训营笔试题解(代码片段)

...圈题目思路代码四、简答题题目思路前言第五届字节跳动青训营-后端专场笔试题解,简单做了一下,选择题和简答题不知道是否正确,编程题是通过了的,有问题欢迎评论,我会及时改正的~一、单选题选AQUIC&... 查看详情

字节跳动青训营笔试题解(代码片段)

...圈题目思路代码四、简答题题目思路前言第五届字节跳动青训营-后端专场笔试题解,简单做了一下,选择题和简答题不知道是否正确,编程题是通过了的,有问题欢迎评论,我会及时改正的~一、单选题选AQUIC&... 查看详情

青训营pro️从0到1实现一个自己的前端约定路由项目脚手架️工具~(代码片段)

文章目录前言一、Node基础API核心API-无需`require`内置API-需要`require`无需`install`1.fs与异步IO①同步读取文件②异步读取文件③promisify2.buffer与字符集3.http练习1node基础fs\\buffer\\http搭建一个http服务4.stream5.子进程二... 查看详情

青训营pro️从0到1实现一个自己的前端约定路由项目脚手架️工具~(代码片段)

文章目录前言一、Node基础API核心API-无需`require`内置API-需要`require`无需`install`1.fs与异步IO①同步读取文件②异步读取文件③promisify2.buffer与字符集3.http练习1node基础fs\\buffer\\http搭建一个http服务4.stream5.子进程二... 查看详情

字节跳动青训营每日一练编程题

1:实现一个36进制的加法0-9a-z。#include<bits/stdc++.h>typedeflonglongll;constllN=2e5+10;usingnamespacestd;ints[N];intmain()int_;stringa,b;while(cin>>a>>b)stringans;intpos 查看详情

结营啦!有缘相聚于青训,未来高处见呀~~(代码片段)

📸叮!记·字节跳动第一届青训营顺利结营啦!从8月份的青训营,到9月份的实训营,搁置了许久的结营心得终于拾起来辽!🎬开营进行时从答疑会开始,负责人仔细的阐述了本次训练营的性质和... 查看详情

青训营月影老师告诉我写好javascript的三大原则之——过程抽象(代码片段)

...5.总结过程抽象/HOF/装饰器命令式/声明式参加了这次字节青训营的活动,见到了传说中的月影老师࿰ 查看详情

青训营pro前端框架设计理念-vue3动机-手写实现mini-vue(代码片段)

...2.响应式①reactive②依赖收集3.虚拟DOM4.diffpatch更新diff对比青训营实战班的课程也结束了,今天先来撸一遍周五杨村长带来的mini-vue课程。错过课程的小伙伴一 查看详情

青训营pro前端框架设计理念-vue3动机-手写实现mini-vue(代码片段)

...2.响应式①reactive②依赖收集3.虚拟DOM4.diffpatch更新diff对比青训营实战班的课程也结束了,今天先来撸一遍周五杨村长带来的mini-vue课程。错过课程的小伙伴一 查看详情

青训营月影老师告诉我写好javascript的四大技巧——封装函数(代码片段)

如何写好JavaScript是每一个前端工程师一直以来在思考的问题,月影老师告诉我们一些写好JavaScript的原则,同时也教了一些我们如何写好JavaScript的技巧,今天来继续跟着月影老师学JavaScript吧~~起步今天我们来讨论函数... 查看详情

第五届字节青训营笔试后端编程练习题解(代码片段)

文章目录前言T1.36进制加法(模拟)题面思路代码T2.电影院选座(DFS)题面思路代码T3.IP地址(DFS)题面思路代码前言前段时间🐏了,今天简单写了一下,不知道如何提交代码进行评测,题... 查看详情

青训营月影老师告诉我写好javascript原则与技巧大总结

文章目录各司其责保证正确风格优先封装函数过程抽象组件封装妙用特性相关博文YK菌花了十几天的时间总结了月影老师三个小时的课程,对大部分案例都进行了自己的复现,并且进行了一些思考,今天来对之前博文&... 查看详情