Kalan's Blog

Software Engineer / Taiwanese / Life in Fukuoka

Current Theme light

Introduction

Svelte is one of the front-end frameworks that I really like. Its simple syntax, flexibility, the author's philosophy, and various animation and transition features make me enjoy using it. I have written several articles expressing my thoughts on Svelte:

Svelte Summit took place on October 18, 2020. Due to the pandemic, it was held as a fully online event, with a total of 7 hours and 17 talks. Here, I will share some interesting talks and my own thoughts.

The Zen of Svelte

This talk discusses the various "philosophies" within Svelte, which are similar to the philosophies in Python.

Svelte starts with its syntax, aiming to minimize the learning curve so that even non-front-end developers or beginners can easily pick it up. By reading the official documentation, you can get a glimpse of it. Besides reading the tutorial, you can also use the REPL, where you can directly manipulate the code to see the actual effects.

Svelte Official Tutorial

In his talk Rethink Reactivity, author Rich Harris mentioned:

Rich Harris - Rethinking reactivity

Frameworks are not tools for organizing your code, frameworks are tools for organizing your mind. — Rich Harris

For example, the emergence of React revolutionized concepts like components, immutability, reactivity, and state in the front-end, leading to a wave of subsequent frameworks that fundamentally changed front-end development practices. More than the "syntax" of a framework, what is more important are the concepts and design philosophies that these frameworks convey.

The author also mentioned that tools like jQuery allowed people without a computer science or programming background, like himself, to become part of web development. From this, we can get a glimpse of the author's vision for Svelte.

Note: I discussed more in this article, you can refer to it if you're interested.

harris-jquery

If you understand HTML, CSS, and JavaScript, it means you can write Svelte, and vice versa. Unlike traditional template engines, Svelte compiles differently. Template engines often work with the backend, where variables are passed to the template engine and transformed into HTML code. In Svelte, all the code becomes corresponding JavaScript (when not in SSR mode), and can be executed dynamically.

I like this philosophy. Writing code doesn't necessarily mean becoming an engineer, just like cooking doesn't have to be done by a chef. Sometimes it's just about solving everyday problems with hands-on experience.

From another perspective, you may wonder if it's a good idea to skip learning JavaScript fundamentals and directly learn Svelte. My thought is: "It depends on the goal."

If you plan to become a qualified front-end engineer, at some point, you will inevitably need to understand the underlying principles. However, if you just want to build a web page to solve your own problem, it's okay not to have a deep understanding of the fundamentals.

This talk also mentioned the article The Zen of Just Writing CSS. In Svelte, you can directly write styles within components, achieving the integration of HTML, CSS, and JavaScript. Svelte automatically generates a hash to avoid naming conflicts. In React, we often use CSS-in-JS solutions to avoid such issues. But is there a better way to handle styling?

For example, in styled-components, we can write like this:

const Component = styled.div`
  padding: ${props => props.padding}px;
`

// With theme
const ThemeComponent = styled.div`
  background-color: ${props => props.theme.mainColor};
`;

This is a very useful and interesting approach. We can define a theme in the context and dynamically use it when declaring components. However, due to dynamic declaration, these styles need to be executed at runtime because we don't know what props will be passed in. This means that styles cannot be completely statically generated.

But in Svelte, we can directly render a separate stylesheet using SSR:

result: {
	js,
	css,
	ast,
	warnings,
	vars,
	stats
} = svelte.compile(source: string, options?: {...})

The styles declared in the component will be added to css, allowing separation of styles and code. Honestly, whether this is good or bad may be subjective. For example, constants like shared colors and fonts may not be as convenient to use compared to solutions like styled-components.

Prototyping with Svelte

Svelte's built-in directives such as transition and animation are very useful. Even designers can quickly create prototypes to validate their ideas. You can also quickly see the results in the REPL. Recommended for designers who want to challenge engineers who claim something cannot be done.

How does Svelte's crossfade function work

crossfade is a very useful transition. What is crossfade? It is the movement of an element between two areas.

In this talk, the speaker explains how crossfade is implemented in Svelte and demonstrates its usage in their own product.

Crossfade uses the concept of key in Svelte. Each time it is executed, it looks for the corresponding key node, calculates the position between the two nodes, and applies a transition animation. Since it can be used with the built-in transition directive in Svelte, it is relatively convenient to use. (Example taken from the official tutorial)

Svelte Animation

Svelte has a built-in directive called animate. When using it with each loop, if the elements in the list change, the animation will be triggered. It is used like this:

{#each list as item (item.id)}
  <div animate:flip>
    ...
  </div>
{/each}

In Svelte, there is a built-in animation called flip, which is a technique for animation. First, we record the initial and final positions of the elements (First, Last), then calculate the differences between them (width, height, offset), and finally perform the animation. You can refer to Tech Bridge's column article FLIP Technique Review for a detailed explanation.

Svelte simplifies this series of calculations into a ready-to-use flip, combined with the effect of crossfade like this:

All movements have a transition, making it more comfortable, right?

Unlocking The Power of Svelte action

This talk is about Svelte's action, which has a similar feeling to hooks. In Svelte, you can write like this:

<div use:keyboard={{shortcut}} class="player-container">
</div>

Here, use is the Svelte action directive. keyboard is a custom function, and its function signature looks like this:

function keyboard(node, options) {}

This function can return an object that includes update and destroy, representing the update function when the parameter changes and the function to be executed when destroyed. Since the first parameter is the node itself, it is very convenient for DOM-related operations.

The speaker shares their experience using Svelte action in the video, which is worth referring to.

Demystifying Svelte Transitions

This talk explains how Svelte Transitions are implemented and breaks down the source code step by step, which is quite fascinating! Svelte's transition mechanism is very interesting and not based on dynamic JavaScript manipulation.

When using animations with jQuery, jQuery adds inline styles to the elements based on the parameters you provide, like this:

$('.div').animate({
  ...
})

jQuery continuously updates the inline CSS properties until the animation stops. In other words, the internal implementation might look like this (pseudo code):

while (!stop) {
  updateCSS()
}

setTimeout(() => stop = true, duration);

This means that JavaScript keeps executing code during this time. When the application becomes larger, it noticeably affects performance.

So how do other frameworks typically handle this? For example, in Vue and React, you can write like this:

// Vue
<transition name="fade">
    <p v-if="show">hello</p>
</transition>

// React
<CSSTransition in={inProp} timeout={200} classNames="fade">
  <div>
		
  </div>
</CSSTransition>

In Vue, it adds corresponding class names like fade-leave-active, fade-enter-active, fade-enter, etc., while React follows a similar principle by adding class names like fade-enter, fade-active, fade-exit, fade-exit-active, etc. However, the transition styles need to be defined by yourself in CSS.

Although this avoids the performance issues caused by JavaScript calculations, sometimes relying solely on CSS for animations can be cumbersome. Is there a way to combine the dynamic calculation capabilities of JavaScript with the assistance of CSS animations? Yes, Svelte achieves this.

When you declare the following code, Svelte does several things:

<div transition:scale={{duration: 1000}}>
</div>
  1. transition represents applying the same transition to both entering and leaving the element.
  2. Svelte calculates how many frames this animation needs. Assuming the animation duration is 1000 milliseconds, the calculation is 1000 / 16, which is 62.5 frames. Here, 16 is taken to represent the time in milliseconds required for each frame at a refresh rate of 60Hz (1000 / 60).
  3. Svelte sets the start time of the animation as 0 and the end time as 1, naming it t, and applies an easing function as interpolation to t.
  4. Svelte dynamically creates a CSS keyframe and applies this animation to the element.
  5. The style is dynamically added to the current document using stylesheet.insertRule.

Let's take a look at the source code:

// src/runtime/internal/style_manager.ts
export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: number, duration: number, delay: number, ease: (t: number) => number, fn: (t: number, u: number) => string, uid: number = 0) {
	const step = 16.666 / duration;
	let keyframes = '{\\n';

	for (let p = 0; p <= 1; p += step) {
		const t = a + (b - a) * ease(p);
		keyframes += p * 100 + `%{${fn(t, 1 - t)}}\\n`;
	}

	const rule = keyframes + `100% {${fn(b, 1 - b)}}\\n}`;
	const name = `__svelte_${hash(rule)}_${uid}`;
	const doc = node.ownerDocument as ExtendedDoc;
	active_docs.add(doc);
	const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet);
	const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});

	if (!current_rules[name]) {
		current_rules[name] = true;
		stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
	}

	const animation = node.style.animation || '';
	node.style.animation = `${animation ? `${animation}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`;

	active += 1;
	return name;
}

After executing this function, it generates CSS similar to the following:

@keyframes __svelte__dynamic_hash_name {
  0% {
    transform: scale(0);
  }
  1.6% {
    transform: scale(0);  
  }
  ...
  100% {
    transform: scale(1);
  }
}

The example code above is simplified, and the actual situation may vary depending on the duration and easing function. Svelte not only helps you create transitions but also manages them for you. For example, when you stop a transition halfway, Svelte removes and stops the animation for you, and it also manages various lifecycles.

This way, it combines the performance of CSS Animation and the benefits of dynamically controlling animations using JavaScript.

If you want to learn more, you can watch the video for a clearer explanation!

Futuristic Web Development

Rich Harris himself appears to talk about the changing process of next-generation web development. However, the current ideas are not final, so let's look forward to it with anticipation.

In the past, when bundling resources, we used tools like Rollup or Webpack for bundling and relied on their dependency tracking mechanism to update. At that time, the bottleneck was that browsers generally did not understand ES6 syntax and import mechanisms. These tools were created to allow developers to better manage their code. Now, browser support is gradually becoming more complete, and more and more applications rely directly on native ES modules. For example, recent tools like VuePress and Snowpack directly use native ES modules. The biggest advantage is that we don't have to wait so long to see the results. Instead, we can directly import the code through the browser mechanism, so we don't have to wait for bundlers to compile the code. Svelte also plans to use snowpack in the future. If you're interested, you can take a look.

Afterword

I have created a series of tutorial videos about Svelte for this IT Ironman event. I explained various features of Svelte, advanced applications, common UI implementations, Svelte principles, and finally implemented a simplified version of Svelte. If you're interested, you can check them out.

I will also convert the content from the videos into articles for easier reading. Please continue to follow my blog or subscribe to the RSS feed!

Prev

站在中間的那群人 / 做出選擇的那群人

Next

Learn from Kotlin: Kotlin DSL and Annotation

If you found this article helpful, please consider buy me a drink ☕️ It'll make my ordinary day shine✨

Buy me a coffee

作者

Kalan 頭像照片,在淡水拍攝,淺藍背景

愷開 | Kalan

Hi, I'm Kai. I'm Taiwanese and moved to Japan in 2019 for work. Currently settled in Fukuoka. In addition to being familiar with frontend development, I also have experience in IoT, app development, backend, and electronics. Recently, I started playing electric guitar! Feel free to contact me via email for consultations or collaborations or music! I hope to connect with more people through this blog.