- 2021-4-26 初探
从去年开始,就开始听说 svelte
,算得上三大框架之外的黑马,最大的亮点是没有运行时依赖,打包完全是原生js操作,没有虚拟dom,返璞归真,原汁原味普通js。
今天没啥事,来学一学。截至2021-4-26 版本 v3.37.0
问 | 答 |
---|---|
叫什么 | Svelte [svelt] 英语苗条的、修长的。 |
解决什么问题 | js框架,三大框架之外的选项 |
有什么亮点 | No Runtime,没有类库,没有虚拟dom,体积会小 |
有什么劣势 | 周边还不行 |
使用感受&推荐语 | 新东西尝鲜 |
官方说明
官方github
https://github.com/sveltejs/svelte
gitee国内源
https://gitee.com/mirrors/svelte
简单一看源码 src
下有两个文件夹:
compiler
显然是负责编译的,源码都是tscompile
parse
preprocess
utils
runtime
运行时animate
easing
internal
motion
store
transition
等
只看名称倒是还行,看得懂。
其他主要看文档。有个中文的 https://www.sveltejs.cn/tutorial/basics
基础使用
先不管三七二十一,快给我命令行,让我跑起来!
# 创建项目
npx degit sveltejs/template demo
# 可选 把项目转成ts
node scripts/setupTypescript.js
# 安装依赖并运行
yarn&&yarn dev
hey,还真跑起来了。基于 roolup 技术栈。
看一下 package.json
- roolup 相关依赖
- svelte 主库和相关check
- ts相关
依赖挺简单的。
看一下 Playground 的一个demo
这是 app.svelte
的一个实例,提供变量供渲染:
<script>
let src = 'tutorial/image.gif';
let name = 'Rick Astley';
</script>
<img {src} alt="{name} dances.">
这段代码会被编译成这样:
/* App.svelte generated by Svelte v3.37.0 */
import {
SvelteComponent,
attr,
detach,
element,
init,
insert,
noop,
safe_not_equal
} from "svelte/internal";
function create_fragment(ctx) {
let img;
let img_src_value;
let img_alt_value;
return {
c() {
img = element("img");
if (img.src !== (img_src_value = src)) attr(img, "src", img_src_value);
attr(img, "alt", img_alt_value = "" + (name + " dances."));
},
m(target, anchor) {
insert(target, img, anchor);
},
p: noop,
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(img);
}
};
}
let src = "tutorial/image.gif";
let name = "Rick Astley";
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
export default App;
- 引入了相关内置属性和方法
- 填充
create_fragment
方法 - 补充data
- 书写 class
语法介绍
下面介绍一些语法,拿 vue
语法进行比较。
v-html
使用{@html string}
,p.innerHTML=ctx[0]
@click
使用on:click={fn}
来书写,其他的事件on:
开头computed
可以使用$:
标签语句,叫响应式声明。
let count = 0;
$:doubled = count * 2;
// 逻辑
$: console.log(`the count is ${count}`);
// 一组逻辑
$: {
console.log(`the count is ${count}`);
alert(`I SAID THE COUNT IS ${count}`);
}
这不就是 label标签语法么,(label 标签语句 - MDN)前段时间 vue 想用 ref: a
时候被骂惨了不是:因为 Vue Ref 提案,我又刷了遍 label 语法
转眼就在这用上了?hhh
赋值能触发,但是数组操作不会更新。还是要赋值,两个办法,操作完重新赋值,或者不用 push操作直接用解构
function addNumber() {
numbers.push(numbers.length + 1);
numbers = numbers;
}
function addNumber() {
numbers = [...numbers, numbers.length + 1];
}
有点意思。
原理先不看了。
语法继续:
- 父组件传递 props 子组件如何接收?
export let answer
,这里的 export 表示 prop 奇怪吧,语义改变了。 - 如何实现 prop.defalut 呢?子组件
export let answer=1
,这就是默认值了。 - 父组件传递多个值可以使用
<Info {...obj} />
的方式传递,子组件要不单独拆——写多个 export 接收,要不就$$props
直接获取,官方说这样难以优化。细节先忽略 v-if
这里使用模板语法来实现,就普通模板语法,写起来感觉不是很方便,不如v-if
好使{#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}
v-for
这里也是模板的 each 实现{#each items as item, index}
{#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}
如何实现索引
key
?{#each items as item (item.id)}
最后追加一个 括号表示key- 如何通过异步请求更新页面,比如列表页?这里还是模板语法
仔细看案例:
<script>
let promise = getRandomNumber();
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);
}
}
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}
有点意思。页面一加载就执行了 promise
方法。pending展示第一部分,得到结果,赋值为 number 进行展示。其中第一个和第三个可以忽略。
{#await promise then value}
<p>the value is {value}</p>
{/await}
- 事件使用
on:
开头,也有一些修饰符,和vue不同,这里用的是|
,多个也使用它来分割- preventDefault
- stopPropagation
- passive
- capture
- once
- self
<button on:click|once={handleClick}>
Click me
</button>
子组件事件传递
emit
,这里svelte
内置了,但看起来好麻烦。附件on:message
就可以了import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
事件传递多层嵌套如何解决?首先可以在中间组件中使用
on:message
简写来实现转发message
事件。这块说起来绕 https://www.sveltejs.cn/tutorial/event-forwarding- input里的 v-model 如何实现。
<input bind:value={name}>
其他的bind:checked={yes}
:ref
处理dom<canvas bind:this={canvas}/>
页面先let canvas
,当组件挂载了会自动改写canvas
变量- 生命周期
onMount
和onDestroy
,这一点和 vue倒是差不多 - 生命周期
beforeUpdate
和afterUpdate
函数 - vue里的
nextTick
用的是tick
状态管理store,也就是vue里的vuex或者独立的eventBus 内置了
svelte/store/writable
import { writable } from 'svelte/store';
export const count = writable(0);
此时,所有的组件都可以引入 count
count.set(0)
直接赋值,重置count.update(n=>n+1)
通过高阶函数修改- 通过
count.subscribe(value=>{x=value})
订阅变化,其他地方随便改,主动订阅来响应变化,并返回一个销毁函数 - 需要主动销毁
这样很麻烦,忘了咋办。骚操作来了 <p>{$count}</p>
通过前缀表示自动订阅和销毁。
刚才看到 writable
,自然也有 readable
只读。 reable(value,()=>{})
,有点绕,https://www.sveltejs.cn/tutorial/readable-stores
还有 store 里的 derived
,表示派生,似乎是 vue store里的 getter
vue里的transtion 也有映射 tweened
, 内置 svelte/motion/tweened
先看到这。。https://www.sveltejs.cn/tutorial/tweened
后面我看了,都是进阶,慢慢看吧。
原理浅析
虚拟DOM是否高效?如何实现最小差异更新?
在字节发的文章中看到,区分 jsx和 template阵营。这里用到了 位掩码
bitMask 技术追踪脏值。
这个有点高级了。
位掩码是把多个布尔值存储在一个整数中的技术。比如 0000 1000
这一串数字,这可以表示 1 所在的位置是脏数据,0 表示没有变化。
这意味着 0000 1101
,从右向左,第一位、第三位、第四位发生了变化。
这种表示方式最无脑,也最有效省事。但因为js数字长度限制,svelte使用数组来存放,二进制位数超了就增加长度。这个数组叫 component.$.dirty
有了这个数组我们就知道哪些数据发生了变化(理论上、概念上)。
不同于 vue react 的 VDOM diff,svelte记录了数据和dom节点的对应关系。
-1 参考链接
社区优秀文章