欢迎光临
我们一直在努力

Web3 全栈开发(一):前端 + Golang + Solidity 技术栈全景

为什么我要走 Web3 全栈这条路

去年年底我花了两周时间,把市面上能找到的 Web3 招聘 JD 翻了个遍。结果发现一个很扎心的现象:纯智能合约开发的岗位少得可怜。

原因也不难理解。用 OpenZeppelin 的库写一个 ERC20 代币合约,可能就几十行代码;一个复杂的 DeFi 合约,撑死三四百行。合约部署之后基本不会频繁更新,项目方不太可能养一个“每月只写几天 Solidity”的人。

那市场上真正需要的是什么?能独立搞定从合约交互到页面展示的完整流程的全栈开发者。简单说就是:Solidity 写链上逻辑,前端负责用户界面和钱包交互,后端负责数据索引、事件监听和业务逻辑。三者打通,你才是一个“能交付产品”的人。

这也是我决定走 Web3 全栈路线的核心原因。接下来我会用一个系列博客记录我的学习过程,由浅入深。这是第一篇,先把 Web3 的基础概念、前端 API 和后端 Golang API 的整体面貌理清楚。

先把 Web3 的几个核心概念搞清楚

很多教程一上来就讲“去中心化”“不可篡改”,但这些词太抽象了。我更愿意从一次交易的全过程来理解 Web3 到底在干什么。

从钱包到上链:一笔交易的生命周期

假设你用 MetaMask 给某个合约发一笔交易,实际发生的事情是这样的:

  • 钱包签名:你的私钥在本地对交易内容(目标地址、金额、Gas 设置等)进行签名。私钥永远不会离开你的设备。
  • 广播到 RPC 节点:签名后的原始交易被发送到一个 RPC 节点。RPC 节点是连接你本地应用和区块链网络的“网关”,常见的有 Alchemy、Infura、QuickNode 等。
  • 进入 Mempool:交易先到内存池排队,等待被验证者打包。
  • 打包上链:验证者根据 Gas 费优先级选择交易,打包进区块。以太坊大约每 12 秒出一个块。
  • 确认:随着后续区块不断叠加,你的交易确认数增加,最终趋于不可回滚。
  • 这个流程里有两个角色特别关键:RPC 节点和钱包。钱包管签名,节点管通信,缺一不可。

    链上 vs 链下:面试必问的分界线

    Web3 面试里有一道几乎必出的题:“你如何理解链上和链下的边界?”[reference:0]

    我的理解是:链上负责信任,链下负责性能。

    • 必须链上:资产所有权、交易记录、需要公开验证的规则。这些一旦上链,就不可篡改。
    • 必须链下:用户画像、搜索索引、高频计算、大数据存储。链上存储成本极高,而且性能有限,什么都往链上塞既不经济也不现实。

    一个经典的例子:你的 Go 后端可以把所有用户的余额做成叶子节点,生成一棵 Merkle Tree,只把 Merkle Root 放到链上。这样链上只存一个哈希值,验证时提供 Merkle Proof 即可[reference:1]。数据在链下,信任在链上——这个思路在 Web3 后端设计中非常常见。

    前端怎么跟链打交道:从 ethers.js 到 viem

    搞清楚了概念,接下来看代码。Web3 前端和普通前端最大的区别是:你不能直接读写区块链,必须通过钱包 Provider 或 RPC 节点来交互[reference:2]。

    前端 Web3 库的演进:为什么我选了 viem

    几年前大家用的主要是 web3.js 和 ethers.js。web3.js 的 API 偏底层、设计老旧,现在新项目基本不推荐了[reference:3]。ethers.js 在类型设计和 Provider/Signer 抽象上更现代,一度是主流。

    但到了 2026 年,情况变了。viem 正在成为前端交互的事实标准。它的卖点很明确:

    • 体积小:viem 压缩后约 35KB,ethers.js 超过 300KB,web3.js 超过 600KB[reference:4]。对于移动端用户来说,这个差距很致命。
    • 类型安全:viem 基于 TypeScript 重新设计了 EVM 客户端交互层,RPC 接口的类型定义非常严格[reference:5]。
    • 粒度控制:ethers.js 抽象了很多细节,viem 则给你更细粒度的 API 控制。

    wagmi 就是建立在 viem 之上的 React Hooks 封装,提供了 useAccount、useContractRead、useContractWrite 这些开箱即用的 hook,大幅简化了钱包连接和合约调用的流程[reference:6]。

    一个最小可用的 viem 示例

    先装依赖:

    npm install viem

    创建一个 public client,用来读取链上数据:

    import { createPublicClient, http, formatEther } from 'viem'
    import { mainnet } from 'viem/chains'

    const client = createPublicClient({
    chain: mainnet,
    transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
    })

    // 读取某个地址的 ETH 余额
    const balance = await client.getBalance({
    address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    })

    console.log(`余额: ${formatEther(balance)} ETH`)

    如果要调用合约的只读方法,用 readContract:

    const result = await client.readContract({
    address: '0xYourContractAddress',
    abi: contractABI, // 从 Solidity 编译产物中导出的 ABI
    functionName: 'balanceOf',
    args: ['0xUserAddress'],
    })

    这里的 ABI 是关键。它是合约的“接口描述文件”,用 JSON 格式定义了合约里有哪些函数、参数类型和返回值。前端拿到 ABI 和合约地址,才能知道怎么调用合约[reference:7]。

    如果要写数据(比如转账、调用合约的状态变更函数),就需要 wallet client:

    import { createWalletClient, custom } from 'viem'

    const walletClient = createWalletClient({
    chain: mainnet,
    transport: custom(window.ethereum) // MetaMask 注入的 provider
    })

    const hash = await walletClient.writeContract({
    address: '0xYourContractAddress',
    abi: contractABI,
    functionName: 'transfer',
    args: ['0xRecipient', 1000000000000000000n],
    })

    读操作不需要 Gas 和签名,写操作需要。 这个区别一定要记清楚,面试经常考。

    后端用 Golang 怎么连链:go-ethereum ethclient

    前端负责跟用户交互,但很多场景下你需要后端直接跟链打交道:批量查询余额、监听事件、构建交易、做数据索引。Golang 在这个层面有天然优势——并发模型好、性能强、部署简单,特别适合做链上数据的监听和索引服务。

    Go 这边跟链交互的核心库是 go-ethereum,也就是 geth 的官方 Go SDK。它提供了 ethclient 包,封装了 JSON-RPC 协议,给你类型安全的高层 API[reference:8]。

    安装和连接节点

    go get github.com/ethereum/go-ethereum@v1.13.5

    写一个最简单的连接测试:

    package main

    import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
    )

    func main() {
    client, err := ethclient.Dial("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY")
    if err != nil {
    log.Fatal("连接节点失败:", err)
    }
    defer client.Close()

    // 获取最新区块号
    header, err := client.HeaderByNumber(context.Background(), nil)
    if err != nil {
    log.Fatal(err)
    }
    fmt.Printf("当前区块: %d\\n", header.Number.Uint64())

    // 查询余额
    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
    log.Fatal(err)
    }
    fmt.Printf("余额: %s wei\\n", balance.String())

    // 换算成 ETH
    ethValue := new(big.Float).Quo(
    new(big.Float).SetInt(balance),
    big.NewFloat(1e18),
    )
    fmt.Printf("余额: %s ETH\\n", ethValue.Text('f', 6))
    }

    这段代码能跑通,说明你的 Go 环境和节点连接都没问题。ethclient 支持 BalanceAt、BlockByNumber、TransactionByHash、FilterLogs 等常用方法,覆盖了绝大部分链上数据读取的需求。

    版本兼容性:一个容易踩的坑

    go-ethereum 的版本迭代比较快,不同版本之间有些 API 会变。比如 SuggestGasPrice 在 v1.13.5 之后被移除,换成了 SuggestGasTipCap(EIP-1559 之后的新接口)[reference:9]。如果你的目标链还没启用 EIP-1559,这个替换就会出问题。

    建议做法:锁定一个稳定版本(比如 v1.13.5),在 go.mod 里明确指定,不要随意升级。

    全栈视角下的面试加分点

    这一篇主要打基础,但既然目标是面试和简历,有些东西从一开始就要有意识地积累。

    第一个能加分的能力是“用资金链路说话”。 面试官问你一笔交易怎么处理的,如果你只说“签名→广播→上链”,那基本会被判定为概念型选手。更好的回答是:钱从哪来 → 经过哪些校验 → 什么时候算成功 → 失败了怎么补偿[reference:10]。把这条链画清楚,比背十个名词都有用。

    第二个是“主动提异常场景”。 Web3 系统最怕的就是异常:区块回滚(Reorg)、交易失败、服务重启后如何恢复监听。面试官特别爱问“如果回滚怎么办”“如果重启怎么办”。哪怕你还没做过完整的项目,在学每个 API 的时候,都要有意识地想一下:如果这一步出错了,系统该怎么兜底[reference:11]。

    下一篇要做什么

    这一篇我们把概念和工具的面貌过了一遍。下一篇我会写一个真正能跑起来的最小 DApp:写一个最简单的 Solidity 合约,用 Remix 部署到测试网,然后分别用 viem 前端和 Go 后端去调用它。从“能跑通”开始,比从“能理解”开始更重要。

    如果你也在走这条路,欢迎在评论区聊聊你的学习进度。

    赞(0)
    未经允许不得转载:171主机测评 » Web3 全栈开发(一):前端 + Golang + Solidity 技术栈全景
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址