聊聊Svelte.js技术它做了什么以及如何实现的(下)

聊聊Svelte.js技术它做了什么以及如何实现的(下)

一. 循环语句

     循环渲染这个我们肯定是离不开了, Svelte.js给我们提供了有趣的书写方式。

<script>
  const arr = ['x1', 'x2', 'x3'];
</script>

<ul>
  {#each arr as item}
    <li>{item}</li>
  {/each}
</ul>

image.png

     虽然Svelte.js没有虚拟dom, 但是循环的时候也需要key, 它的写法有点特殊。

<script>
  const arr = ['x1', 'x2', 'x3'];
</script>

<ul>
  {#each arr as item, i (i)}
    <li>{i} --- {item}</li>
  {/each}
</ul>

image.png

     上面的(i)就是key值, i是序号必须使用,item隔开。

二. 循环语句编译成什么

image.png
初始化就一个变量这个没啥说的。
image.png

image.png

     图二的ctx是个素组, ctx[0]是我们定义的arr, ctx[1]是当前li对应的值。

三. 循环语句如何处理key

下面是设置了key的循环体编译后的代码。
image.png

image.png

我们重点看下更新dom数据的时候key是怎么工作的。

image.png

其实在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>
  1. 子组件直接import引入子组件即可使用。
  2. 父组件行间传递参数, 子组件export接收。
  3. 父组件可使用扩展运算符传递参数。
  4. 父组件on:emit={handleEmit}的形式监听子组件发布的事件。
  5. 子组件引入createEventDispatcher实现事件的发布。
  6. 子组件可以随便改变传递过来的值, 不会影响该值在父组件内的值(重点)。
  7. 父组件传递给子组件的值的改变, 会重新渲染子组件。

五. 父子组件打包分析

image.png

image.png

六. 样式

     在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>

image.png

class:black={isBlack}的意思就是只有isBlacktrue才会赋予black这个className。

image.png

如果要设置全局样式可以在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>
  1. onMount 将在组件首次呈现到DOM之后运行。
  2. onDestroy 当销毁组件时调用。
  3. beforeUpdate 在DOM更新前运行。
  4. afterUpdate 在DOM更新后运行。

image.png

注意生命周期可以多次调用如下:

  onMount(() => {
    console.log('mounted1');
  });

  onMount(() => {
    console.log('mounted2');
  });

image.png

八. 异步请求

     异步请求与组件结构的融合设置, 比如我们平时些项目要为不同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}标签, 但是在这里处理错误其实不太好。

打包代码的样子

image.png

image.png

image.png

handle_promise方法里面, 如果判断我们传入的是promise则替我们执行promise并把结果赋予上去, 他还有其他复杂操作我们不用深究。
image.png

     看起来挺实用的写法其实也不太实用。

九. 计算属性

     你可以在 JavaScript 中用标识符标记一个语句,如下所示:$: foo = bar。它会在 foo = bar 语句中添加一个名为 $ 的标识符(如果之前未定义 foo,则严格模式下会出错)。
     所以在这种情况下,当 Svelte 看到任何带有 $: 前缀的语句时,它就知道左边的变量要从右边的变量中获取值。我们现在有了一种方法可以将一个变量的值绑定到另一个变量。

在js里面直接编写是不会报错的, 长知识了原来我们可以利用这点, 开发自己的编译器来创造新的语法规则:
image.png

image.png

计算属性的用法:
<script>
  let n = 1;

  $: nn = n * 2;

  function addn() {
    n++;
  }
</script>

<div>
  <button on:click={addn}>点了{nn}次</button>
</div>

上述的nn就永远等于n*2

打包后如何实现的

image.png

image.png

十. 观察者

<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>

image.png

  1. 最开始大括号的内容默认会执行一次。
  2. 大括号内的值如果发生变化就导致大括号里的代码整体执行一次。
  3. $:可以标示条件语句。
打包后的代码

image.png

不得不佩服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}
  1. 第一要引入动画fade
  2. 标签定义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}
  1. in:whoosh指定了动画使用whoosh函数。
  2. whoosh返回的是动画的延迟时间、执行时间、以及css效果等等。

这个就不讨论打包文件了。

十二. 输入框双向绑定

<script>
  let value = '';
</script>

<div>
  <input type="text" bind:value />
  <p>value: {value}</p>
</div>

image.png

打包文件

image.png

image.png

做法也比较强硬, 就是

十三. 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文件我们要处理一下, 方便导出。

改装前

image.png

改装后

image.png

具体的使用方式
<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.

     这次就是这样, 希望和你一起进步。

自信自律, 终身学习.

5.5k 声望
6.8k 粉丝
0 条评论
推荐阅读
学会了slidev.js的开发者, 也许可以考虑不用ppt了?
&ensp;&ensp;&ensp; 作为一个开发者, 每次使用PPT或者Word写简历或者述职都好痛苦啊, 这些App也不太适合展示代码, 但是今天来的家人们有福了, 一款叫做是slidev的库露出了邪魅的微笑 官网地址。

lulu_up8阅读 1.3k

安全地在前后端之间传输数据 - 「3」真的安全吗?
在「2」注册和登录示例中,我们通过非对称加密算法实现了浏览器和 Web 服务器之间的安全传输。看起来一切都很美好,但是危险就在哪里,有些人发现了,有些人嗅到了,更多人却浑然不知。就像是给门上了把好锁,还...

边城31阅读 7.2k评论 5

封面图
涨姿势了,有意思的气泡 Loading 效果
今日,群友提问,如何实现这么一个 Loading 效果:这个确实有点意思,但是这是 CSS 能够完成的?没错,这个效果中的核心气泡效果,其实借助 CSS 中的滤镜,能够比较轻松的实现,就是所需的元素可能多点。参考我们...

chokcoco20阅读 2.1k评论 2

在前端使用 JS 进行分类汇总
最近遇到一些同学在问 JS 中进行数据统计的问题。虽然数据统计一般会在数据库中进行,但是后端遇到需要使用程序来进行统计的情况也非常多。.NET 就为了对内存数据和数据库数据进行统一地数据处理,发明了 LINQ (L...

边城17阅读 1.9k

封面图
【已结束】SegmentFault 思否写作挑战赛!
SegmentFault 思否写作挑战赛 是思否社区新上线的系列社区活动在 2 月 8 日 正式面向社区所有用户开启;挑战赛中包含多个可供作者选择的热门技术方向,根据挑战难度分为多个等级,快来参与挑战,向更好的自己前进!

SegmentFault思否20阅读 5.6k评论 10

封面图
过滤/筛选树节点
又是树,是我跟树杠上了吗?—— 不,是树的问题太多了!🔗 相关文章推荐:使用递归遍历并转换树形数据(以 TypeScript 为例)从列表生成树 (JavaScript/TypeScript) 过滤和筛选是一个意思,都是 filter。对于列表来...

边城18阅读 7.7k评论 3

封面图
你可能不需要JS!CSS实现一个计时器
CSS现在可不仅仅只是改一个颜色这么简单,还可以做很多交互,比如做一个功能齐全的计时器?样式上并不复杂,主要是几个交互的地方数字时钟的变化开始、暂停操作重置操作如何仅使用 CSS 来实现这样的功能呢?一起...

XboxYan21阅读 1.6k评论 1

封面图

自信自律, 终身学习.

5.5k 声望
6.8k 粉丝
宣传栏