碰到Cannot find module了吗? 来看看require函数与NodeJS模块加载

简介: 目录下面谈谈require函数先搞清楚是什么require能用来干什么?非内置的模块,也想用require来加载怎么做?require函数加载原理那么在npm registry上的库,怎么进行加载?好了,前面提了几个围绕了是否重新开一个NodeREPL终端来require JS库的问题解答”Cannot find module"问题解答是否需要重启Node REPL 或者修改代码是否需要重启正在的NodeJS进程的问题解答为何npm install lodash之后为何能够直接在node终端直接require构建代码共享,开源文化总结

在NodeJS项目开发过程中,我们经常使用公共JS库。


比较常用的做法就是通过npm去install目标js库,然后这个库会被放在node_modules目录下。


接着,我们自己写的JS文件中,使用require("目标js")库来使用他人共享的代码,这样很容易事半功倍。


下面谈谈require函数

本文会使用Node REPL:这是一个交互式的NodeJS代码执行终端,更多参考: http://nodejs.cn/learn/how-to-use-the-nodejs-repl


先搞清楚是什么

首先,直接说require的函数功能:用来加载目标js库,并返回当前库公开的属性成员函数/变量。


我们打开terminal终端/Command,输入: node //打开Node REPL


然后输入下面内容:


require


this.require === require


image.png

所以这里得到结论:require是Node引擎上下文(context)的内置对象属性,也就是全局对象的require属性,可调用或者使用this.require也行。


require能用来干什么?

初学NodeJS会了解到它有内置模块,比如fs,http 等。好,这里我们试着require('fs') //加载文件系统模块


require('fs')


image.png

非内置的模块,也想用require来加载怎么做?

在当前目录下,我们编写一个product.js(内容如下)。然后,试着用require来加载看看。

const products = {data:[]}
function getData(){
  return products.data;
}

Node REPL终端输入:require('./product.js')

image.png

require函数能够加载这个product.js,不过不像内置模块一样,需要通过给路径来定位到js文件,如:require('./product') 或者 require('./product.js')


这里我们看到打印的对象没有任何属性,require返回值为 :{}  //没有任何属性。


require函数加载原理

由于NodeJS模块都遵循了CommonJS规范,根据CommonJS规范,JS库的开发者如果需要开发某些函数对外部模块使用,需要使用module.exports或者exports


具体如下:


module.exports.属性名 = 函数引用 //这里将当前JS内的某个函数赋给module.exports


或者:


exports.属性名 = 函数引用


像前面一个版本的product.js相当于

const products = {data:[]}
function getData(){
  return products.data;
}
//默认情况,module.exports 这个对象没有任何属性,如下代码。
module.exports = {}

好,修改product.js文件,继续在终端跑require('./product')看看(输出仍旧为:{} )。

这里需要退出当前node终端,重新进入(请读者带着一个思考题:为什么需要重新打开一个node REPL终端)。

image.png

那么在npm registry上的库,怎么进行加载?

比如输入require('lodash') 马上发现错误了,“Cannot find module 'lodash'", 这个错误经常容易见到(有时候拿到一个NodeJS项目忘记跑npm install了)。


image.png

也可以输入require('restify'), require加载一些常见的模块试试。


通常我们需要使用命令安装JS库:npm install 目标JS库名,再来使用它共享的功能。


我们试试看,安装完lodash库之后,继续在Node终端输入require('lodash') 可以使用了,这里不需要重启(想想为什么)。


image.png

好了,前面提了几个围绕了是否重新开一个NodeREPL终端来require JS库的问题

为何node终端能够加载到product.js, export内部函数之后又需要重启,引入外部JS库又不需要重开一个Node REPL终端。。。


这里需要讲讲require的另一个伙伴,module函数。


它跟require函数一样都挂载在上下文中,也是全局对象的一个属性,它的作用是管理整个项目的模块。


image.png

上面所示,在一个有product.js 和node_modules/lodash这个模块,进入node终端,打印module显示的了模块加载的细节,这里稍微留意一下children(目前是一个空的数组)。


解答”Cannot find module"问题

但是paths非空,我们使用require加载函数的时候,node引擎会从内置模块和paths对应的路径去查找模块,找不到才会抛出类似异常:“Cannot find module 'lodash'"


当我们跑了npm install 库名, 对应模块被下载到node_module目录,加载的时候才能定位到库,正常使用该库功能。


在含有package.json的目录中,执行npm install命令,可以一次性下载dependencies属性声明的全部依赖库,在我们写的js文件中能够正常使用。


解答是否需要重启Node REPL 或者修改代码是否需要重启正在的NodeJS进程的问题

继续在终端输入require('./product') ,然后输入 module, 再次输出module对象,它的children已经多了一个Module对象(id对应到了product.js)。


当我们修改了product.js的时候,node引擎发现module对象已经记录加载过product.js了,不会重新进行加载。所以,虽然最新代码导出了getData函数,可是我们加载到的仍旧是:{}//无任何函数导出。


image.png

解答为何npm install lodash之后为何能够直接在node终端直接require

这个很简单,因为node启动,默认会查找到当前目录下的node_modules目录(不管目录存在不存在)。


当我们require一个不存在的js模块的时候,module对象找不到模块,它的children属性并不会有任何变动。


所以只需要安装了,就可以require加载。

image.png

构建代码共享,开源文化

这算是个题外话,前面有文章写过关于实现商品的增删查改,https://blog.csdn.net/geeklevin/article/details/109403172


读者可以下载这个JS:https://codechina.csdn.net/geeklevin/nodejs-api-002-crud/-/blob/master/product.js,使用require调用就能获得对商品对增删查改的功能。


通过对代码进行封装,对外开放几个函数,隐藏了细节,也简化了对功能的使用。


var pm = require('./product')
//调用库开放函数
pm.getData()
//其他调用
pm.其他函数()

好用的代码像诗,越简洁,越好用,那些垃圾代码只会慢慢沉寂下来无人问津。



npm registry (https://www.npmjs.com/

在npm registry上面有很多开发者发布的库,当我们想要使用一个功能,或者实现一个功能之前,不妨先上去找找有没有别人写好的库。


如果没有,那么遵循CommonJS(前面说的module.exports)来组织我们的代码发布共享,解决问题同时帮助他人,微薄之力能够推动社区进步呢。


npm社区就这样通过一个个普通开发者,分享一个个的小的库,培养了一个丰富多彩的技术生态。


Atwood’s Law是Jeff Atwood在2007年提出的:“any application that can be written in JavaScript, will eventually be written in JavaScript.”


这里我们在简单大胆的总结,require和module互相协作产生的模块加载机制,是整个NodeJS开源文化的基石之一;而CommonJS就是一个脱离了框架的协议。


这也在很多语言中反复出现,像python/java的import包,CommonJS就像一个包协议约定了库的共享的标准格式,npm对标maven central/python libs。


这套协议加上加载模式相关的接口模式,很值得借鉴。


总结

本文简单的介绍了require和module函数,Node引擎内使用module来管理模块,使用require加载模块。


基于这个技术和协议延伸了:如何做代码分享,构建JS模块分享的生态。


本篇从使用函数反向思考简单涉猎,更多细节可以自行阅读NodeJS源码。


篇幅有限,后续会发表更多技术补充。细心的读者可以发现,本文对于加载lodash这个模块没有更多深入解析,这种发布在npm registry上的包的解析,可以自行阅读它的代码,这一块还是比较好玩的。


参考:


https://nodejs.org/en/knowledge/getting-started/what-is-require/


https://nodejs.org/docs/v0.4.2/api/modules.html (这里的模块加载方式已经过时,不过刚好没有找到module的解析,可以看看,原理类似)



目录
相关文章
|
8月前
|
JavaScript
node下的two.js调用one.js出现无法编译问题 Cannot find module ‘c:
node下的two.js调用one.js出现无法编译问题 Cannot find module ‘c:
82 0
|
JavaScript
Syntax Error: Error: Cannot find module ‘node-sass‘
Syntax Error: Error: Cannot find module ‘node-sass‘
353 0
|
18天前
|
JavaScript
node环境之Error: Cannot find module ‘chalk’ 报错无法解决的问题—-网上说让你npm install chalk 基本是没有用的-优雅草央千澈解决方案
node环境之Error: Cannot find module ‘chalk’ 报错无法解决的问题—-网上说让你npm install chalk 基本是没有用的-优雅草央千澈解决方案
node环境之Error: Cannot find module ‘chalk’ 报错无法解决的问题—-网上说让你npm install chalk 基本是没有用的-优雅草央千澈解决方案
|
小程序
小程序wepy踩坑-Cannot find module 'D:\node_modules\npm\bin\npm-cli.js'
小程序wepy踩坑-Cannot find module 'D:\node_modules\npm\bin\npm-cli.js'
261 0
|
3月前
|
JavaScript 前端开发
Node.js 函数
10月更文挑战第5天
29 3
|
6月前
|
数据采集 JavaScript 前端开发
NodeJS技巧:在循环中管理异步函数的执行次数
在Node.js网络爬虫开发中,管理异步函数执行次数是关键。利用Promise.all、async/await或async库能优雅地控制并发。示例展示如何用async/await配合代理IP抓取数据,避免触发反爬策略。在循环中,每个异步请求只执行一次,保证请求有序进行,提高爬虫的稳定性和效率。通过正确的方法,可以有效应对网络爬虫的挑战。
NodeJS技巧:在循环中管理异步函数的执行次数
|
6月前
|
运维 监控 JavaScript
函数计算产品使用问题之将自定义层(nodejs 20)添加到了函数中,本地运行没有问题,但在函数计算中出现问题,该怎么办
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
7月前
|
JavaScript 前端开发
Node.js 函数
Node.js 函数
34 4
|
7月前
Error: Cannot find module ‘node:url‘【已解决】
Error: Cannot find module ‘node:url‘【已解决】
336 3

热门文章

最新文章