自定义模块
程序员自己写的模块
我们对代码的封装是以模块(一个独立的.js文件)为单位进行的。一般的做法是实现好某一个功能之后,封装成一个模块,然后在其它文件中使用这个模块。
类比于js自定义函数,自定义模块的使用场景是:
- 代码需要在项目重用
- 代码需要提供给他人使用
- 代码虽然不需要重用,但封装成模块有利于优化代码结构,方便后期维护与扩展
一共有两步:
- 定义模块。就是创建一个js文件,有导出。
- 使用模块。在需要使用的地方去导入模块文件。
定义模块
所谓定义模块,就是新建一个js文件。文件取名时,要注意一下:
- 一般会用模块名给它命名。类比于核心模块,例如,你的模块叫myModule,则这个js文件最好叫myModule.js
- 不要与核心模块的名字重复了。就像我们定义变量不要与核心关键字重名,你自己定义的模块也不要叫fs.js,因为nodejs有一个核心模块就叫fs.js。
- 要记得导出模块
示例:我们定义一个模块,文件名是tools.js。 在js文件内我们定义一些函数,变量,它们会根据我们的业务要求做一些不同的工作。最后根据情况导出这些函数,变量。
//tools.js
const relativeTime = (oldTime) => {
const t = new Date(oldTime)
// Date.now():现在的时间戳(毫秒)
// t.getTime():旧时间的时间戳(毫秒)
const diff = Date.now() - t.getTime() // 相隔多少毫秒
// Math.floor 向下取整: 1.7年 ---> 1年前
const year = Math.floor(diff / (1000 * 3600 * 24 * 365))
if (year) {
return `${year}年前`
}
const month = Math.floor(diff / (1000 * 3600 * 24 * 30))
if (month) {
return `${month}月前`
}
const day = Math.floor(diff / (1000 * 3600 * 24))
if (day) {
return `${day}天前`
}
const hour = Math.floor(diff / (1000 * 3600))
if (hour) {
return `${hour}小时前`
}
const minute = Math.floor(diff / (1000 * 60))
if (minute) {
return `${minute}分钟前`
} else {
return '刚刚'
}
}
const formatDate = (dateTime) => {
// console.log(date)
// date = new Date();
const date = new Date(dateTime) // 转换成Data();
console.log(date)
var y = date.getFullYear()
console.log(y)
var m = date.getMonth() + 1
m = m < 10 ? '0' + m : m
var d = date.getDate()
d = d < 10 ? ('0' + d) : d
return y + '-' + m + '-' + d
}
// 通过module.exports来导出模块
module.exports = {
formatDate,
relativeTime
};
导出模块
在文件尾部,使用module.exports来导出模块。
// 格式
module.exports = 要导出的内容
注意:
-
module.exports 是固定写法,一般放在文件的最末尾,也只用一次。
-
module.exports表示当前模块要暴露给其它模块的功能。
- 它可以导出对象,数组,函数等等类型。为了方便组织代码,导出对象的情况比较多。
- 不必要导出所有函数,对象,数组等。那些没有导出的部分就相当于这个模块的内部变量了。
导入模块
完成了模块定义之后,我们就可以在另一个文件中使用这个模块了。
基本步骤是
1. 导入模块;
2. 先打出来看看;
格式:
const 模块名 = require('./模块路径')
当一个模块被成功引入之后,就可以类比使用核心模块的过程一样去使用它们了。
// index.js
// 1. 导入模块
// 注意这里使用的是相对路径。可省略.js.
const myMath = require('./myMath');
// 在使用之前请先打印出来看看
console.log(myMath);
// 2. 使用模块中的方法
let rs = myMath.add(23,45);
console.log(rs)
注意:
- 使用require语句引入你定义好的模块
- 这里必须使用
相对路径的格式去引入自定义模块。"./" 也不能省略。
导出模块的两种方式
导出模块有两种方式
- exports
- module.exports
// 定义方法,常量
const myPI = 3.14
function add(a,b) {return a + b ;}
// 导出,两种方法任意都可以
// 方法一:
exports.myPI = myPI
exports.add = add
// 方法二:
module.exports.myPI = myPI
module.exports.add = add
// 方法二(变形)
module.exports = {
myPI,
add
}
在阅读其它人的代码时,可能会遇到这两种不同的写法。所以我们还是有必要了解一下的。
cookie模块,body-parser模块,arry-flatten模块中的导出均采用不同的方式。
两个对象的关系
- 初始exports和module.exports是指向同一块内存区域,其内容都是一个空对象。(exports是module.exports的别名)即:
exports === module.exports // 输出是 true
所以下面两种写法的效果是一样的:
//1 mymodule.js
exports.f = function(){ }
exports.pi = 3.1415926
//2 mymodule.js
module.exports.f = function(){ }
module.exports.pi = 3.1415926
-
在定义模块时:
如果直接给exports对象赋值(例如:exports={a:1,b:2}),此时,exports就不会再指向module.exports,而转而指向这个新对象,此时,exports与module.exports不是同一个对象。
在引入某模块时:以该模块代码中
module.exports指向的内容为准。
## npm init 命令
在某个目录下开启小黑窗,输入如下命令:
# init 初始化
npm init
它会启动一个交互式的程序,让你填入一些关于本项目的信息,最后生成一个package.json文件。
如果你希望直接采用默认信息,可以使用:
npm init --yes
// 或者是 npm init -y
说明:
- 这个命令只需要运行一次,它的目的仅仅是生成一个package.json文件。
- 如果项目根目录下已经有了package.json文件,就不需要再去运行这个命令了。
- 这个package.json文件后期是可以手动修改的。
package.json文件
它一般是由npm init命令创建出来的,它的整体内容是一个json字符串,用来对当前项目进行整体描述。其中最外层可以看作是一个js的对象(每一个属性名都加了"",这就是一个典型的json标记)。这个文件中有非常多的内容,我们目前学习如下几个:
- name: 表示这个项目的名字。如是它是一个第三方包的话,它就决定了我们在require()时应该要写什么内容。
- version:版本号
- keywords:关键字
- author: 作者
- descrption: 描述
其它可参考
1.javascript.ruanyifeng.com/nodejs/pack…
node_modules文件夹
作用
在使用npm install 命令时,会从npm网站下载对应的包到这个文件夹中,这个文件夹中保存着我们从npm中下载来的第三方包。
执行逻辑
当键入npm install XXX之后(这里假设这个XXX包是存在的,也没有出现任何的网络错误):
-
如果有package.json
(1) 修改package.json文件。根据开发依赖和生产依赖的不同,决定把这句记录在加在devDependencies或者是dependencies列表中。
(2) 修改node_modules文件夹
- 如果有node_modules文件夹,则直接在下面新建名为XXX的文件夹,并从npm中下来这个包。如果这个包还有其它的依赖,则也会下载下来。
- 如果没有node_modules,则先创建这个文件夹,再去下载相应的包
-
如果没有package.json。会给一个警告信息。
说明:
- 建议先用init命令创建package.json之后,再去install
- 在分享代码时,我们一般不需要把node_modules也给别人(就像你不需要把jquery.js给别人,因为他们可以自己去下载)。对方拿到我们的代码之后,先运行
npm install(后面不接任何的包名),自己去安装这些个依赖包。
全局安装包和本地安装包
我们通过npm install 命令来安装包,简单说就是把包从npm的官网(或者是指定的镜像源)下载到我们自己的电脑中。那具体这个包下载到哪里了,还是有一点讲究的。
分成两类:
-
全局安装: 包被安装到了系统目录(一般在系统盘的node_modules中)。
-
命令:
npm install -g 包名或者npm install 包名 -g -
辅助提示:
-
npm root -g // 查看全局包的安装目录 npm list -g --depth 0 //查看全局安装过的包
-
-
-
局部安装(或者叫本地安装),包安装在当前项目的根目录下(与package.json同级)的node_modules中。
- 命令:
npm install 包名
- 命令:
全局包与本地包的区别
-
全局安装的包一般可提供直接执行的命令。我们通过对一些工具类的包采用这种方式安装,如:
gulp, nodemon, live-server,nrm等。
-
本地安装的包是与具体的项目有关的, 我们需要在开发过程中使用这些具体的功能。
一个经验法则:
- 要用到该包的命令执行任务的就需要全局安装。
- 要通过require引入使用的就需要本地安装。
全局安装nrm包
作用
nrm 这个工具是帮助我们切换安装包的来源的。因为下载包时,默认是从npm官网(国外的网站)下载,速度可能会比较慢,我们可以手动去切换* 安装来源*。
不应该只限于某个具体的项目,所以我们采用全局安装的方式来安装它。
nrm包的地址:www.npmjs.com/package/nrm
nrm的使用方法
操作步骤
共三步
// 第一步: 全局安装
npm install nrm -g
// 第二步:列出所有的源信息
// (*)标注的就是当前使用的源
nrm ls
// 第三步:根据需要切换源
// 例如:指定使用taobao源
nrm use taotao
// 接下来,正常安装你需要的包
-
nrm ls 的查看讲解
如果nrm 命令不能用,请看这里:
const NRMRC = path.join(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.nrmrc');
全局安装nodemon包
作用
它能帮我们自动检测到我们的代码的修改,并自动重新运行我们的代码
我们每次修改了代码,要想代码生效都需要重启http服务器:
- 进入小黑窗
- 按下ctrl+c,停止已有http服务器。
- 手动运行:node index.js 来重启服务器。
这有点麻烦哈。
有没有一个工具会自动检测到我们的修改并自动重新运行我们的代码呢?有,它叫nodemon。地址
安装 nodemon
通过npm包管理工具来进行安装。
步骤:
在任意位置 打开一个小黑窗,输入如下命令
npm install -g nodemon
回车。
此操作需要联网,根据网络速度所耗时间不同。如果这个命令执行完成并没有报错,就是说明安装成功了。
对上面的命令说明如下:
- npm是一个工具,用来管理node代码中要使用的第三方模块。它是随着node的安装而自动安装的:如果你安装node,则npm也已经安装过了,你可以直接使用。
- -g 表示全局安装。它也可以写在nodemon后面。即
npm install nodemon -g。
使用nodemon
等待安装成功之后,使用方法也非常简单:在命令中,使用nodemon来代替node。
例如,原来是:
node server.js
现在是:
// 改成 nodemon server.js
nodemon server.js
它的好处在于会自动监听server.js这个文件的变化,如果变化了,就会重新自动再去运行。相当于是:
while(server.js 变化了){
node server.js
}
说明:
- 它是一个第三方的包(其它程序员写的工具)
- 之前的node server.js还是可以用的。
注意:出现系统禁止执行 nodemon.ps1文件的解决办法
解决办法:
https://blog.csdn.net/weixin_38289787/article/details/108352666
1. 以管理员身份运行PowerShell
2. 执行:get-ExecutionPolicy,回复Restricted,表示状态是禁止的
3. 执行:set-ExecutionPolicy RemoteSigned
4. 输入Y
卸载包
当一个包不使用的时候,我们要将其卸载
卸载包分为两种:本地包和全局包的卸载
-
本地包卸载:
-
- 进入到你想要卸载的包所在的文件夹,到package.json这一层即可
- 打开cmd小黑窗
- 在小黑窗中执行
npm uninstall 包名
-
-
全局包的卸载:
- 1.在任意地方打开小黑窗
-
- 输入
npm uninstall 全局包名 -g
- 输入
开发依赖和生产依赖(了解)
在本地安装包时,表示我们这个项目要用到这个包,换句话说,我们这个项目要想成功运行,要依赖于这些个包。
但在,具体在项目进行的哪一阶段依赖于这些包呢?也有具体的差异。
npm包从创建到发布
背景
在工作中我们积累了一些自己的功能代码。这些功能代码可以在其它项目中重复使用,此时我们就可以选择把代码打包放在[npm]上,在需要要使用的项目中,通过npm install去下载下来。
npm项目初始化
在本地磁盘上创建一个空项目,取文件夹名为myNpm。注意请先去npm网官去确定一下,这个项目名是否已经被占用了。(如果这个名字已经被占用了,则你是无法向npm上上传的)。
检查方式
npm view 包名
# 这个命令用来查看 某个包的信息
# 如果返回404,说明这个项目名在npm官网上找不到,此时你就可以使用。
# 否则,说明不可用。
很确定地告诉你,myNpm这个包已经被别人占用了,你需要去自己用另一个名字哈。
npm init --yes
命令来创建一个package.json文件,对你项目myNpm信息进行设置。
完成功能开发
正常开发,完成你的代码。在默认情况下,index.js是这个项目的入口文件。
下面是一个最简单的示例:
//tools.js
const relativeTime = (oldTime) => {
const t = new Date(oldTime)
// Date.now():现在的时间戳(毫秒)
// t.getTime():旧时间的时间戳(毫秒)
const diff = Date.now() - t.getTime() // 相隔多少毫秒
// Math.floor 向下取整: 1.7年 ---> 1年前
const year = Math.floor(diff / (1000 * 3600 * 24 * 365))
if (year) {
return `${year}年前`
}
const month = Math.floor(diff / (1000 * 3600 * 24 * 30))
if (month) {
return `${month}月前`
}
const day = Math.floor(diff / (1000 * 3600 * 24))
if (day) {
return `${day}天前`
}
const hour = Math.floor(diff / (1000 * 3600))
if (hour) {
return `${hour}小时前`
}
const minute = Math.floor(diff / (1000 * 60))
if (minute) {
return `${minute}分钟前`
} else {
return '刚刚'
}
}
const formatDate = (dateTime) => {
// console.log(date)
// date = new Date();
const date = new Date(dateTime) // 转换成Data();
console.log(date)
var y = date.getFullYear()
console.log(y)
var m = date.getMonth() + 1
m = m < 10 ? '0' + m : m
var d = date.getDate()
d = d < 10 ? ('0' + d) : d
return y + '-' + m + '-' + d
}
// 通过module.exports来导出模块
module.exports = {
formatDate,
relativeTime
};
切换当前npm源到官网
由于我们需要把包上传到npm上,所以要先确保当前的npm源是npmjs.org。与之相关的命令有如下两条。
(1)查看当前的npm的registry配置.
npm config get registry
# 查看当前的npm的registry配置,确保是https://registry.npmjs.org
# 如果不是,可以通过如下命令来设置
npm config set registry https://registry.npmjs.org
# 手动设置registry
(2)或者使用nrm工具来设置:
连接npm
npm adduser
这个命令需要输入三个信息以供连接上npmjs:
- 用户名
- 密码
- 邮箱(是你在npmjs官网上注册时使用的信息)
如果你已经不是第一次连接了,这一步是可以省略的。
你也可以通过如下命令检查自己是否连接成功了。
npm who am i
如果成功了,则可以进行最后一步了:publish
如果想退出:
npm logout
把包上传到npm
npm publish
如果成功:
-----------------------------------
npm notice
npm notice package: tool61@1.0.0
npm notice === Tarball Contents ===
npm notice 1.3kB index.js
npm notice 220B package.json
npm notice === Tarball Details ===
npm notice name: tool61
npm notice version: 1.0.0
npm notice package size: 855 B
npm notice unpacked size: 1.5 kB
npm notice shasum: 3bfba7bc92e242810a850ac39ded7ebe992a6d9c
npm notice integrity: sha512-VoRuxxbcGzXen[...]v3tKWYUT1B1AQ==
npm notice total files: 2
npm notice
+ tool61@1.0.0
出错的可能是:
- 这个包名被别人先用了。
- 包的版本号不对:每次publish时,包的版本号都应该要大于之前的版本号。
- 文件过大。你可能需要创建
.npmignore文件来设置在打包时要忽略哪些文件。如下是一个demo.
# .npmignore
/node_modules
npm-debug.log
/src
/examples
/build
如果没有报错,则表示一切ok,你可以用你的帐号密码登陆npm,去查看你名下的package是否有了myNpm
下载使用
通过npm install 包名即可来安装包。 然后,告诉你的小伙伴们去下载使用吧。
删除包
npm unpublish --force //强制删除
如果你的包没有什么用处,建议删除掉,以节约公共资源。
更新包
- 修改代码,保存。
- 更新版本号。可直接在package.json中修改:只能改大,不能改小。
- 重新publish
require的加载机制
在我们使用一个模块时,我们会使用require命令来加载这个模块。以加载一个自定义模块为例,require(文件名)的效果是:
- 执行这个文件中的代码
- 把这个文件中的module.exports对象中的内容返回出来。
以如下代码为例:
// moudule1.js
var a = 1;
var b = 2;
console.log(a+b);
var c = a+b;
module.exports = {
data: c
}
在index.js中使用模块
// index.js
const obj = require('./moudule1.js');
console.log(obj);
//这里的obj对象就是moudule1.js中的module.exports对象
require加载规则:
-
require优先加载缓存中的模块。同一个模块第一次require之后,就会缓存一份,下一次require时就直接从缓存中去取。 -
如果是加载核心模块,直接从内存中加载,并缓存
- 加载核心模块的格式是
const xxx = require("模块名")。不能写相对路径!
- 加载核心模块的格式是
-
如果是相对路径,则根据路径加载自定义模块,并缓存
- 以
require('./main')为例( 省略扩展名的情况) - 先加载
main.js,如果没有再加载main.json,如果没有再加载main.node(c/c++编写的模块),找不到就报错。
- 以
-
如果不是自定义模块,也不是核心模块,则加载第三方模块
以
require('XXX')为例:- node 会去本级 node_modules 目录下的xxx文件夹中找xxx.js ----> xxx.json ----> xxx.node(找到一个即返回),找到并缓存。
- 如果找不到,node 则取上一级目录中的node_modules下找 ,
node_modules/xxx目录,规则同上 - 如果一直找到代码文件的文件系统的根目录还找不到,则报错:模块没有找到。
在module.paths命令中可以看到搜索路径中包含node_modules这个文件夹
附:npm 常用命令
-
查看
npm -v // 查看npm 版本 where node // 查看node的安装目录 where npm // 查看npm的安装目录 npm root -g // 查看全局包的安装目录 npm list -g --depth 0 // 查看全局安装过的包 -
升级 npm
npm install npm --global // 简写成 -g npm install npm -g -
初始化
package.jsonnpm init -y // 或者是npm init --yes -
安装第三方包
// 安装当前目录下package.json中列出的所有的包 // 如果之前安装了包,又在package.json中手动删除依赖 // 它相当是删除包 npm install // 全局安装 npm install 包名 -g // npm install -g 包名 // 本地安装,没有指定版本,默认安装最新的版本 npm install 包名 // 一次安装多个包,空格隔开 npm install 包名1 包名2 包名3 // 安装指定版本的包 npm install 包名@版本号 // 简写。把install简写成 i npm i 包名 -
删除已安装的包
npm uninstall 本地安装的包名 npm uninstall 全局安装的包名 -g -
设置npm的register
如果你不想用
nrm,下面这个原生的命令也可以切换镜像源(从哪里下载包)。npm config set registry https://registry.npm.taobao.org ## 所有npm i 包 都会从taobao的镜像去下载。 ## 配置后可通过下面方式来验证是否成功 npm config get registry
\