Svelte在2019年中成为后起之秀,接下来,让我们来简单的了解一下,这个新的框架。
“ Svelte是一种构建用户界面的全新框架。像React和Vue这样的流行框架会在浏览器中完成大部分工作,而Svelte会将这些工作转变为在构建应用程序时发生的编译步骤。”
以上这段话是在他们的官方网站上所描述,但这到底是什么意思?它能够将繁重的工作从运行时转到编译的时候。(例如npm build
时或者是yarn build
时)
本文将通过以下TODO的项目示例来让大家更加直观的了解这个框架:
与其它框架不同的是,Svelte 没有cli脚手架工具来帮助我们快速构建一个项目,但是为了使我们构建项目更加简单,我们将使用一个入门模板并使用degit来获取他 本教程的要求:
npx degit sveltejs / template svelte-todo
cd svelte-todo
现在,我们已经有了一个适用于小型应用的汇总结构模板,现在,我们可以开始动手了。
看一下我们的结构, main.js
中的内容如下:
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
name: 'world'
}
});
export default app;
这里需要注意的是,
.svelte
文件,这是svelte的一个组件,在第三行中,我们实例化了该对象。<div id="root">
来给ReactDOM进行渲染或者使用Vue中的el:'body'
来绑定该元素现在,我们来看一下App.svelte
文件:
<script>
// code
</script>
<style>
// css
</style>
<!-- 可以有一个或者多个html/svelte 标签来让你选择-->
在<script>
标签中通常写普通的JS代码,<style>
标签中写样式(如果你想写Scss的话,也可以进行预处理)。除了这两个标签之外的所有内容都将成为我们正在写的组件的模板。
为什么要使用export let name;
呢?这就是我们如何定义属性或者Props的方法,还记得在main.js中我们如何给Props赋予值吗?
要查看此操作的实际效果,请在svelte-todo目录中运行cmd的同时运行run build
,该服务已被预先配置为热更新。
yarn dev
在这之后,我们打开localhost:5000
来查看我们的应用。props是通过export let [PROPNAME]
在组件内部声明来定义的。然后我们可以通过几种方式提供给Svelte组件。将其作为值传递给HTML属性,方法如下:
<PersonComponent title = {name} {age} bind:shouldSleep = {sleepy} bind:happy />
title= {name} 传递 {name}
给 props title
bind:shouldSleep={sleepy}
我们可以在子组件中更改此值bing:happy
变为bind:happy={happy}
。提供完这些东西以后,当我们在组件内部访问它们时,我们的props 将保持上面给出的值。例如,我们提供一个 Robot
组件,activated={true}
,那当我们进入activated
内部的Robot
时,值将为true。打开 main.js 然后查看结果。现在删除所有的内容。取而代之的是,添加一个<main>
标签,并自己在Hello World 中编写,并且这将成为你自己的内容,在这个过程中,还包含了CSS的编译,不信的话你给样式的背景加入一些渐变色吧。Svelte会自动将所有样式的范围调整到每一个组件,因此不需要BEM或者其他的CSS界定方法,但是要到达HTML标签,我们需要进入全局的范围中,幸运的是,我们现在可以使用:global{element}
来将样式放在应用的全局范围内,现在,我们的App.svelte
文件如下:
<main>
Hello world!
</main>
<style>
:global(html) {
/* this will apply to <html> */
background: linear-gradient(#FF6F3F, #FFC877);
}
</style>
上面的内容还不是我们想要的TODO应用,因此,让我们更加进一步:我们要做的第一件事就是进入 src目录中,创建一个新的文件夹components
,并且在这里面创建Tode.svelte
文件。在这个文件中,添加以下内容:
<script>
let todos = [];
let value = "";
$: amount = todos.reduce((sum, todo) => (todo.done ? ++sum : sum), 0);
function addTodo() {
if (value === "") return alert("please input a todo");
const newTodo = {
text: value,
done: false
};
todos = [...todos, newTodo];
value = "";
}
</script>
<div class="wrapper">
<div class="container">
<div class="todos">
{#each todos as todo}
<p> {todo.text} </p>
{:else}
<p class="no-todos">Add a new todo!</p>
{/each}
</div>
<div />
<button on:click={addTodo}>add todo</button>
<input bind:value placeholder="Your Todo Here" />
<p>You've done {amount} todos out of {todos.length}</p>
</div>
</div>
现在,我们首先会看到一个非常奇怪但是有效的JS标签:$:
,它实际上表示一个反应性语句。它可以在如图所示的一行中显示,也可以作为块语句显示,并且如果其中包含的任何变量发生更改,它将重新评估或重新运行,因此在这一行中,我们仅计算完成的待办事项数。
如果你以前用过其他的流行框架,你会发现这里实际上直接改变了状态,虽然这通常是一个很大的禁忌,但这是很巧妙的办法,还需要注意的是,我们这里不使用todos.push(newTodo)
。而是重新分配待办事项。这是由于svelte决定何时更新。Svelte只会更新待做项。这也意味着:
const family = { father: {name: "Jonathan"}}
const parent = family.father
parent.name = "Nathan"
因为我们没有重新分配家庭,因此不会触发更新。
接下来,我们研究在模板中看到的奇怪模式。那是一个“互相抚摸”的障碍。块有很多种,您应该看一下小型的文档以了解所有知识,您需要了解的所有知识都可以遍历一个数组todos,并为我们提供分别访问每个值的方式,就像todo,并在其中返回模板对于它们中的每一个,如果数组为空,则显示后面的项。如果您熟悉javascript,可以将其与以下内容类似:
if (arr.length < 0) return <Placeholder />
return arr.map(item => <Item {item} />)
最后,我们使用on:[event]
和bind:[attribute]
,它们位于最底层,作为回顾,我们来看下面这些内容:
<button on:click={addTodo}>add todo</button>
<input bind:value placeholder="Your Todo Here" />
第一个是我们的事件挂钩,它的简单含义是“在此触发器上执行该操作”。在这种情况下,“ on click
do addTodo
”是我们之前在script标签中定义的功能。第二种方法为给定的值创建双向绑定,这样当您键入时,值会自动更新。如果您输入“洗碗”,value将变成“洗碗”。
现在,我们可以创建TODO并展示出来,但是不能删除它们。所以,在我们的components
文件夹中创建一个TodoItem.svelte
文件,并且执行以下步骤:
todo.done
添加一个div,该div包含带标签的复选框和一个绑定的选中值class:done
等于的标签上添加**a标签 **todo.done
.<script>
export let todo;
</script>
<style>
.container {
padding: 12px 24px;
border-bottom: 1px solid silver;
}
label {
display: block;
padding-left: 15px;
text-indent: -15px;
}
input {
width: 13px;
height: 13px;
padding: 0;
margin: 0;
vertical-align: bottom;
position: relative;
top: -1px;
}
label.done {
text-decoration: line-through;
}
</style>
<div class="container">
<label class:done={todo.done}>
<input type="checkbox" bind:checked={todo.done} />
{todo.text}
</label>
</div>
我添加了一些样式,但是我鼓励您自己对应用进行设置样式 如果这成功了,那我们成功做了一个自己的组件。我们要做的最后一件事就是将全新的TODOItem放入Todo中:返回Todo.svelte
并在script标签中添加一个import
语句:import TodoItem from './TodoItem.svelte'
,转到模板并替换{todo.text}
为<TodoItem bind:todo />
。这会将todo
作为名为todo
的道具发送给TodoItem
,这就是我们之前在TodoItem
中声明的导出todo变量中的内容。代码如下:
<script>
import TodoItem from "./TodoItem.svelte";
let todos = [];
let value = "";
$: amount = todos.reduce((sum, todo) => (todo.done ? ++sum : sum), 0);
function addTodo() {
if (value === "") return alert("please input a todo");
const newTodo = {
text: value,
done: false
};
todos = [...todos, newTodo];
value = "";
}
</script>
<style>
.wrapper {
display: flex;
justify-content: center;
align-items: center;
}
.container {
display: grid;
grid-template-rows: 8fr 1fr 1fr;
padding: 8px;
width: 600px;
max-width: 800px;
height: 600px;
background: aliceblue;
border: 3px solid #f2bb4d;
border-radius: 5px;
}
.no-todos {
width: 100%;
height: 40px;
font-size: 22px;
}
.todos {
height: 100%;
overflow-y: auto;
margin-bottom: 24px;
}
</style>
<div class="wrapper">
<div class="container">
<div class="todos">
{#each todos as todo}
<TodoItem bind:todo />
{:else}
<p class="no-todos">Add a new todo!</p>
{/each}
</div>
<div />
<button on:click={addTodo}>add todo</button>
<input bind:value placeholder="Your Todo Here" />
<p>You've done {amount} todos out of {todos.length}</p>
</div>
</div>
完成了!小型的项目外观虽然有些怪异,但其核心非常简单。没有花哨的架构,
本文原文来自 Medium,作者:Yuval Datner , 本文仅进行翻译,转载请注明出处!