你问的Svelte来了--静态编译直出DOM独立分发Web Components位掩码变化追踪

Posted 奋飛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你问的Svelte来了--静态编译直出DOM独立分发Web Components位掩码变化追踪相关的知识,希望对你有一定的参考价值。

Svelte

Svelte 是一种全新的构建用户界面的方法。传统框架如 React 和 Vue 在浏览器中需要做大量的工作,而 Svelte 将这些工作放到构建应用程序的编译阶段来处理。与使用虚拟(virtual)DOM 差异对比不同。Svelte 编写的代码在应用程序的状态更改时就能像做外科手术一样更新 DOM。

上述是官方的介绍,提取关键词:

  1. 用户界面的方法:定位是UI框架。
  2. 编译阶段处理: Svelte 直接将模板编译成了原生 DOM,而 vue 等框架会将模板编译成虚拟DOM;浏览器支持原生 DOM 的渲染,无需运行时处理。
  3. 与使用虚拟(virtual)DOM 差异对比不同:直接编译成原生DOM,因此不具备基于 render function 的组件的强大抽象能力。
  4. 像做外科手术一样更新 DOM:采用一种 Bitmask-based change tracking 的机制配合赋值语句实现的。(这是本文介绍的重点

Svelte 的核心在于通过静态编译减少框架运行时的代码量

示例

App.svelte

<h1> count</h1>
<button on:click=handleClick>加1</button>
<button on:click=resetClick>重置</button>

<script>
	let count = 0
	function handleClick() 
		count +=1
	
	function resetClick () 
		count = 0
	
</script>

<style>
	button 
		background-color: #fff;
	
</style>

js 编译后的结果

/* App.svelte generated by Svelte v3.38.2 */
import  SvelteComponent, ...  from "svelte/internal";

function create_fragment(ctx) 
	let h1;
	let t0;
	let t1;
	let button0;
	let t3;
	let button1;
	let mounted;
	let dispose;

	return 
		c() 
			h1 = element("h1");
			t0 = text(/*count*/ ctx[0]);
			t1 = space();
			button0 = element("button");
			button0.textContent = "加1";
			t3 = space();
			button1 = element("button");
			button1.textContent = "重置";
			attr(button0, "class", "svelte-1328v8p");
			attr(button1, "class", "svelte-1328v8p");
		,
		m(target, anchor) 
			insert(target, h1, anchor);
			append(h1, t0);
			insert(target, t1, anchor);
			insert(target, button0, anchor);
			insert(target, t3, anchor);
			insert(target, button1, anchor);

			if (!mounted) 
				dispose = [
					listen(button0, "click", /*handleClick*/ ctx[1]),
					listen(button1, "click", /*resetClick*/ ctx[2])
				];

				mounted = true;
			
		,
		p(ctx, [dirty]) 
			if (dirty & /*count*/ 1) set_data(t0, /*count*/ ctx[0]);
		,
		i: noop,
		o: noop,
		d(detaching) 
			if (detaching) detach(h1);
			if (detaching) detach(t1);
			if (detaching) detach(button0);
			if (detaching) detach(t3);
			if (detaching) detach(button1);
			mounted = false;
			run_all(dispose);
		
	;


function instance($$self, $$props, $$invalidate) 
	let count = 0;

	function handleClick() 
		$$invalidate(0, count += 1);
	

	function resetClick() 
		$$invalidate(0, count = 0);
	

	return [count, handleClick, resetClick];


class App extends SvelteComponent 
	constructor(options) 
		super();
		init(this, options, instance, create_fragment, safe_not_equal, );
	


export default App;

css编译结果

button.svelte-1328v8pbackground-color:#fff

简化一下js编译内容:


	c() ,	// create
	m() ,	// mount
	p() ,	// update
	i() ,	// intro
	o() ,	// outro
	d() 	// destroy

上述各个方法,包裹了对原生 DOM 操作的方法,所以在运行时浏览器可以直接执行。

核心

Svelte 和 vue 等框架最大的不同就是编译成原生 DOM,其意味着单组件可以迁移或者在其他任何前端框架下使用「可独立分发的 Web Components」(因为其不存在运行时构建及对一些标签的支持等问题,不需要每个组件都要复制一份框架),当然 vue 等框架也推出了一些单组件构建的工具。

构建 web 组件:

  1. 使用 Svelte 官方组件模板 创建组件

    npx degit sveltejs/component-template my-hello
    
  2. 修改原文件

    • package.jsonname: 'MyHello'
    • 文件名修改:src\\Component.svelte --> src\\MyHello.svelteindex.js中同步修改引入地址)
    • rollup.config.js:svelte( customElement: true )
  3. 编写源文件 MyHello.svelte

    <svelte:options tag="my-hello" />
    <h1>Hello name</h1>
    
    <script>
      export let name
    </script>
    
    <style>
      h1 
        color: bule;
      
    </style>
    
  4. 生成 web 组件

    yarn
    yarn build
    
  5. 验证,创建 test.html,引入构建后内容

    <body>
      <script src="./dist/index.js"></script>
      <my-hello name="ligang"></my-hello>
    </body>
    

基于位掩码的变化追踪

基于位掩码的变化追踪(Bitmask-based change tracking)是 Svelte 处理响应的方案。

掩码

在计算机学中指的是一串二进制数字,通过与目标数字的按位操作,达到屏蔽指定位的目的。

位掩码

  • 二进制:是由1和0两个数字组成的,它可以表示两种状态,即开和关。所有输入电脑的任何信息最终都要转化为二进制。
  • 位运算:对二进制进行逻辑运算。程序中的所有数在计算机内存中都是以二进制的形式储存的。
运算符用法描述
按位与a & b对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
按位或a | b对于每一个比特位,当两个操作数相应的比特位至少有一个1时,结果为1,否则为0。
按位异或a ^ b对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。
按位非~ a反转操作数的比特位,即0变成1,1变成0。
左移a << b将 a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。
有符号右移a >> b将 a 的二进制表示向右移b(< 32) 位,丢弃被移出的位。
无符号右移a >>> b将 a 的二进制表示向右移b(< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

老鼠试毒(经典例子)

有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?

答案:采用位掩码, 2 10 = 1024 2^10=1024 210=1024 ,最多 10 只。
s t a t e s x > = b u c k t e t s x > = log ⁡ s t a t e s b u c k e t s x > = l o g ( b u c k t e t s ) l o g ( s t a t e s ) states^x >= bucktets \\\\ x >= \\log_states buckets \\\\ x >= \\fraclog(bucktets)log(states) \\\\ statesx>=bucktetsx>=logstatesbucketsx>=log(states)log(bucktets)
其中, s t a t e s = t i m e T o T e s t / t i m e T o D i e + 1 states = timeToTest / timeToDie + 1 states=timeToTest/timeToDie+1

(1000).toString(2)    // "1111101000"

简化示例(有7瓶水),来说明执行过程:

水(第n瓶)3号位2号位1号位
(第7瓶)111
(第6瓶)110
(第5瓶)101
(第4瓶)100
(第3瓶)011
(第2瓶)010
(第1瓶)001

第一只老鼠:喝掉1号位为1的水(0b1010101)
第二只老鼠:喝掉2号位为1的水(0b1100110)
第三只老鼠:喝掉3号位为1的水(0b1111000)

死亡(老鼠编号1、2、3)结论(第几瓶)
10b001 => 第1瓶
1、20b011 => 第3瓶
1、30b101 => 第5瓶
1、2、30b111 => 第7瓶
20b010 => 第2瓶
2、30b110 => 第6瓶
30b100 => 第4瓶
function poorMouse (buckets, timeToDie, timeToTest) 
  let states = timeToTest / timeToDie + 1
  let temp = Math.log(buckets) / Math.log(states)
  return Math.ceil(temp)

svelte 中位掩码的使用:

变量对应位
a0b001
b0b010
c0b100

实际生产中的使用

8421 权限管理

const Get = 1
const Post = 2
const Put = 4
const Delete = 8

function resolvePremission (perm) 
  return 
    get: (perm & Get) === Get,
    post:  (perm & Post) === Post,
    put: (perm & Put) === Put,
    delete:  (perm & Delete) === Delete
  

位运算可以确保最小的内存占用,但单个位掩码中包含的标志数量是有限的。在 javascript 中,所有数字变量默认都是32位有符号整数,其允许包含32个不同的标志。要超越次限制,就必须移动到另一个变量中去。

  • 如果标志数量不会超过单个变量中允许的数量,则位掩码是一个很好的选择,以提高数据操作的效率并减少内存占用。
  • 在单个变量中包含 32 个标志可以是减少管理 32 个不同变量的膨胀的好方法。尤其在 json 文件或 SQL 数据库中可能更重要。

以上是关于你问的Svelte来了--静态编译直出DOM独立分发Web Components位掩码变化追踪的主要内容,如果未能解决你的问题,请参考以下文章

Svelte 入门第一课

Svelte入门——Web Components实现跨框架组件复用

Svelte入门——Web Components实现跨框架组件复用

Svelte入门——Web Components实现跨框架组件复用

2021 年年度最佳开源软件

浅入浅出现代前端框架单页面