前言:本篇文章以 Express.js 4.x 为主体,并结合 Mongoose 和 Vue.js 实现数据的真实时间 CRUD,需要对 Node.js 和 MongoDB 有一定的了解。
一、Express.js 的基础
1.1 安装和创建项目
先在本地安装 Node.js 和 MongoDB,然后在命令行中执行以下命令:
npm install -g express-generator express --view=pug myapp
其中 -g
代表全局安装 express-generator,myapp
是项目名称。
1.2 配置路由
在 app.js
中,可以看到已经有一些路由的示例,不过我们需要自己定义一些路由:
// javascriptcn.com 代码示例 // 在头部引入路由文件 var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var productsRouter = require('./routes/products'); // 中间部分省略... // 在底部配置路由 app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/api/products', productsRouter);
对于 productsRouter
,我们需要自己定义它。在 routes
文件夹下新建 products.js
:
// javascriptcn.com 代码示例 var express = require('express'); var router = express.Router(); // 查找所有商品 router.get('/', function(req, res, next) { // Todo: 实现查找所有商品的逻辑 }); // 查找某个商品 router.get('/:id', function(req, res, next) { // Todo: 实现查找某个商品的逻辑 }); // 新增商品 router.post('/', function(req, res, next) { // Todo: 实现新增商品的逻辑 }); // 修改商品 router.put('/:id', function(req, res, next) { // Todo: 实现修改商品的逻辑 }); // 删除商品 router.delete('/:id', function(req, res, next) { // Todo: 实现删除商品的逻辑 }); module.exports = router;
1.3 安装和使用 MongoDB
在命令行中执行以下命令安装 Mongoose:
npm install mongoose --save
使用以下代码连接 MongoDB 数据库(也可以从 process.env.MONGODB_URI
取得 MONGODB_URI 环境变量):
var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/myapp');
1.4 编写数据模型
在 models
文件夹下新建 product.js
,定义商品的数据模型:
// javascriptcn.com 代码示例 var mongoose = require('mongoose'); var ProductSchema = new mongoose.Schema({ name: { type: String, required: true }, price: { type: Number, required: true }, imageUrl: { type: String } }, { timestamps: true }); module.exports = mongoose.model('Product', ProductSchema);
其中 timestamps
表示会自动在 Product
模型中添加 createdAt
和 updatedAt
字段。
1.5 在路由中使用模型
在 routes/products.js
中使用模型:
// javascriptcn.com 代码示例 var Product = require('../models/product'); router.get('/', function(req, res, next) { Product.find(function(err, products) { if (err) { return next(err); } res.json(products); }); }); router.get('/:id', function(req, res, next) { Product.findById(req.params.id, function(err, product) { if (err) { return next(err); } res.json(product); }); }); router.post('/', function(req, res, next) { var product = new Product(req.body); product.save(function(err, product) { if (err) { return next(err); } res.json(product); }); }); router.put('/:id', function(req, res, next) { Product.findById(req.params.id, function(err, product) { if (err) { return next(err); } product.name = req.body.name; product.price = req.body.price; product.imageUrl = req.body.imageUrl; product.save(function(err, product) { if (err) { return next(err); } res.json(product); }); }); }); router.delete('/:id', function(req, res, next) { Product.findByIdAndRemove(req.params.id, function(err) { if (err) { return next(err); } res.json({ message: '删除成功!' }); }); });
现在我们可以在 Postman 等工具中测试接口是否正常工作。
二、Vue.js 的基础
2.1 安装和创建项目
在命令行中执行以下命令安装 Vue:
npm install vue --save
新建 index.html
文件,并在其中添加以下代码:
// javascriptcn.com 代码示例 <div id="app"> <h2>商品列表</h2> <table> <thead> <tr> <th>名称</th> <th>价格</th> <th>图片</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="product in products" :key="product._id"> <td>{{ product.name }}</td> <td>{{ product.price }}</td> <td><img :src="product.imageUrl" alt=""/></td> <td> <!-- 编辑商品 --> <button @click="editProduct(product)">编辑</button> <!-- 删除商品 --> <button @click="deleteProduct(product)">删除</button> </td> </tr> </tbody> </table> <div class="form"> <h2>新增/编辑商品</h2> <form @submit.prevent="saveProduct"> <p> <label for="name">名称:</label> <input id="name" name="name" v-model="formData.name" required placeholder="请输入商品名称"/> </p> <p> <label for="price">价格:</label> <input id="price" name="price" v-model.number="formData.price" required placeholder="请输入商品价格"/> </p> <p> <label for="imageUrl">图片:</label> <input id="imageUrl" name="imageUrl" v-model="formData.imageUrl" placeholder="请输入商品图片地址"/> </p> <button type="submit">{{ formStatus }}</button> </form> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> new Vue({ el: '#app', data: function() { return { formStatus: '保存', products: [], formData: { _id: '', name: '', price: '', imageUrl: '' } }; }, mounted: function() { this.fetchProducts(); }, methods: { // 查询所有商品 fetchProducts: function() { var vm = this; fetch('/api/products') .then(function(res) { return res.json(); }) .then(function(products) { vm.products = products; }) .catch(function(err) { console.error(err); }); }, // 获取某个商品的信息 getProduct: function(productId) { var vm = this; fetch(`/api/products/${productId}`) .then(function(res) { return res.json(); }) .then(function(product) { vm.formData = { _id: product._id, name: product.name, price: product.price, imageUrl: product.imageUrl }; }) .catch(function(err) { console.error(err); }); }, // 新增/编辑商品 saveProduct: function() { var vm = this; if (!vm.formData._id) { // 新增商品 fetch('/api/products', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(vm.formData) }) .then(function(res) { return res.json(); }) .then(function(product) { vm.products.push(product); vm.formData = { _id: '', name: '', price: '', imageUrl: '' }; vm.formStatus = '保存'; }) .catch(function(err) { console.error(err); }); } else { // 编辑商品 fetch(`/api/products/${vm.formData._id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(vm.formData) }) .then(function(res) { return res.json(); }) .then(function(product) { var updateIndex = vm.products.findIndex(function(p) { return p._id === product._id; }); vm.products.splice(updateIndex, 1, product); vm.formData = { _id: '', name: '', price: '', imageUrl: '' }; vm.formStatus = '保存'; }) .catch(function(err) { console.error(err); }); } }, // 编辑商品信息 editProduct: function(product) { this.formData = { _id: product._id, name: product.name, price: product.price, imageUrl: product.imageUrl }; this.formStatus = '更新'; }, // 删除商品 deleteProduct: function(product) { var vm = this; fetch(`/api/products/${product._id}`, { method: 'DELETE' }) .then(function(res) { return res.json(); }) .then(function(message) { var removeIndex = vm.products.findIndex(function(p) { return p._id === product._id; }); vm.products.splice(removeIndex, 1); }) .catch(function(err) { console.error(err); }); } } }); </script>
在浏览器中打开 index.html
,我们会看到一个空的商品列表。
三、实现真实时间 CRUD
3.1 在 Express.js 中启用跨域
因为 Vue.js 是在浏览器中使用的,而 Express.js 是在服务器端运行的,所以需要启用跨域。
在 app.js
文件中添加以下代码(安装 cors
包):
npm install cors --save
var express = require('express'); var cors = require('cors'); var app = express(); app.use(cors()); // 启用跨域
3.2 在 Vue.js 中启用真实时间 CRUD
在 index.html
中添加以下代码:
// javascriptcn.com 代码示例 <script> // 在头部添加 Socket.io 的引用 var socket = io(); new Vue({ // 在 data 中添加 socket 和 isConnected data: function() { return { socket: socket, isConnected: false, formStatus: '保存', products: [], formData: { _id: '', name: '', price: '', imageUrl: '' } }; }, // 在 mounted 中添加以下代码 mounted: function() { var vm = this; vm.socket.on('connect', function() { vm.isConnected = true; }); vm.socket.on('disconnect', function() { vm.isConnected = false; }); vm.socket.on('product created', function(product) { vm.products.push(product); }); vm.socket.on('product updated', function(updateProduct) { var updateIndex = vm.products.findIndex(function(product) { return product._id === updateProduct._id; }); vm.products.splice(updateIndex, 1, updateProduct); }); vm.socket.on('product deleted', function(deleteProduct) { var removeIndex = vm.products.findIndex(function(product) { return product._id === deleteProduct._id; }); vm.products.splice(removeIndex, 1); }); vm.fetchProducts(); }, // 在 methods 中添加以下代码 saveProduct: function() { var vm = this; if (!vm.formData._id) { // 新增商品 vm.socket.emit('product create', vm.formData); } else { // 编辑商品 vm.socket.emit('product update', vm.formData); } }, deleteProduct: function(product) { var vm = this; vm.socket.emit('product delete', product); } }); </script>
在 public
目录下新建 index.js
文件,添加以下代码(连接 Socket.io 并将其作为 socket
变量返回):
// javascriptcn.com 代码示例 var socket = io(); socket.on('connect', function() { console.log('已连接到服务器!'); }); socket.on('disconnect', function() { console.error('服务器连接已断开!'); }); socket.on('product created', function(product) { console.log(`新增商品:${product.name}`); }); socket.on('product updated', function(updateProduct) { console.log(`编辑商品:${updateProduct.name}`); }); socket.on('product deleted', function(deleteProduct) { console.log(`删除商品:${deleteProduct.name}`); }); module.exports = socket; // 将 socket 作为模块导出
然后在 app.js
文件中,为 productsRouter
添加下列代码:
// javascriptcn.com 代码示例 var socket = require('./public/index'); // 引入 socket.io router.post('/', function(req, res, next) { var product = new Product(req.body); product.save(function(err, product) { if (err) { return next(err); } // 发送新增消息 socket.emit('product created', product); res.json(product); }); }); router.put('/:id', function(req, res, next) { Product.findById(req.params.id, function(err, product) { if (err) { return next(err); } product.name = req.body.name; product.price = req.body.price; product.imageUrl = req.body.imageUrl; product.save(function(err, product) { if (err) { return next(err); } // 发送编辑消息 socket.emit('product updated', product); res.json(product); }); }); }); router.delete('/:id', function(req, res, next) { Product.findByIdAndRemove(req.params.id, function(err, product) { if (err) { return next(err); } // 发送删除消息 socket.emit('product deleted', product); res.json({ message: '删除成功!' }); }); });
现在在浏览器中编辑和删除商品时,我们会看到 index.js
中打印出相应的消息。
四、总结
本文讲解了如何使用 Express.js+Mongoose+Vue.js 实现真实时间的数据 CRUD。具体包括:Express.js 的基础、Vue.js 的基础、实现真实时间 CRUD 等。
文章展示了使用最普遍的前端语言 JS 和最流行的库/框架来完成实时数据的演示及操作,可用于初学者快速了解前端开发的相关知识。
完整示例代码可以在 Github 中获取。
五、参考文献
[1] Express.js 入门教程
[2] Mongoose 官方文档
[3] Vue.js 官方文档
[4] Socket.io 官方文档
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6545be7a7d4982a6ebf5d0bd