作为一名涉足去中心化网络的前端 JavaScript 开发人员,您可能遇到过许多 Web3 开发解决方案。但是,这些解决方案通常侧重于钱包集成和交易执行,这就造成了学习曲线,偏离了熟悉的 Web2 开发体验。
但不用担心!有一种解决方案可以无缝衔接 Web2 和 Web3,它就是 Juno。
在本篇博文中,我们将探讨如何利用 Vue 和 Juno 的强大功能来开发去中心化应用程序(dApps)。加入我们的旅程,揭开 Juno 的神秘面纱,让您轻松创建非凡的去中心化体验!
导言
在我之前的博文中,我讨论了 React 和 Angular 这两个流行的 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 现在已经上线,并完全由区块链提供支持。🎉
资源
- Juno 文档和入门:https://juno.build/docs/intro
- 本教程的源代码可在我们的 GitHub 代码库中获取。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。