DApp 开发入门指南
DApp 开发入门指南 🔨
1. DApp 基础概念
1.1 什么是 DApp?
去中心化应用(DApp)是基于区块链的应用程序,特点是:
- 后端运行在区块链网络
- 前端可以是任何框架
- 使用智能合约处理业务逻辑
- 数据存储在区块链上
1.2 DApp 架构
前端 (Web/Mobile)
↕️
Web3 接口层
↕️
智能合约层
↕️
区块链网络
2. 开发环境搭建
2.1 基础工具安装
# 安装 Node.js 和 npm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install node
# 安装 Hardhat
npm install --save-dev hardhat
# 安装 Web3.js 或 Ethers.js
npm install web3
# 或
npm install ethers
2.2 项目初始化
# 创建新项目
mkdir my-dapp
cd my-dapp
# 初始化项目
npm init -y
npx hardhat init
# 安装前端依赖
npm install react react-dom
npm install @web3-react/core @web3-react/injected-connector
3. 智能合约开发
3.1 合约示例
// contracts/TodoList.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TodoList {
struct Task {
uint id;
string content;
bool completed;
}
mapping(uint => Task) public tasks;
uint public taskCount;
event TaskCreated(uint id, string content);
event TaskCompleted(uint id, bool completed);
function createTask(string memory _content) public {
taskCount++;
tasks[taskCount] = Task(taskCount, _content, false);
emit TaskCreated(taskCount, _content);
}
function toggleCompleted(uint _id) public {
Task memory _task = tasks[_id];
_task.completed = !_task.completed;
tasks[_id] = _task;
emit TaskCompleted(_id, _task.completed);
}
}
3.2 合约测试
// test/TodoList.test.js
const { expect } = require("chai");
describe("TodoList", function() {
let TodoList;
let todoList;
let owner;
beforeEach(async function() {
TodoList = await ethers.getContractFactory("TodoList");
[owner] = await ethers.getSigners();
todoList = await TodoList.deploy();
await todoList.deployed();
});
it("Should create a new task", async function() {
await todoList.createTask("Test task");
const task = await todoList.tasks(1);
expect(task.content).to.equal("Test task");
});
});
4. 前端开发
4.1 React 组件示例
// src/components/TodoList.js
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import TodoList from '../artifacts/contracts/TodoList.sol/TodoList.json';
const TodoListComponent = () => {
const [tasks, setTasks] = useState([]);
const [newTask, setNewTask] = useState('');
const [contract, setContract] = useState(null);
useEffect(() => {
const init = async () => {
if (window.ethereum) {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(
CONTRACT_ADDRESS,
TodoList.abi,
signer
);
setContract(contract);
loadTasks(contract);
}
};
init();
}, []);
const loadTasks = async (contract) => {
const taskCount = await contract.taskCount();
const loadedTasks = [];
for (let i = 1; i <= taskCount; i++) {
const task = await contract.tasks(i);
loadedTasks.push(task);
}
setTasks(loadedTasks);
};
const createTask = async () => {
if (!newTask) return;
try {
const tx = await contract.createTask(newTask);
await tx.wait();
setNewTask('');
loadTasks(contract);
} catch (error) {
console.error("Error creating task:", error);
}
};
return (
<div>
<h1>Todo List</h1>
<input
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="New task..."
/>
<button onClick={createTask}>Add Task</button>
<ul>
{tasks.map(task => (
<li key={task.id.toString()}>
{task.content}
{task.completed ? " ✓" : ""}
</li>
))}
</ul>
</div>
);
};
export default TodoListComponent;
4.2 Web3 集成
// src/utils/web3.js
import { InjectedConnector } from '@web3-react/injected-connector';
import { Web3Provider } from '@ethersproject/providers';
export const injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42]
});
export const getLibrary = (provider) => {
const library = new Web3Provider(provider);
library.pollingInterval = 12000;
return library;
};
5. IPFS 集成
5.1 文件存储
import { create } from 'ipfs-http-client';
const ipfs = create({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' });
async function uploadToIPFS(file) {
try {
const added = await ipfs.add(file);
const url = `https://ipfs.infura.io/ipfs/${added.path}`;
return url;
} catch (error) {
console.error('Error uploading file: ', error);
}
}
5.2 元数据存储
async function saveMetadata(data) {
const metadata = JSON.stringify(data);
try {
const added = await ipfs.add(metadata);
return added.path;
} catch (error) {
console.error('Error saving metadata: ', error);
}
}
6. 部署和维护
6.1 部署脚本
// scripts/deploy.js
async function main() {
const TodoList = await ethers.getContractFactory("TodoList");
const todoList = await TodoList.deploy();
await todoList.deployed();
console.log("TodoList deployed to:", todoList.address);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
6.2 监控和维护
// 事件监听
contract.on("TaskCreated", (id, content) => {
console.log(`New task created: ${content} (ID: ${id})`);
updateUI();
});
// 错误处理
function handleError(error) {
if (error.code === 4001) {
console.log('Transaction rejected by user');
} else if (error.code === -32603) {
console.log('Internal JSON-RPC error');
}
// 处理其他错误...
}
7. 最佳实践
7.1 安全考虑
- 输入验证
- 权限控制
- 重入攻击防护
- Gas 优化
7.2 用户体验
- 交易等待提示
- 错误友好提示
- 离线功能支持
- 响应式设计
8. 相关资源
- React 文档
- Hardhat 文档
- IPFS 文档
- Web3.js 文档
- Ethers.js 文档