在现代 Web 开发中,前后端分离的架构已经成为主流。本文将详细介绍如何使用 Vue3、Node.js、MySQL、Electron 和 Express 实现一个完整的用户登录、文章管理和截屏功能的应用。我们将从项目的初始化开始,逐步实现各个功能模块,并提供详细的代码示例。
项目初始化
前端:Vue3
首先,我们使用 Vue CLI 创建一个新的 Vue3 项目:
bash
npm install -g @vue/cli
vue create vue-electron-app
cd vue-electron-app
选择默认配置或根据需要进行自定义配置。
后端:Node.js 和 Express
在项目根目录下创建一个新的文件夹 server
,并在其中初始化一个新的 Node.js 项目:
bash
mkdir server
cd server
npm init -y
npm install express mysql body-parser cors
创建 server.js
文件并设置基本的 Express 服务器:
javascript
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 3000;
app.use(cors());
app.use(bodyParser.json());
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
数据库:MySQL
创建一个新的 MySQL 数据库和表:
sql
CREATE DATABASE vue_electron_app;
USE vue_electron_app;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
实现用户登录功能
后端:用户登录 API
在 server
文件夹中创建一个新的文件 auth.js
,并实现用户注册和登录功能:
javascript
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'vue_electron_app'
});
router.post('/register', (req, res) => {
const { username, password } = req.body;
const hashedPassword = bcrypt.hashSync(password, 10);
db.query('INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword], (err, result) => {
if (err) return res.status(500).send(err);
res.status(201).send('User registered');
});
});
router.post('/login', (req, res) => {
const { username, password } = req.body;
db.query('SELECT * FROM users WHERE username = ?', [username], (err, results) => {
if (err) return res.status(500).send(err);
if (results.length === 0) return res.status(404).send('User not found');
const user = results[0];
const isPasswordValid = bcrypt.compareSync(password, user.password);
if (!isPasswordValid) return res.status(401).send('Invalid password');
const token = jwt.sign({ id: user.id }, 'secret_key', { expiresIn: '1h' });
res.status(200).send({ token });
});
});
module.exports = router;
在 server.js
中引入并使用该路由:
javascript
const authRoutes = require('./auth');
app.use('/auth', authRoutes);
前端:用户登录页面
在 Vue 项目中创建一个新的组件 Login.vue
:
vue
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="login">
<div>
<label for="username">Username:</label>
<input type="text" v-model="username" required />
</div>
<div>
<label for="password">Password:</label>
<input type="password" v-model="password" required />
</div>
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
async login() {
try {
const response = await axios.post('http://localhost:3000/auth/login', {
username: this.username,
password: this.password
});
localStorage.setItem('token', response.data.token);
this.$router.push('/dashboard');
} catch (error) {
console.error('Login failed:', error);
}
}
}
};
</script>
实现文章管理功能
后端:文章管理 API
在 server
文件夹中创建一个新的文件 articles.js
,并实现文章的 CRUD 操作:
javascript
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
const jwt = require('jsonwebtoken');
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'vue_electron_app'
});
const authenticate = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access denied');
jwt.verify(token, 'secret_key', (err, decoded) => {
if (err) return res.status(401).send('Invalid token');
req.userId = decoded.id;
next();
});
};
router.post('/articles', authenticate, (req, res) => {
const { title, content } = req.body;
db.query('INSERT INTO articles (title, content) VALUES (?, ?)', [title, content], (err, result) => {
if (err) return res.status(500).send(err);
res.status(201).send('Article created');
});
});
router.get('/articles', authenticate, (req, res) => {
db.query('SELECT * FROM articles', (err, results) => {
if (err) return res.status(500).send(err);
res.status(200).send(results);
});
});
router.put('/articles/:id', authenticate, (req, res) => {
const { id } = req.params;
const { title, content } = req.body;
db.query('UPDATE articles SET title = ?, content = ? WHERE id = ?', [title, content, id], (err, result) => {
if (err) return res.status(500).send(err);
res.status(200).send('Article updated');
});
});
router.delete('/articles/:id', authenticate, (req, res) => {
const { id } = req.params;
db.query('DELETE FROM articles WHERE id = ?', [id], (err, result) => {
if (err) return res.status(500).send(err);
res.status(200).send('Article deleted');
});
});
module.exports = router;
在 server.js
中引入并使用该路由:
javascript
const articleRoutes = require('./articles');
app.use('/api', articleRoutes);
前端:文章管理页面
在 Vue 项目中创建一个新的组件 ArticleManager.vue
:
vue
<template>
<div>
<h2>Article Manager</h2>
<form @submit.prevent="createArticle">
<div>
<label for="title">Title:</label>
<input type="text" v-model="title" required />
</div>
<div>
<label for="content">Content:</label>
<textarea v-model="content" required></textarea>
</div>
<button type="submit">Create Article</button>
</form>
<ul>
<li v-for="article in articles" :key="article.id">
<h3>{{ article.title }}</h3>
<p>{{ article.content }}</p>
<button @click="deleteArticle(article.id)">Delete</button>
<button @click="editArticle(article)">Edit</button>
</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
title: '',
content: '',
articles: []
};
},
async created() {
await this.fetchArticles();
},
methods: {
async fetchArticles() {
try {
const response = await axios.get('http://localhost:3000/api/articles', {
headers: { Authorization: localStorage.getItem('token') }
});
this.articles = response.data;
} catch (error) {
console.error('Failed to fetch articles:', error);
}
},
async createArticle() {
try {
await axios.post('http://localhost:3000/api/articles', {
title: this.title,
content: this.content
}, {
headers: { Authorization: localStorage.getItem('token') }
});
this.title = '';
this.content = '';
await this.fetchArticles();
} catch (error) {
console.error('Failed to create article:', error);
}
},
async deleteArticle(id) {
try {
await axios.delete(`http://localhost:3000/api/articles/${id}`, {
headers: { Authorization: localStorage.getItem('token') }
});
await this.fetchArticles();
} catch (error) {
console.error('Failed to delete article:', error);
}
},
editArticle(article) {
this.title = article.title;
this.content = article.content;
// Implement update logic here
}
}
};
</script>
实现截屏功能
Electron:截屏功能
在项目根目录下安装 Electron:
bash
npm install electron --save-dev
创建 main.js
文件并配置 Electron 主进程:
javascript
const { app, BrowserWindow, ipcMain, desktopCapturer } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
enableRemoteModule: false,
nodeIntegration: false
}
});
win.loadURL('http://localhost:8080');
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
ipcMain.handle('capture-screen', async () => {
const sources = await desktopCapturer.getSources({ types: ['screen'] });
return sources[0].thumbnail.toDataURL();
});
创建 preload.js
文件并配置预加载脚本:
javascript
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
captureScreen: () => ipcRenderer.invoke('capture-screen')
});
前端:截屏功能页面
在 Vue 项目中创建一个新的组件 ScreenCapture.vue
:
vue
<template>
<div>
<h2>Screen Capture</h2>
<button @click="captureScreen">Capture Screen</button>
<img v-if="screenshot" :src="screenshot" alt="Screenshot" />
</div>
</template>
<script>
export default {
data() {
return {
screenshot: null
};
},
methods: {
async captureScreen() {
try {
this.screenshot = await window.electron.captureScreen();
} catch (error) {
console.error('Failed to capture screen:', error);
}
}
}
};
</script>
结语
通过本文,我们详细介绍了如何使用 Vue3、Node.js、MySQL、Electron 和 Express 实现一个完整的用户登录、文章管理和截屏功能的应用。希望这篇文章能为你提供有价值的参考,帮助你更好地理解和实现前后端分离的应用开发。
如果你有任何问题或建议,欢迎在评论区留言讨论。Happy coding!