从零开始构建你的第一个以太坊DApp,开发实例详解

默认分类 2026-02-16 12:36 2 0

区块链技术的浪潮中,去中心化应用(DApp)正逐渐从概念走向现实,以太坊,作为最具影响力的智能合约平台,为DApp的开发提供了强大的基础设施,本文将通过一个简单但完整的实例,带你一步步了解以太坊DApp的开发流程,从智能合约编写到前端交互,让你对DApp开发有一个直观的认识。

DApp概述与核心组成部分

一个典型的以太坊DApp通常由以下几个核心部分组成:

  1. 智能合约(Smart Contract):运行在以太坊区块链上的后端代码,负责定义应用的业务逻辑和数据规则,使用Solidity语言编写,部署在以太坊网络上。
  2. 前端(Frontend):用户与DApp交互的界面,通常使用Web技术(HTML, CSS, JavaScript)构建,它通过调用智能合约的方法与区块链进行交互。
  3. 区块链(Blockchain):提供去中心化、不可篡改的数据存储和交易执行环境。

开发环境准备

在开始之前,我们需要准备以下开发工具和环境:

  1. Node.js 和 npm:JavaScript运行时环境和包管理器。
  2. Truffle Suite:流行的以太坊开发框架,包含Truffle(开发环境、测试框架、构建管道)、Ganache(个人区块链,用于本地测试)和Drizzle(与前端交互的库)。
  3. MetaMask:浏览器钱包插件,允许用户与以太坊区块链交互,管理私钥和进行交易签名。
  4. 代码编辑器:如Visual Studio Code。

安装步骤相对简单,请访问各工具的官方网站下载并安装相应版本。

DApp开发实例:一个简单的“任务清单”(Todo List)DApp

我们将开发一个简单的Todo List DApp,用户可以添加任务、标记任务完成和查看所有任务。

步骤1:创建项目并初始化

  1. 创建一个新的项目目录,例如ethereum-dapp-tutorial
  2. 在终端中进入该目录,并使用Truffle初始化项目:
    truffle init
  3. 这将创建以下标准目录结构:
    • contracts/:存放智能合约文件。
    • migrations/:存放部署脚本文件。
    • test/:存放测试文件。
    • truffle-config.js:Truffle配置文件。

步骤2:编写智能合约

  1. contracts目录下,创建一个新的Solidity文件,命名为TodoList.sol

  2. 编写智能合约代码:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    contract TodoList {
        // 定义一个结构体来表示任务
        struct Task {
            uint id;
            string content;
            bool completed;
        }
        // 任务数组
        Task[] public tasks;
        // 任务计数器
        uint public taskCount = 0;
        // 添加任务的函数
        function createTask(string memory _content) public {
            taskCount++;
            tasks.push(Task(taskCount, _content, false));
        }
        // 切换任务完成状态的函数
        function toggleCompleted(uint _id) public {
            Task storage task = tasks[_id - 1]; // 数组索引从0开始
            task.completed = !task.completed;
        }
        // 可选:获取任务数量
        function getTaskCount() public view returns (uint) {
            return taskCount;
        }
    }

    这个合约定义了任务的结构,提供了创建任务和切换任务完成状态的方法。

步骤3:编译智能合约

  1. 在终端中,确保在项目根目录下,运行:
    truffle compile
  2. 如果编译成功,Truffle会在build/contracts目录下生成对应的ABI(Application Binary Interface)和字节码文件,ABI是前端与智能合约交互的桥梁。

步骤4:编写部署脚本

  1. migrations目录下,创建一个新的迁移脚本文件,命名为2_deploy_contracts.js(数字前缀表示部署顺序)。

  2. 编写部署脚本:

    const TodoList = artifacts.require("TodoList");
    module.exports = function (deployer) {
      deployer.deploy(TodoList);
    };

步骤5:部署到本地测试网络(Ganache)

  1. 确保Ganache已经启动并运行在默认端口(7545)。
  2. truffle-config.js中配置本地网络:
    module.exports = {
      networks: {
        development: {
          host: "127.0.0.1",
          port: 7545, // Ganache默认端口
          network_id: "*", // 匹配任何network_id
        },
      },
      compilers: {
        solc: {
          version: "0.8.0", // 指定Solidity编译器版本
        },
      },
    };
  3. 在终端中运行部署命令:
    truffle migrate --network development
  4. 如果部署成功,你会在Ganache界面上看到新的交易和地址变化,部署的合约地址也会显示在终端中。

步骤6:构建前端界面

  1. 在项目根目录下创建一个src目录(或public目录),用于存放前端文件。

  2. src目录下创建以下文件:

    • index.html:主页面
    • style.css:样式文件(可选,用于美化界面)
    • app.js:前端逻辑
  3. index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>以太坊Todo List DApp</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            <h1>以太坊Todo List</h1>
            <div class="form">
                <input type="text" id="taskInput" placeholder="输入新任务...">
                <button id="addTaskBtn">添加任务</button>
            </div>
            <ul id="taskList">
                <!-- 任务列表将在这里动态生成 -->
            </ul>
        </div>
        <script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js" type="application/javascript"></script>
        <script src="app.js"></script>
    </body>
    </html>
  4. app.js 内容(核心交互逻辑):

    // 声明变量
    let todoList;
    const todoListAddress = "0x..."; // 替换为你的合约部署地址
    const todoListABI = [/* 这里粘贴你的TodoList合约的ABI */]; // 从build/contracts/TodoList.json中获取
    // 初始化函数
    async function init() {
        // 连接到以太坊网络(通过MetaMask)
        if (typeof window.ethereum !== 'undefined') {
            console.log('MetaMask is installed!');
            try {
                // 请求账户访问
                await window.ethereum.request({ method: 'eth_requestAccounts' });
                const provider = new ethers.providers.Web3Provider(window.ethereum);
                const signer = provider.getSigner();
                todoList = new ethers.Contract(todoListAddress, todoListABI, signer);
                // 监听事件并刷新任务列表
                todoList.on('TaskCreated', (id, content, completed) => {
                    console.log('Task created:', id, content, completed);
                    loadTasks();
                });
                todoList.on('TaskCompleted', (id, completed) => {
                    console.log('Task completed:', id, completed);
                    loadTasks();
                });
                loadTasks();
            } catch (error) {
                console.error('User denied account access', error);
            }
        } else {
            console.error('MetaMask is not installed. Please install it to use this DApp.');
            alert('请安装MetaMask钱包!');
        }
    }
    // 加载任务列表
    async function loadTasks() {
        const taskCount = await todoList.getTaskCount();
        const taskListElement = document.getElementById('taskList');
        taskListElement.innerHTML = ''; // 清空现有列表
        for (let i = 1; i <= taskCount; i++) {
            const task = await todoList.tasks(i - 1); // 数组索引从0开始
            const li = document.createElement('li');
            li.innerHTML = `
                <span class="${task.completed ? 'completed' : ''}">${task.content}</span>
                <button onclick="toggleTaskCompleted(${task.id})">${task.completed ? '未完成' 
    随机配图
    : '完成'}</button> `; taskListElement.appendChild(li); } }