前言

初识 Arweave 是在看到这样的一条新闻:隐私社交工具 Maskbook 集成 Arweave 支持永久储存文件

然后岛娘就问猫猫去做一下研究,看看能不能把 Matataki 的东西存储到 Arweave 上。
起初我只是抱着一个很简单的心态来看待这个问题,我觉得肯定很简单。于是我前往了 Arweave 的官网,获取了我的第一笔 AR 钱包和金额,虽然只有少量的 0.25 AR,但是看了看官方的文档,部署起来肯定不会需要太多的花费的。

研究

去到了 GitHub,创建了一个名为 ArcLight 的项目,专门用来存储相关的项目内容,这个项目最早定义是一个储存 App,你可以储存你的文件到 Arweave 中,永久可用。
起初用的是 Arweave.js,这个是官方的 HTTP API 的 Node.js wrapper,里面集成了很多基本的方法和使用接口所需要的东西。
我想着这个项目应该是一个类似后端的程序,我就拍拍脑袋开始写 app.js,然后添加 Koa 作为后端框架支持开始工作。但是一开始我就发现这个 API 不简单,很多时候的操作要 verify 很多,也需要测试很多才行。
然后开始去浏览他们的 dApp 列表
看了一会儿之后点击 Publish App 的时候需要选择 App 类型,看到那么多的类型的时候我才反应过来,原来 Arweave 能做这么多事情。这个 Arweave 有点类似曾经的 ZeroNet,也像 IPFS 能做的事情,但是 Arweave 能做的就是能用普通的浏览器访问这些以哈希值存储的内容,并且运行优美的 Web App。
第一天就想着,我试着部署一个网站上去算了。

部署一个纯粹的 HTML 文件

最初我去 Codepen 上找了一个参考的背景 CSS 动画写法,然后在这个里面决定好我们的主题颜色:粉色,然后修改了一个标题和一些小动画出来制作出来了一个完全只有 HTML 和 CSS 构成的一个 index.html 文件。
然后使用官方给的 Arweave Deploy 命令行工具部署到了 Arweave 的去中心化网络上。
部署一个纯粹的 HTML 文件和 favicon 的文件夹看起来似乎如鱼得水,就按照官方的说明:

arweave deploy-dir ./public --key-file secrets/ayaka.key.json

用这个命令就能完成存储在 public 文件夹下面整个文件夹的部署工作,这个过程看起来很简单很简洁,ayaka.key.json 就是你从网站上免费获取的下载的那个文件。
对于 Arweave 区块纺 而言,你的用户 ID 和你的认证凭据就是那个 JSON 文件,请一定小心保管。
部署的最早的版本看上去很简陋,什么都没有,地址

第二天的研究

第二天我对 Arweave.js 进行了很细致的研究,比如如何使用这个 Javascript 库完成文件上传和 对 Transactions 进行 sign 和 submit,然后就意识到我们需要的是一个独立的,去中心化,不需要后端的前端 App,这让我从那个 app.js 和 Koa 的梦中醒来,发现自己并不需要这样,我们只需要像往常一样构建一个普通的 Vue + Webpack 的网站就可以了,因为可以上传到 Arweave 的东西可以是一整个文件夹,只要我打包好就行。

于是我开始着手改造整个 ArcLight 项目,去掉了 public 文件夹,保留了存储 key 的 secrets 文件夹,然后为 Vue + Webpack 初始化使用

vue init webpack .

然后一个崭新的 Vue 项目就构建起来了。

搬迁原有的东西

像往常一样,我在 src/router/index.js 中写下 / 的地址对应的是 Landing 页面,也就是原本部署到网络的页面写到 Landing.vue 这个文件下。
因为岛娘的有一部分需求是要能够存储 Podcast 或者是图片或者是 PDF 之类的文件,完成去中心化的文件存储,于是我准备了 Flower Dance 这首音乐来作为测试对象,又添加了 DIYGod 写的 Aplayer 作为我们的播放器,这样可以避免使用原生的

构建一些新的东西

因为有音乐播放器,有音乐,我们需要一个音乐目录和音乐菜单,还有播放音乐的页面。于是建立了 Music.vueMusic/_id.vue 这两个文件。在这个时候,我们依然还没有一个具体的原型,为了构建的速度足够快,我选择让每一个页面的背景都是相同的,只有 title 也就是大标题不一致。比如 Music 选单里,标题就是 Music,播放页面的标题就是歌曲的 name 参数等等。

我们需要给 Music 页面写一个音乐列表,

<template>
  <div class="music-list">
    <b-menu>
      <b-menu-list v-for="(audio, index) in audioList" :key="index" label="Music List">
        <b-menu-item :label="audio.name" tag="router-link" target="_blank" :to="audio.path"></b-menu-item>
      </b-menu-list>
    </b-menu>
  </div>
</template>

<script>
export default {
  data () {
    return {
      audioList: [
        {
          name: 'Flower Dance',
          path: '/music/flowerdance'
        }
      ]
    }
  }
}
</script>

<style lang="less" scoped>
.music-list {
  padding-top: 200px;
  text-align: center;
  margin-top: 20rem;
  width: 50%;
  border-radius: 20px;
  margin: 0 auto;
}
/deep/ .menu-list a {
  border-radius: 10px;
  color: white;
}
/deep/ .menu-list a:hover {
  background-color: #E56D9B;
}
/deep/ .menu-label {
  font-size: 1rem !important;
}
/deep/ span {
  font-size: 1.2rem !important;
}
</style>

注意这里使用的 /deep/ 是为了改变我使用的 Buefy 的 Menu 样式。

有了这些内容,我们写一个 Header 来帮助用户定位,

<template>
  <div class="header">
    <div class="link-container">
      <router-link :to="{ name: 'Landing' }" class="link">
        <img src="../assets/logo.png" style="height: 3rem">
      </router-link>
      <router-link :to="{ name: 'Landing' }" class="link text-link">
        ArcLight
      </router-link>
      <router-link :to="{ name: 'MusicMenu' }" class="link text-link">
        Music
      </router-link>
    </div>
  </div>
</template>

<style lang="less" scoped>
.link-container {
  margin-top: 2rem;
  margin-left: 2rem;
  width: 100%;
  display: flex;
  align-items: center;
}
.link {
  margin-left: .75rem;
  font-size: 1.2rem;
}
.text-link {
  margin-bottom: 5px;
}
.text-link:hover {
  animation-name: colorChange;
  animation-duration: 0.25s;
  animation-timing-function: ease-in-out;
  color: #ff92bc;
}
@keyframes colorChange {
  0% {
    color: white;
  }
  100% {
    color: #ff92bc;
  }
}
/deep/ a {
  color: white;
}
</style>

好了,现在我们有了一个完整的页面来显示:




大概就是长成这样。

我们尝试使用

yarn build

构建我们的页面 App,然后我们再来 deploy-dir,一定能成功。
yarn build 不会花很久,然后我们继续运行同一个命令,这次我们把 目录改成 yarn build 之后生成的文件目录 ./dist

arweave deploy-dir ./dist --key-file secrets/ayaka.key.json

整个过程都看起来没什么问题,我们的 Transaction 已经被提交并且能够被访问了,我们点击链接。

???
为什么什么都没有?

打开开发者工具查看 Console 发现所有资源全部都报错了,说明没有到服务器上,可是我们使用工具查阅 transaction:https://viewblock.io/arweave/tx/7wRXzBZqahTCGn1iZlCqpMuAToZplGtqxkNYM5ywztg
发现的确已经部署上去了,问题出在哪里?

部署失败了

我们尝试官方文档说的:

arweave deploy ./dist/index.html --package --key-file secrets/ayaka.key.json

我们再去尝试访问:
https://7xr6o7ief7vrfrlm7ixvekkphritxtylffurzcmp67ckkfc6mvnq.arweave.net/_ePnfQQv6xLFbPovUilPPFE7zwspaRyJj_fEpRReZVs
发现还是不行。为什么呢?
从理论上而言,我们使用 Webpack 打包文件的话,是需要一个 source map 和 js 文件来协同工作的,就像参照 ASCII 表格来分析二进制代码一样。可是我们查阅 transaction,文件 vendor 都是被打包上传好的。
我发现自己一个人是解决不了这个问题的,于是在 Discord 上提问:

他们建议我使用 status 命令看看状态,因为文件必须要被 mined 到一个 accepted block 之后才可以被访问:

但是结果显而易见,是成功的状态。

第三天的挣扎

发现原因

我提出了问题之后,在 Discord 上有一位十分热心的用户帮我看了问题,在第三天的时候给了回复:

他说的确看起来一切都是被上传的,可是不知道为什么 proxy 系统没有正常工作提供路由地址。
也有人说是不是没有使用 –package 参数打包 index.html 文件导致的,可是我尝试了这个方法,也不能确保文件正常,比如被编码过后的网站图标变得不可用了,即便打开开发者工具栏依然可以看到是 base64 编码的 data/image

不久之后他提出了问题的关键所在,关键所在就是我不能对着每一个资源使用绝对路径,而是相对路径,这样就能访问到我们需要的东西了。

解决问题

想要解决这个问题也很简单,我们需要去 config/index.js 的配置文件中告诉 Webpack 我们的链接替换方式,
我们打开 config/index.js 文件就能看到:

build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

    /**
        * Source Maps
        */

    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
    }

assetsSubDirectoryassetsPublicPath 的位置需要进行调整。
应该调整为下面的两个值,在每个值前面确保是 ./ 就可以了:

build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: './static',
    assetsPublicPath: './',

    /**
     * Source Maps
     */

    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }

于是现在我们再尝试

yarn build
arweave deploy-dir ./dist --key-file secrets/ayaka.key.json

点击新的链接: https://arweave.net/DdjCHM9GiWxQ7NnRQq_jJ7Ib4kOe8tlSchdu4nGbVWw ,就发现一切都可以用了。

结论

在 Arweave 的 Web App 上,你必须使用 相对路径 来填充你的资源(Assets),而不是默认配置的 绝对路径
这个问题在其他前端框架上一样可能出现问题,因为都是基于 Webpack 给的打包文件和 Source Map,所以如果你也在给 Arweave 开发 Web App,就要小心这个问题了。

Webpack 的确可以在 Arweave 上使用,所有的外部资源都可以被调用,但是一定要注意:你必须使用 相对路径 来填充你的资源(Assets),而不是默认配置的 绝对路径
另外就是,如果使用 vue-router 来进行路由的话,记得模式应该设置为 Hash。如果你使用 React 或者其他前端框架,也应该考虑相应的问题。
以上就是我在 Arweave 踩到的坑啦。

最后

谢谢 Discord 帮我寻找问题的每一个人,谢谢 Arweave 团队能够做出这样惊喜的作品。
也谢谢其他 dApp 作者给的启发,没有你们就没有现在的 ArcLight。

现在 ArcLight 应邀 Arweave 团队在 GitCoin 的 Hackthon 邀请,现在作为一个可以上传和发布音乐作品的 Web App 存在着,在将来的一个月都会专注于开发这个部分的内容。

链接

隐私社交工具 Maskbook 集成 Arweave 支持永久储存文件: https://www.chainnews.com/news/107645751530.htm
ArcLight: https://github.com/AyakaLab/ArcLight
Arweave.js: https://github.com/ArweaveTeam/arweave-js
dApp 列表: https://mtfvznw2pwxykoicvxpoe7ao5rp4nhaueueux2bbe4klxankdhra.arweave.net/ZMtcttp9r4U5Aq3e4nwO7F_GnBQlCUvoIScUu4GqGeI/
Arweave Deploy: https://github.com/ArweaveTeam/arweave-deploy
Arweave Viewer: https://viewblock.io/arweave