What's the Vue lit you posted on GitHub three days ago?

No reprint without authorization, original address: https://github.com/axuebin/ar...

Write in front

On the afternoon of September 18, Beijing time, Youda sent a microblog. There were few cruel words. Seeing this expression, everyone knows that something big is going to happen. Sure enough, when I was writing this article, I took a look at GitHub and just came across the release:

We know that the release of general open source software is a final version. Let's take a look at the official introduction to this release version:

Today we are proud to announce the official release of Vue.js 3.0 "One Piece".

For more information about this release version, you can focus on: https://github.com/vuejs/vue-next/releases/tag/v3.0.0

In addition, I found another thing on the big GitHub vue-lit , my intuition told me that this is the next generation of xxx facing the future, so I clicked in and took a look at the new toy.

Hello World

Proof of concept mini custom elements framework powered by @vue/reactivity and lit-html.

It seems to be a particularly big verification attempt. Seeing custom element and lit HTML and guessing blindly is a tool that can render vue written web components directly in the browser.

Lit html is mentioned here, which will be introduced later.

According to the Demo given by Youda, let's try Hello World:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">
      import {
        defineComponent,
        reactive,
        html,
        onMounted
      } from 'https://unpkg.com/@vue/lit@0.0.2';
  
      defineComponent('my-component', () => {
        const state = reactive({
          text: 'Hello World',
        });
        
        function onClick() {
          alert('cliked!');
        }
  
        onMounted(() => {
          console.log('mounted');
        });
  
        return () => html`
          <p>
            <button @click=${onClick}>Click me</button>
            ${state.text}
          </p>
        `;
      })
    </script>
  </head>
  <body>
    <my-component />
  </body>
</html>

Without any compilation and packaging tools, directly open the index HTML, it looks OK:

!

You can see that what is rendered here is a Web Component, and the mounted life cycle is also triggered.

About lit HTML and lit element

Before watching Vue lit, let's take a look at lit HTML and lit element. These two things have been out for a long time, and maybe not everyone knows them.

lit-html

lit-html Many people may not be familiar with or even have not seen it.

So what is it? The answer is HTML template engine.

If there is no body feeling, let me ask a question, what are the core things of React? Everyone will answer: jsx, virtual DOM, diff, yes, these things constitute the React of UI = f(data).

Take a look at the syntax of jsx:

function App() {
  const msg = 'Hello World';
  return <div>{msg}</div>;
}

Let's look at the syntax of lit HTML:

function App() {
  const msg = 'Hello World';
  return html`
    <div>${msg}</div>
  `;
}

We know that jsx needs to be compiled, and its bottom layer is still createElement. Lit html is different. It is based on the tagged template, so it can run on the browser without compilation. Combined with HTML Template, it can play as it wants. It has stronger expansion ability. Isn't it fragrant?

Of course, no matter jsx or lint HTML, this App needs to be render ed to the real DOM.

Lint HTML implements a Button component

Direct code (omit style code):

<!DOCTYPE html>
<html lang="en">
<head>
  <script type="module">
    import { html, render } from 'https://unpkg.com/lit-html?module';

    const Button = (text, props = {
      type: 'default',
      borderRadius: '2px'
    }, onClick) => {
      // Click event
      const clickHandler = {
        handleEvent(e) { 
          alert('inner clicked!');
          if (onClick) {
            onClick();
          }
        },
        capture: true,
      };

      return html`
        <div class="btn btn-${props.type}" @click=${clickHandler}>
          ${text}
        </div>
      `
    };
    render(Button('Defualt'), document.getElementById('button1'));
    render(Button('Primary', { type: 'primary' }, () => alert('outer clicked!')), document.getElementById('button2'));
    render(Button('Error', { type: 'error' }), document.getElementById('button3'));
  </script>
</head>
<body>
  <div id="button1"></div>
  <div id="button2"></div>
  <div id="button3"></div>
</body>
</html>

effect:

performance

Will lit HTML perform better than React? I haven't read the source code carefully here, and I haven't conducted relevant experiments, so I can't make a conclusion.

However, it can be guessed that lit HTML does not use the diff like algorithm, but directly updates based on the same template. It seems that this method will be lighter.

However, we often ask the question "what is the use of key when rendering lists?", This can't be solved in lit HTML. If I delete one of the items in the long list, the whole long list will be updated once according to the update based on the same template of lit HTML. This performance is much worse.

//TODO: bury a pit and see later

lit-element

lit-element What is this?

Keywords: web components.

example:

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  static get properties() {
    return {
      msg: { type: String },
    };
  }
  constructor() {
    super();
    this.msg = 'Hello World';
  }
  render() {
    return html`
      <p>${this.msg}</p>
    `;
  }
}

customElements.define('my-element', MyElement);

effect:

Conclusion: Web Component can be written in React like syntax.

So, lit element is a base class that can create web components. Analyze what the above Demo and lit element did:

  1. static get properties: you can set the state of the setter
  2. constructor: initialize state
  3. render: renders elements through lit HTML and creates ShadowDOM

In short, lit element complies with the Web Components standard. It is a class. Based on it, you can quickly create Web Components.

For more information on how to use lit element for development, I won't expand here.

Web Components

Does browser native ability smell good?

Before talking about Web Components, I want to ask you, do you remember jQuery? Its convenient selector is unforgettable. But then document With the emergence and widespread use of the API queryselector, it seems that people have slowly forgotten jQuery.

The browser native API is easy enough to use. We don't need to use jQuery to operate DOM.

You Dont Need jQuery

Later, haven't you directly operated on DOM for a long time?

Yes, the emergence of frameworks (Libraries) such as React / Vue has helped us do a lot of things. We can no longer operate DOM through complex DOM API s.

What I want to say is, will React and others be replaced by browser native capabilities like jQuery one day if browser native capabilities are easy enough to use?

Componentization

Frameworks (Libraries) such as React / Vue have done the same thing. Before, the native ability of the browser could not be realized. For example, create a reusable component that can be rendered anywhere in the DOM.

And now? It seems that we can create reusable components without using any frameworks and libraries, or even package and compile them. Will we abandon the so-called frameworks and libraries one day and directly use the native API or use the frameworks and libraries based on the Web Components standard?

Of course, the future is unknowable

I am not a brainless boaster of Web Components, but we need to program for the future.

Let's take a look at some of the main functions of Web Components.

Custom elements: Custom elements

As the name suggests, a user-defined element is an HTML element that can be defined by the definition of the CustomElementRegistry, such as:

window.customElements.define('my-element', MyElement);

Then you can use it directly through < my element / >.

According to the specification, there are two types of Custom elements:

  • Autonomous custom elements: independent elements that do not inherit any HTML elements. You can directly < my element / >
  • Customized bundle in elements: inherited from HTML elements. For example, {extensions: 'p'} is used to identify the inherited P elements. It needs to be < p is = "my element" ></p>

There are also differences in the implementation of the two Custom elements:

// Autonomous custom elements
class MyElement extends HTMLElement {
  constructor() {
    super();
  }
}

// Customized bud in elements: inherited from p elements
class MyElement extends HTMLParagraphElement {
  constructor() {
    super();
  }
}

More about Custom elements

Life cycle function

In the constructor of Custom elements, you can specify multiple callback functions, which will be called at different life times of the element.

  • connectedCallback: when the element is first inserted into the document DOM
  • disconnectedCallback: when the element is deleted from the document DOM
  • Adptedcallback: when an element is moved to a new document
  • attributeChangedCallback: when an element adds, deletes, or modifies its own attributes

Let's pay attention to attributeChangedCallback, which executes this callback function whenever the attribute of an element changes and obtains the relevant information of the element:

attributeChangedCallback(name, oldValue, newValue) {
  // TODO
}

It should be noted that if you need to trigger the attributeChangedCallback() callback function after an attribute of an element changes, you must listen to this attribute:

class MyElement extends HTMLElement {
  static get observedAttributes() {
    return ['my-name'];
  }
  constructor() {
    super();
  }
}

The callback method is triggered when the my name attribute of the element changes.

Shadow DOM

Web Components is a very important feature, which can encapsulate the structure and style inside the component and isolate it from other codes on the page. This feature is implemented through Shadow DOM.

With regard to Shadow DOM, I mainly want to talk about the feature of CSS style isolation. The selector s inside and outside the Shadow DOM cannot be obtained from each other, so there is no way to use externally defined styles internally. Of course, there is no way to obtain internally defined styles externally.

What are the benefits of this? Focus and style isolation. Shadow DOM solves some problems in style through local HTML and CSS. It feels like vue's scope. There is no need to care about whether the selector and CSS rule will be covered by others or not. Therefore, the element selector is very simple: title / item, etc., without any tools or naming constraints.

More about Shadow DOM

Templates: Templates

You can use < template > to add HTML content in the Shadow DOM of a Web Component:

<body>
  <template id="my-paragraph">
    <style>
      p {
        color: white;
        background-color: #666;
        padding: 5px;
      }
    </style>
    <p>My paragraph</p>
  </template>
  <script>
    customElements.define('my-paragraph',
      class extends HTMLElement {
        constructor() {
          super();
          let template = document.getElementById('my-paragraph');
          let templateContent = template.content;

          const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));
        }
      }
    )
  </script>
  <my-paragraph></my-paragraph>
</body>

effect:

We know that < template > will not be rendered directly, so can we define multiple < template > and then choose to render different < template > according to different conditions when customizing elements? The answer, of course, is yes.

More about Templates

vue-lit

After introducing lit HTML / element and Web Components, let's return to the Vue lit.

First of all, we can see the following paragraph in the Release of Vue 3.0:

The @vue/reactivity module exports functions that provide direct access to Vue's reactivity system, and can be used as a standalone package. It can be used to pair with other templating solutions (e.g. lit-html) or even in non-UI scenarios.

It means that the @ vue/reactivity module can also design a solution to directly access Vue responsive system by cooperating with solutions similar to lit HTML.

Coincidentally, right, isn't this Vue lit?

Source code analysis

import { render } from 'https://unpkg.com/lit-html?module'
import {
  shallowReactive,
  effect
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
  • Lit HTML provides core render capabilities
  • @Responsiveness provided by Vue / Vue system

Here is a brief explanation of shallowReactive and effect, which are not expanded:

shallowReactive: simply understood as "shallow response", similar to "shallow copy", it is only the first layer of response data

const state = shallowReactive({
  a: 1,
  b: {
    c: 2,
  },
})

state.a++ // Responsive
state.b.c++ // Non responsive

effect: a simple understanding is a watcher

const state = reactive({
  name: "Front end test",
});
console.log(state); // The object returned here is the object after Proxy
effect(() => {
  console.log(state.name); // Whenever the name data changes, the effect will be re executed
});

Then look down:

export function defineComponent(name, propDefs, factory) {
  // propDefs
  // If it is a function, it is directly regarded as a factory function
  // If it is an array, listen to them and trigger the attributeChangedCallback callback function
  if (typeof propDefs === 'function') {
    factory = propDefs
    propDefs = []
  }
  // Call the function of Web Components to create Custom Elements
  customElements.define(
    name,
    class extends HTMLElement {
      // Monitor propDefs
      static get observedAttributes() {
        return propDefs
      }
      constructor() {
        super()
        // Create a shallow response
        const props = (this._props = shallowReactive({}))
        currentInstance = this
        const template = factory.call(this, props)
        currentInstance = null
        // beforeMount lifecycle
        this._bm && this._bm.forEach((cb) => cb())
        // Define a Shadow root, and the internal implementation cannot be accessed and modified by JavaScript, similar to the < video > tag
        const root = this.attachShadow({ mode: 'closed' })
        let isMounted = false
        // watcher
        effect(() => {
          if (!isMounted) {
            // beforeUpdate lifecycle
            this._bu && this._bu.forEach((cb) => cb())
          }
          // Call the core rendering capability of lit HTML. Refer to the Demo of lit HTML above
          render(template(), root)
          if (isMounted) {
            // update lifecycle
            this._u && this._u.forEach((cb) => cb())
          } else {
            // When rendering is complete, set isMounted to true
            isMounted = true
          }
        })
      }
      connectedCallback() {
        // mounted lifecycle
        this._m && this._m.forEach((cb) => cb())
      }
      disconnectedCallback() {
        // unMounted lifecycle
        this._um && this._um.forEach((cb) => cb())
      }
      attributeChangedCallback(name, oldValue, newValue) {
        // It will be triggered every time the parameters in propDefs are modified
        this._props[name] = newValue
      }
    }
  )
}

// Mount lifecycle
function createLifecycleMethod(name) {
  return (cb) => {
    if (currentInstance) {
      ;(currentInstance[name] || (currentInstance[name] = [])).push(cb)
    }
  }
}

// Export lifecycle
export const onBeforeMount = createLifecycleMethod('_bm')
export const onMounted = createLifecycleMethod('_m')
export const onBeforeUpdate = createLifecycleMethod('_bu')
export const onUpdated = createLifecycleMethod('_u')
export const onUnmounted = createLifecycleMethod('_um')

// Export all API s for lit hteml and @ vue/reactivity
export * from 'https://unpkg.com/lit-html?module'
export * from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'

The simplified version is helpful to understand

On the whole, in order to better understand, we can simplify it without considering the life cycle:

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, factory) {
  customElements.define(
    name,
    class extends HTMLElement {
      constructor() {
        super()
        const root = this.attachShadow({ mode: 'closed' })
        effect(() => {
          render(factory(), root)
        })
      }
    }
  )
}

These processes are as follows:

  1. Create Custom Elements for Web Components
  2. Create a ShadowRoot node of Shadow DOM
  3. The passed in factory and the internally created ShadowRoot node are rendered by the render of lit HTML

Looking back, the DEMO provided by Youda:

import {
  defineComponent,
  reactive,
  html,
} from 'https://unpkg.com/@vue/lit'

defineComponent('my-component', () => {
  const msg = 'Hello World'
  const state = reactive({
    show: true
  })
  const toggle = () => {
    state.show = !state.show
  }
  
  return () => html`
    <button @click=${toggle}>toggle child</button>
    ${state.show ? html`<my-child msg=${msg}></my-child>` : ``}
  `
})

My component is the name passed in, and the second is a function, that is, the factory passed in. In fact, it is the first parameter of lit HTML. It just introduces the reactive ability of @ vue/reactivity and turns the state into a response.

No problem. Consistent with Vue 3.0 Release, @ vue/reactivity can cooperate with lit HTML to combine Vue and Web Components. Isn't it interesting.

Write at the end

Maybe you just wrote this little toy on a whim, but it can be seen that this may really be a general trend.

I guess these keywords will burst out suddenly in the near future: unbound / es modules / web components / custom element / shadow dom

Is it worth looking forward to?

Thinking may be relatively shallow and the writing style is limited. You are welcome to point out the shortcomings.

More articles

You can follow the "front-end test" of the official account, or add your wechat friend qianduanshilian to join the exchange group.

Tags: Vue.js

Posted by barbs75 on Mon, 09 May 2022 12:36:37 +0300