1 作用
Express,快速的创建 Web 网站的服务器或 API 接口的服务器。
Express 是基于内置的 http 模块进一步封装出来的
等同于Web API 和 jQuery 的关系。后者是基于前者进一步封装出来的
Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。
Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。
2 创建基本的web服务器
const express = require('express')
// 创建web服务器
const app = express();
app.listen(80, () => {
console.log('server is running');
})
3 接收get和post请求
const express = require('express')
const app = express();
app.get('/user', (req, res) => {
res.send({name: 'zs', age: 18})
})
app.post('/user', (req, res) => {
res.send('post请求成功')
})
app.listen(80, () => {
console.log('server is running');
})
4 获取url的参数req.query
const express = require('express')
const app = express();
app.get('/user', (req, res) => {
res.send(req.query)
})
app.listen(80, () => {
console.log('server is running');
})
5 获取动态匹配参数req.params
const express = require('express')
const app = express();
app.get('/user/:id', (req, res) => {
res.send(req.params)
})
app.listen(80, () => {
console.log('server is running');
})
注意: 前端只传入18, id是后端路径上的动态参数
app.get('/user/:id/:name', (req, res) => {
res.send(req.params)
})
post 接收方式用req.body
6 托管静态资源
const express = require('express');
const app = express();
app.use(express.static('clock'));
app.listen(80, () => {
console.log('start');
})
Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。
因此,存放静态文件的目录名不会出现在 URL 中。
clock是要托管静态目录
托管多个
app.use(express.static('clock'));
app.use(express.static('files'));
访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件
7 挂载路径前缀
const express = require('express');
const app = express();
app.use('/prefix', express.static('clock'));
app.listen(80, () => {
console.log('start');
})
也就是需要通过带有 /prefix 前缀地址来访问
若是不挂载前缀,即app.use(express.static(‘clock’));
则直接http://localhost/index.html 便可访问
8 Express路由
客户端的请求与服务器处理函数之间的映射关系
Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数。
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
8.1 创建路由
① 创建路由模块对应的 .js 文件
② 调用 express.Router() 函数创建路由对象
③ 向路由对象上挂载具体的路由
④ 使用 module.exports 向外共享路由对象
⑤ 使用 app.use() 函数注册路由模块
路由模块文件:
const express = require('express')
const router = express.Router() // 创建路由对象
router.get('/list', (req, res) => {
res.send('in list');
});
module.exports = router;
关键点在于 express.Router() // 创建路由对象
以及共享出去
使用路由:
const express = require('express')
const app = express()
const user_list = require('./3.路由')
// 注册路由模块,并添加前缀
app.use('/api', user_list);
app.listen(80, () => {
console.log('start');
})
9 中间件
依然是水
对这次请求进行预处理。
Express 的中间件,本质上就是一个 function 处理函数,
中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。
9.1 全局中间件
const express = require('express')
const app = express()
const x = (req, res, next) => {
console.log('全局中间件');
next();
}
app.use(x);
app.get('/', (req, res)=> {
console.log('get 请求');
})
app.listen(80, () => {
console.log('start');
})
重点在于next(),
以及app.use()调用
9.2 中间件作用—统一添加自定义属性
多个中间件之间,共享同一份 req 和 res
const express = require('express')
const app = express()
const x = (req, res, next) => {
req.a = 10
next();
}
app.use(x);
app.get('/', (req, res)=> {
console.log('/ 下的a:' + req.a);
})
app.get('/list', req => {
console.log('/list 下的a:' + req.a);
})
app.listen(80, () => {
console.log('start');
})
9.3 局部生效中间件
不使用 app.use() 定义的中间件,叫做局部生效的中间件
const express = require('express')
const app = express()
const x = (req, res, next) => {
req.a = 10
next();
}
app.get('/', x, (req, res)=> {
console.log('/ 下的a:' + req.a);
})
app.get('/list', req => {
console.log('/list 下的a:' + req.a);
})
app.listen(80, () => {
console.log('start');
})
关键在于app.get(‘/‘, x, (req, res) => { … }
9.4 定义多个局部中间件
const express = require('express')
const app = express()
const x = (req, res, next) => {
console.log('第一个');
next();
}
const y = (req, res, next) => {
console.log('第二个');
next();
}
app.get('/', x, y, (req, res)=> {
console.log('/目录');
})
app.get('/list', req => {
console.log('/list');
})
app.listen(80, () => {
console.log('start');
})
访问/list 不会有任何执行。
还有数组写法:
app.get('/', [x, y], (req, res)=> {
console.log('/目录');
})
9.5 中间件注意事项
① 一定要在路由之前注册中间件
② 客户端发送过来的请求,可以连续调用多个中间件进行处理
③ 执行完中间件的业务代码之后,不要忘记调用 next() 函数
④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
⑤ 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象
9.6 中间件分类
- 应用级别 :通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件
- 路由级别:绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上
9.7 错误级别中间件
专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
注意:错误级别的中间件,必须注册在所有路由之后!
const express = require('express')
const app = express()
app.get('/', (req, res)=> {
throw new Error('服务器内部错误');
res.send('home');
})
app.use((err, req, res, next) => {
res.send('Error ' + err.message)
})
app.listen(80, () => {
console.log('start');
})
app.use((err, req, res, next) => { … }
9.8 内置中间件
① express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)
② express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)
③ express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)
9.9 内置中间件—解析json
若是不配置解析表单中间件app.use(express.json()),
则req.body默认是underfined
const express = require('express')
const app = express()
app.use(express.json())
app.post('/', (req, res)=> {
res.send(req.body);
})
app.listen(80, () => {
console.log('start');
})
9.10 内置中间件— 解析 URL-encoded 格式
一般是表单默认提交格式
若是不配置app.use(express.urlencoded({extended: false}))
则req.body默认是{}
app.use(express.urlencoded({extended: false})) 是固定写法
const express = require('express')
const app = express()
app.use(express.json());
app.use(express.urlencoded({extended: false}))
app.post('/', (req, res)=> {
res.send(req.body);
})
app.post('/add',(req, res)=>{
res.send(req.body);
})
app.listen(80, () => {
console.log('start');
})
9.11 第三方中间
使用样例:
① 运行 npm install body-parser 安装中间件
② 使用 require 导入中间件
③ 调用 app.use() 注册并使用中间件
在 express@4.16.0 之前的版本中,经常使用 body-parser 这个第三方中间件来解析请求体数据。
Express 内置的 express.urlencoded 中间件,就是基于 body-parser 这个第三方中间件进一步封装出来的。
9.12 自定义中间件
custom-body-parser 文件:
const qs = require('querystring')
const parser = (req, res, next) => {
// 存储客户端发来请求体数据
let str = '';
// 监听获取客户端发送到服务器的数据。
req.on('data', (chunck) => {
str += chunck
})
// 监听接收完毕事件
req.on('end', ()=> {
console.log('接收完成:' + str);
req.body = qs.parse(str);
next();
})
}
module.exports = parser
数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器
主要是侦听 接收数据 事件 — data
以及接收完毕事件 — end
然后利用’querystring’模块的parse将字符串转换
引入:
const express = require('express');
const parser = require('./11.custom-body-parser')
const app = express()
app.use(parser);
app.post('/', (req, res) => {
res.send(req.body);
})
app.listen(80, () => {
console.log('start');
})
9.13 应用级别中间件
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件。
9.14 路由级别中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。
它的用法和应用级别中间件没有任何区别。
只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上
const express = require('express')
const app = express()
const router = express.Router() // 创建路由对象
router.use((req, res, next) => {
req.query.age = 18
next()
})
router.get('/list', (req, res) => {
console.log(req.query.age);
res.send('in list');
});
app.use('/', router)
app.listen(80)
10 使用Express写接口
定义服务器
const express = require('express')
const app = express()
const router = require('./13 apiRouter')
// 表单默认格式提交 post 专用
app.use(express.urlencoded({extended:false}))
app.use('/api', router)
app.listen(80)
写接口
const express = require('express')
// 创建路由
const router = express.Router()
// 挂载路由
router.get('/list', (req, res) => {
const query = req.query
res.send({
status: 0,
msg: 'get 请求成功',
data: query
})
})
router.post('/list', (req, res) => {
const query = req.body
res.send({
status: 0,
msg: 'post 请求成功',
data: query
})
});
module.exports = router
get:
post:
11 跨域
解决接口跨域问题的方案主要有两种:
① CORS(主流的解决方案,推荐使用)
② JSONP(有缺陷的解决方案:只支持 GET 请求)
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域。
一个是file协议,一个是http协议所以跨域了
11.1 cors中间件解决
安装
npm i cors
使用
const express = require('express');
// 解决跨域中间件
const cors = require('cors');
const app = express();
const router = require('./13 apiRouter');
app.use(express.urlencoded({extended:false}));
// 解决跨域
app.use(cors())
app.use('/api', router);
app.listen(80)
在路由之前调用 app.use(cors()) 配置中间件
11.2 CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。
CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了 CORS 的接口。
11.3 Access-Control-Allow-Origin
响应头部中可以携带一个 Access-Control-Allow-Origin 字段
其中,origin 参数的值指定了允许访问该资源的外域 URL。
Access-Control-Allow-Origin: <origin> | *
res.setHeader('Access-Control-Allow-Origin', 'http://baidu.com')
res.setHeader('Access-Control-Allow-Origin', '*')
- 有特定域名则表示只允许指定域名请求
- 通配符* 表示允许来自任何域的请求
11.4 Access-Control-Allow-Headers
默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头:
- Accept
- Accept-Language、
- Content-Language、
- DPR、
- Downlink、
- Save-Data、
- Viewport-Width、
- Width
- Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header')
X-Custom-Header是自定义头信息。
多个请求头可以用英文逗号分割。
11.5 Access-Control-Allow-Methods
默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。
希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,
通过 Access-Control-Alow-Methods 来指明实际请求所允许使用的 HTTP 方法
res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, HEAD')
11.6 CORS请求分类
简单请求:同时满足以下两大条件的请求。
① 请求方式:GET、POST、HEAD 三者之一
② HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、 Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-formurlencoded、multipart/form-data、text/plain)
预检请求:只要符合以下任何一个条件的请求
① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
② 请求头中包含自定义头部字段
③ 向服务器发送了 application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一 次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
两者区别:
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。
11.7 JSONP接口
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// 1. 得到函数的名称
const funcName = req.query.callback
// 2. 定义要发送到客户端的数据对象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一个函数的调用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,响应给客户端
res.send(scriptStr)
})
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
app.use(cors())
// 导入路由模块
const router = require('./16.apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)
使用jQuery发起
// 4. 为 JSONP 按钮绑定点击事件处理函数
$('#btnJSONP').on('click', function () {
$.ajax({
type: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success: function (res) {
console.log(res)
},
})
})
其中req.query.callback 是一个jQuery前缀+随机数: jQuery34105356366114972404_1624436483824
其中模版字符串得出
jQuery34105356366114972404_1624436483824({“name”:”zs”,”age”:22})
响应给客户端
12 链接数据库
安装
npm i mysql
配置
const mysql = require('mysql');
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'Aa111111',
database: 'my_db_01'
})
db.query('select * from users', (err, results) => {
if (err) return console.log(err.message);
console.log(results);
})
12.1 插入数据
const user = {
username: 'nimaleb',
password: '123456'
}
const sql = 'insert into users (username, password) values (?, ?)';
db.query(sql, [user.username, user.password], (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
如果数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速插入数据:
const user = {
username: 'nima',
password: '123456'
}
const sql = 'insert into users set ?';
db.query(sql, user, (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
12.2 更新数据
const user = {
id: 1,
username: 'nim',
password: '123456'
}
const sql = 'update users set username=?, password=? where id=?';
db.query(sql, [user.username, user.password, user.id], (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速更新表数据:
const user = {
id: 1,
username: 'nim',
password: '123456'
}
const sql = 'update users set ? where id=?';
db.query(sql, [user, user.id], (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
12.3 删除数据
const sql = 'delete from users where id=?';
db.query(sql, 10, (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
12.4 标记删除
const sql = 'update users set status=1 where id=?';
db.query(sql, 1, (err, results) => {
if(err) return console.log(err.message);
console.log(results);
if (results.affectedRows === 1) {
console.log('success');
}
})
所谓的标记删除,就是在表中设置类似于 status 这样的状态字段,来标记当前这条数据是否被删除
13 前后端身份认证
13.1 web开发模式
目前主流的 Web 开发模式有两种,分别是:
① 基于服务端渲染的传统 Web 开发模式
② 基于前后端分离的新型 Web 开发模式
基于服务端渲染:
服务器发送给客户端的 HTML 页面,是在服务器通过字符串的拼接,动态生成的。
因此,客户端不需要使用 Ajax 这样的技术额外请求页面的数据。
13.2 身份认证方式
① 服务端渲染推荐使用 Session 认证机制
② 前后端分离推荐使用 JWT 认证机制
13.3 Session认证机制
重点词:HTTP协议无状态性、 Cookie
HTTP 协议的无状态性,指的是客户端的每次 HTTP 请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态。
http无状态,所以用到cookie进行身份区别
Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。
它由一个名称(Name)、一个值(Value)和其它几个用 于控制 Cookie 有效期、安全性、使用范围的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器。
Cookie的几大特性: ① 自动发送 ② 域名独立 ③ 过期时限 ④ 4KB 限制
Cookie来源:
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中。
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给 服务器,服务器即可验明客户端的身份。
由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过 Cookie 的形式发送给浏览器。
Session认证机制
关键在于用户信息存储在了服务器内存中,通过原先发送给客户端,现在接收到返回的cookie的来查找对应信息是否存在。而不像以前直接对用户信息进行交互。
13.4 安装配置express-session
npm i express-session
配置session中间件
const express = require('express')
const session = require('express-session')
const app = express()
app.use(session({
secret:'keyboard', // 可以为任意字符串
resave: false, // 固定写法
saveUninitialized: true // 固定写法
}))
向session中存数据
// 登录
app.post('/api/login', (req, res) => {
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// 配置了express-session中间件才有req.session
req.session.user = req.body // 用户信息
req.session.isLogin = true // 登录状态
res.send({ status: 0, msg: '登录成功' })
})
req.session 的属性随自己设置,如req.session.name;
主要用于存取数据
从 session 中取数据:
// 获取用户姓名的接口
app.get('/api/username', (req, res) => {
if(!req.session.isLogin) {
return res.send({status:1, msg: 'fail'})
}
res.send({
status: 0,
msg: 'success',
username: req.session.user.username
})
})
清空session信息
// 退出登录的接口
app.post('/api/logout', (req, res) => {
req.session.destroy();
req.send({
status: 0,
msg: '退出登录成功'
})
})
req.session.destroy();
13.5 JWT
英文全称:JSON Web Token 是目前最流行的跨域认证解决方案。
由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。
原理
用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份
13.6 JWT组成部分
分别是 Header(头部)、Payload(有效荷载)、Signature(签名)。
三者之间使用英文的“.”分隔,格式如下:
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
13.7 JWT使用方式
客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStorage 或 sessionStorage 中。
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。
推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中,格式如下:
13.8 express安装使用JWT
安装
npm install jsonwebtoken express-jwt
jsonwebtoken 用于生成 JWT 字符串
express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
引入:
// 导入JWT相关两个包
const jwt = require('jsonwebtoken');
const express_jwt = require('express-jwt');
**
① 当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
② 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密
生成 JWT 字符串:
// 定义 secret 密钥:
const secret_key = 'xdeimnslig';
// 登录接口
app.post('/api/login', function (req, res) {
const userinfo = req.body
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({ status: 400, message: '登录失败!' })
}
const token = jwt.sign({ username: userinfo.username }, secret_key, { expiresIn: '30s' })
res.send({
status: 200,
message: '登录成功!',
token: token // 要发送给客户端的 token 字符串
})
})
调用 jsonwebtoken 包提供的 sign() 方法,将用户的信息加密成 JWT 字符串,响应给客户端.
{ expiresIn: ‘30s’ } 设置有效生存时间30s
注意:不要把密码加密到token,否则有泄密风险
13.9 将 JWT 字符串还原为 JSON 对象:
app.use(expressJWT({
secret: secret_key,
algorithms:['HS256']
}).unless({ path: [/^\/api\//] }));
服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 Token 解析还原成 JSON 对象;
app.use(expressJWT({
secret: secret_key,
algorithms:['HS256']
})
其中algorithms 设置加密算法
.unless({path: [/^\/api\//]}) 指定哪些接口不用访问权限
这解析出来的信息会挂载到req.user 属性上。
获取用户信息:
// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
res.send({
status: 200,
message: '获取用户信息成功!',
data: req.user
})
})
当 express-jwt 这个中间件配置成功之后,即可在那些有权限的接口中,使用 req.user 对象,来访问从 JWT 字符串 中解析出来的用户信息了,
token值需要添加 Bearer ,固定写法
13.10 捕获错误信息:
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '无效token'
})
}
res.send({
status: 500,
message: '未知内部错误'
})
})
14 模块化规范
传统开发模式的主要问题:
① 命名冲突
② 文件依赖
通过模块化解决上述问题。
模块化就是把单独的一个功能封装到一个模块(文件)中,模块之间相互隔离,但是可以通过特定的接口公开内部成 员,也可以依赖别的模块。
ES6之前的浏览器模块化规范:
- AMD
- CMD
服务端的有:CommonJS
- 模块分为 单文件模块 与 包
- 模块成员导出:module.exports 和 exports
- 模块成员导入:require(‘模块标识符’)
大一统模块化规范:ES6模块化
在 ES6 模块化规范诞生之前,Javascript 社区已经尝试并提出了 AMD、CMD、CommonJS 等模块化规范。
ES6模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入模块成员使用 import 关键字
- 暴露模块成员使用 export 关键字
15 nodejs es6模块化
15.1 安装配置
由于nodejs对es6支持不是很好。所以需要第三方在编译时转换。
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
npm install --save @babel/polyfill
项目跟目录创建文件 babel.config.js
const presets = [
["@babel/env", {
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
}
}]
];
module.exports = { presets };
执行:
npx babel-node index.js
15.2 导出导入
- 默认导出语法 export default 默认导出的成员
- 默认导入语法 import 接收名称 from ‘模块标识符’
导出:
// 当前文件模块为 m1.js
// 定义私有成员 a 和 c
let a = 10
let c = 20
// 外界访问不到变量 d ,因为它没有被暴露出去
let d = 30
function show() {}
// 将本模块中的私有成员暴露出去,供其它模块使用
export default {
a,
c,
show
}
导入:
// 导入模块成员
import m1 from './m1.js'
console.log(m1) // { a: 10, c: 20, show: [Function: show] }
每个模块中,只允许使用唯一的一次 export default,否则会报错!
15.3 按需导入导出
- 按需导出语法 export let s1 = 10
- 按需导入语法 import { s1 } from ‘模块标识符’
// 当前文件模块为 m1.js
// 向外按需导出变量 s1
export let s1 = 'aaa'
// 向外按需导出变量 s2
export let s2 = 'ccc'
// 向外按需导出方法 say
export function say() {}
注意:每个模块中,可以使用多次按需导出// 导入模块成员
import { s1, s2 as ss2, say } from './m1.js'
console.log(s1) // 打印输出 aaa
console.log(ss2) // 打印输出 ccc
console.log(say) // 打印输出 [Function: say]
15.4 按需和默认同时使用
导出
let a = 10
let c = 20
function show() { }
export default {
a,
c,
show
}
export let s1 = 'aaa'
export let s2 = 'ccc'
export function say() { }
导入
import m1, {s1,s2, say} from './m1.js'
console.log(m1);
console.log(s1);
console.log(s2);
console.log(say);
2.5 直接导入并执行模块代码
m2.js
for(let i = 0; i < 3; i++) {
console.log(i)
}
import './m2.js'