不只 Vue3 , 尤大在 github 还更新了这个

陈大鱼头 ... 2021-8-3 Js
  • 前端
  • Js
  • Vue3
  • Lit-html
  • 进阶
About 5 min

# 前言

近期前端最火的话题莫过于 Vue v3.0.0 release 的发布了。

不过除了 Vue 3 之外,鱼头在尤大的 github (opens new window) 上还发现了悄咪咪上线的DEMO - vue-lit (opens new window)

下载下来之后发现还挺有意思的,所以就来跟大家一起分享下。

# 正文

项目 README.md (opens new window) 里有这么一句话:

Proof of concept mini custom elements framework powered by @vue/reactivity (opens new window) and lit-html (opens new window).

@vue/reactivity (opens new window)lit-html (opens new window) 提供支持的概念性迷你 custom elements (自定义标签)框架。

下载下来之后发现这是尤大一个实验性的玩具DEMO(当然后续会发展得如何,那就看尤大了)。

项目目录如下:

https://bucket.krissarea.com/blog/js/vue-lit/1.png

在浏览器打开 example.html ,我们能看到:

https://bucket.krissarea.com/blog/js/vue-lit/2.gif

然后 DEMO 代码如下(由于代码太长,本文仅展示部分代码):

<my-component></my-component>
<script type="module">
  import {
    defineComponent,
    reactive,
    html,
    onMounted,
    onUpdated,
    onUnmounted
  } from './index.js'

  defineComponent('my-component', () => {
    const state = reactive({
      text: 'hello',
      show: true
    })
    const toggle = () => {
      state.show = !state.show
    }
    const onInput = e => {
      state.text = e.target.value
    }

    return () => html`
      <button @click=${toggle}>toggle child</button>
      <p>
      ${state.text} <input value=${state.text} @input=${onInput}>
      </p>
      ${state.show ? html`<my-child msg=${state.text}></my-child>` : ``}
    `
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

看到这里就明白了,这是一个让 Web Components 支持类 Vue 语法的小工具。

我们再来看看源码(由于代码太长,本文仅展示除了生命周期以外的核心代码):

import { render } from 'https://unpkg.com/lit-html?module'
import {
  shallowReactive,
  effect
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'

let currentInstance

import { render } from 'https://unpkg.com/lit-html?module'
import {
  shallowReactive,
  effect
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'

export function defineComponent(name, propDefs, factory) {
  if (typeof propDefs === 'function') {
    factory = propDefs
    propDefs = []
  }

  customElements.define(
    name,
    class extends HTMLElement {
      static get observedAttributes() {
        return propDefs
      }
      constructor() {
        super()
        const props = (this._props = shallowReactive({}))
        currentInstance = this
        const template = factory.call(this, props)
        currentInstance = null
        const root = this.attachShadow({ mode: 'closed' })
        effect(() => {
          render(template(), root)
        })
      }
    }
  )
}

export * from 'https://unpkg.com/lit-html?module'
export * from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

这里我们来介绍下这个DEMO所涉及到的五个概念

# @vue/reactivity

这是 Vue3 里比较重要的一个模块,它是 Composition API 的核心,用于实现数据响应。

# shallowReactive

shallowReactive()Composition API 之一,用于响应数据的第一层,类似于浅拷贝。

调用方式如下:

import {
    shallowReactive,
    isReactive
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
const props = shallowReactive({ n: 1 })
console.log(isReactive(props.n)) // false
console.log(isReactive(props)) // true
1
2
3
4
5
6
7

如上所示,props 才是一个 reactive 对象,而 props.n 不是

# effect

effect() 则是一个观察者,调用方法如下:

import {
    effect,
    reactive
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
let dummy
const counter = reactive({ num: 0 })
effect(() => (dummy = counter.num))
console.log(dummy === 0) // true
counter.num = 7
console.log(dummy === 7) // true
1
2
3
4
5
6
7
8
9
10

# lit-html

官网 (opens new window) 介绍,它是一个高效,富有表现力,可扩展性的 HTML 模板。它支持创建跟更新 DOM,也兼容所有的浏览器。

使用方式如下:

import {
    html,
    render
} from 'https://unpkg.com/lit-html?module'
let sayHello = (name) => html`<h1>Hello ${name}</h1>`
render(sayHello('World'), document.body)
render(sayHello('Everyone'), document.body)
1
2
3
4
5
6
7

这个时候页面就会渲染出一个 <h1>Hello Everyone</h1>,效果如下:

https://bucket.krissarea.com/blog/js/vue-lit/3.png

还挺好用,不过网上对其褒贬不一,有兴趣的可以去搜索引擎搜一搜,本文不作展开。

# Web Components

至于 Web Components 应该是老生常谈的技术了,简单来说就是原生的组件化技术,简单的例子如下:

<h1><context-span>example</context-span></h1>
<script src="./wc.js"></script>
1
2
// wc.js
class ContextSpan extends HTMLElement {
    constructor() {
      super();

      const style = document.createElement('style');
      const span = document.createElement('span');
      span.textContent = this.textContent;

      const shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(style);
      shadowRoot.appendChild(span);

      style.textContent = `
        span:hover { text-decoration: underline; }
        :host-context(h1) { font-style: italic; }
        :host-context(h1):after { content: " - 我是一个H1" }
        :host { background: rgba(0,0,0,0.1); padding: 2px 5px; }
      `;
    }
}
customElements.define('context-span', ContextSpan);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

效果如下:

https://bucket.krissarea.com/blog/js/vue-lit/4.png

# vue-lit

我们回到 vue-lit 来,vue-lit 有可能只是尤大一时兴起的玩具,但是也有可能是 vue 结合 原生组件的第一步。

VueReact 此类前端框架出来不久之后,Web Components 就应运而生,虽然直到如今地位还不能跟这些前端框架相比,但是 Web Components 始终是浏览器的一大发展,极有可能就取代众多前端框架成为新的开发模式,毕竟当年 JQuery 大火之后,浏览器也出了许许多多模拟它 API 的原生函数。

所以看到这个工具,鱼头也是非常激动,虽然不确定后续会如何发展,但是对于在 Web Components 里使用 Vue 的这种模式,还是非常期待的。

# 后记

如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95 也可以扫码关注公众号,订阅更多精彩内容。 https://bucket.krissarea.com/blog/base/qrcode-all1.png

Last update: June 25, 2023 00:16
Contributors: fish_head , 陈大鱼头