Svelte 语法糖系列一 @const

315 阅读3分钟

“我正在参加「掘金·启航计划」”

对于各个框架的语法糖。

我如果只是看文档,结果大概率会是,我知道这个东西,只是知道而已。这个语法糖在什么时候使用?我用这个方法不也是一样可以做出来吗?

这些疑问如果没有真实场景和业务的打磨,对框架的就只是停留在会用的阶段。高质量的代码就拜拜。

下面将根据自己实际业务,探讨语法糖的使用场景、分析利弊。

个人总结梳理。如有错误,欢迎指正!

语法糖 @const

场景示例:做一个二层菜单的功能(当前看起来是手风琴效果...,后续有新想法会继续使用这个例子),步骤如下。

  1. 模拟出菜单格式(此处只是模拟数据用来使用@const语法糖,具体业务具体分析。)
  2. html中循环
  3. 写入用到的样式
<script lang='ts'>
const menuList = [
 { title: '首页' },
 { title: '系统管理', children: [ { title: '菜单管理' }, { title: '项目管理' } ] },
 { title: '文档管理', children: [ { title: '个人文档' } ] },
]
</script>

{#each menuList as menu}
 <p>
   {menu.title} {#if menu.children && menu.children.length > 0}<i> > </i>{/if}
 </p>
 {#if menu.children && menu.children.length > 0}
  {#each menu.children as menuChild}
   <div class='submenu'>
    {menuChild.title}
   </div>
  {/each}
 {/if}
{/each}
<style>
p {color: #000;}
.submenu {display: none;color: #000000;}
</style>

我们可以发现,用到了两次:menu.children && menu.children.length > 0

方案一:使用函数 isHasChildHandle

<script lang='ts'>
 const isHasChildHandle = (menu) => menu && menu.length > 0
</script>
{#each menuList as menu}
 <div>
 {menu.title} {#if isHasChildHandle(menu)}<i> > </i>{/if}
 </div>
 {#if isHasChildHandle(menu)}
  ...
 {/if}
{/each}

可以看出重复代码减少了,如果需要修改也会方便很多,但isHasChildHandle还是会运行两次。

大多数时候我们会觉得无所谓,只是改了两个地方,但是如果是完成下面的功能(未使用我们的方法,因为那并不是解决方案):

<script lang='ts'>
 let active = '首页'
 const menuList = [
  { title: '首页', children: [] },
  { title: '系统管理', children: [ { title: '菜单管理' }, { title: '项目管理' } ] },
  { title: '文档管理', children: [ { title: '个人文档' } ] },
 ]
 const menuClick = (menu) => {
   active = menu.title
 }
</script>
{#each menuList as menu}
 <div
  class="menu"
  class:has-child={menu.children && menu.children.length > 0}
  class:active={menu.title === active}
  on:click={() => menuClick(menu)}
 >
 {menu.title} {#if menu.children && menu.children.length > 0}<i> > </i>{/if}
 </div>
 {#if menu.children && menu.children.length > 0}
  {#each menu.children as menuChild}
   <div
    class='submenu'
    class:active={menu.title === active}
    class:block={active === menu.title}
   >
    {menuChild.title}
   </div>
  {/each}
 {/if}
{/each}
<style>
    .menu {padding: 10px;cursor: pointer;font-size: 16px;}
    .menu:hover {color: #7c88a0;}
    .submenu {display: none;color: #000000;}
    .block {display: block}
    .active {color: #0b69a8;}
</style>

这段代码中我们会根据计算结果来动态的更新:class if,甚至在submenu中的one-child判断条件还不相同。

我尝试来分解一下代码,看看在一次循环中需要运算的语句:

  1. class:has-child={menu.children && menu.children.length > 0}
  2. class:active={menu.title === active}
  3. {#if menu.children && menu.children.length > 0}<i> > </i>{/if}
  4. {#if menu.children && menu.children.length > 0}
  5. class:active={menu.title === active}
  6. class:block={active === menu.title}

严谨的来说:在一次循环中需要运算7*menuList.length次,使用isHasChildHandle仅仅是使代码量减少了,实际运算次数并没有减少。

想象一下,如果放在复杂业务中需要计算的次数非常多怎么办?是否意味着代码跑的慢?

这里是补充:若在submenu中再增加一行class:one-child={menu.children && menu.children.length === 1},子循环中增加条件后,再想想复杂度!((3*menu.children.length)*menuList.length)

方案二:使用 @const

先看一下官方文档的解释:文档链接

标签{@const ...}定义了一个局部常量。

仅允许作为{#if}{:else if}、 {:else}、 {#each}、 {:then}、 {:catch}<Component /><svelte:fragment />的直接子集(as direct child)。

与原生JavaScript一样@const:它是只读的、不能分配的、它的作用域仅限于声明它的块级。

不要问有没有let,只能说暂时没有,且@const还有一些小问题存在。建议关注github.

修改后的代码如下:

{#each menuList as menu}
 {@const hasChild = menu.children && menu.children.length > 0}
 {@const isActive = active === menu.title}
 <div
  class="menu"
  class:has-child={hasChild}
  class:active={isActive}
  on:click={() => menuClick(menu)}
 >
  {menu.title} {#if hasChild}<i> > </i>{/if}
 </div>
 {#if hasChild}
  {#each menu.children as menuChild}
   <div
    class='submenu'
    class:active={isActive}
    class:block={isActive}
   >
    {menuChild.title}
   </div>
  {/each}
 {/if}
{/each}

// {isActive} // ReferenceError: isActive is not defined

再次查看在一次循环中需要运算的语句:

  1. {@const hasChild = menu.children && menu.children.length > 0}
  2. {@const isActive = active === menu.title}

严谨的来说需要:在一次循环中需要运算3 * menu.length次,比较能够看出对性能的提升。

如果你想在线查看最终结果:在线预览