1. 模版引擎
1.1 koa加载模板引擎的作用
- 视图渲染:模版引擎允许开发者将动态数据与静态 HTML 模板结合,生成最终的 HTML 页面内容。开发者可以专注于编写模板逻辑,而无需关注页面的具体 HTML 结构。
- 代码组织和可维护性:视图逻辑与应用程序逻辑分离,代码结构将更加清晰,可维护性提高。
- 数据传递和处理:模板引擎提供了变量插值、条件语句、循环等功能,允许开发者将数据传递给视图层并进行复杂的渲染逻辑。
在 Koa 中,常见的模板引擎包括 Nunjucks、Handlebars、EJS 等。开发者可以选择合适的模板引擎,并使用 Koa 提供的中间件集成到应用程序中。我们这里选择koa-nunjucks-2。
npm install koa-nunjucks-2
在views目录下创建三个文件index.njk、todo.njk和contact.njk。
index.njk为共用的模版,其他两个模版在此基础上添加。共用模板中需要添加{% block content %}和 {% endblock %}表示子模板的加载区域。在这个区域内编写代码是无用的,因为会被子模版的内容覆盖。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1>共用{{title}}</h1>
{% block content %}
{# main content goes here #}
{% endblock %}
</body>
</html>
todo.njk为子模板,使用{% extends "index.njk" %}来继承index.njk模版。
{% extends "index.njk" %}
{% block content %}
<article>
<h2>todo页模版{{ pageTitle }}</h2>
<div class="content">
{{ pageContent }}
</div>
<h4>一段内容</h4>
</article>
{% endblock %}
contact.njk
{% extends "index.njk" %}
{% block content %}
<article>
<h2>contact页模版{{ pageTitle }}</h2>
<div class="content">
{{ pageContent }}
</div>
<h4>一段内容</h4>
</article>
{% endblock %}
在启动页面index.js中,定义路由访问后加载的不同模板。
const Koa = require('koa');
const Router = require('koa-router');
const nunjucks = require('koa-nunjucks-2');
const path = require('path');
const app = new Koa();
const router = new Router();
// 配置 Nunjucks 中间件
app.use(nunjucks({
ext: 'njk',
path: path.join(__dirname, 'views'),
}));
// 定义路由
router.get('/', async (ctx) => {
await ctx.render('contact', {
title:"contact页面",
pageTitle: 'My Website',
pageContent: '欢迎来到八青妹的地盘',
});
});
// 待办页面路由
router.get('/todo', async (ctx) => {
await ctx.render('todo', {
title:"page页面",
pageTitle:"这,是待办页面",
pageContent:"欢迎光临"
});
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
访问http://localhost:3000/ 加载的模板是index.njk和contact.njk,访问http://localhost:3000/ 加载的模板是index.njk和todo.njk。
2. 数据库mysql
1. 安装 MySQL 驱动程序
首先,通过 npm 安装 MySQL 驱动程序:
npm install mysql
2. 创建数据库连接
在项目中创建一个文件(例如 db.js),用于配置和管理数据库连接:
const mysql = require('mysql');
const connection = mysql.createConnection({
host: 'localhost',
user: 'your-username',
password: 'your-password',
database: 'your-database'
});
connection.connect((err) => {
if (err) {
console.error('Error connecting to the database:', err.stack);
return;
}
console.log('Connected to database as ID', connection.threadId);
});
module.exports = connection;
3. 执行基本的 SQL 查询
在你的应用中,你可以使用上面创建的 connection 对象来执行 SQL 查询。下面是一个示例,展示如何在 Express.js 应用中使用 MySQL 进行基本的 CRUD(创建、读取、更新、删除)操作:
const express = require('express');
const connection = require('./db');
const app = express();
app.use(express.json());
// 创建数据
app.post('/users', (req, res) => {
const { name, age } = req.body;
const query = 'INSERT INTO users (name, age) VALUES (?, ?)';
connection.query(query, [name, age], (error, results) => {
if (error) {
return res.status(500).send('Error inserting data');
}
res.status(201).send(`User added with ID: ${results.insertId}`);
});
});
// 读取数据
app.get('/users', (req, res) => {
const query = 'SELECT * FROM users';
connection.query(query, (error, results) => {
if (error) {
return res.status(500).send('Error fetching data');
}
res.status(200).json(results);
});
});
// 更新数据
app.put('/users/:id', (req, res) => {
const { id } = req.params;
const { name, age } = req.body;
const query = 'UPDATE users SET name = ?, age = ? WHERE id = ?';
connection.query(query, [name, age, id], (error, results) => {
if (error) {
return res.status(500).send('Error updating data');
}
res.status(200).send(`User with ID: ${id} updated`);
});
});
// 删除数据
app.delete('/users/:id', (req, res) => {
const { id } = req.params;
const query = 'DELETE FROM users WHERE id = ?';
connection.query(query, [id], (error, results) => {
if (error) {
return res.status(500).send('Error deleting data');
}
res.status(200).send(`User with ID: ${id} deleted`);
});
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
4. 处理数据库连接错误
在实际应用中,处理数据库连接错误是非常重要的。你可以在 db.js 中添加错误处理逻辑:
connection.connect((err) => {
if (err) {
console.error('Error connecting to the database:', err.stack);
process.exit(1); // 退出应用程序
}
console.log('Connected to database as ID', connection.threadId);
});
connection.on('error', (err) => {
console.error('Database error:', err);
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
// 如果连接丢失,则重连
connection.connect((err) => {
if (err) {
console.error('Error reconnecting to the database:', err.stack);
process.exit(1);
}
console.log('Reconnected to database as ID', connection.threadId);
});
} else {
throw err;
}
});
5. 使用连接池
对于高并发的应用,建议使用连接池来提高性能:
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'your-username',
password: 'your-password',
database: 'your-database'
});
pool.getConnection((err, connection) => {
if (err) {
console.error('Error getting connection from pool:', err.stack);
return;
}
console.log('Connected to database as ID', connection.threadId);
connection.release();
});
module.exports = pool;
在应用中使用连接池:
const express = require('express');
const pool = require('./db');
const app = express();
app.use(express.json());
app.get('/users', (req, res) => {
pool.query('SELECT * FROM users', (error, results) => {
if (error) {
return res.status(500).send('Error fetching data');
}
res.status(200).json(results);
});
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
通过以上步骤,你可以在 Node.js 应用中使用 MySQL 数据库进行各种操作。使用连接池可以显著提高应用的性能和稳定性。
3. 文件上传
相比于 Node.js 原生的 req.on('data') 和 req.on('end') 事件,我们采用提供了更加友好和高级的 API的一个 Node.js 中间件——Busboy ,这个中间件使得开发者能够更方便地处理上传和表单数据。
Busboy 主要用于处理 HTTP 请求中的文件上传和表单数据。它提供了一个简单而强大的接口,用于解析 HTTP 请求体中的各种类型的数据,包括:
- 文件上传: Busboy 能够处理
multipart/form-data格式的文件上传请求,并提供事件回调接口,便于开发者对上传的文件进行处理。 - 表单数据解析: Busboy 能够解析
application/x-www-form-urlencoded格式的表单数据,并以键值对的形式返回给开发者。 - JSON 数据解析: 虽然 Busboy 主要用于解析
multipart/form-data和application/x-www-form-urlencoded格式的数据,但它也可以通过配置选项来处理 JSON 数据。
Busboy 的使用方法相对简单,通过监听一系列事件来处理上传和表单数据。下面是如何在一个 Node.js 应用中使用 Busboy 的基本步骤:
1. 安装 Busboy
首先需要通过 npm 安装 Busboy:
npm install busboy
2. 使用 Busboy 处理文件上传和表单数据
以下是一个简单的示例,展示了如何在 Express.js 应用中使用 Busboy 来处理文件上传:
const express = require('express');
const Busboy = require('busboy');
const fs = require('fs');
const path = require('path');
const app = express();
app.post('/upload', (req, res) => {
const busboy = new Busboy({ headers: req.headers });
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const saveTo = path.join(__dirname, 'uploads', path.basename(filename));
file.pipe(fs.createWriteStream(saveTo));
file.on('end', () => {
console.log(`File [${fieldname}] finished`);
});
});
busboy.on('field', (fieldname, val) => {
console.log(`Field [${fieldname}]: value: ${val}`);
});
busboy.on('finish', () => {
console.log('Done parsing form!');
res.writeHead(200, { 'Connection': 'close' });
res.end('Upload complete');
});
return req.pipe(busboy);
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
3. 解析多个文件和字段
Busboy 通过触发事件处理文件和字段,这使得它非常灵活。你可以根据需要添加更多的逻辑来处理多个文件和字段:
app.post('/upload-multiple', (req, res) => {
const busboy = new Busboy({ headers: req.headers });
const files = [];
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const saveTo = path.join(__dirname, 'uploads', path.basename(filename));
file.pipe(fs.createWriteStream(saveTo));
file.on('end', () => {
files.push(filename);
console.log(`File [${fieldname}] finished`);
});
});
busboy.on('field', (fieldname, val) => {
console.log(`Field [${fieldname}]: value: ${val}`);
});
busboy.on('finish', () => {
console.log('Done parsing form!');
res.writeHead(200, { 'Connection': 'close' });
res.end(`Upload complete: ${files.join(', ')}`);
});
return req.pipe(busboy);
});
4. 处理 JSON 数据
虽然 Busboy 主要用于 multipart/form-data 和 application/x-www-form-urlencoded 格式的数据,但你可以使用其他中间件来处理 JSON 数据,例如 body-parser:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post('/json', (req, res) => {
console.log('Received JSON data:', req.body);
res.send('JSON data received');
});
5. 错误处理
在实际应用中,处理错误是非常重要的。你可以监听 Busboy 的 error 事件来处理解析过程中的错误:
app.post('/upload-error', (req, res) => {
const busboy = new Busboy({ headers: req.headers });
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const saveTo = path.join(__dirname, 'uploads', path.basename(filename));
file.pipe(fs.createWriteStream(saveTo));
file.on('end', () => {
console.log(`File [${fieldname}] finished`);
});
});
busboy.on('field', (fieldname, val) => {
console.log(`Field [${fieldname}]: value: ${val}`);
});
busboy.on('finish', () => {
console.log('Done parsing form!');
res.writeHead(200, { 'Connection': 'close' });
res.end('Upload complete');
});
busboy.on('error', (error) => {
console.error('Error while parsing form:', error);
res.status(500).send('Internal Server Error');
});
return req.pipe(busboy);
});
通过以上步骤,你可以在 Node.js 应用中使用 Busboy 轻松处理文件上传和表单数据。Busboy 提供了简单而强大的事件驱动接口,使得处理各种类型的数据变得更加方便和灵活。
总结
通过上述步骤和示例代码,我们成功地在 Koa 框架中集成了模板引擎、连接了 MySQL 数据库,并使用 Busboy 处理文件上传,这些功能都是构建现代 Web 应用程序的关键组成部分。首先,借助 Koa 和 koa-nunjucks-2 模板引擎,我们可以通过模板文件轻松渲染动态 HTML 内容,模板继承机制则帮助我们实现了代码复用和结构化的页面开发。其次,通过 mysql 模块,我们实现了与 MySQL 数据库的连接,并进行CRUD操作,使用连接池提高了数据库连接的效率和应用的稳定性,同时处理数据库连接错误,确保应用的健壮性。
最后,利用 Busboy 中间件,我们能够处理 multipart/form-data 格式的文件上传和表单数据,简单的事件驱动接口使得文件和表单数据的处理更加灵活和方便,通过监听事件,可以处理多个文件和字段,并且能够友好地处理错误。通过这些功能的集成和实现,您可以构建一个功能齐全、性能高效的 Web 应用程序,模板引擎提供了视图层的灵活性和可维护性,数据库连接确保了数据存储和操作的高效性,而文件上传功能则增强了应用的交互性和用户体验。这些都是构建现代 Web 应用不可或缺的部分。希望这些示例和步骤对您的实际开发有所帮助,能够为您提供一个良好的起点,让您可以进一步扩展和完善您的应用程序。Happy coding!