import{_ as s,o as n,c as a,a as l}from"./app.33820b61.js";const F=JSON.parse('{"title":"前端工程化","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js","slug":"node-js","link":"#node-js","children":[{"level":3,"title":"什么是Node.js","slug":"什么是node-js","link":"#什么是node-js","children":[]},{"level":3,"title":"Node.js的应用场景","slug":"node-js的应用场景","link":"#node-js的应用场景","children":[]},{"level":3,"title":"Node.js的参数传递","slug":"node-js的参数传递","link":"#node-js的参数传递","children":[]},{"level":3,"title":"Node中的全局对象","slug":"node中的全局对象","link":"#node中的全局对象","children":[]}]},{"level":2,"title":"模块化开发","slug":"模块化开发","link":"#模块化开发","children":[{"level":3,"title":"模块化的初衷","slug":"模块化的初衷","link":"#模块化的初衷","children":[]},{"level":3,"title":"CommonJS","slug":"commonjs","link":"#commonjs","children":[]},{"level":3,"title":"ESModule","slug":"esmodule","link":"#esmodule","children":[]},{"level":3,"title":"深入理解模块加载","slug":"深入理解模块加载","link":"#深入理解模块加载","children":[]},{"level":3,"title":"拓展内容","slug":"拓展内容","link":"#拓展内容","children":[]}]},{"level":2,"title":"包管理工具","slug":"包管理工具","link":"#包管理工具","children":[{"level":3,"title":"npm","slug":"npm","link":"#npm","children":[]},{"level":3,"title":"package.json","slug":"package-json","link":"#package-json","children":[]},{"level":3,"title":"npx","slug":"npx","link":"#npx","children":[]},{"level":3,"title":"npm包的发布","slug":"npm包的发布","link":"#npm包的发布","children":[]},{"level":3,"title":"npm包的开发调试","slug":"npm包的开发调试","link":"#npm包的开发调试","children":[]},{"level":3,"title":"PNPM","slug":"pnpm","link":"#pnpm","children":[]}]},{"level":2,"title":"系统学习Webpack","slug":"系统学习webpack","link":"#系统学习webpack","children":[{"level":3,"title":"Node内置模块 path","slug":"node内置模块-path","link":"#node内置模块-path","children":[]},{"level":3,"title":"初识Webpack","slug":"初识webpack","link":"#初识webpack","children":[]}]}],"relativePath":"note/Front-end Engineering.md","lastUpdated":1676737739000}'),e={name:"note/Front-end Engineering.md"},o=l(`
Node.js是一个基于V8 JavaScript引擎的JavaScript运行时环境
可以简单总结出Node.js和浏览器的区别
Chrome浏览器
Blink负责解析HTML文档,遇到JavaScript标签时将内容交给V8引擎
Blink 是 Google Chrome 浏览器的渲染引擎,V8 是 Blink 内置的 JavaScript 引擎
Node.js

process.argv
#process.argv
返回一个数组
process.argv[2]
读取来自命令行的额外参数process.argv[0]
process.argv[1]
分别为node.exe
的绝对路径和目标文件
的绝对路径// sum.js
const x = process.argv[2]
const y = process.argv[3]
console.log(x + y)
# 通过命令行运行node执行脚本 并传入参数
node sum.js 5 10 # 15
console.log
打印内容到stdout并加上换行符console.clear
清空当前stdout中的内容console.trace
打印字符串Trace:
到stderr 在浏览器的控制台选项卡中,我们可以通过输入JS代码与之交互,在Node.js中同样提供了类似的功能
node
即可进入在浏览器中,我们可以在JS代码中访问全局对象window
,代表当前标签窗口
在Node.js中的全局对象名为global
,在控制台输出global
对象:
> global
<ref *1> Object [global] {
global: [Circular *1],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
performance: Performance {
nodeTiming: PerformanceNodeTiming {
name: 'node',
entryType: 'node',
startTime: 0,
duration: 2245.9675999991596,
nodeStart: 1.7120999991893768,
v8Start: 7.749699998646975,
bootstrapComplete: 56.47019999846816,
environment: 28.44789999909699,
loopStart: 97.62589999847114,
loopExit: -1,
idleTime: 2070.0206
},
timeOrigin: 1675854922619.539
},
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
}
}
Buffer
clearImmediate
clearInterval
clearTimeout
console
process
queueMicrotask(callback)
setImmediate(callback, [, ...args])
setInterval(callback, delay[, ...args])
setTimeout(callback, delay[, ...args])
TextDecoder
TextEncoder
URL
URLSearchParams
WebAssembly
__dirname
__filename
exports
module
require()
__dirname
当前模块的目录名__filename
当前模块的文件名exports
module
require()
将在模块章节中讲解global
对象 #global
是一个全局对象
process
console
setTimeout
等都有被放入到global
中window
对象上的这无异于增加了开发者的心智负担,所以在最新的ECMA标准中出现了globalThis
,指向全局对象
globalThis
指向window
对象globalThis
指向global
对象两个全局对象的区别:在浏览器中通过var
定义的变量会被放到window
对象上,而Node.js不会
// moduleA.js
const moduleA = (function(){
const name = "Ziu"
const age = 18
const run = () => {
console.log(name + age + 'is running.')
}
return {
name,
age,
run
}
})()
// moduleB.js
console.log(moduleA.name) // 在其他模块中调用
CommonJS是一种规范,当初命名为ServerJS,旨在浏览器以外的地方使用,后为体现其广泛性,改名为CommonJS,简称CJS
规范 是用来指导 实现的
Node
是CommonJS在服务端的代表实现Browserify
是CommonJS在浏览器中的一种实现 (正在被淘汰)WebPack
打包工具具备支持CommonJS的支持和转换所以,Node.js对CommonJS进行了支持和实现,让JavaScript在Node上运行时可以实现模块化开发
.js
文件都是一个单独的模块exports
module.exports
require
// env.js
exports.name = 'Ziu'
exports.age = 18
// utils.js
module.exports = {
sum: function(x, y) {
return x + y
}
}
// index.js
const utils = require('utils.js')
utils.sum(1, 2) // 3
const { sum } = require('utils.js')
sum(1, 2) // 3
const { name, age } = require('env.js')
console.log(name, age) // Ziu 18
exports
的本质 #exports
和require
在Node中的本质
exports
是一个对象,我们可以在这个对象中添加很多属性,添加的属性则会被导出 require
导入时:const env = require('env.js')
env
这个变量等于env.js
中的exports
对象env
是exports
对象的引用赋值{ id: '...', exports: { ... }, loaded: true, ... }
require
导入模块,模块中的代码也不会重新执行(module.loaded
属性) exports
对象缓存上取值// utils.js
exports.a = 0
// 1s后修改a值
setTimeout(() => {
exports.a = 1
}, 1000)
// 2s后检查a值
setTimeout(() => {
console.log(exports.a) // 2
}, 2000)
// index.js
const utils = require('./utils')
console.log(utils.a) // 0
setTimeout(() => {
console.log(utils.a) // 1
utils.a = 2 // 反过来修改a值
}, 1500)
在上述代码中,utils
对象中的属性a
在一秒后被赋值为1
,因此在index.js中输出utils.a
得到了两次不同的结果
反过来,在index.js中修改导入的utils.a
的值后,修改结果也会反映在exports.a
上,输出的值为2
实际开发中不要修改导入模块中的变量,改变原模块中变量的值并不规范
module.exports
#在Node.js中,真正常用的导出方式是module.exports
module.exports
本质上就是exports
对象(同一个内存地址)exports
对象赋值,将需要导出的内容统一导出module.exports
重新赋值,即改变了exports
对象的指向,后续的修改不再影响原模块中的变量const name = 'Ziu'
const run = () => console.log(name + 'is running.')
module.exports = {
name,
run
}
既然如此,为什么还要存在exports
这个概念呢?
module.exports
的概念的Module
类,每一个模块都是Module
的实例,也就是module
exports
,而是module.exports
module
对象中的exports
属性是exports
对象的一个引用 module.exports === exports === utils
如果module.exports
不再引用exports
对象了,修改exports
对象也就没有意义了
// utils.js
module.exports = {
name: 'Ziu'
}
exports.age = 18
// index.js
const utils = require('utils.js')
console.log(utils.name) // Ziu
console.log(utils.age) // undefined
当使用module.exports = { ... }
后,模块中原有的exports
不再被导入识别,导入的内容将变为module.exports
指定的对象内容
require
的本质 #require
是一个函数,可以帮助我们导入一个文件(模块)中导出的对象
.js
后缀,直接使用require('./utils')
index.js
,直接使用require('./tools')
导入tools/index.js
这涉及到require
在匹配路径后的查找规则:
分为三种情况:内置模块、自定义路径、包名
const path = require('path')
const utils = require('./{filename}')
./
../
/
{filename}.js
文件{filename}.json
文件{filename}.node
文件{filename}
作为路径继续查找index
文件 {filename}/index
{filename}/index.js
文件Cannot find module 'xxx'
const lodash = require('lodash')
node_modules
中查找node_modules/{package_name}/index.js
node_modules
找不到则继续向上查找,直到查找到根目录的node_modules
require
的位置相关module
上都有一个属性loaded
loaded === false
表示该模块尚未被加载require
引入时会检查该属性是否为true
export
和import
用法概览 #ESModule借助export
和import
导入导出内容,需要注意的是导入导出的并不是对象
export
定义的是当前模块导出的接口,import
可以导入来自其他不同模块的接口
export default
可以设置默认导出对象export { ... }
可以统一导出多个内容export
和import
都可以使用as
关键字重命名导出/导入的接口import * from 'xxx'
export * from 'xxx'
批量导入/导出// utils.js
export function sum(a, b) {
return a + b
}
export function sub(a, b) {
return a - b
}
export default function log(...args) {
console.log(...args)
}
export {
name: 'Ziu',
age: 18
}
export const ENV_VARIABLE = 'Hello, World!'
// index.js
import { sum, sub, name, age, ENV_VARIABLE } from './utils'
import log from './utils.js'
sum(1, 2) // 3
sub(2, 3) // -1
log(name, age, ENV_VARIABLE) // 'Ziu' 18 'Hello, World!'
需要注意的是,在浏览器中要使用ESModule,需要为<script>
标签添加module
标记:
<script src="index.js" type="module"></script>
type="module"
的JS代码后,会分析模块中导入的ESModule模块另外,export
与import
必须位于模块的顶层,如果位于作用域内会报错,因为这就无法对代码进行静态分析优化了
export
详解 #export
有两种导出方式:
export const name = 'Ziu'
export { v1, v2 } export * from 'xxx'
export default AGE = 18
export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值我们援引之前介绍CJS时的案例,将后缀名改为mjs
即可在Node中运行ESModule模块代码
初始获得的a
值为0,经过1s后,在utils.mjs
中修改了a的值,这时导入utils.mjs
模块的其他模块可以获取到a
最新的值
// utils.mjs
export let a = 0
// 1s后修改a值
setTimeout(() => {
a = 1
}, 1000)
// index.mjs
import { a } from './utils.mjs'
console.log(a) // 0
setTimeout(() => {
console.log(a) // 1
}, 1500)
index.mjs
导入的本质是一个接口拓展阅读:CommonJS与ESModule加载模块的异同
import
详解 #检查下述代码:
foo()
import { foo } from 'foo'
import
命令具有提升效果,会提升到整个模块的顶部import
的执行早于函数的调用,import
命令是在编译阶段执行的,在代码运行之前import
是静态执行,所以不能使用表达式和变量(只有运行时才有值)import 'lodash'
import 'lodash'
import * from 'utils'
add(1, 2)
export * from 'utils'
*
一次性导入模块中所有导出的变量、函数、类import()
函数 #通过import
命令导入的模块是静态的,会被提升到模块顶部,并不支持条件导入
ES2020引入了import()
函数,可以通过import()
函数实现条件导入,动态加载ESModule模块
const main = document.querySelector('main');
import(\`./section-modules/\${someVariable}.js\`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
})
await
同步地操作它import()
函数可以在模块外的JS脚本中使用,用于在运行时加载外部模块,类似于require()
require()
,import()
是异步加载模块通过.then
函数处理导入的模块时,行为和import
是相同的:
.then
入参为默认导出对象.then(({ add, sub }) => { ... })
应用场景
按需加载:按钮点击后才加载相关的JS文件
btn.addEventListener('click', () => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open()
})
.catch(err => console.log(err))
})
条件加载:根据主题色加载不同JS文件
if(darkMode) {
import('dark.js').then(() => ...)
} else {
import('light.js').then(() => ...)
}
传入动态值
let moduleName = () => ['Home', 'History', 'User'][0]
import(\`./\${moduleName()}.js\`)
import.meta
#ES2020引入了i
,它仅能在模块内部使用,包含一些模块自身的信息,即模块元信息
import.meta.url
返回当前模块的URL路径 fetchData.js
,要在模块内引入一个名为data.json
的数据:import( new URL('data.json', import.meta.url) )
file://
协议的链接import.meta.scriptElement
<script>
标签,相当于document.currentScript
规范中并未规定i
中包含哪些属性,一般包括上面两个属性
ESModule的解析过程可以分为三个阶段:
Construction
Module Record
Instatiation
export const name = 'Ziu'
,会将变量name
添加到模块环境记录中 Module Enviroment Record
Evaluation
name = 'Ziu'

文章推荐:ES modules: A cartoon deep-dive
require()
是同步加载模块,而ESModule模块的import
命令是异步加载模块import
命令拥有一个独立的模块依赖的解析阶段设想有以下两文件 a.js
与b.js
:
// a.js
exports.done = false
const b = require('./b.js')
console.log('在 a.js 之中,b.done = %j', b.done)
exports.done = true
console.log('a.js 执行完毕')
// b.js
exports.done = false
const a = require('./a.js')
console.log('在 b.js 之中,a.done = %j', a.done)
exports.done = true
console.log('b.js 执行完毕')
// main.js
const a = require('./a')
const b = require('./b')
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done)
执行脚本main.js
,先执行a.js
:
done
值为false
a.js
的代码暂停执行 进入b.js
并等待其执行完毕在b.js
中:
done
值为false
a.js
从a.js
模块中取exports
对象false
(a.js
执行已经执行的部分)b.js
继续向下执行 执行完毕后 将执行权交还给a.js
回到a.js
中:
最终输出:
在 b.js 之中,a.done = false
b.js 执行完毕
在 a.js 之中,b.done = true
a.js 执行完毕
在 main.js 之中, a.done=true, b.done=true
总结:
ESModule的导入和导出与CommonJS有本质不同:
// a.mjs
import { bar } from './b.mjs'
console.log('a.mjs')
console.log(bar)
export let foo = 'foo'
// b.mjs
import { foo } from './a.mjs'
console.log('b.mjs')
console.log(foo)
export let bar = 'bar'
执行a.mjs
后发现报错了:ReferenceError: Cannot access 'foo' before initialization
,变量foo
未定义
a.mjs
的依赖关系时,发现其依赖了b.mjs
b.mjs
并解析它的依赖关系b.mjs
的过程中,发现它又依赖了a.mjs
a.mjs
而是认为a.mjs
这个模块的Module Record
已经存在了console.log(foo)
时发现foo
未定义 抛出错误要实现预期效果,可以将foo
与bar
改写为取值函数,这时执行就不会报错了:
// a.mjs
import { bar } from './b.mjs'
console.log('a.mjs')
console.log(bar())
export function foo() {
return 'foo'
}
// b.mjs
import { foo } from './a.mjs'
console.log('b.mjs')
console.log(foo())
export function bar() {
return 'bar'
}
这是因为函数function
具有提升作用,在a.mjs
中执行import { bar } from './b.mjs'
之前,foo
就有定义了。
因此在进入b.mjs
执行console.log(foo())
时可以取到foo
,代码可以顺利执行
另:如果将foo
定义为函数表达式export const foo = () => 'foo'
,由于没有变量提升,代码仍然会报错
ESModule和CommonJS另一个重要区别就是:
ESModule模块是在浏览器与服务端通用的,之前在解读CommonJS时介绍了它拥有的一些内部变量(模块变量):
arguments
require
module
exports
__filename
_dirname
这些变量在ESModule模块中都是不存在的,且顶层的this
不再指向当前模块,而是undefined
在Node.js中,普通的.js
文件会被默认解析为CommonJS,要使用ESModule有两种方式:
.mjs
并且不可省略 .mjs
结尾的文件时,将按照ESModule的规则解析其导入导出关系package.json
中的type
字段修改为module
.js
文件都将被作为ESModule模块解析.cjs
package.json
中的字段 #全局安装、局部安装、开发依赖、生产依赖
package.json
用来记录项目的配置信息,包括项目名、版本、项目入口、脚本、依赖项
通过npm init -y
初始化项目,会为我们生成一个package.json
文件
强烈建议阅读官方文档对package.json
的解读:package.json
main
与exports
字段 #main
字段
main
字段为一个JS模块import { something } from 'es-module-package'
导入时main
字段指定的模块查找导出内容exports
字段
exports
字段优先级高于main
字段,它具有多种用法:exports
字段:exports: { "./submodule": "./src/submodule.js" }
import submodule from 'es-module-package/submodule'
时,会按照以下路径查找模块:./node_modules/es-module-package/src/submodule.js
main
的别名dependencies
和devDependencies
的区别 #dependencies
应当包含依赖的最小集,在此之上添加的文档、测试、调试、构建等的依赖,都应当被放进devDependencies
npm install xxx
后,只会安装包本身以及其中的dependencies
运行时依赖external
官方对两个字段的定义:
dependencies: Dependencies are specified in a simple object that maps a package name to a version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.
devDependencies: If someone is planning on downloading and using your module in their program, then they probably don't want or need to download and build the external test or documentation framework that you use.
总结:将与代码运行时无关的依赖放入devDependencies
,可以让他人在使用你开发的库时,少安装一些不必要的依赖。
~1.2.0
和^1.2.0
有什么区别 #版本号简要说明:[major 主要版本].[minor 次要版本].[patch 补丁版本]-[alpha | beta 测试阶段].[测试版本号]
~
Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not.
~1.2.0
:允许安装1.2.0 - 1.3.0
的版本~1.2
:允许安装1.2.0 - 1.3.0
的版本~1
:允许安装1.x
的版本,不允许上升到2.0.0
^
Allows changes that do not modify the left-most non-zero element in the [major, minor, patch]
tuple.
允许对1.0.0
及更高版本进行补丁和次要更新,对版本0.x >=0.1.0
进行补丁更新,对版本0.0.x
不进行更新。
^1.2.3
:允许安装1.2.3 - 2.0.0
的版本^0.2.3
:允许安装0.2.3 - 0.3.0
的版本^0.0.3
:允许安装0.0.3 - 0.0.4
的版本参考:node-semver
Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not.
devDependenciesAllows changes that do not modify the left-most non-zero element in the [major, minor, patch] tuple.
允许对版本 1.0.0 及更高版本进行补丁和次要更新,对版本 0.X >=0.1.0 进行补丁更新,对版本 0.0.X 不进行更新。
npx
命令用来在项目路径下执行node_modules/.bin
下的命令,默认这些命令都位于node_modules/.bin
中,如果不cd进去shell找不到它们,在项目根目录调用它们自然会报未知命令的错误。
以webpack为例:
webpack
会报错:未知命令npx webpack
来替我们调用 ./node_modules/.bin/webpack
package.json
中定义script
npm run xxx
后,会为我们将.bin
中的命令添加进系统的PATH中.bin
中的命令了webpack
命令了package.json
中的bin
字段 #.bin
目录下的可执行文件从何处来?由npm官方文档中对package.json/bin
字段的介绍可以知道:
A lot of packages have one or more executable files that they'd like to install into the PATH. npm makes this pretty easy (in fact, it uses this feature to install the "npm" executable.)
To use this, supply a
bin
field in your package.json which is a map of command name to local file name. When this package is installed globally, that file will be either linked inside the global bins directory or a cmd (Windows Command File) will be created which executes the specified file in thebin
field, so it is available to run byname
orname.cmd
(on Windows PowerShell). When this package is installed as a dependency in another package, the file will be linked where it will be available to that package either directly bynpm exec
or by name in other scripts when invoking them vianpm run-script
.
如果第三方包在package.json
中声明了bin
字段:命令名称 -> 本地文件名
的映射,如bin: { "webpack": "bin/webpack.js" }
在执行安装npm i xxx
时,会由包管理工具创建.bin
目录,并创建一个可执行命令并将其软链接到目标文件
推荐阅读:三面面试官:运行 npm run xxx 的时候发生了什么?
如果你希望发布自己的npm包,主要涉及到的命令有:
npm init -y # 初始化项目 生成package.json
npm login # 登录自己的npm账号
npm publish # 将包发布到npm上 后续版本更新也使用此命令
npm unpublish # 取消发布 删除发布的npm包
npm deprecate # 让发布的npm包过期
执行npm init -y
后,为我们生成的默认package.json
如下:
{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {},
"devDependencies": {},
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
其中keywords
author
license
都会展示在此包的npm网站上,除此之外还可以定义homepage
repository
字段
在npm包的开发过程中,如果要调试代码,那我们不可能每次修改代码都通过提交到npm后,重新下载到本地来调试代码
一般来讲有三种解决方案:
import lodash from 'lodash-es'
修改为import lodash from '... debug/lodash-es'
package.json
中的依赖版本为file:包的路径
npm link
命令 软连接 npm link
,将提示:success Registered "my-project".
成功注册名为my-project
的包npm link "my-project"
即可将当前项目的node_modules
中的此包软连接到正在开发中的包的路径npm link
导致的依赖关系问题 npm i yalc -g
模拟发布yalc publish
yalc link my-project
:向node_modules
创建指向包内容的符号链接,并且不会修改package.json
中的内容yalc add my-project
:将包拉到当前项目根目录.yalc
中,并将 file:.yalc/my-project
依赖关系注入到package.json
中(file:包的路径
)传统包管理工具如npm yarn都有以下两个痛点:
node_modules
太重 硬盘压力较大node_modules
中 导致即使某些间接依赖虽然没有被安装 却能正常引入)PNPM(performant npm)有以下优点:
node_modules
中的文件链接自特定的内容寻址存储库node_modules
代码无法访问任意包(避免了幽灵依赖)
操作系统使用不同的文件系统,对真实的硬盘读写操作做了一层抽象,借由文件系统,我们得以方便地操作和访问文件的真实数据
F:/Video/abc.mp4
和 F:/Video/cba.mp4
指向了同一片硬盘数据空间 abc.mp4
或cba.mp4
之一,并不会影响到其他文件对该片数据空间的读取copy foo.js foo_copy.js
cp foo.js foo_copy.js
mklink /H aaa_hard.js aaa.js
ln foo.js foo_hard.js
mklink aaa_soft.js aaa.js
ln -s foo.js foo_copy.js
打开符号链接文件,会直接跳转到原文件,且符号链接文件不可写
这样,多个项目之间可以方便地共享相同版本的依赖包,减小了硬盘的压力。
以安装axios
为例,我们在某个项目中执行了pnpm add axios
.pnpm/axios
中,并且硬链接到硬盘中的数据文件node_modules
创建文件夹axios
,将其中的文件硬链接到硬盘中的相同位置axios
,在执行pnpm add axios
后:只需要在各自的node_modules
创建硬链接到那片数据空间即可node_modules
的根目录下 node_modules
目录下 node_modules
根目录中的依赖包,是node_modules/.pnpm
中硬链接文件的的符号链接node_modules/.pnpm
中,包含了附加版本信息的真实文件(硬链接到硬盘数据的文件).pnpm
文件夹中对应版本的硬链接文件上
pnpm === npm init -y
项目初始化pnpm install === npm i
安装项目依赖pnpm add <pkg> === npm install <pkg>
安装npm包pnpm remove <pkg> === npm uninstall <pkg>
移除npm包pnpm <script> === npm run <script>
执行项目脚本~/.pnpm-store
C:/Users/Ziu/.pnpm-store
~/path === $HOME/path
用户目录./path
相对路径/path
根目录<pnpm home directory>/store
~/.local/share/pnpm/store
%LOCALAPPDATA%/pnpm/store
~/Library/pnpm/store
可以通过命令行,获取到这个目录:
pnpm store path # 获取到硬链接store的路径
如果当前目录创建了很多项目,尽管PNPM已经节省了空间,但是由于所有依赖都通过硬链接存放在同一个store中
随着文件数量增长,导致store越来越大,可以通过命令行,移除冗余文件造成的体积占用
pnpm store prune # prune修剪 从store中删除当前未被引用的包 以释放store的空间
/
\\
\\\\
目前也支持 /
可以通过Node的path模块来抹平不同平台的差异
常用API
path.extname
后缀名
path.basename
文件名
path.dirname
文件夹名
path.join
拼接多个路径
p2
通过../
指定了上一级路径,所以从abc
紧接着就是why
了const path = require('path')
const p1 = 'abc/cba'
const p2 = '../why/kobe/james.txt'
console.log(path.join(p1, p2)) // \\\\abc\\\\why\\\\kobe\\\\james.txt
path.resolve
将多个路径片段解析、拼接为一个绝对路径 path.resolve()
将返回当前工作目录的绝对路径path模块在webpack的使用中有较大作用,所以预先介绍了一下,下面正式进入Webpack的学习