一,简介
这东西是啥,这是来自官网的回答(官网地址:svelte.dev )。简单来说,这个框架是个没有运行时的框架,依赖于打包工具,不像Vue一样,就算没有了打包工具也可以用。
It is similar to JavaScript frameworks such as React and Vue, Svelte converts your app into ideal JavaScript at build time, rather than interpreting your application code at run time.
也是个依赖组件化思想的框架,也有单文件组件。
A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a .svelte file.
二,基本语法
1,基础搭建
-
可以参考这个文档: svelte.dev/blog/settin…
-
也可以使用
vitecn.vitejs.dev/guide/
2,单文件组件格式
每个组件都是由.svelte结尾的文件来组成,大概格式如下。下面也简单展示了数组展示的语法,非常简单。
<!-- HTML区域 -->
<h1 class="title">{ name }</h1>
<script>
// JS区域
let name = 'Hello World';
</script>
<style>
/* CSS区域 */
.title {
color: #ff3e00;
}
</style>
注意,这几个标签的上下顺序是可以换的。
3,动态标签属性
上面已经展示了数据展示的方法,是使用一个{}来获取JS里的数据,而这个语法,可以用在标签属性上面,如下:
<script>
let src = 'tutorial/image.gif';
</script>
<img src={src} alt="A man dances.">
需要注意的是,我们写
attribute变量的时候,其实可以<img src="{src}">
如上所示,属性和属性值变量都是同个字src;所以以后遇到这样写的话,其实有个简化语法如下:
<img {src} alt="A man dances.">
class属性的简写方式
很多时候我们需要根据判断条件来添加class属性,如下
<button
class="{current === 'foo' ? 'selected' : ''}"
on:click="{() => current = 'foo'}"
>foo</button>
这样我们可以简写为如下的方式
<button
class:selected="{current === 'foo'}"
on:click="{() => current = 'foo'}"
>foo</button>
如果变量名和class名一样的话,可以继续简写,如下
<script>
let current = 'foo';
let selected = current === 'foo';
</script>
<button
class:selected="{selected}"
on:click="{() => current = 'foo'}"
>foo</button>
简写为
<script>
let current = 'foo';
let selected = current === 'foo';
</script>
<button
class:selected
on:click="{() => current = 'foo'}"
>foo</button>
4,嵌套组件
在单文件组件里面引入其他单文件组件,如下:
App.svelte文件如下:
<p>This is a paragraph.</p>
<Nested/>
<script>
import Nested from './Nested.svelte';
</script>
<style>
p {
color: purple;
}
</style>
Nested.svelte文件如下:
<p>This is another paragraph.</p>
值得一提的是,在App.svelte里面定义的样式,并不会影响到Nested.svelte,这是应为每个组件的样式都是scoped的。
5,渲染HTML
使用{@html ...}来代替{...}可以直接渲染HTML标签,如下:
<script>
let string = `this string contains some <strong>HTML!!!</strong>`;
</script>
<p>{@html string}</p>
会有引起XSS攻击的风险
6,事件响应
<script>
let count = 0;
function handleClick() {
// event handler code goes here
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
7,计算属性
就是一个依赖于其他变量的值,如姓名依赖姓和名;如下:
<script>
let count = 0;
$: doubled = count * 2; // 这是一个计算属性,依赖于count
function handleClick() {
count += 1;
}
</script>
<p>{count} doubled is {doubled}</p>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
8,变量依赖语句
来自官网的说法是如下:
We're not limited to declaring reactive values — we can also run arbitrary statements reactively.
也就是说除了计算属性,其实任意的句式都是可以响应式地依赖于变量,如下:
<script>
let count = 0;
function handleClick() {
count += 1;
}
// $后跟表达式
$: console.log(`the count is ${count}`);
// $后跟代码块
$: {
console.log(`the count is ${count}`);
alert(`I SAID THE COUNT IS ${count}`);
}
// $后跟if等语句
$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
9,数组、对象的响应式更新
Because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.
所以使用push、splice等数组元素操作的方法是无法触发视图更新的
<script>
let arr = [1, 2, 3, 4];
function changeArr() {
arr.push(arr.length + 1) // 无法触发更新
arr[arr.length] = arr.length + 1 // 会触发更新(但Vue里面这样赋值是不会更新的)
arr = [...arr, arr.length + 1] // 会触发更新
}
</script>
<p>{arr}</p>
<button on:click={changeArr}>
change array
</button>
10,组件Props
组件传值,如下:
App.svelte如下:
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42}/>
Nested.svelte如下:
<script>
export let answer; // 无默认值写法
// or
export let answer = 'a mystery'; // 默认值写法
</script>
<p>The answer is {answer}</p>
如果需要传多个属性,可以直接传一个解构对象,如下:
App.svelte如下:
<script>
import Info from './Info.svelte';
const pkg = {
name: 'svelte',
version: 3,
speed: 'blazing',
website: 'https://svelte.dev'
};
</script>
<Info {...pkg}/>
Info.svelte如下:
<script>
export let name;
export let version;
export let speed;
export let website;
</script>
<p>
The <code>{name}</code> package is {speed} fast.
Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a>
and <a href={website}>learn more here</a>
</p>
11,条件渲染
<script>
let x = 7;
</script>
{#if x > 10}
<p>{x} is greater than 10</p>
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
12,列表渲染
<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
</script>
<h1>The Famous Cats of YouTube</h1>
{#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}
因为这个是个对象数组,也可以对cat进行解构,如下:
...
{#each cats as { id, name }, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{i + 1}: {name}
</a></li>
{/each}
...
如果你需要对列表数组进行更新,那么默认地,它的更新是类似于下面所说的:
By default, when you modify the value of an each block, it will add and remove items at the end of the block, and update any values that have changed. That might not be what you want.
所以需要为每一个item添加一个key,以让它知道哪个item是哪个item,当更新某个item时就去找那个item,而不是从新开始遍历渲染。所以添加key的语法也很简单,如下:
{#each items as item (item.id)}
<Item />
{/each}
13,Await Blocks
<script>
async function getRandomNumber() {
const res = await fetch(`tutorial/random-number`);
const text = await res.text();
if (res.ok) {
return text;
} else {
throw new Error(text);
}
}
let promise = getRandomNumber();
function handleClick() {
promise = getRandomNumber();
}
</script>
<button on:click={handleClick}>
generate random number
</button>
{#await promise}
<p>...waiting</p>
{:then number}
<p>The number is {number}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
14,事件
行内事件
事件在上面的例子可以看到是通过使用on:来监听事件的;事件也可以直接定义成行内的形式,如下:
<script>
let m = { x: 0, y: 0 };
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>
事件修饰符
类似于Vue里面的.once、.prevent、.stop等这些修饰符一样,是起到修改事件触发行为的作用,在Svelte里面,修饰符列表如下:
preventDefault— calls event.preventDefault() before running the handler. Useful for client-side form handling, for example.stopPropagation— calls event.stopPropagation(), preventing the event reaching the next elementpassive— improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)nonpassive— explicitly set passive: falsecapture— fires the handler during the capture phase instead of the bubbling phase ()once— remove the handler after the first time it runsself— only trigger handler if event.target is the element itself
使用时如下:
<script>
function handleClick() {
alert('no more alerts')
}
</script>
<button on:click|once={handleClick}>
Click me
</button>
组件事件
其实就是类似于Vue里面的$emit操作,在子组件里面触发事件,由父组件监听并给予处理器;如下:
App.svelte如下:
<script>
import Inner from './Inner.svelte';
function handleMessage(event) {
// 参数在event.detail里面获取
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>
Inner.svelte如下:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
// 子组件通过dispatch触发事件
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
15,监听深层组件事件
当一个组件嵌套了一个组件,那个组件又嵌套了一个组件时,这时最外层组件想监听到最里层组件事件时,需要把内部组件事件由里往外一步步向外dispatch,类似于冒泡,但这很麻烦,所以Svelte提供了一个简写方式,就是位于中间的组件可以省去一些写法,如下:
App.svelte如下:
<script>
import Outer from './Outer.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<!-- 这个事件来自Inner -->
<Outer on:message={handleMessage}/>
Outer.svelte如下:
<script>
import Inner from './Inner.svelte';
</script>
<!-- 本来这里也需要引入createEventDispatcher,后再dispatch给App -->
<Inner on:message/>
Inner.svelte如下:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
16,监听子组件的DOM事件
App.svelte如下:
<script>
import CustomButton from './CustomButton.svelte';
function handleClick() {
alert('clicked');
}
</script>
<CustomButton on:click={handleClick}/>
CustomButton .svelte如下:
<style>
button {
height: 4rem;
width: 8rem;
background-color: #aaa;
border-color: #f1c40f;
color: #f1c40f;
font-size: 1.25rem;
background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
background-position: 100%;
background-size: 400%;
transition: background 300ms ease-in-out;
}
button:hover {
background-position: 0;
color: #aaa;
}
</style>
<!-- 把原生DOM事件直接Dispatch -->
<button on:click>
Click me
</button>
17,双向绑定
类似于Vue里面的v-model,如下:
<script>
let name = 'world';
</script>
<input bind:value={name}>
<h1>Hello {name}!</h1>
也可以用在CheckBox上面,如下:
<script>
let yes = false;
</script>
<label>
<input type=checkbox bind:checked={yes}>
Yes! Send me regular email spam
</label>
{#if yes}
<p>Thank you. We will bombard your inbox and sell your personal details.</p>
{:else}
<p>You must opt in to continue. If you're not paying, you're the product.</p>
{/if}
<button disabled={!yes}>
Subscribe
</button>
用在单选框是这样写:
<script>
let scoops = 1;
</script>
<h2>Size</h2>
<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>
用在多选框是这样写的:
<script>
let flavours = ['Mint choc chip'];
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
<h2>Flavours</h2>
{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}
单选框和多选框都需要通过bind:group来确定是通过一组的选项,bind:group相同则同一组;下面看看怎么在select里面使用:
<script>
let questions = [
{ id: 1, text: `Where did you go to school?` },
{ id: 2, text: `What is your mother's name?` },
{ id: 3, text: `What is another personal fact that an attacker could easily find with Google?` }
];
let selected;
</script>
<select bind:value={selected}>
{#each questions as question}
<option value={question}>
{question.text}
</option>
{/each}
</select>
这个option的value是个对象,当select选择哪个option时,select的value就是哪个option的value。如果这个select是多选的情况下,那么可以代替checkbox,如下:
<script>
let flavours = ['Mint choc chip'];
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
<h2>Flavours</h2>
<select multiple bind:value={flavours}>
{#each menu as flavour}
<option value={flavour}>
{flavour}
</option>
{/each}
</select>
18,绑定元素大小
也就是元素的大小改变时,这个宽高可以获取并绑定到某个值上面,如下:
<script>
let w;
let h;
let size = 42;
let text = 'edit me';
</script>
<style>
input { display: block; }
div { display: inline-block; }
span { word-break: break-all; }
</style>
<input type=range bind:value={size}>
<input bind:value={text}>
<p>size: {w}px x {h}px</p>
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
19,引用
类似于DOM操作,获取页面元素节点对象,在Svelte可以这样:
<script>
import { onMount } from 'svelte';
let canvas;
console.log(canvas) // undefined
// 必须在组件挂载后才生效
onMount(()=>{
console.log(canvas.width) // 32
})
</script>
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
20,修改Props
在Vue里面子组件是不能直接修改props的,必须通过$emit通知父组件更改,而在Svelte里面有个语法可以实现在子组件直接修改props而父组件的值也跟着改变,也就是说两个值绑定到一起了:
App.svelte如下:
<script>
import Inner from './Inner.svelte';
let value = 0
</script>
<div>
{ value }
</div>
<Inner value={value} />
<!--如果不加bind,那么子组件修改的只是内部的value变量,父组件的value变量是不会被修改的-->
<Inner bind:value={value} />
<!--加了bind,那么子组件修改了value变量,父组件的value变量也会跟着被修改-->
Inner.svelte如下:
<script>
export let value;
function handler() {
value += 1
}
</script>
<button on:click={ handler }>
Add ({ value })
</button>
21,生命周期回调
Every component has a lifecycle that starts when it is created, and ends when it is destroyed.
onMount
The one you'll use most frequently is onMount, which runs after the component is first rendered to the DOM.
<script>
import { onMount } from 'svelte';
onMount(() => {
// ...
});
</script>
onDestroy
beforeUpdate、afterUpdate
tick
这个类似于Vue里面的vm.$nextTick(()=>{}),会等待DOM更新后再执行回调,是这样使用的,如下:
<script>
import { tick } from 'svelte';
await tick();
// DOM更新后做的事情写在这里
</script>
22,Store全局状态管理
类似于Vuex,但是这个是Svelte自带的功能,比如我们需要一个count的变量,它会被所有的组件访问,可以这么做,如下:
stores.js如下:
import { writable } from 'svelte/store';
export const count = writable(0);
其他组件想修改时直接引入后调用回调修改,如下:
<script>
import { count } from './stores.js';
function increment() {
count.update(n => n + 1);
}
function decrement() {
count.update(n => n - 1)
}
function reset() {
count.set(0);
}
</script>
但是在组件里面想使用这个值,就必须先订阅,如下:
<script>
import { count } from './stores.js';
let count_value;
count.subscribe(value => {
count_value = value;
})
</script>
<div>{ count_value }</div>
但是这样子还有个问题,如果组件不断地创建摧毁,组件会不断地订阅store的变量,会引起内存泄漏,官方解析如下:
If the component was instantiated and destroyed many times, this would result in a memory leak.
所以在每次组件订阅一个store变量后,需要在组件摧毁时调用取消订阅函数以保证内存空间得到释放;但是问题是取消订阅函数在哪呢,请看下面例子就知道了:
<script>
import { onDestroy } from 'svelte';
import { count } from './stores.js';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
})
onDestroy(unsubscribe)
</script>
<div>{ count_value }</div>
自动订阅
但是这样又会引起另外一个问题,就是如果订阅的store变量特别多的话,这样写是特别地麻烦的,需要为每一个订阅的变量调用订阅和取消订阅的函数,所以最最最简单的方式订阅变量是像下面这样子:
<script>
import { count } from './stores.js';
</script>
<div>{ $count }</div>
对,就是直接在变量前面加上$前缀就行,十分方便,也不需要去担心内存泄漏问题。当我们需要修改时,可以这样:
<script>
import { count } from './stores.js';
</script>
<div>{ $count }</div>
<button on:click="{_ => $count += 1}">Add</button>
这里的$count += 1就相当于count.update(n => n + 1)或者count.set($count + 1)。
只读Store
在上面的例子里面的那些store都是可写的,可修改的,但是有时候我们需要的只是只读的数据,所以可以通过下面的方法来创建只读变量:
stores.js如下:
import { readable } from 'svelte/store';
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
The first argument to readable is an initial value, which can be null or undefined if you don't have one yet. The second argument is a start function that takes a set callback and returns a stop function. The start function is called when the store gets its first subscriber; stop is called when the last subscriber unsubscribes.
可以看到只读的意思是我们自己不能手动从“外面”去修改值,而不是不能修改值的意思;从这个readable函数的第二个参数可以知道这一点。
衍生store
这个叫做derived,意思是基于其他store而生成的store,其实就是Vuex里的getters的概念,用法如下:
import { readable, derived } from 'svelte/store';
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
const start = new Date();
export const elapsed = derived(
time,
t => Math.round((t - start) / 1000)
);
// 这个elapsed就是一个getter
derived函数第一个参数是其他store,第二个是个回调,参数为其他store的值,返回这个getter的值。
23,ContextAPI
这个类似于Store,都是用于全局的变量共享,主要由setContext和getContext组成,用法如下示例:
// A.svelte
<script>
import { setContext } from 'svelte';
let globalVal = '这是个共享变量';
let key = '一个钥匙'
setContext(key, {
globalVal
})
</script>
<div>
<slot></slot>
</div>
// B.svelte
<script>
import { getContext } from 'svelte';
let key = '一个钥匙';
let { globalVal } = getContext(key);
</script>
<div>
<h1>This is the B</h1>
<div>
{ globalVal }
</div>
</div>
// App.svelte
<script>
import A from './lib/A.svelte'
import B from './lib/B.svelte'
</script>
<A>
<B></B>
</A>
可见B组件可以通过key来使用到A组件里的变量,但是与Store不同的是,这些变量的共享不是整个App,而是某个组件,如官网的原话所说
If a component calls
setContext(key, context), then any child component can retrieve the context withconst context = getContext(key).
这里的any child component是指子组件以及包括像上面例子那样的通过slot的形式,所以兄弟组件之间(除了slot关系)无法使用ContextAPI
24,组件代码共享(Sharing Code)
共享代码,如果某个组件需要一些相同的变量,那么我们需要共享代码,官方的解释是:
Very occasionally, you'll need to run some code outside of an individual component instance.
下面是个例子:
// Foo.svelte
<script context="module">
let counter = 0;
</script>
<script>
export let name;
function change() {
counter++;
console.log(counter + ' from ' + name)
}
</script>
<h3>{ name }</h3>
<button on:click={change}>Click Me</button>
// App.svelte
<script>
import Foo from './Foo.svelte'
</script>
<Foo name='A'></Foo>
<Foo name='B'></Foo>
如下为运行结果,可以看到在两个Foo组件实例内,counter 变量为共享的内部变量,不管是点击A还是B,打印出来的数字都一直是往上升的,而不是依照各自的起点开始算
但是需要注意的一点是,这个共享的数据是没有响应式的,也就是说当你把这个渲染到界面上,然后你改变了这个数据的值,这时界面是不会有数据改变的
如果我们在<script context="module">的模块里面导出东西(如函数),那么这个东西就可以被其他组件引用,上面例子可以改成如下:
// Foo.svelte
<script context="module">
let counter = 0;
// 导出一个函数用于重新计算改值
export function clearCunter() {
counter = 0
}
</script>
// ...
// App.svelte
<script>
import Foo, { clearCunter } from './lib/Foo.svelte'
</script>
<button on:click={ clearCunter }>clearCunter</button>
<Foo name='A'></Foo>
<Foo name='B'></Foo>
25,渐变
通过从svelte/transition引入渐变函数,然后再在需要条件显示的元素外层加上transition:xxx属性,就可以实现渐变动画显示隐藏了,十分简单,如下:
<script>
import { fade } from 'svelte/transition';
let visible = true;
</script>
<label>
<input type="checkbox" bind:checked={visible}>
visible
</label>
{#if visible}
<p transition:fade>
Fades in and out
</p>
{/if}
位移渐变,如下元素会在向y轴方向200px的地方渐变消失,并且会有如下情况:
Note that the transition is reversible — if you toggle the checkbox while the transition is ongoing, it transitions from the current point, rather than the beginning or the end.
<script>
import { fly } from 'svelte/transition';
let visible = true;
</script>
<label>
<input type="checkbox" bind:checked={visible}>
visible
</label>
{#if visible}
<p transition:fly="{ { y:200, duration:2000 } }">
Fades in and out
</p>
{/if}
in & out
其实如果使用transition指示器会自动创建反向动画,如上面那个例子,transition:fly="{ { y:200, duration:2000 } }"会在元素消失的时候应用你写的动画,而在显示的时候应用与你写的相反的动画;其实,我们可以自己来决定显示和隐藏分别使用什么动画,如下例子:
<script>
import { fly, fade } from 'svelte/transition';
let visible = true;
</script>
<label>
<input type="checkbox" bind:checked={visible}>
visible
</label>
{#if visible}
<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>
Flies in and out
</p>
{/if}
在元素显示的时候应用in的动画,在元素消失的时候应用out的动画。
渐变事件
也就是说在元素开始显示、元素开始显示完成、元素开始隐藏、元素隐藏结束这四个节点都会有相应的触发事件,使用如下:
....
{#if visible}
<p
transition:fly="{{ y: 200, duration: 2000 }}"
on:introstart="{() => status = 'intro started'}"
on:outrostart="{() => status = 'outro started'}"
on:introend="{() => status = 'intro ended'}"
on:outroend="{() => status = 'outro ended'}"
>
Flies in and out
</p>
{/if}
....
local标识
如下例子,如果如果我们对items增加一个元素或者减少一个元素是会触发个体的渐变动画的,而问题是当我们改变isShow为false,整个列表也会触发渐变动画.
<script>
import { slide } from 'svelte/transition';
let isShow = true;
let items = [1,2,3,4];
</script>
{#if isShow}
{#each items as item}
<div transition:slide>
{item}
</div>
{/each}
{/if}
而要解决这个问题,也就是让渐变动画只发生在个体变化时上,而不是整个列表的显示隐藏都会触发动画,我们需要为渐变属性加上local的标识,像这样transition:xxx|local,如下例子:
...
{#if isShow}
{#each items as item}
<div transition:slide|local>
{item}
</div>
{/each}
{/if}
...
26,组件插槽
和Vue的语法基本是一样的,如下
// Box.svelte
<div class="box">
<slot></slot>
</div>
// App.svelte
<Box>
<h1>Hello World</h1>
<div>this is a content</div>
</Box>
默认值
// Box.svelte
<div class="box">
<slot>
<em>no content was provided</em>
</slot>
</div>
具名插槽
// ContactCard.svelte
<article class="contact-card">
<h2>
<slot name="name">
<span class="missing">Unknown name</span>
</slot>
</h2>
<div class="address">
<slot name="address">
<span class="missing">Unknown address</span>
</slot>
</div>
<div class="email">
<slot name="email">
<span class="missing">Unknown email</span>
</slot>
</div>
</article>
// App.svelte
<ContactCard>
<span slot="name">
P. Sherman
</span>
<span slot="address">
42 Wallaby Way<br>
Sydney
</span>
</ContactCard>
判断传了哪些插槽
每一个svelte组件都会有一个$$slots变量,该变量是个对象用于判断该组件的哪些插槽被外界赋值,比如
// App.svelte
<Box>
<div slot="title">Hello</div>
<div>Body</div>
</Box>
// Box.svelte
<div>
<div>{ JSON.stringify($$slots) }</div>
<slot name="title">undefined</slot>
<slot></slot>
</div>
这时可以知道$$slots为{ 'title': true, 'default': true }
插槽传值(slot props)
可以把带有插槽子组件的变量值通过插槽传递给父组件,如下:
// App.svelte
<script>
import Hoverable from './Hoverable.svelte';
</script>
<Hoverable let:hovering={hovering}>
<div class:active={hovering}>
{#if hovering}
<p>I am being hovered upon.</p>
{:else}
<p>Hover over me!</p>
{/if}
</div>
</Hoverable>
<style>
div {
padding: 1em;
margin: 0 0 1em 0;
background-color: #eee;
}
.active {
background-color: #ff3e00;
color: white;
}
</style>
// Hoverable.svelte
<script>
let hovering;
function enter() {
hovering = true;
}
function leave() {
hovering = false;
}
</script>
<div on:mouseenter={enter} on:mouseleave={leave}>
<slot hovering={hovering}></slot>
</div>
27,内置组件
内置组件就是一些特殊的组件,不需要用户创建,由svelte内部提供,有如下几个
1,递归组件
这个组件 <svelte:self />代表的含义就是引用这个组件的组件本身,也就是说在某个组件A内引用这个组件时,这个组件代表的就是组件A,这种情况其实是有的,一般用于需要递归的数据,如在做评论列表、文件结构树等等;这些树型结构的数据一般有这个特点,就是数据根上面包含了几个数据项,这些数据项的内部属性基本一样,这些数据项本身可能还会做为更深层次数据的根。
接下来看一个评论组件的例子:
// comment.svelte
<script>
export let authorName; // 评论者名称(假设此值唯一)
export let content; // 评论内容
export let date; // 评论时间
export let target = ''; // 评论对象名称
export let commentsList = []; // 回复消息列表
</script>
<div>
<div>
<div>评论者名称:{ authorName }</div>
{#if !!target}
<div>回复:{ target }</div>
{/if}
<div>评论时间:{ date }</div>
</div>
<div>{ content }</div>
{#if commentsList.length > 0 }
<div class="commentsList">
{#each commentsList as comment}
<svelte:self {...comment}/>
{/each}
</div>
{/if}
</div>
<style>
.commentsList {
padding-left: 20px;
}
</style>
2,动态组件
组件<svelte:component this={'组件名称'}/>就是动态组件,当我们在某个组件内部已经引入了其他组件的前提下,通过对<svelte:component />的this属性传递组件名称,这个组件便会变成这个名称代表的那个组件
3,编译器选项组件
The
<svelte:options>element allows you to specify compiler options.
就像上述所说的一样,这个组件可以改变编译器选项,具体的属性有如下几个:
immutable={true}— you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changedimmutable={false}— the default. Svelte will be more conservative about whether or not mutable objects have changedaccessors={true}— adds getters and setters for the component's propsaccessors={false}— the defaultnamespace="..."— the namespace where this component will be used, most commonly "svg"tag="..."— the name to use when compiling this component as a custom element
简单解释一下:
- immutable 当此值为ture的时候,svelte的编译器将会对传进来的prop与之前的prop进行值对比,如果两个值一样,那么组件将不会触发更新;相反,也就是值为false(默认值)时,只要传入的prop值有改动,不管值是否一致,都会触发更新