深入node学习2 实现commonjs

node中的模块

node主要有两种,esmodule,和commonjs,经过webpack编译,esmodule会被打包成commonjs的模式。
esmodule是静态模块,可以tree-shaking,而commonjs是动态的,无法tree-shaking。如

a.js
const path = require('path')
function A () {}
moduel.exports = {}
b.js
if(true){
require('./a.js')
}

如上,commonjs支持这种写法,这种写法只能在运行的时候才知道if是否正确,所以rqeuire里面的东西webpack事先不知道,所以无法tree-shaking。而esmodule可以在静态编译,所以他知道里面的东西,也知道里面哪些东西可以用,哪些东西不能用。所以可以tree-shaking。

模块规范

  • 1 每个文件都是一个模块,每个模块外面有一个函数包裹。
  • 2 文件需要被别人所使用,需要导出module.exports = xxx
  • 3 如果需要使用别人,就需要使用require

模块的分裂

  • 1核心模块,内置模块(node自带的,如fs)
  • 2 第三方模块(如co)
  • 3 文件模块,别人引用的时候需要通过相对路劲或者绝对路劲来引用。

内置模块

  • fs模块有readFile和readFileSync,一个是同步,一个是异步,当代码执行的时候,尽量少用同步,因为会阻塞。
  • 而require内部就是通过readFileSync实现文件的同步读写的。
    而且readFIleSync读取文件不存在的时候会发生异常。所以fs提供了fs.existsSync来判断存不存在,同步的。
  • path是常见的Node内置模块,用来处理路劲,如path.resolve()
console.log(path.resolve('a','b','c')) 
//解析绝对路劲,解析默认是采用了process.cwd(),来拼接上参数。

在这里插入图片描述
在这里插入图片描述
所以一般加上__dirname,__dirname获取当前执行文件所在的目录。

  • process.cwd()遇到路劲’/'会回到根目录。
  • path.join仅仅只会拼接路劲。
  • path.extname(),取扩展名,.js .jsx这些
  • path.basename(a.js, .js), 取文件名,得到a
  • path.relative(‘a/b/c’, a) 得到…/…/,根据路劲获取相对路劲
  • path.dirname(’/a/b/c’),取c文件的父路劲。__dirname的实现就是path.dirname

字符串如何变成js执行。

1。 eval,受执行环境影响
2. new Function()" 模板引擎的实现原理",不会受环境影响

new Function('console.log(1)')

new Function最后一个参数就是函数体。
在这里插入图片描述
这样需要给字符串包装一层函数才能实现。

node中实现了一个模块’vm’!!

在这里插入图片描述
Function只能访问自己的变量和全局变量。但还是可能污染。
而node中的vm是不受影响的,沙箱环境。(快照(执行前记录信息,执行后还原信息))(proxy实现)

vm

在这里插入图片描述
Vm提供runinThisContex,只在全局一个上下文中执行。runinThisContext不会产生函数。
在这里插入图片描述

require的实现

  • 1 读取文件
  • 2 读取文件后将里面的内容包装成一个函数
  • 3 通过runInThiwContext将他变成Js语法。
    在这里插入图片描述
    require就相当于把q文件的内容包装到iffe函数里面去。
    调式源码,node的require的实现大致分为了九步
  • 1 require方法-》调用Module.prototype.require方法
  • 2 Module._load 加载模块
  • 3 Module.resolveFilename方法就是把路劲变成绝对路劲,加上后缀名,.js .json方便缓存
  • 4 new Module拿到绝对路径创建一个模块
  • 5 module.load对模块进行加载
  • 6 根据文件后缀Module.extensions[’.js’]去做策略加载
  • 7 采用同步读取文件
  • 8 增加一个函数的壳子,并且让函数执行,通过vm.runInThisContext,让module.exports作为this
  • 9 用户默认拿到module.exports返回结果
  • 最终返回exports对象。

实现:

思路:首先要通过传入的路劲获取一个绝对路劲并且加上后缀名。接着创建一个模块module,然后缓存起来。然后通过module.load去同步加载模块,并给module.exports赋值,最后导出即可。
实现:
获取绝对路劲
在这里插入图片描述

在这里插入图片描述
实现resolveFilename先,
在这里插入图片描述
在这里插入图片描述
先判断传入的路劲是否存在,不存在加上后缀名后再判断。返回一个带后缀的绝对路劲。
在这里插入图片描述
接着创建module实例,然后缓存起来,再然后直接load加载模块。
在这里插入图片描述
load主要通过策略模式,通过不同的后缀名调用不同的办法。
在这里插入图片描述
先看json的
在这里插入图片描述
如果文件名为json,就同步读取文件内容并JSON.pars解析,再给module.exports赋值。在这里插入图片描述
最后返回Module.exports.
在这里插入图片描述
这样就完成对json的require。接着看js的
在这里插入图片描述
js的稍微复杂一点,因为读取文件内容后,比如读取的文件内容是
在这里插入图片描述
需要外层包一层函数,通过字符串包一层函数,然后通过vm.runInThisContext在全局上下文中执行这个字符串,也就是定义函数。这个函数大概长这样

const fn = function(exports, module, __dirname, __filename){
	//这里的内容是读取到的
	const a = 1
	module.exports = a //对module.exports赋值
}

然后在执行fn.call()将module等对应参数传入。这样module实例上的exprots属性就有值了,再return module.exports就完成了。
在这里插入图片描述
并且缓存效果也完成了。
这样简单的Commonjs规范就完成了。

require文件模块的查找规范

  • 判断路劲是不是核心模块,再看下是不是第三方,不是的话就继续往下判断。
  • 默认查找同名文件,如果没找到尝试添加查找.js和.json文件
  • 如果没有就找同名文件夹(当成一个包使用),同名文件夹找到了,先查找package.json文件,有的话就根据main查找文件,没有package.json就查找index.js文件,如果index.js文件还没有就报错。

第三方模块

引用也是没有相对路劲。

代码中的第三方模块
  • 1 首先默认会往上查找,查找nde_module下同名的文件夹,同名文件夹找到了,先查找package.json文件,有的话就根据main查找文件,没有package.json就查找index.js文件,如果index.js文件还没有就报错。
  • 2 如果没找到,就继续查找上级的node_module,可以通过哦module.paths获取查找的路劲。
    在这里插入图片描述

会沿着这个路劲一直向上找,没找到就报错。

全局模块

全局安装,安装到电脑中的npm下。比如全局下命令行执行node -v,npm安装的全局模块在npm下生成了一个快捷方式,只能在命令行中使用,执行对应的文件。(尽量不要使用cnpm,安装时无法锁定版本,会出现很多问题。)

实现全局模块使用

步骤: 1 配置package.json中的bin
2添加命令 #! usr/bin/env node 表示用node执行此文件
3 执行npm link连接。
在这里插入图片描述
在这里插入图片描述
运行npm link让他映射到npm全局。
在这里插入图片描述
这样运行gm就会打印aaa了。

本地安装模块

依赖关系(开发依赖,生成依赖,同等依赖),打包依赖,可选依赖。

  • node_module下的.bin文件夹意味着你安装的一些模块可以在命令行运行。
  • 如npx webpack,就会在找到当前node_module下的bin目录,如果模块不存在会先安装再使用,使用后可以自动删除。
  • 使用npm run xxx的时候,默认是在执行之前,将环境变量添加到全局下,所以可以使用,但是命令执行完毕后会删除对应的path。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值