【Node.js】(三)Node.js模块化、包管理工具、express框架

八. Node.js模块化

1. 介绍

将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为模块化
其中拆分出的每个文件就是一个模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。

(1). 模块化的好处

  1. 防止命名冲突 2. 高复用性 3. 高维护性

2. 模块暴露数据

创建me.js

//声明函数
function tiemo() {
    console.log('贴膜....');
}
//暴露数据
module.exports = tiemo;

创建index.js

//导入模块
const tiemo = require('./me.js');
//调用函数
tiemo();

(1). 暴露数据

模块暴露数据的方式有两种:

  1. module.exports = value
  2. exports.name = value

使用时有几点注意:

  • module.exports 可以暴露 任意 数据
  • 不能使用 exports = value 的形式暴露数据,模块内部 module 与 exports 的隐式关系 exports = module.exports = {} ,require 返回的是目标模块中 module.exports 的值

3. CommonJS 规范

module.exports、exports 以及 require 这些都是 CommonJS 模块化规范中的内容。
而Node.js是实现了CommonJS 模块化规范,二者关系有点像JavaScript与ECMAScript

九. 包管理工具

1. npm

npm全称 Node Package Manager , 翻译为中文意思是 Node的包管理工具

(1). npm基本使用

创建一个空目录,然后以此目录作为工具目录 启动命令行工具,执行 npm init
image.png
npm init 命令的作用是将文件夹初始化为一个 包 ,交互式创建 package.json 文件
package.json 是包的配置文件,每个包都必须要有 package.json
package.json 内容实例:

{
  "name": "01_npm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

image.png

初始化的过程中还有一些注意事项:

  1. package name ( 包名 ) 不能使用中文、大写,默认值是 文件夹的名称 ,所以文件夹名称也不

能使用中文和大写

  1. version ( 版本号 )要求 x.x.x 的形式定义, x 必须是数字,默认值是 1.0.0
  2. ISC 证书与 MIT 证书功能上是相同的,关于开源证书扩展阅读**http://www.ruanyifeng.com/bl **

**og/2011/05/how_to_choose_free_software_licenses.html **

  1. package.json 可以手动创建与修改
  2. 使用 npm init -y 或者 npm init --yes 极速创建 package.json

2. 生产环境与开发环境

开发环境是程序员 专门用来写代码 的环境,一般是指程序员的电脑,开发环境的项目一般 只能程序员自己访问
生产环境是项目 代码正式运行 的环境,一般是指正式的服务器电脑,生产环境的项目一般 每个客户都可以访问
(1). 生产依赖与开发依赖
image.png

3. 全局安装

我们可以执行安装选项 -g 进行全局安装

npm i -g nodemon

全局安装完成之后就可以在命令行的任何位置运行 nodemon 命令
该命令的作用是 自动重启 node 应用程序

(1). 修改windows执行策略

windows 默认不允许 npm 全局命令执行脚本文件,所以需要修改执行策略

  1. 以管理员身份打开powershell命令行
  2. 键入命令 set-ExecutionPolicy remoteSigned
  3. 键入 A 然后敲回车

4. 安装指定版本的包

项目中可能会遇到版本不匹配的情况,有事就需要安装指定版本的包,可以使用下面的命令

## 格式
npm i <包名@版本号>
## 示例
npm i jquery@1.11.2

5. 删除依赖

项目中可能需要删除某些不需要的包,可以使用下面的命令

## 局部删除
npm remove uniq
npm r uniq
## 全局删除
npm remove -g nodemon

6. 配置命令别名

通过配置命令别名可以更简单的执行命令
配置package.json中的scripts属性

{
    .
    .
    .
    "scripts": {
    "server": "node server.js",
    "start": "node index.js",
    },
    .
    .
}

配置完成之后,可以使用别名执行命令

npm run server
npm run start

用start的话 可以省略run

7. cnpm

淘宝镜像

(1). 安装

npm install -g cnpm --registry=https://registry.npmmirror.com

(2). npm配置淘宝镜像

用npm也可以使用淘宝镜像,配置的方式有两种

  1. 直接配置
  2. 工具配置
直接配置

npm config set registry https://registry.npmmirror.com/

工具配置

使用 nrm 配置 npm 的镜像地址 npm registry manager

  1. 安装nrm

npm i -g nrm

  1. 修改镜像

nrm use taobao

  1. 检查是否配置成功

npm config list
检查 registry 地址是否为 https://registry.npmmirror.com/ , 如果 是 则表明成功

8. yarn

yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方网址:https://yarnpkg.com/

npm i -g yarn

9. 管理发布包

(1). 创建与发布

我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下:

  1. 创建文件夹,并创建文件 index.js, 在文件中声明函数,使用 module.exports 暴露
  2. npm 初始化工具包,package.json 填写包的信息 (包的名字是唯一的)
  3. 注册账号 **https://www.npmjs.com/signup **
  4. 激活账号 ( 一定要激活账号 )
  5. 修改为官方的官方镜像 (命令行中运行 nrm use npm )
  6. 命令行下 npm login 填写相关用户信息
  7. 命令行下 npm publish 提交包

(2). 更新包

后续可以对自己发布的包进行更新,操作步骤如下

  1. 更新包中的代码
  2. 测试代码是否可用
  3. 修改 package.json 中的版本号
  4. 发布更新

npm publish

(3). 删除包

执行如下命令删除包
npm unpublish --force

删除包需要满足一定的条件,**https://docs.npmjs.com/policies/unpublish **
你是包的作者
发布小于 24 小时
大于 24 小时后,没有其他包依赖,并且每周小于 300 下载量,并且只有一个维护者

10. nvm

(1). 介绍

nvm 全称 Node Version Manager 顾名思义它是用来管理 node 版本的工具,方便切换不同版本的 Node.js

(2). 下载安装

首先先下载 nvm,下载地址 https://github.com/coreybutler/nvm-windows/releases
选择 nvm-setup.exe 下载即可(网络异常的小朋友可以在资料文件夹中获取)

(3). 常用命令

image.png

十. express框架

1. 介绍

  • express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址:**https://www.expressjs.com.cn/ **
  • 简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)

2. 使用

(1). 下载

npm i express

(2). 使用

//1. 导入 express
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
app.get('/home', (req, res) => {
    res.send('hello express');
})
//4. 监听端口 启动服务
app.listen(4000, () => {
    console.log('服务已经启动,端口1000 正在监听中');
})

3. express路由

(1). 什么是路由

官方定义: 路由确定了应用程序如何响应客户端对特定端点的请求

(2). 路由的使用

一个路由的组成有 请求方法 , 路径 和 回调函数 组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式如下:

app.(path,callback)

//导入 express
const express = require('express');
//创建应用对象
const app = express();
//创建 get 路由
app.get('/home', (req, res) => {
    res.send('网站首页');
});
//首页路由
app.get('/', (req, res) => {
    res.send('我才是真正的首页');
});
//创建 post 路由
app.post('/login', (req, res) => {
    res.send('登录成功');
});
//匹配所有的请求方法
app.all('/search', (req, res) => {
    res.send('1 秒钟为您找到相关结果约 100,000,000 个');
});
//自定义 404 路由
app.all("*", (req, res) => {
    res.send('<h1>404 Not Found</h1>')
});
//监听端口 启动服务
app.listen(3000, () => {
    console.log('服务已经启动, 端口监听为 3000');
});

(3). 获取请求参数

express 框架封装了一些 API 来方便获取请求报文中的数据,并且兼容原生 HTTP 模块的获取方式

//导入 express
const express = require('express');
//创建应用对象
const app = express();
//获取请求的路由规则
app.get('/request', (req, res) => {
    //1. 获取报文的方式与原生 HTTP 获取方式是兼容的
    console.log(req.method);
    console.log(req.url);
    console.log(req.httpVersion);
    console.log(req.headers);
    //2. express 独有的获取报文的方式
    //获取查询字符串
    console.log(req.query); // 『相对重要』
    // 获取指定的请求头
    console.log(req.get('host'));
    res.send('请求报文的获取');
});
//启动服务
app.listen(3000, () => {
    console.log('启动成功....')
})

(4). 获取路由参数

路由参数指的是 URL 路径中的参数(数据)

app.get('/:id.html', (req, res) => {
    res.send('商品详情, 商品 id 为' + req.params.id);
});

小练习

根据路由参数响应歌手的信息
路径结构如下

/singers/1.html

显示歌手的姓名和图片

{
  "singers": [
    {
      "singer_name": "周杰伦",
      "singer_pic": "http://y.gtimg.cn/music/photo_new/T001R150x150M0000025NhlN2yWrP4.webp",
      "other_name": "Jay Chou",
      "singer_id": 4558,
      "id": 1
    },
    {
      "singer_name": "林俊杰",
      "singer_pic": "http://y.gtimg.cn/music/photo_new/T001R150x150M000001BLpXF2DyJe2.webp",
      "other_name": "JJ Lin",
      "singer_id": 4286,
      "id": 2
    },
  ]
}
//1. 导入 express
const express = require('express');
//2. 创建应用对象
const app = express();


const { singers } = require('./singers.json');
// console.log(singers);
//3. 创建路由规则
app.get('/singers/:id.html', (req, res) => {
    let { id } = req.params;


    let result = singers.find(item => {
        if (item.id == Number(id)) {
            return true;
        }
    });
    res.end(`
    <!DOCTYPE html>
    <html lang="en">


    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>


    <body>
        <h1>${result.singer_name}</h1>
        <img src="${result.singer_pic}" alt="">
    </body>


    </html>`);
})
//4. 监听端口 启动服务
app.listen(4000, () => {
    console.log('服务已经启动, 端口4000 正在监听中');
})

4. express响应设置

express 框架封装了一些 API 来方便给客户端响应数据,并且兼容原生 HTTP 模块的获取方式

//获取请求的路由规则
app.get("/response", (req, res) => {
    //1. express 中设置响应的方式兼容 HTTP 模块的方式
    res.statusCode = 404;
    res.statusMessage = 'xxx';
    res.setHeader('abc', 'xyz');
    res.write('响应体');
    res.end('xxx');
    //2. express 的响应方法
    res.status(500); //设置响应状态码
    res.set('xxx', 'yyy');//设置响应头
    res.send('中文响应不乱码');//设置响应体
    //连贯操作
    res.status(404).set('xxx', 'yyy').send('你好朋友')
    //3. 其他响应
    res.redirect('http://baidu.com')//重定向
    res.download('./package.json');//下载响应
    res.json();//响应 JSON
    res.sendFile(__dirname + '/home.html') //响应文件内容
});

5. express中间件

(1). 什么是中间件

中间件(Middleware)本质是一个回调函数
中间件函数 可以像路由回调一样访问 请求对象(request) , 响应对象(response)

(2). 中间件的类型

  • 全局中间件
  • 路由中间件

(3). 定义全局中间件

每一个请求 到达服务端之后 都会执行全局中间件函数
声明中间件函数

let recordMiddleware = function (request, response, next) {
    //实现功能代码
    //.....
    //执行next函数(当如果希望执行完中间件函数之后,仍然继续执行路由中的回调函数,必须调用next)
    next();
}

image.png

我把中间件理解为一个守卫,一个分流的守卫

应用中间件

app.use(recordMiddleware);

声明时可以直接将匿名函数传递给 use

app.use(function (request, response, next) {
    console.log('定义第一个中间件');
    next();
})

可以定义多个中间件

(3). 定义路由中间件

如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件

app.get('/路径', `中间件函数`, (request, response) => {
});
app.get('/路径', `中间件函数1`, `中间件函数2`, (request, response) => {
})

(4). 静态资源中间件

express 内置处理静态资源的中间件

//4. 监听端口 启动服务
app.listen(4000, () => {
    console.log('服务已经启动, 端口4000 正在监听中');
})
//引入express框架
const express = require('express');
//创建服务对象
const app = express();
//静态资源中间件的设置,将当前文件夹下的public目录作为网站的根目录
app.use(express.static('./public')); //当然这个目录中都是一些静态资源
//如果访问的内容经常变化,还是需要设置路由
//但是,在这里有一个问题,如果public目录下有index.html文件,单独也有index.html的路由,
//则谁书写在前,优先执行谁
app.get('/index.html', (request, response) => {
    respsonse.send('首页');
});
//监听端口
app.listen(3000, () => {
    console.log('3000 端口启动....');
});

注意事项:

  1. index.html 文件为默认打开的资源
  2. 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
  3. 路由响应动态资源,静态资源中间件响应静态资源

(5). 获取请求体数据body-parser

express 可以使用 body-parser 包处理请求体
第一步:安装

npm i body-parser

第二步:导入 body-parser 包

const bodyParser = require(‘body-parser’);

第三步:获取中间件函数

//处理 querystring 格式的请求体
let urlParser = bodyParser.urlencoded({ extended: false }));
//处理 JSON 格式的请求体
let jsonParser = bodyParser.json();

第四步:设置路由中间件,然后使用 request.body 来获取请求体数据

app.get('/login', (req, res) => {
    res.sendFile(__dirname + '/index.html')
})
app.post('/login', urlParser, (req, res) => {
    console.log(req.body);
    res.send('获取用户的数据');
})

获取到的请求体数据:
image.png

6. Router

(1). 什么是Router

express中的Router是一个完整的中间件和路由系统,可以看做是一个小型的app对象。

(2). Router作用

对路由进行模块化,更好的管理路由

(3). Router使用

创建独立的JS文件(homeRouter.js)

//1. 导入 express
const express = require('express');
//2. 创建路由器对象
const router = express.Router();
//3. 在 router 对象身上添加路由
router.get('/', (req, res) => {
    res.send('首页');
})
router.get('/cart', (req, res) => {
    res.send('购物车');
});
//4. 暴露
module.exports = router;

主文件:

const express = require('express');
const app = express();
//5.引入子路由文件
const homeRouter = require('./routes/homeRouter');
//6.设置和使用中间件
app.use(homeRouter);
app.listen(3000, () => {
    console.log('3000 端口启动....');
})

7. EJS模板引擎

(1). 什么是模板引擎

模板引擎是分离 用户界面和业务数据 的一种技术

(2). 什么是EJS

EJS是一个高效的 JavaScript 的模板引擎
官网:http://ejs.co/
中文站:http://ejs.bootcss.com/

(3). EJS使用

下载安装EJS

npm i ejs --save

//1.引入ejs
const ejs = require('ejs');
//2.定义数据
let person = ['张三', '李四', '王二麻子'];
//3.ejs解析模板返回结构
//<%= %> 是ejs解析内容的标记,作用是输出当前表达式的执行结构
let html = ejs.render('<%= person.join(",") %>', { person: person });
//4.输出结果
console.log(html);

(4). EJS常用语法

执行JS代码

<% code %>

输出转义的数据到模板上

<%= code %>

输出非转义的数据到模板上

<%- code %>

8

8. express-generator

express-generator文档
这个工具可以快速搭建骨架,不用自己一点点去编写

9. 文件上传功能

(1). 在routers下创建index.js

//显示网页的表单
router.get('/portrait',(req,res)=>{
  res.render('portrait')
});


//处理文件上传
router.post('/portrait',(req,res)=>{
  res.send('成功')
});

添加get和post方法

(2). 在views下创建portrait.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
    <h2>文件上传</h2>
    <hr>


    <form action="/portrait" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username"><br>
        头像:<input type="file" name="portrait"><br>
        <hr>
        <button>点击提交</button>
    </form>
</body>
</html>

当地址栏url为http://localhost:3000/portrait时,会进入到portrait.ejs页面中

注意这里的enctype="multipart/form-data"一定要添加上去,要不然获取不到图片文件,放不到请求体里面

(3). formidable 处理文件请求包

formidable文档

//引入formidable
const formidable = require('formidable');
//处理文件上传
router.post('/portrait', (req, res) => {
  //创建 form 表单对象
  const form = formidable({ multiples: true });
  //解析请求报文
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    console.log(fields);// text radio checkbox select
    console.log(files); // file
    res.send('ok')
  });
});

我们可以看到这个方法中有 fields 和 files 两个参数
在 fields 中主要储存text radio checkbox select 类型的数据
在 files 中主要储存 file 类型的数据

(4). 存储图片

//创建 form 表单对象
  const form = formidable({
    multiples: true,
    //设置上传文件的保存目录
    uploadDir: __dirname + '/../public/images',
    //保持文件后缀
    keepExtensions: true,
  });

加入 upliadDir 和 keepExtensions
upliadDir:设置上传文件的保存目录
keepExtensions:开启保持文件后缀功能
image.png
可以看到已经拿到图片了,但是将来用户还要访问该图片,所以还要保存图片的url。
加入将来用户要访问这张图片,那访问的url一定是http://localhost:3000/images/6015787a953a61594d413a500.jpg 这样的一个路径

form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    // console.log(fields);// text radio checkbox select
    // console.log(files); // file
    ///images/6015787a953a61594d413a500.jpg
    //服务器保存该图片的访问 URL
    let url = '/images/' + files.portrait.newFilename;
    res.send(url)
  });

当然,未来这一块肯定是要吧url存入数据库

10. 防盗链

有时候我们在html中引用外部图片地址,会发现图片显示不出来,是因为那个图片网站设置了防盗链。
原理:发送请求会查看请求头的referer是否为设置的域名,如果不是,就禁止访问

app.use((req, res, next) => {
  let referer = req.get('referer');
  if (referer) {
    //实例化
    let url = new URL(referer);
    //获取 hostname
    let hostname = url.hostname;
    console.log(hostname);
    //判断
    if (hostname != '127.0.0.1') {
      //响应404
      res.status.(404).send('<h1>404 Not Found!</h1>');
      return;
    }
  }
  next();
})
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值