Talk is cheap. Show me your code

Svelte 迷途求索(二) —— 组件传参和状态管理

一、简单体验

Svelte 的开发体验和 Vue 3 很类似

在 Svelte 组件中也是将 JS 写在 <script> 标签中,将 CSS 写在 <style> 中

稍微不同的点在于,DOM 模板不需要写在 <template> 中,而是可以写在任何地方

<script>
    import Nested from './Nested.svelte';
    const title = 'Hello World';
</script>

<style>
    p { color: purple; }
</style>

<h1>{title}</h1>
<p>Let's Enjoy Sevelte</p>
<Nested/>

Svelte 中的变量自带响应式特性,数据变更时会即时渲染到页面上

同时 Svelte 还提供了 $: 用来实现一段响应式逻辑,就像 Vue 中的 Computed 和 Watch

<script>
    let count = 0;
    const handleClick = () => count += 1;
    $: doubled = count * 2;
    $: if (count > 9) {
        console.log('count is dangerously high!');
count = 9; };
</script> <button on:click={handleClick}>Click</button> <p>{count} doubled is {doubled}</p>

总之 Svelte 是一个很好上手的框架,更多的特性或语法可以通过官方文档了解,《Svelte 迷途求索》将着重介绍 Svelte 实战中可能遇到的问题

 

 

二、创建 Props:父对子传参

在开发的时候,如果子组件中的某个数据需要通过父组件传入,就需要在子组件中声明 props

// 子组件

<script lang="ts">
  export let text: string = undefined;
</script>

<p>hello {text}</p>

没错,在 Svelte 中创建 props 就是这么简单粗暴,只需要使用 let 创建一个变量,然后通过 export 导出即可

可以给这个 prop 添加一个默认值,不管是上面的 undefined,还是一个具体的值,那么这个 prop 就会被标记为可选属性

如果没有设置默认值,这个 prop 就是一个必填属性。使用该组件时如果没有提供该属性,则会打印警告

 

 

三、通过 prop 传入函数:子对父传参

上面已经介绍过,组件的 prop 是通过 let 创建的

而使用 const, class, function 创建的 prop 都是只读属性,即使通过 export 导出,也不会接收外部参数

 

 

然而通过 prop 传入一个函数的情况还是挺常见的,比如子组件向父组件传参

好在虽然不能使用 function,但还是可以使用箭头函数作为 prop

下面这个例子中,子组件通过回调函数 onChange 实现了向父组件传参

// 子组件

<script lang="ts">
  // onChange 需要父组件提供
  export let onChange = (v: number) => {};

  let count: number = 0;

  const addCount = () => {
    count += 1;
    typeof onChange === 'function' && onChange(count);
  };
</script>

<button on:click={addCount}>Click</button>
<p>count: {count}</p>
// 父组件

<script lang="ts">
  import Child from './child.svelte';

  const onChange = (v: number) => {
    console.log('onChange', v);
  };
</script>

<Child {onChange} />
<!-- 
  * 等价于:
  * <Child onChange={onChange} />
 -->

 

 

四、状态管理:复杂情况的组件传参

通过 props 可以很轻松的实现父组件对子组件的传参

如果是兄弟组件之间传参,可以运用状态提升的思想来实现

但如果是更复杂的情况呢?比如祖孙组件,甚至是几个没有直接关系的组件。这时候就可以使用 Svelte 自带的状态管理

// stores/index.js

import { writable } from 'svelte/store';

export const titleStore = writable('Hello World');

export const userInfo = writable({
  name: 'Wise.Wrong',
  blog: 'https://www.cnblogs.com/wisewrong/',
});

svelte/store 模块提供了三个函数 writable、readable、derived,分别用来创建可写状态、只读状态、派生状态

其中最常用的是可写状态 writable, 上面的 stores/index.js 中就使用 writable 创建了两个 store 对象

但 store 对象是不能直接用于页面渲染的,比如下面的用法就会出错

这是因为 writable 创建的是包含 set、update、subscribe 方法的 store 对象

其中 subscribe 会在 store 更新的时候触发,类似 Vue 中的 watch

基于这个特性,我们可以这样读取 store 的值:

它确实生效了,但这样也太不优雅了,其实只需要在 store 前面加一个 $ 就行了

 


 

如果需要在组件中修改 store 的值,可以用到 set 和 update 方法

import { titleStore } from './stores';

const onChange = (v: string) => {
  // set 接收的参数会作为 store 的新值
  titleStore.set(v);
};

const onUpdate = (str: string) => {
  // update 可以接收一个函数,其返回值会作为 store 的新值
  titleStore.update((v: string) => v + str);
}

但如果是在表单中,有时候连 update 或 set 都不需要,因为 store 可以配合 bind: 使用

<script lang="ts">
  // parent.svelte
  import { userInfo } from '../../stores';
  import Child from './child.svelte';
</script>

<div class="title">This is <strong>{$userInfo?.name}</strong></div>
<Child />

<style>
  .title {
    margin-bottom: 12px;
  }
</style>
<script lang="ts">
  // child.svelte
  import { userInfo } from '../../stores';
</script>

<section>
  <div>
    <span>姓名: </span>
    <input type="text" bind:value={$userInfo.name}>
  </div>
  <div>
    <span>博客: </span>
    <input type="text" bind:value={$userInfo.blog}>
  </div>
</section>

 

posted @ 2022-01-27 18:02  Wise.Wrong  阅读(673)  评论(0编辑  收藏  举报