DApp 开发入门指南 🔨

1. DApp 基础概念
1.1 什么是 DApp?
去中心化应用(DApp)是基于区块链的应用程序,特点是:
- 后端运行在区块链网络
- 前端可以是任何框架
- 使用智能合约处理业务逻辑
- 数据存储在区块链上
1.2 DApp 架构
前端 (Web/Mobile)
    ↕️
Web3 接口层
    ↕️
智能合约层
    ↕️
区块链网络2. 开发环境搭建
2.1 基础工具安装
            
            
              bash
              
              
            
          
          # 安装 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 ethers2.2 项目初始化
            
            
              bash
              
              
            
          
          # 创建新项目
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-connector3. 智能合约开发
3.1 合约示例
            
            
              solidity
              
              
            
          
          // 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 合约测试
            
            
              javascript
              
              
            
          
          // 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 组件示例
            
            
              jsx
              
              
            
          
          // 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 集成
            
            
              javascript
              
              
            
          
          // 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 文件存储
            
            
              javascript
              
              
            
          
          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 元数据存储
            
            
              javascript
              
              
            
          
          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 部署脚本
            
            
              javascript
              
              
            
          
          // 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 监控和维护
            
            
              javascript
              
              
            
          
          // 事件监听
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 用户体验
- 交易等待提示
- 错误友好提示
- 离线功能支持
- 响应式设计