Svelte中的双向数据绑定和生命周期

作者:renzp94
时间:2021-03-24 14:30:46

双向数据绑定

Svelte中数据流是自上而下的,即单项数据流(父组件可以将数据通过prop传递给子组件,但是子组件不能直接修改prop影响父组件的值,一般可通过事件传递给父组件,然后父组件在捕获事件并在事件中修改其值)。但是在Svelte中提供了bind:value指令可以实现双向数据流,就像Vue中的v-model

Input

input不同类型实现双向数据流的方式:

  • 输入框(text/password/number)、文本域(textarea):bind:value
  • 单选框(radio):可以通过bind:group来绑定指定当前选中值,通过value来指定值。
  • 多选框(checkbox):可以通过bind:group来绑定指定当前选中值,通过value来指定值,也可通过bind:checked来控制单个多选框状态。

在线 REPL

<script>
    let value = '';
    let textareaValue = '';
    let step = 0;
    let checked = false;
    let radio = '1';
    let checkboxGroup = ['1'];
</script>

<h3>{value}</h3>
<input bind:value />
<br />
<h3>{textareaValue}</h3>
<textarea bind:value={textareaValue} />
<br />
<input type="number" bind:value={step} />
<br />
<input type="range" bind:value={step} />
<br />
radio:{radio}
<label>选项1<input type="radio" bind:group={radio} value="1" /></label>
<label>选项2<input type="radio" bind:group={radio} value="2" /></label>
<label>选项3<input type="radio" bind:group={radio} value="3" /></label>
<br />
checked:{checked}
<input type="checkbox" bind:checked />
<br />
checkboxGroup:{checkboxGroup}
<label>选项1<input type="checkbox" bind:group={checkboxGroup} value="1" /></label>
<label>选项2<input type="checkbox" bind:group={checkboxGroup} value="2" /></label>
<label>选项3<input type="checkbox" bind:group={checkboxGroup} value="3" /></label>

Select

select通过bind:value来实现双向数据流,注意如果没有设置初始值的话,则默认为第一个,如果多选可以设置属性multiple

<script>
    let value = '';
    let valueGroup = [];
</script>

<h3>value:{value}</h3>
<select bind:value>
    <option value="1">选项1</option>
    <option value="2">选项2</option>
    <option value="3">选项3</option>
    <option value="4">选项4</option>
</select>

<h3>valueGroup: {valueGroup}</h3>
<select multiple bind:value={valueGroup}>
    <option value="1">选项1</option>
    <option value="2">选项2</option>
    <option value="3">选项3</option>
    <option value="4">选项4</option>
</select>

可编辑元素

当元素设置属性contenteditable="true"时,元素就变成了一个可修改的元素了。可通过bind:innerHTML来实现双向数据流。

<script>
    let html = '<p>内容</p>';
</script>

<h3>
    html: {html}
</h3>
<div class="html" contenteditable="true" bind:innerHTML={html} />

<style>
    .html {
        border: 1px solid #000;
    }
</style>

Audio、Video

AudioVideo中有一些属性是只读的,不能实现双向绑定:durationbufferedseekableplayedseekingended。可双向数据绑定:currentTimeplaybackRatepausedvolumemuted

块级元素

块级元素的这些属性可以实现双向数据绑定:clientWidthclientHeightoffsetWidthoffsetHeight

this

如果想访问 DOM 元素,就像Vue中的ref那样,可以通过bind:this,注意:bind:this需要在生命周期函数onMount中才能获取到。

在线 REPL

<script>
    import { onMount } from 'svelte';
    let input;

    onMount(() => {
        console.log(input);
    });
</script>

<input type="text" bind:this={input} />

组件的双向数据

组件的双向数据绑定也可通过bind:value来实现。

在线 REPL

Input.svelte

<script>
    export let value;
</script>

<input bind:value />

App.svelte

<script>
    import Input from './Input.svelte';

    let value = '';
</script>

<div>value:{value}</div>
<Input bind:value />

生命周期

Svelte主要有一下几个生命周期:

  • script:在script标签中的代码都会在创建前运行。
  • onMount:当组件创建完成首次渲染 DOM 后触发此生命周期。如果在此生命周期中返回一个函数,则在销毁组件时调用。
  • beforeUpdate:当组件状态发生变化且在 DOM 更新前会触发此生命周期。
  • afterUpdate:当组件状态发生变化且 DOM 更新后会触发此生命周期。
  • onDestroy:当组件销毁时触发此生命周期。

其中,还有一个tick函数,此函数的作用相当于Vue中的nextTick。当组件状态发生改变不会立即更新 DOM,如果想在更新组件状态后获取最新 DOM 可以使用tick

在线 REPL

<script>
    import { onMount, beforeUpdate, afterUpdate, onDestroy, tick } from 'svelte';
    let name = 'world';
    let h;
    console.log('创建前');

    onMount(() => {
        console.log('onMount');

        return () => {
            console.log('onMount return function');
        };
    });

    beforeUpdate(() => {
        console.log('beforeUpdate:', h?.innerText);
    });

    afterUpdate(() => {
        console.log('afterUpdate:', h?.innerText);
    });

    onDestroy(() => {
        console.log('onDestroy');
    });

    const onClick = async () => {
        name = 'Svelte';
        console.log('onClick:', h?.innerText);
        await tick();
        console.log('onClick tick:', h?.innerText);
    };
</script>

<h1 bind:this={h} on:click={onClick}>Hello {name}!</h1>