头图

作为一名涉足去中心化网络的前端 JavaScript 开发人员,您可能遇到过许多 Web3 开发解决方案。但是,这些解决方案通常侧重于钱包集成和交易执行,这就造成了学习曲线,偏离了熟悉的 Web2 开发体验。

但不用担心!有一种解决方案可以无缝衔接 Web2 和 Web3,它就是 Juno

Juno官方网站

在本篇博文中,我们将探讨如何利用 Vue 和 Juno 的强大功能来开发去中心化应用程序(dApps)。加入我们的旅程,揭开 Juno 的神秘面纱,让您轻松创建非凡的去中心化体验!

导言

在我之前的博文中,我讨论了 ReactAngular 这两个流行的 JavaScript 前端框架的类似解决方案。如果这两个框架中的任何一个是您的首选,我建议您浏览这些具体的文章,以获得量身定制的见解。

Juno如何工作

如果你还不了解 Juno,它是一个功能强大的开源区块链即服务平台,旨在让去中心化应用程序开发变得更加容易。可以把它想象成一个无服务器平台,类似于谷歌Firebase或AWS Amplify等流行服务,但增加了区块链技术的优势。Juno 完全在区块链上运行您的应用程序,确保完全去中心化和安全的基础设施。

通过利用Internet Computer区块链网络和基础设施,Juno 为您创建的每个应用程序引入了一个名为 “Satellites” 的独特概念。这些 Satellites 作为强大的智能合约,封装了您的整个应用程序,包括 JavaScript、HTML 和图像文件等网络资产,以及简单的数据库、文件存储和身份验证机制。通过 Juno,您可以完全控制应用程序的功能和数据。

构建一个 Dapp

让我们开始构建我们的第一个去中心化应用程序,简称“dapp”。在这个例子中,我们将创建一个笔记应用程序,允许用户存储和检索数据条目,以及上传文件。

本教程和代码示例使用了 Vue Composition API。

初始化

在将 Juno 集成到应用程序之前,需要创建一个 satellite。该过程在文档中有详细的解释。

此外,还需要安装SDK。

npm i @junobuild/core

完成这两个步骤后,您可以在 Vue 应用程序的根目录(例如 App.vue)中使用 satellite ID 初始化 Juno。这将配置库与您的智能合约进行通信。

<script setup lang="ts">
import { onMounted } from 'vue'
import { initJuno } from '@junobuild/core'

onMounted(
  async () =>
    await initJuno({
      satelliteId: 'pycrs-xiaaa-aaaal-ab6la-cai'
    })
)
</script>

<template>
<h1>Hello World</h1>
</template>

配置完成!现在,您的应用程序已经可以用于 Web3 了!😎

身份验证

为了确保用户身份的安全性和匿名性,需要对用户进行登录和注销。要做到这一点,可以将相关函数绑定到应用程序中任何位置的 call-to-action 按钮。

<script setup lang="ts">
import { signIn, signOut} from '@junobuild/core'
</script>

<button @click="signIn">Sign-in</button>
<button @click="signOut">Sign-out</button>

为了与其他服务建立无缝集成,库和 satellite 组件在用户成功登录后自动在您的智能合约中生成新条目。此功能使库能够在任何数据交换期间有效地验证权限。

为了监视并深入了解该条目,从而访问有关用户状态的信息,Juno提供了一个名为authSubscribe() 的可观察函数。您可以根据需要灵活地多次使用此函数。然而,你也可以创建一个在整个应用中有效传播用户信息的 store。

import { ref, type Ref } from 'vue'
import { defineStore } from 'pinia'
import { authSubscribe, type User } from '@junobuild/core'

export const useAuthStore = defineStore('auth', () => {
  const user: Ref<User | null | undefined> = ref(undefined)

  const unsubscribe = authSubscribe((u) => (user.value = u))

  return { user, unsubscribe }
})

这样,在应用程序的顶层订阅它就变得非常方便。

<script setup lang="ts">
import { useAuthStore } from '../stores/auth.store'
import { storeToRefs } from 'pinia'

const store = useAuthStore()
const { user } = storeToRefs(store)
</script>

<template>
  <template v-if="user !== undefined && user !== null">
    <slot />
  </template>

  <template v-else>
    <p>Not signed in.</p>
  </template>
</template>

存储文档

Juno提供了一个名为“Datastore”的功能,旨在将数据直接存储在区块链上。Datastore 由一组集合组成,其中每个集合保存文档,这些文档由您选择的键唯一标识。

在本教程中,我们的目标是存储笔记,因此必须按照文档中提供的说明创建一个集合。为集合选择合适的名称,例如“notes”。

一旦设置好应用程序并创建了必要的集合,就可以利用库的 setDoc 函数将数据持久化到区块链上。此功能使您能够安全且不变地存储笔记。

import { setDoc } from "@junobuild/core";

// TypeScript example from the documentation
await setDoc<Example>({
  collection: "my_collection_key",
  doc: {
    key: "my_document_key",
    data: myExample,
  },
});

由于集合中的文档是通过唯一的密钥来标识的,因此我们使用 nanoid 来创建密钥--这是一种用于 JavaScript 的微型字符串 ID 生成器。

<script lang="ts" setup>
import { ref } from 'vue'
import { setDoc } from '@junobuild/core'
import { nanoid } from 'nanoid'

const inputText = ref('')

const add = async () => {
  const key = nanoid()

  await setDoc({
    collection: 'notes',
    doc: {
      key,
      data: {
        text: inputText.value
      }
    }
  })
}
</script>

<template>
  <textarea rows="5" placeholder="Your diary entry" 
            v-model="inputText"></textarea>

  <button @click="add">Add</button>
</template>
请注意,为简单起见,本教程提供的代码片段不包括适当的错误处理,也不包括复杂的表单处理。

检索文档列表

为了检索存储在区块链上的文档集合,我们可以使用库的 listDocs 函数。这个多功能函数允许加入各种参数,以方便数据过滤、排序或分页。

出于本教程的目的,我们将保持示例的简约性。我们的目标是在挂载组件时简单地列出所有用户数据。

<script lang="ts" setup>
import { listDocs } from '@junobuild/core'
import { onMounted, ref } from 'vue'

const items = ref([])

const list = async () => {
  const { items: data } = await listDocs({
    collection: 'notes'
  })

  items.value = data
}

onMounted(async () => await list())
</script>

<template>
  <p v-for="(item, index) in items">
    <span>
      {{ index + 1 }}
    </span>
    <span>{{ item.data.text }}</span>
  </p>
</template>

文件上传

在去中心化网络上存储数据是一项复杂的任务。然而,Juno 的设计旨在为需要轻松存储和检索用户生成内容(如照片或文件)的应用程序开发人员简化这一过程。

在处理文档时,第一步是按照文档中提供的说明创建一个集合。在本教程中,我们将重点实施图片上传,因此该集合可以恰当地命名为 “images”。

为确保存储数据的唯一性和正确识别,每个文件都有唯一的文件名和路径。这一点非常重要,因为数据是在网络上提供的,每条数据都应该有一个独特的 URL。

要实现这一点,我们可以使用用户唯一ID的文本表示形式和每个上传文件的时间戳的组合来创建一个键。通过访问我们之前在存储中声明的属性,我们可以检索相应的用户键。

<script lang="ts" setup>
import { ref } from 'vue'
import { useAuthStore } from '@/stores/auth.store'
import { storeToRefs } from 'pinia'
import { uploadFile } from '@junobuild/core'

const file = ref(undefined)

const store = useAuthStore()
const { user } = storeToRefs(store)

const setFile = (f) => (file.value = f)

const upload = async () => {
  // Demo purpose therefore edge case not properly handled
  if ([null, undefined].includes(user.value)) {
    return
  }

  const filename = `${user.value.key}-${file.value.name}`

  const { downloadUrl } = await uploadFile({
    collection: 'images',
    data: file.value,
    filename
  })

  console.log('Uploaded', downloadUrl)
}
</script>

<template>
  <input type="file" @change="(event) => setFile(event.target.files?.[0])" />

  <button @click="upload">Upload</button>
</template>

一旦一个资源被上传,一个 downloadUrl 返回,它提供了一个直接的 HTTPS 链接,可以在web上访问上传的资源。

列出资源

为了检索存储在区块链上的资产集合,我们可以利用库提供的 listAssets 函数。这个函数在参数方面提供了灵活性,允许我们根据需要对文件进行过滤、排序或分页。

与前面的文档示例类似,我们将保持这个示例的简约性。

<script lang="ts" setup>
import { listAssets } from '@junobuild/core'
import { onMounted, ref } from 'vue'

const assets = ref([])

const list = async () => {
  const { assets: images } = await listAssets({
    collection: 'images'
  })

  assets.value = images
}

onMounted(async () => await list())
</script>

<template>
  <img loading="lazy" :src="asset.downloadUrl" v-for="asset in assets" />
</template>

部署 🚀

在开发和构建应用程序之后,下一步是将其部署到区块链上。为此,您需要在终端中执行以下命令来安装 Juno 命令行接口(CLI):

npm i -g @junobuild/cli

安装过程完成后,您可以按照文档中的说明并从终端登录来访问您的 satellite。这将使你的机器能够控制你的 satellite。

juno login

最后,您可以使用以下命令部署项目:

juno deploy

恭喜你!您的 Vue dapp 现在已经上线,并完全由区块链提供支持。🎉

资源


杭州程序员张张
11.8k 声望6.7k 粉丝

Web/Flutter/独立开发者/铲屎官