共计 5003 个字符,预计需要花费 13 分钟才能阅读完成。
聊聊 Svelte.js 技术它做了什么以及如何实现的(下)
一. 循环语句
循环渲染这个咱们必定是离不开了, Svelte.js
给咱们提供了乏味的书写形式。
<script>
const arr = ['x1', 'x2', 'x3'];
</script>
<ul>
{#each arr as item}
<li>{item}</li>
{/each}
</ul>
尽管 Svelte.js
没有虚构 dom, 然而循环的时候也须要key
, 它的写法有点非凡。
<script>
const arr = ['x1', 'x2', 'x3'];
</script>
<ul>
{#each arr as item, i (i)}
<li>{i} --- {item}</li>
{/each}
</ul>
下面的 (i)
就是 key
值, i
是序号必须应用 ,
与item
隔开。
二. 循环语句编译成什么
初始化就一个变量这个没啥说的。
图二的 ctx
是个素组, ctx[0]
是咱们定义的 arr
, ctx[1]
是以后 li 对应的值。
三. 循环语句如何解决 key
上面是设置了 key 的循环体编译后的代码。
咱们重点看下更新 dom 数据的时候 key 是怎么工作的。
其实在 Svelte.js
外面也须要 diff 算法来更新 dom, 这个点须要明确。
四. 父子组件
父组件
/src/App.svelte
<script>
import Son from './ 子组件.svelte';
const options = {msg: 'xxxxxx',};
// 如此传递属性
const footer = {
footer1: 'footer1',
footer2: 'footer2',
};
// 解决订阅事件
function handleEmit(res) {console.log('相应事件 1', res);
console.log('相应事件 2', res.detail);
}
</script>
<div>
<p> 父组件 </p>
<Son {options} {...footer} on:emit={handleEmit} />
</div>
子组件
/src/ 子组件.svelte
<script>
import {createEventDispatcher} from 'svelte';
export let options;
export let footer1;
export let footer2;
// 触发事件公布
const dispatch = createEventDispatcher();
function emit() {
dispatch('emit', {msg: '你好',});
}
</script>
<div>
<p> 子组件: {options.msg}</p>
<p>footer1: {footer1}</p>
<p>footer2: {footer2}</p>
<button on:click={emit}> 点击触发事件 </button>
</div>
- 子组件间接
import
引入子组件即可应用。 - 父组件行间传递参数, 子组件
export
接管。 - 父组件可应用扩大运算符传递参数。
- 父组件
on:emit={handleEmit}
的模式监听子组件公布的事件。 - 子组件引入
createEventDispatcher
实现事件的公布。 - 子组件能够轻易扭转传递过去的值, 不会影响该值在父组件内的值 (
重点
)。 - 父组件传递给子组件的值的扭转, 会从新渲染子组件。
五. 父子组件打包剖析
六. 款式
在 Svelte.js
外面款式是默认沙盒的, 也就是说咱们在一个 .svelte
文件外面写的款式不会渗透到全局。
很不便很爽的一点是咱们不必模板字符串的形式拼接, 这样写款式真爽。
<script>
const color = 'red';
const isBlack = true;
</script>
<div>
<p> 文字 1 </p>
<p style="color:{color}" class:black={isBlack}> 文字 2 </p>
</div>
<style>
p {
color: blueviolet;
font-size: 29px;
font-weight: 800;
}
.black {border: 1px solid black;}
</style>
class:black={isBlack}
的意思就是只有 isBlack
为true
才会赋予 black
这个 className。
如果要设置全局款式能够在 html
或者 main.js
文件外面引入。
七. 生命周期
生命周期的概念当初根本所有库都有, svelte
在这一点做的也很不错。
<script>
import {onMount, onDestroy, beforeUpdate, afterUpdate} from 'svelte';
onMount(() => {console.log('mounted');
});
onDestroy(() => {console.log('onDestroy');
});
beforeUpdate(() => {console.log('beforeUpdate');
});
afterUpdate(() => {console.log('afterUpdate');
});
</script>
onMount
将在组件首次出现到 DOM 之后运行。onDestroy
当销毁组件时调用。beforeUpdate
在 DOM 更新前运行。afterUpdate
在 DOM 更新后运行。
留神生命周期能够屡次调用如下:
onMount(() => {console.log('mounted1');
});
onMount(() => {console.log('mounted2');
});
八. 异步申请
异步申请与组件构造的交融设置, 比方咱们平时些我的项目要为不同 dom 块写 loading 成果, 这样就要有 n 个 loading 变量, 而上面的办法会使咱们少定义一些变量。
<script>
function ajax() {return new Promise((res) => {setTimeout(() => {res('申请胜利');
}, 1000);
});
}
</script>
<div>
{#await ajax()}
<p>...loading</p>
{:then res}
<p>
res: {res}
</p>
{/await}
</div>
成果为申请中显示 loading, 申请完显示内容, 前面还能够加一个 {:catch err}
标签, 然而在这里处理错误其实不太好。
打包代码的样子
在 handle_promise
办法外面, 如果判断咱们传入的是 promise 则替咱们执行 promise 并把后果赋予下来, 他还有其余简单操作咱们不必深究。
看起来挺实用的写法其实也不太实用。
九. 计算属性
你能够在 JavaScript 中用标识符标记一个语句,如下所示:$: foo = bar。它会在 foo = bar 语句中增加一个名为 $ 的标识符(如果之前未定义 foo,则严格模式下会出错)。
所以在这种状况下,当 Svelte 看到任何带有 $: 前缀的语句时,它就晓得右边的变量要从左边的变量中获取值。咱们当初有了一种办法能够将一个变量的值绑定到另一个变量。
在 js 外面间接编写是不会报错的, 长常识了原来咱们能够利用这点, 开发本人的编译器来发明新的语法规定:
计算属性的用法:
<script>
let n = 1;
$: nn = n * 2;
function addn() {n++;}
</script>
<div>
<button on:click={addn}> 点了 {nn} 次 </button>
</div>
上述的 nn
就永远等于n*2
。
打包后如何实现的
十. 观察者
<script>
let n = 0;
// 1: 大括号内的全副执行
$: {
const titel = 'n 的值为:';
console.log(titel + n);
}
// 2: 加判断条件
$: if (n > 5) {alert('n 大于 5');
}
function addn() {n++;}
</script>
<div>
<button on:click={addn}> 点了 {n} 次 </button>
</div>
- 最开始大括号的内容默认会执行一次。
- 大括号内的值如果发生变化就导致大括号里的代码整体执行一次。
$:
能够标示条件语句。
打包后的代码
不得不拜服 svelte
把标识符玩的很出彩。
十一. 动画
第一种: 自带动画(淡入淡出)
<script>
import {fade} from 'svelte/transition';
let visible = true;
function change() {visible = !visible;}
</script>
<button on:click={change}> 点击动画 </button>
{#if visible}
<div transition:fade> 第一种 </div>
{/if}
- 第一要引入动画
fade
。 - 标签定义
transition:fade
。
第二种自定义动画
<script>
import {elasticOut} from 'svelte/easing';
let visible = true;
function change() {visible = !visible;}
// 自定义
function whoosh(node, params) {const existingTransform = getComputedStyle(node).transform.replace(
'none',
''
);
return {
delay: params.delay || 0,
duration: params.duration || 400,
easing: params.easing || elasticOut,
css: (t, u) => `transform: ${existingTransform} scale(${t})`,
};
}
</script>
<button on:click={change}> 点击动画 </button>
{#if visible}
<div in:whoosh> 自定义 </div>
{/if}
in:whoosh
指定了动画应用 whoosh 函数。whoosh
返回的是动画的延迟时间、执行工夫、以及 css 成果等等。
这个就不探讨打包文件了。
十二. 输入框双向绑定
<script>
let value = '';
</script>
<div>
<input type="text" bind:value />
<p>value: {value}</p>
</div>
打包文件
做法也比拟强硬, 就是
十三. vue
外面应用 svelte
组件
svelte
的一大劣势就是跨平台, 它能够应用在任何框架内, 因为他就是原生 js 代码, 这里看下咱们如何在 vue
我的项目中应用它。
<script>
let n = 0;
function addn() {
n++;
console.log('触发了:addn');
}
</script>
<div>
<button on:click={addn}> 点了 {n} 次 </button>
</div>
/src/main.js
import App from './App.svelte';
export default App;
这里是把咱们简略写个点击 事件
的代码打包, yarn build
之后把 bundle.js
复制到名为 xxx
的文件夹中的 index.js
文件中, 放到指标工程的node_modules
, 这是为了模仿实在的应用场景。
index.js
文件咱们要解决一下, 不便导出。
改装前
改装后
具体的应用形式
<script>
import xxx from "xxx";
export default {
name: "App",
mounted() {
new xxx({target: document.body, // 轻易传入你想插入的元素与初始值});
},
};
</script>
十四. 技术选型
平时开发怎么可能两个技术混合应用, 比方我用 vue
开发曾经引入了 vue 的 runtime 就没必要在应用这门技术了, 然而如果你是用 svelte
开发了一些跨平台的兼容性特地好的组件还是能够思考应用 svelte
来做的, 这样不论是 react
还是 vue
都能够方便使用你的组件。
要留神在工程体量较小的时候 svelte
的确有劣势的, 然而逻辑简单之后就不太好用了, 咱们看了它的打包文件的写法就能得悉它在打包后必然会呈现大量的逻辑代码, 所以逻辑多到肯定水平后其实性能不肯定比 runtime 的模式好了。
end.
这次就是这样, 心愿和你一起提高。