Recently, I wanted to solve a scene. When I was writing documents for VE-Charts, I wanted to make a code example to demonstrate the function. After changing the code, I could intuitively see the changes of components. The previous version used Docsify, and docsify came with a VUep. Vuep is the solution to the scenario I need. But the VUEP version is older. Vue3 components are not currently supported. So you want to develop a component that runs the code sample on its own.

ES Modules specification

ES Modules (ESM) is the official standardized module system for JavaScript

evolution

Prior to ES6, there were familiar module-loading solutions such as CommonJS for the server (Node.js) and AMD, which relied on third-party libraries for browser-loading modules.

CommonJS is still widely used in front-end engineering, which can be seen from three aspects:

  • We rely on the release inNPMMost of the third-party modules are packaged with default supportCommonJS
  • throughWebpackBuild front-end resources that are compatibleNode.jsThe environmentCommonJS
  • We wroteESMThe code needs to passBabelconvertCommonJS

trend

The good news is that browsers are starting to support Modules natively, and Node.js continues to push support for ES Modules

ESM standardization is a work in progress

Client and server implementation differences

Use ES Modules in Node.js

Since Node.js V13.2.0, there are two ways to properly parse ESM standard modules. You also need –experimental-modules to use ESM modules.

  • Suffixed.mjsClosing document
  • Suffixed.jsThe end of the file, and inpackage.jsonDeclaration field intype δΈΊ module

// esmA/index.mjs

export default esmA

// or

// esmB/index.js

export default esmB

// esmB/package.json

{

  "type": "module"

}
Copy the code
  • Suffixed.cjsThe file at the end will continue to parse asCommonJSThe module

Use ES Modules in your browser

Modern browsers already natively support loading ES Modules by putting type=”module” in the

This allows you to use import and export statements in your scripts

<script type="module">
  // include script here
</script>
Copy the code

Dependencies are handled in Node.js

In modern front-end engineering development environments, dependencies between modules are described according to package.json. After installing modules, all modules are placed in the node_modules folder. For example, the package.json description relies on Lodash:

{
  "name": "test"."version": "0.0.1"."dependencies": {
    "lodash": "^ 4.17.21"}}Copy the code

Handle dependencies in the browser

Similarly, to handle dependencies between modules in the browser, there is a new proposal for import-maps

Define the mapping between the module name and the module address by declaring the

Such as:

<script type="importmap">
{
  "imports": {
    "lodash": "https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"}}</script>
Copy the code

Handle dependencies and use modules in the browser

Importmap is still in the proposal stage, and browser compatibility is slow at the moment, but will continue into the future. We can use es-module-shims to make browsers compatible.

<! -- UNPKG -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

<! -- Declare dependencies -->
<script type="importmap">
{
  "imports": {
    "app": "./src/app.js"}}</script>

<! -- Use module -->
<script type="module">
import 'app'
</script>
Copy the code

Vue SFC introduction

What is Vue SFC?

SFC stands for Single-file Components in Vue Ecology

A VUE component is described with the.vue extension

Features:

  • πŸ“ Full syntax highlighting
  • πŸ“¦ CommonJS module
  • 🎨 Component-scoped CSS

Code examples:

How to compile Vue SFC?

Vue project needs to use vue-loader or rollup-plugin-vue to compile and convert SFC files into executable JS

Vue 2

Vue – loader depends on:

  • @vue/component-compiler-utils
  • vue-style-loader

Vue 3

Vue – loader @ next depends on:

  • @vue/compiler-core

Vite 2

@ vitejs/plugin – vue depends on:

  • @vue/compiler-sfc

@vue/ Compiler – How SFC works

To compile a Vue SFC component, compile the component’s template, script, and style, respectively

API

+--------------------+ | | | script transform | +----->+ | | +--------------------+ | +--------------------+ | +--------------------+ | | | | | | facade transform +----------->+ template transform | | | | | | +--------------------+  | +--------------------+ | | +--------------------+ +----->+ | | style transform | | | +--------------------+Copy the code

Facade Module, which will eventually compile into the following structurerenderMethod component pseudocode

// main script
import script from '/project/foo.vue? vue&type=script'
// template compiled to render function
import { render } from '/project/foo.vue? vue&type=template&id=xxxxxx'
// css
import '/project/foo.vue? vue&type=style&index=0&id=xxxxxx'

// attach render function to script
script.render = render

// attach additional metadata
// some of these should be dev only
script.__file = 'example.vue'
script.__scopeId = 'xxxxxx'

// additional tooling-specific HMR handling code
// using __VUE_HMR_API__ global

export default script
Copy the code

Vite & Vue SFC Playground

Official apps built on @vue/ Compiler-SFC are Vite and Vue SFC Playground, the former running on the server side and the latter running on the browser side.

The dependence of Vite

  • Vite 2 through plug-ins@vitejs/plugin-vueVue 3 single file component support
  • The underlying dependence@vue/compiler-sfc

Vue SFC Playground’s dependence

  • @vue/compiler-sfc
  • In factSFC PlaygroundIs based on@vue/compiler-sfc/dist/compiler-sfc.esm-browser.jsBuild ES Modules

What is the difference between the two procedures for compiling an SFC?

The compilation of modules in SFC Playground stems from Vite support for SSR

Vite

  • 1. check all import statements and record id -> importName map
  • 2. check all export statements and define exports
  • 3. convert references to import bindings & import.meta references

SFC Playground

  • 0. instantiate module
  • 1. check all import statements and record id -> importName map
  • 2. check all export statements and define exports
  • 3. convert references to import bindings
  • 4. convert dynamic imports
  • append CSS injection code

The compilerHelloWorld.vueComponent differences?

Vite

// /components/HelloWorld.vue
import {defineComponent} from "/node_modules/.vite/vue.js? v=49d3ccd8";
const _sfc_main = defineComponent({
  name: "HelloWorld".props: {
    msg: {
      type: String.required: true}}});import { toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "/node_modules/.vite/vue.js? v=49d3ccd8"

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("h1".null, _toDisplayString(_ctx.msg), 1 /* TEXT */))
}

_sfc_main.render = _sfc_render
_sfc_main.__file = "/Users/xiaoyunwei/GitHub/private/slides-vite-demo/src/components/HelloWorld.vue"
export default _sfc_main
Copy the code

SFC Playground

// ./HelloWorld.vue
const __sfc__ = {
  name: "HelloWorld".props: {
    msg: {
      type: String.required: true}}}import { toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("h1".null, _toDisplayString($props.msg), 1 /* TEXT */))
}
__sfc__.render = render
__sfc__.__file = "HelloWorld.vue"
export default __sfc__
Copy the code

The compilerApp.vueComponent differences?

Vite

// ./App.vue
import {defineComponent} from "/node_modules/.vite/vue.js? v=49d3ccd8";
import HelloWorld from "/src/components/HelloWorld.vue";
const _sfc_main = defineComponent({
  name: "App".components: {
    HelloWorld
  }
});

import { resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "/node_modules/.vite/vue.js? v=49d3ccd8"

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_HelloWorld = _resolveComponent("HelloWorld")

  return (_openBlock(), _createBlock(_component_HelloWorld, { msg: "Hello Vue 3 + TypeScript + Vite" }))
}

_sfc_main.render = _sfc_render
_sfc_main.__file = "/Users/xiaoyunwei/GitHub/private/slides-vite-demo/src/App.vue"
export default _sfc_main
Copy the code

SFC Playground

// ./App.vue
import HelloWorld from './HelloWorld.vue'

const __sfc__ = {
  name: 'App'.components: {
    HelloWorld
  }
}

import { resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "vue"
function render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_HelloWorld = _resolveComponent("HelloWorld")

  return (_openBlock(), _createBlock(_component_HelloWorld, { msg: "Hello Vue SFC Playground" }))
}
__sfc__.render = render
__sfc__.__file = "App.vue"
export default __sfc__
Copy the code

You can see that the underlying logic is basically the same when compiling SFC.

Abstract the ability to compile SFC into ES Modules

Borrowed from the Vue SFC Playground, built two wheels 🎑

  • Vue – sfc2esm: github.com/xiaoluobodi…
  • Vue SFC – the sandbox: github.com/xiaoluobodi…

If you’re interested, follow us on GitHub

vue-sfc2esm

Compile Vue SFC into ES Modules.

function

  • πŸ’ͺ is written in TypeScript
  • 🌳 TreeShakable & SideEffects Free
  • πŸ“ Virtual file system (compilation supported.vue/.jsFile).
  • πŸ‘¬ Friendly error message

The core logic

  • vue-sfc2esmInternally, a virtual πŸ“ file system is implemented to record the relationship between files and code.
  • vue-sfc2esmWill be based on@vue/compiler-sfcCompile the SFC code intoES Modules.
  • compiledES ModulesThe code can be applied directly to modern browsers.

Sample code for compiling app.vue:

<script type="module">
import { createApp as _createApp } from "vue"

if (window.__app__) {
  window.__app__.unmount()
  document.getElementById('app').innerHTML = ' '
}

document.getElementById('__sfc-styles').innerHTML = window.__css__
const app = window.__app__ = _createApp(__modules__["DefaultDemo.vue"].default)
app.config.errorHandler = e= > console.error(e)
app.mount('#app')
</script>
Copy the code

πŸ’‘ Before using ES Modules, you need to introduce Vue in advance

<script type="importmap">
  {
    "imports": { "vue": "https://cdn.jsdelivr.net/npm/vue@next/dist/vue.esm-browser.js"}}</script>
Copy the code

vue-sfc-sandbox

Vue-sfc-sandbox is an upper application of VUE-SFC2ESM and is also based on @VUE/Compiler-SFC to provide real-time editing and preview of SFC sandbox components.

function

πŸ—³ ️ SFC sandbox

  • πŸ’ͺ is written in TypeScript
  • 🌳 TreeShakable & SideEffects Free
  • πŸ“ Virtual file system (compilation supported.vue/.jsFile)
  • πŸ‘¬ friendly error prompt, based on VUE-SFC2ESM
  • πŸ§ͺ convert Vue SFC files to ES Modules
  • πŸ”Œ supports external CDN, such as UNPKG and JsdeliVR.
  • 🧩 Load Import Maps.

✏️ Editor panel

  • 🎨 Codemirror 6 based code editor.
  • πŸ§‘πŸ’» developer-friendly, built-in highlighting code, interactive panels rendering the REPL sandbox environment.

πŸ‘“ Preview Panel

  • ⚑️ Compile SFC files in real time
  • πŸ” Full screen view

Future and Present Situation

✨ function

  • Online real-time compilation & previewSFCFile /Vue 3component
  • Support for passing outCDN
  • Support the incomingImport Maps, the incoming URL must be ESM

πŸ’  future

  • Export SFC components
  • Support real-time compilationReactcomponent
  • Editor intelligent prompt

πŸ’‰ pain points

  • Cannot be packaged directlyCommonJS ζˆ– UMDFormat of the packet
  • There are too many third-party dependency requests, and the waiting time is obvious

πŸ–– broken

  • CommonJS To ES Modulesplan
    • jspm.org/
    • esm.sh/
    • www.jsdelivr.com/esm
    • www.skypack.dev/
  • Vite 2 relies on a prebuild solution

Similar engineering

Similar to SFC-Sandbox, Vue technology stack provides an editor + presentation tool online

  • Vuep – 🎑 A component for rendering Vue components with live Editor and preview.
  • demosify – Create a playground to show the demos of your projects.
  • codepan – Like codepen and jsbin but works offline (Archived).

Future front-end engineering construction

Although browsers can now load ES Modules, they still have some of the pain points mentioned above.

But now in 2021, there are a number of new front-end build tools that you can call the next generation, such as EsBuild, Snowpack, Vite, WMR, etc.

You can see this article Comparing the New Generation of Build Tools, which analyzed and compared the next Generation of front-end building Tools from the aspects of tool configuration, development services, production construction, SSR construction and so on.

The resources

  • JavaScript modules: developer.mozilla.org/zh-CN/docs/…
  • ES modules: A cartoon deep – dive: hacks.mozilla.org/2018/03/es-…
  • The import – maps: github.com/WICG/import…
  • Es – module – shims: github.com/guybedford/…
  • Vue 3 Template Explorer: vue-next-template-explorer.netlify.app/
  • Vue SFC Playground: sfc.vuejs.org/
  • Comparing the New Generation of Build Tools: css-tricks.com/comparing-t…

Mobile reading

Follow my technical official number to find this article as well.

Original text: transpile – vue – SFC – to – es – modules. Vercel. App