Svelte 组件之间通讯的 6 种方法
使用组件设计用户界面的主要挑战是管理不同组件上的应用状态。而 Svelte 提供了强大的能力实现在组件中进行数据传递。
“Great communication begins with connection.“
— Oprah Winfrey
译注:奥普拉·盖尔·温弗里(英语:Oprah Gail Winfrey,1954年1月29日-),生于美国密西西比州,美国电视脱口秀主持人、制作人、投资家、慈善家及演员,美国最具影响力的非洲裔名人之一,时代百大人物。
她是入选时代百大人物次数最多者,总共9次。2005年美国在线举办票选活动—《最伟大的美国人》,她被选为美国最伟大的人物中的第九位。
接下来开始来了解 6 种实现 Svelte 组件间通讯的方法,他们分别是:
- 将数据发送到子组件: Props
- 在组件内渲染 HTML: Slots
- 子组件将数据通过事件方式通知父组件: Events
- 通过上下文 API 传递数据: Context API
- 在所有组件的实例之间共享数据: Module Context
- 在任意组件之间共享数据: Store
在本文中,我们写了一些简单的示例,应用了不同的 Svelte 组件通讯技术。这些代码不会包含所有零碎的东西,只是为了让你了解这些技术的本质。
1. Props
在真实应用中,经常需要将数据从一个组件传递到其他的子组件。最基本的方法就是使用组件属性,在 Svelte 中可以使用 export 关键字来定义组件属性。
请注意下图代码中高亮部分的 export 关键字:
Card.svelte with props
简单的引入 Card 组件,并通过属性将数据传递给 Card 。
<script> import Card from './Card.svelte'; </script> <Card userid="#2312312" name="Jhon Doe"/>
提示: 在 Svelte 中使用 Typescript 可以解决组件内的属性类型检查问题。
2. Slots
Slots (槽) —— 子组件可以通过使用槽来传递要渲染的内容,这是基于 Web 组件槽的建议。
<div class="card"> <slot/> </div>
槽的默认定义方法是在子组件中使用 <slot>
这个 HTML 标签。
槽有助于将组件设计为一个模板,同时也可以基于命名的方式来注入 HTML 内容。下面栗子展示命名槽的做法:
Card component with slots
3. Events
在设计组件时,捕获来自子组件的事件是一件很有意思的事情。继续更新上述的代码,当我们点击心图标的时候就会在喜欢与不喜欢之间做切换。如图所示:
{createEventDispatcher}
函数。然后在子组件中通过这个函数来触发事件。来看看代码: <script> import { createEventDispatcher } from 'svelte'; //Declare the dispatch const dispatch = createEventDispatcher(); export let id = 'ID'; export let name = 'unknown' export let favor = false; let hearts = ['♡','♥']; let heartIcon = hearts[0]; //if you wonder $: its svelte way make reactive statement $: heartIcon = favor ? hearts[1] : hearts[0]; const favorite = (id) => { favor = !favor; //Dispatch the favorite event with object data dispatch('favorite', {id, favor}); } </script> <div class="card"> <i>{id}</i> <h2>{name}</h2> <span class="btnHeart" on:click={() => favorite(id)}>{heartIcon}</span> </div> <style> .card{position:relative;margin:50px auto;width:300px;padding:20px;border-radius:9px;background-color:#F5F7F0; } i{ color:#999;} h2{margin:0; color:#491D3C;} .btnHeart{position:absolute;right:20px; top:10px;font-size:32px;cursor:pointer; } </style>
CardEvent.svelte 文件根据点击来分发事件。
这里我们获取到三个值,分别是 id, name, favor ,然后通过 dispatch 分发给父组件:
<script> import Card from './CardEvent.svelte' let user ={ id : '#1212', name: 'Jhon Doe', favor : false } const whenFavored = (event) => user.favor = event.detail.favor; </script> <Card {...user} on:favorite={whenFavored}/>
App.svelte using CardEvent.svelte
这里使用了 {...}
操作符来设置 user
对象作为子组件的属性,并侦听 favorite
点击事件,当有点击的时候触发 whenFavored()
函数执行并设置值到父对象中。
黑科技
- 在多级嵌套的组件中,可以使用
<card on:favorite />
来转发事件,它将事件传递给父组件,这个机制同样也适用于 DOM 的事件。 - 可以将子组件作为一个引用传递给父组件,并在父组件中调用所有子组件输出的方法。例如可以使用
<Card bind:this={userCard} />
来引用Card
组件为一个userCard
对象。
4. Context API
上下文 API。 现在我们进入 Svelte 通讯技术里高级部分,这部分非常有用。上下文 API 提供了一个强大的机制实现组件间的对话。
“Communication is only effective when we communicate in a way that is meaningful to the recipient, not ourselves.“
"只有当我们以对接收者而不是我们自己有意义的方式进行沟通时,沟通才有效。“— Rich Simmonds (LinkedIn 联合创始人兼 CEO)
首先顶层的组件需要使用 setContext()
来设置数据,然后其他组件通过 getContext()
来获取数据。是不是相当简单?
//App.svelte <script> import Card from './CardContext.svelte' import {setContext} from 'svelte'; let user ={ id:123456, name:'Jhon Doe', favor : true } setContext('user', user); </script> <Card/>
我们通过上下文 API 将对象 user 传递给所有子组件:
<script> import {getContext} from 'svelte'; //Get the Ancestor user object const user = getContext('user'); let hearts = ['♡','♥']; let heartIcon = hearts[0]; //Apply the favor value to reactive heartIcon $: heartIcon = user.favor ? hearts[1] : hearts[0]; </script> <div class="card"> <i>{user.id}</i> <h2>{user.name}</h2> <span class="btnHeart">{heartIcon}</span> </div> <style> .card{ position:relative; margin:50px auto; width:300px; padding:20px; border-radius:9px;background-color:#F5F7F0;} i{ color:#999;} h2{margin:0; color:#491D3C;} .btnHeart{ position:absolute; right:20px; top:10px; font-size:32px; cursor:pointer; } </style>
CardContext.svelte
注意,上下文中的状态仅对子组件有效,如果要使用组件的多个实例,而不使一个组件的状态干扰其他组件的状态,这将非常有用。
5. Module Context (模块上下文)
在 Svelte 中,同一个组件的多个实例想要共享相同数据是非常简单的,只需要将这些变量定义在 <script context='module'></script>
之间即可。
来看这么一个例子,当点击标签名词的时候,其他组件实例也会相应的展示相同效果。
首先在 App.svelte 中使用 users 对象创建 Card 实例并使用属性来传递 user 对象。
<script> import Card,{ clearAll } from './CardWithModuleContext.svelte' let users=[ {id:101, name:'Jhon Doe', tags:['Javascript','HTML','PHP']}, {id:102, name:'Mark Dane', tags:['C++','HTML','CSS']}, {id:103, name:'Clark Jose', tags:['Javascript','HTML','CSS']}, ]; </script> <button on:click={clearAll} style="border-radius:9px;" >Clear All </button> {#each users as user} <Card {user}/> {/each}
App.svelte
同时添加一个模块方法 {clearAll}
来清除所有高亮的标签:
<script context="module"> //data shared accross each instance of this componenet let tagSelected; export function clearAll() { tagSelected = "" } </script> <script> import { onMount, onDestroy } from "svelte"; export let user; let choosed; let interval; const selectIt = (tag) => { tagSelected = tag; choosed = tagSelected } //read the data on interval onMount(() => { interval = setInterval(()=> choosed = tagSelected, 100); }); //destroy the timer onDestroy(() => clearInterval(interval)); </script> <div class="card"> <i>#{user.id}</i> <h2>{user.name}</h2> <dl> {#each user.tags as tag} <dd on:click={()=>selectIt(tag)} class:apply={choosed == tag}>{tag}</dd> {/each} </dl> </div> <style> .card{width:300px;padding:20px;border-radius:9px;background-color:#F5F7F0;margin-bottom:10px;} i{ color:#999;} h2{margin:0; color:#491D3C;} dl{margin:0; padding:0; margin-top:10px;} dd{display:inline-block;margin:0; margin-right:10px; color:#999; padding:1px 10px; border:1px solid #ccc; border-radius:15px; font-size:14px;cursor:pointer; } .apply{color:#900; background:#ccc;} </style>
CardWithModuleContext.svelte
变量 tagSelected
将在组件的不同实例间共享,理解起来非常有趣,这里添加一个 100 毫秒的定时器来更新标签的高亮。如你所见,子组件中的所有逻辑就是用来和其他实例通讯的。
6. Store
随着应用越来越复杂,增加越来越多的特性,越来越多的组件,复杂度日益增加。这个时候我们需要在组件的层次结构之外保存一些应用的状态。而 Svelte 内建的 store 就可以实现这个。
在 Svelte 存储中,可以存单个对象、数组等。Svelte 提供了多种存储,包括 writable, readable, derived, 或者 custom.
接下来做一个简单例子实现图书列表:
BookStore.js
import { writable } from 'svelte/store' export let bookStore = writable([ {name:"Hamlet",author:"William Shakespeare"}, {name:"The Great Gatsby",author:"F. Scott Fitzgerald"} ]);
BookList.svelte
<script> import { bookStore } from './BookStore.js' </script> <ul> {#each $bookStore as book} <li>{book.name} - {book.author}</li> {/each} </ul>
BookForm.svelte
<script> import { bookStore } from './BookStore.js' let bookName; let author; const addNew = ()=>{ $bookStore = [{name:bookName, author:author},...$bookStore,]; } </script> <input type="text" bind:value={bookName} placeholder="Book Name"/> <input type="text" bind:value={author} placeholder="Author Name"/> <button on:click={addNew}>+ Add Book</button>
App.svelte
<script> import BookList from './BookList.svelte' import BookForm from './BookForm.svelte' </script> <BookForm/> <BookList/>
这里创建了一个 bookStore
作为 writable 数组,通过 $ 这个语法糖在表单和列表组件中引用来访问数据。
Context vs Store
Context 和 Store 二者比较类似,区别在于 Store 可在应用的任一组件中访问,而 Context 只能用于子组件。
示例代码
了解本文中介绍的所有示例,这些示例在 Svelte REPL 中可用。要进行测试,请通过导入不同的卡组件来更新 App.svelte 文件,以检查结果。 (demo)
结论
构建组件之间的通信是应用程序设计中最重要的部分。在Svelte中,我们有用于状态管理的内置功能,可以给我们很好的灵活性来设计更好的应用程序。
本文翻译自 https://betterprogramming.pub/6-ways-to-do-component-communications-in-svelte-b3f2a483913c

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2021 年度 OSC 中国开源项目评选:优秀中国开源原生创企
中国开源软件生态蓬勃发展,近年来涌现出了一大批优秀的开源软件创企,他们不忘初心,深耕开源,回馈社区,为中国开源软件事业添砖加瓦,成为全球开源软件生态中不可忽视的重要力量。 为鼓励这些开源创企对开源社区的卓越贡献,并让更多的开发者认识和了解这些优质的开源创业团队,为中国开源创业者树立行业榜样,我们在今年的开源软件评选活动中特别设置了“优秀中国开源原生创企”奖项。 所谓“开源原生创企”,是指公司基于开源项目而成立,围绕以下商业模式运作: 团队围绕自建开源项目提供商业服务,或 团队基于上游开源项目提供商业服务 我们主要综合以下信息和相关数据,对参与该奖项的 35 家处于创业阶段的开源原生商业公司进行了评估: 公司相关开源项目社区的发展情况:包括 star、fork 等反映项目流行度的数据,以及贡献者数量、issue、PR 等反映社区健康度的数据。 公司相关开源项目所处技术领域的发展前景:参考业内资深技术专家、投资人的意见和建议。 公司发展情况:包括融资进度和规模,公司发展规模,行业合作伙伴与合作案例,业内影响力等情况。 选出以下 20 家开源创企,获得本次“优秀开源原生创企”奖(注:按首字母...
- 下一篇
正则文本替换器 RegexReplacer v1.2 发布
正则文本替换器RegexReplacer已发布1.2版本 主要变动: 优化所有文本编辑框的撤销/恢复快捷键 支持Ctrl+Shift+Z进行恢复(Ctrl+Y仍然支持) 调整Mac OS下的快捷键,使用Command键作为组合键(原来为Ctrl键),与大家的习惯保持统一 RegexReplacer能做什么? 它可以实现复杂的文本替换,虽然写代码或脚本也可以完成,但它简化了这一过程,并且是可视化操作 适用场景 不习惯用awk、sed等命令行工具(它们真的很强大) 对自己写的正则与替换没有十成把握,需要实时查看匹配结果及替换内容作确认 匹配后仅挑选部分匹配内容执行替换 替换较为复杂,比如涉及四则运算,条件判断,序列号生成等需要借助编码或脚本才能完成的场景 非超大文本,比如超过100M,此时awk等流式处理的工具更合适 至于同类工具,我了解的有: awk等命令行工具,区别在于可视化操作 notepad++/vs code/idea等,区别在于替换表达式
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS8安装Docker,最新的服务器搭配容器使用