Logic control

Native HTML doesn’t have logical judgments, such as conditional judgments and loops, but Svelte does

If judgment

If you need to render some HTML based on certain conditions, you can wrap it with an if structure

<script>
	let user = { loggedIn: false };

	function toggle() { user.loggedIn = ! user.loggedIn; } </script> {#ifUser.loggedin} <button on:click={toggle}> </button> {/if}

{#if! User.loggedin} <button on:click={toggle}> </button> {/if}
Copy the code

If is used to display different HTML structures based on the user’s login status

Notice how the if structure is used

{#ifConditional} HTML structure {/if}

Copy the code

If – else judgment

In addition to if judgments, Svelte also provides else judgments to simplify the case of multiple if judgments

We can modify the above code

<script>
	let user = { loggedIn: false };

	function toggle() { user.loggedIn = ! user.loggedIn; } </script> {#ifUser.loggedin} <button on:click={toggle}> </button> {:else} <button on:click={toggle}> </button> {/if}
Copy the code

Notice how the else statement is used, which is a single tag, inside the if tag

{#ifConditional} HTML structure {:else} HTML structure {/if}

Copy the code

Else if judgment

Else if statements can be used to determine more conditions

<script>
	let x = 7;
</script>

{#if x > 10} < p > {x}10< / p > {:else if 5> x} <p>{x} x is less than5</p>
{:else} < p > {x} in5~10Between < / p > {/if}

Copy the code

Else if is used in the same way as else, wrapped in the if structure

Each loop statement

Svelte also provides loop statements, Each, which is more like a cross between vue’s V-for and React’s map

<script>
	let cats = [
		{ id: 'J---aiyznGQ'.name: 'Keyboard Cat' },
		{ id: 'z_AbfPXTKms'.name: 'Maru' },
		{ id: 'OUtn3pvWmpg'.name: 'Henri The Existential Cat'}]; </script><h1>YouTube famous cats</h1>

<ul>
	{#each cats as cat}
		<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
			{cat.name}
		</a></li>
	{/each}

</ul>

Copy the code

Notice how the each statement is used in much the same way as if,

{#each set as individual} HTML {/each}Copy the code

In addition, the each statement takes a second parameter, I

{#each cats as cat, i}
	<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
		{i + 1}: {cat.name}
	</a></li>
{/each}
Copy the code

Now, compare this to vue v-for, and I acts like index, as a subscript

v-for="(item, index) in array"

Copy the code

In fact, svelte provides the use of deconstruction, deconstructing the attributes of a single element in the collection as the first parameter

{#each cats as { id, name }, i}
	<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
		{i + 1}: {name}
	</a></li>
{/each}
Copy the code

The Key of the Each loop statement

The downside of each is that by default, when we modify the value of each, it adds or modifies at the end of the each block and updates any values that have been changed

Take a look at this example, which uses the Each loop to render a list of emojis

As you can see, clicking the “Delete First” button removes the first Emoji component, but the last DOM node, and then updates the names of the remaining DOM nodes, but not the emojis

The logic is as follows

  • Define the HTML structure in child components
  • The child component displays the corresponding emoticon content based on the passed value
  • Modify operations in the parent component

Subcomponents emojis. Svelte

<script>
	const emojis = {
        apple: "🍎".banana: "🍌".carrot: "🥕".doughnut: "🍩".egg: "🥚"
	}

	// The value of name changes according to the value of prop
	export let name;

	/ /... But the emoji variable is given when the component is initialized
	const emoji = emojis[name];
</script>

<p>
    <span>The {name} expression is {emoji}</span>
</p>

<style>
	p {
		margin: 0.8 em 0;
	}
	span {
		display: inline-block;
		padding: 0.2 em 1em 0.3 em;
		text-align: center;
		border-radius: 0.2 em;
		background-color: #FFDFD3;
	}
</style>

Copy the code

The parent component App. Svelte

<script>
  import Emoji from "./children/Emojis.svelte"
  let emojis = [
            { id: 1.name: 'apple' },
            { id: 2.name: 'banana' },
            { id: 3.name: 'carrot' },
            { id: 4.name: 'doughnut' },
            { id: 5.name: 'egg'},];function handleClick() {
        emojis = emojis.slice(1);
 }

</script>

<div>
  <button on:click={handleClick}>Delete the first one</button>
  
  {#each emojis as item}
    <Emoji name={item.name}/>
  {/each}
</div>

<style>
  div{
      box-shadow: 0 0 10px 2px rgba(1.1.1.2);
      margin: 20px;
      padding: 10px;
    }

    button:hover{
      background-color: aqua;
    }  
  </style>

Copy the code

But what we need is to remove the first emoticon component and its DOM node without affecting the others

So when we use Each, we need to give Each block a unique identifier “key”

So, make a change to the each loop

{#each emojis as item (item.id)}
    <Emoji name={item.name}/>
  {/each}
Copy the code

The aboveitem.idActs as a key that tells Svelte which specific DOM node to change when the component is updated

We can use any type of variable as the key, even the item itself, but it’s safer to use numbers or strings

And then, we get what we want

The Event Event

Event listeners and inline events

As we used before, in Svelte, you can use the ON: directive to listen for DOM events

<script>
	let m = { x: 0.y: 0 };

	function handleMousemove(event) {
		m.x = event.clientX;
		m.y = event.clientY;
	}
</script>

<div on:mousemove={handleMousemove}>Mouse real-time position is {m.x} x {m.y}</div>

<style>
	div { width: 100%; height: 100%; }
</style>
Copy the code

Of course, it also looks like native JS. Svelte also supports inline events

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}"> Mouse real-time position is {m.x} x {m.y} </div>Copy the code

In some frameworks, you may see recommendations to avoid using inline event handlers for performance reasons, especially inside loops. This advice does not apply to Svelte — the compiler will always do the right thing, no matter which form you choose.

Event modifier

DOM events have specific modifiers that constrain their behavior, for example, the once modifier, which only executes the event once

<script>
    function handleClick() {
            alert('I'm only going to pop it once.')
    }
</script>

<button on:click|once={handleClick}>
    Click me
</button>
Copy the code

Here are the event modifiers available:

  • preventDefault– Block default events.
  • stopPropagation– Prevents events from bubbling
  • passive– Improves the performance of mouse wheel events and touch events
  • nonpassive– Set passive to false explicitly
  • capture– Event handlers are fired during the event capture phase
  • once– Let the event fire only once
  • self– Emitted only if the event source is the element itself

If more than one modifier is needed, we can call it chained

on:click|once|capture={... }.Copy the code

Component events

Child components can dispatch events to parent components, but you must first create an event dispatcher, which is a built-in function of Svelte that can be imported and used directly

Create a new child component, Inner. Svelte

<script>
	import { createEventDispatcher } from 'svelte';

	const dispatch = createEventDispatcher();

	function sayHello() {
		dispatch('message', {
			text: 'hello'
		});
	}
</script>

<button on:click={sayHello}>Click me to say hello</button>
Copy the code

Receives the meaaage event from the child in the parent component app.svelte

<script>
	import Inner from './children/Inner.svelte';

	function handleMessage(event) {
          console.log(event)
          alert(event.detail.text);
	}
</script>

<Inner on:message={handleMessage}/>
Copy the code

Click the button to trigger the following effect:

A few points to note here:

  • The child componentdispatch(eventName, {})The first argument to the function is the name of the event distributed to the parent, and the second argument is the data distributed
  • The event name that the parent component listens on should be the same as that distributed by the child component
  • createEventDispatcherIt must be called when the component is first instantiated — you can’t execute it internally later, such as in a setTimeout callback or promise.
  • If the parent component does not accept the dispatched event, the child component can still be dispatched successfully without any response

Component nested event forwarding

Unlike DOM events, component events do not bubble. If you want to listen for an event on a deeply nested component, the intermediate component must forward the event

In the following case, we wrap the Inner component in the new Outer. Svelte component

In this way, the Inner component’s events are sent to Outer, who then sends them to App

So we’ll create a dispatcher using createEventDispatcher in the Outer component to send events from the Inner component to the App

Outer.svelte

<script>
	import Inner from './Inner.svelte';
	import { createEventDispatcher } from 'svelte';

	const dispatch = createEventDispatcher();

	function forward(event) {
		dispatch('message', event.detail);
	}
</script>

<Inner on:message={forward}/>

Copy the code

However, when there are many events, the code becomes redundant

But Svelte gives us the shorthand

Event listening with no value means that all events are dispatched

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

<Inner on:message/>
Copy the code

< span style = “max-width: 100%; clear: both; min-width: 100%; clear: both; min-width: 100%

So in app.svelte we can get events from the Inner component

App.svelte

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

	function handleMessage(event) {
		alert(event.detail.text);
	}
</script>

<Outer on:message={handleMessage}/>
Copy the code

Native DOM event forwarding

Event forwarding also applies to DOM events.

We want a click notification on our CustomButton component — to do this, we simply forward the click event on the element in CustomButton.svelte

CustomButton.svelte

<button on:click> </button>Copy the code

App.svelte

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

    function handleClick() {
            alert('Button clicked.');
    }
</script>

<CustomButton on:click={handleClick}/>
Copy the code

Component life cycle

Each component has a life cycle that begins when it is created and ends when it is destroyed.

We can handle our particular logic in key declarative periodic functions

onMount

The most commonly used is onMount, which runs after the component is first rendered into the DOM.

In this example, we load some web images in onMount

Life cycle functions are introduced separately

<script>
	import { onMount } from 'svelte';
	let photos = [];
	onMount(async() = > {const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
		photos = await res.json();
	});
</script>
Copy the code

Lifecycle functions must be called when the component is initialized so that the callback is bound to the component instance — not (for example) in setTimeout.

If the onMount callback returns a function, it will be called when the component is destroyed.

It is recommended that network data requests be placed in onMount to reduce the possibility of data not being loaded after the ODM has finished loading

<script> import { onMount } from 'svelte'; let photos = []; onMount(async () => { const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`); photos = await res.json(); }); </script> <div class="photos"> <h1>Photo album</h1> {#each photos as photo} <figure> <img src={photo.thumbnailUrl} alt={photo.title}> <figcaption>{photo.title}</figcaption> </figure> {:else} <! -- this block renders when photos.length === 0 --> <p>loading... </p> {/each} </div> <style> div{ box-shadow: 0 0 10px 2px rgba(1, 1, 1, .2); margin: 20px; padding: 10px; } </style>Copy the code

onDestory

To run the code when the component is destroyed, use onDestroy.

For example, we could add a setInterval function when the component is initialized and clean it up when it is no longer relevant. Doing so prevents memory leaks.

While it is important to call lifecycle functions during component initialization, it is not important where they are called from.

So, if we wish, we can abstract the logic that handles component destruction into auxiliary functions in utils.js…

New utils.js where we clean up the timer during component destruction

import { onDestroy } from 'svelte';

export function onInterval(callback, milliseconds) {
	const interval = setInterval(callback, milliseconds);
	onDestroy(() = > {
		clearInterval(interval);
		console.log("Timer cleared, component destroyed.")}); }Copy the code

Introduced in timer.svelte

<script> import { onInterval } from './utils.js'; export let callback; export let interval = 1000; onInterval(callback, interval); </script> <p> The component executes the callback function every {interval} seconds </p> <style> p {border: 1px solid blue; padding: 5px; } </style>Copy the code

Use timer.svelte in app.svelte

We use a button to turn the Timer on and off, which in turn controls the mount and destruction of the Timer component

This allows you to monitor the onDestory life cycle’s cleanup of the timer

<script> import Timer from './Timer.svelte'; let open = true; let seconds = 0; const toggle = () => (open = ! open); const handleTick = () => (seconds += 1); </script> <div> <button on:click={toggle}>{open ? 'Off timer' : </button> <p> Timer component has run {seconds} </p> {#if open} <Timer callback={handleTick} /> {:else} < P > Component destruction </p> {/if} </div>Copy the code

Turn the timer on and off a few times and make sure the counter stays ticking and the CPU load increases. This is due to a memory leak because the previous timer was not deleted. Don’t forget to refresh the page before solving the example.

In general, during the onDestory cycle, we can do the following

  • Clear tasks such as timers and timers
  • Destroy some charts
  • Clears animation that is still in execution
  • Clear variables that occupy a large amount of memory

BeforeUpdate and afterUpdate

BeforeUpdate is executed immediately before THE DOM is updated, whereas afterUpdate is executed after the DOM is updated for logic that is processed after the DOM and data are synchronized

In general, these two functions are used to force things that cannot be driven by state changes, such as updating the scrolling position of an element

Here we use a chatbot case, according to the input content, control the chat window scroll

The chatbot references a third-party library called ElizaBot, which can be used in its documentation

To install the library, run NPM Install elizaBot

Here we use it as an auto-reply robot to chat with us

Create a new chat.svelte

<script>
  import Eliza from 'elizabot';
  import { beforeUpdate, afterUpdate } from 'svelte';

 let div;
 let autoscroll;

 beforeUpdate(() = > {
	autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
   console.log("Rolling conditions are available.")}); afterUpdate(() = > {
	if (autoscroll) div.scrollTo(0, div.scrollHeight);
    console.log("Roll over.")});const eliza = new Eliza();

 let comments = [
	{ author: 'eliza'.text: eliza.getInitial() }
 ];


  // Process chat content input
  function handleKeydown(event) {
	if (event.key === 'Enter') {
		const text = event.target.value;
		if(! text)return;

		comments = comments.concat({
			author: 'user',
			text
		});

		event.target.value = ' ';

		const reply = eliza.transform(text);

		setTimeout(() = > {
			comments = comments.concat({
				author: 'eliza'.text: '... '.placeholder: true
			});

			setTimeout(() = > {
				comments = comments.filter(comment= >! comment.placeholder).concat({author: 'eliza'.text: reply
				});
			}, 500 + Math.random() * 500);
		}, 200 + Math.random() * 200);
	}
	}
</script>

<div class="wrap">
  <div class="chat">
    <h1>Eliza</h1>
  
    <div class="scrollable" bind:this={div}>
      {#each comments as comment}
        <article class={comment.author}>
          <span>{comment.text}</span>
        </article>
      {/each}
    </div>
  
    <input on:keydown={handleKeydown}>
  </div>
</div>


<style>

  .wrap{
    box-shadow: 0 0 10px 2px rgba(1.1.1.2);
    margin: 20px;
    padding: 10px;
  }
	.chat {
		display: flex;
		flex-direction: column;
		height: 100%;
		max-width: 320px;
	}

	.scrollable {
		flex: 1 1 auto;
		border-top: 1px solid #eee;
		margin: 0 0 0.5 em 0;
		overflow-y: auto;
	}

	article {
		margin: 0.5 em 0;
	}

	.user {
		text-align: right;
	}

	span {
		padding: 0.5 em 1em;
		display: inline-block;
	}

	.eliza span {
		background-color: #eee;
		border-radius: 1em 1em 1em 0;
	}

	.user span {
		background-color: #0074D9;
		color: white;
		border-radius: 1em 1em 0 1em;
		word-break: break-all;
	}
</style>
Copy the code

This code can be copied and run without worrying about what is written, just look at the timing and effects of beforeUpdate and afterUpdate functions

 beforeUpdate(() = > {
	autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
   console.log("Rolling conditions are available.")}); afterUpdate(() = > {
	if (autoscroll) div.scrollTo(0, div.scrollHeight);
    console.log("Roll over.")});Copy the code

There are a few quirks to the code, which I’ll cover later

 <div class="scrollable" bind:this={div}>
      
 </div>
Copy the code

The abovebind:this={div}Bind the actual DOM element to a variable (see ref in Vue and React).

When we enter something, enter. The robot replied “…” “And reply to our questions later

You can then see the console output

When the chat content increases. The div will automatically scroll up, which will trigger our lifecycle function

The first group is triggered when the component is mounted

In the second group, we send a message and the robot responds… The trigger

The third group was triggered when the robot responded to our questions

tick

The TICK function differs from other lifecycle functions in that you can call it at any time, not just when the component is first initialized.

It returns a Promise that will be resolved once any pending state changes are applied to the DOM (or immediately, if there are no pending state changes)

When you update component state in Svelte, it does not update the DOM immediately.

Instead, it waits until the next microtask to see if there are any other changes that need to be applied, including changes in other components.

Doing so avoids unnecessary work and allows the browser to batch more efficiently.

Rounding js task of macro and micro tasks (event loop) www.jianshu.com/p/53e8597df…

Js, macro and micro task www.cnblogs.com/wangziye/p/…

Create a new tick. svelte

<script>

	let text = `Select some text and hit the tab key to toggle uppercase`;

	async function handleKeydown(event) {
		if(event.key ! = ='Tab') return;

		event.preventDefault();

		const { selectionStart, selectionEnd, value } = this;
		const selection = value.slice(selectionStart, selectionEnd);

		const replacement = /[a-z]/.test(selection)? selection.toUpperCase():selection.toLowerCase(); text = (value.slice(0, selectionStart) +replacement +value.slice(selectionEnd));

		this.selectionStart = selectionStart;
		this.selectionEnd = selectionEnd;
	}
</script>

<style>
    textarea {
        width: 100%;
        height: 200px;
    }
</style>

<textarea value={text} on:keydown={handleKeydown}></textarea>
Copy the code

Above we select a piece of text and press Tab

You can see that the text is cleared and the mouse cursor automatically jumps to the last part of the text

However, this is not what we want, we want the Tab key to be pressed without the selected text being cleared, the text being capitalized, and the cursor position not changing

We can solve this problem by importing the tick…

The tick function is called before selectionStart and selectionEnd are assigned

import { tick } from 'svelte';


await tick();
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
Copy the code

Then we can select and press Tab, and we can see what we want

There’s something special about the code above

const { selectionStart, selectionEnd, value } = this;
Copy the code

Notice the this here, not the this as we understand it, buttextareaThe DOM element itself