I want to solve a scenario recently. When ve-charts
, I want to make a code example to demonstrate the function. After changing the code, you can intuitively see the changes in the components. In the previous version, the document used docsify
, and there is a vuep
. Vuep is to solve the scene I need. But the vuep version is relatively old. Vue3 components are not currently supported. So I want to independently develop a component that runs code examples.
ES Modules specification
ES modules (ESM) is the official standardized module system of JavaScript
Evolution
Before ES6, there were already familiar module loading solutions CommonJS
and AMD
. The former was used for the server, namely Node.js
, while the latter used third-party libraries to implement browser loading modules.
In front-end engineering, CommonJS
wider range of applications. We can see from three aspects:
NPM
of the third-party modules that we rely on published onCommonJS
- The front-end resources built by
Webpack
Node.js
environmentCommonJS
- We write
ESM
code needs byBabel
convertCommonJS
The good news is that browsers have begun to natively support module functions, and Node.js
also continuing to support ES Modules module functions.
ESM standardization is still on the road
The difference between client and server implementation
Use ES Modules in Node.js
Starting from Node.js v13.2.0
, there are two ways to correctly parse the ESM
standard module. In between, you need to add --experimental-modules
to use the ESM module.
.mjs
ending with the extension 060b759ea23615- With the suffix
.js
end of file, and inpackage.json
declared fieldtype
tomodule
// esmA/index.mjs
export default esmA
// or
// esmB/index.js
export default esmB
// esmB/package.json
{
"type": "module"
}
.cjs
will continue to be parsed asCommonJS
module
Use ES Modules in the browser
Modern browsers have native support for load ES Modules
need to type="module"
put <script>
tab, this statement is a script module.
In this way, you can use the import
and export
statements in the script
<script type="module">
// include script here
</script>
Handling dependencies in Node.js
In the modern front-end engineering development environment, package.json
. After the modules are installed, all modules will be placed in the node_modules
folder. For example, the description in package.json depends on lodash
:
{
"name": "test",
"version": "0.0.1",
"dependencies": {
"lodash": "^4.17.21"
}
}
Handle dependencies in the browser
Similarly, to handle the dependencies between modules in the browser, there is currently a new proposal import-maps
Define the mapping relationship between the module name and module address by declaring the attribute type
of the <script>
importmap
E.g:
<script type="importmap">
{
"imports": {
"lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"
}
}
</script>
Handle dependencies and use modules in the browser
importmap
is still in the proposal stage. The current browser compatibility is still very slow, but it will continue to be compatible in the future. We can use es-module-shims make the browser compatible.
<!-- UNPKG -->
<script async src="https://unpkg.com/es-module-shims@0.10.1/dist/es-module-shims.js"></script>
<!-- 声明依赖 -->
<script type="importmap">
{
"imports": {
"app": "./src/app.js"
}
}
</script>
<!-- 使用模块 -->
<script type="module">
import 'app'
</script>
Introduction to Vue SFC
What is Vue SFC?
In the Vue ecosystem, SFC is the abbreviation of single-file components
A Vue component is described by the extension .vue
features:
Code Example:
How to compile Vue SFC?
Vue projects need to use vue-loader
or rollup-plugin-vue
to compile 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-sfc works
To compile a Vue SFC component, you need to compile the components template
, script
and style
API
+--------------------+
| |
| script transform |
+----->+ |
| +--------------------+
|
+--------------------+ | +--------------------+
| | | | |
| facade transform +----------->+ template transform |
| | | | |
+--------------------+ | +--------------------+
|
| +--------------------+
+----->+ |
| style transform |
| |
+--------------------+
facade module will eventually be compiled into the following component pseudo code render
// 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
Vite & Vue SFC Playground
The official applications built on @vue/compiler-sfc
Vite
and Vue SFC Playground
. The former runs on the server side and the latter runs on the browser side.
Vite dependencies
- vite 2 provides Vue 3 single file component support through plug-in
@vitejs/plugin-vue
- underlying dependency
@vue/compiler-sfc
Dependency of Vue SFC Playground
@vue/compiler-sfc
- actually
SFC Playground
is based on @vue/compiler-sfc/dist/compiler-sfc.esm-browser.js Compile ES Modules
What is the difference between the process of compiling SFC between the two?
SFC Playground
of the module in Vite
is derived from the 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
HelloWorld.vue
the difference between the two compiled 060b759ea23d39 components?
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
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__
App.vue
the difference between the two compiled 060b759ea23dbd components?
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
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__
It can be seen that the underlying logic is basically the same when compiling SFC.
Abstract the ability to compile SFC into ES Modules
Learn from Vue SFC Playground and built two wheels🎡
- vue-sfc2esm: https://github.com/xiaoluoboding/vue-sfc2esm
- vue-sfc-sandbox: https://github.com/xiaoluoboding/vue-sfc-sandbox
If you are interested, you can click to GitHub
vue-sfc2esm
Compile Vue SFC into ES modules.
Features
- 💪 Written based on TypeScript
- 🌳 TreeShakable & SideEffects Free
- 📁 Virtual file system (supports compilation of
.vue/.js
files). - 👬 Friendly error message
Core logic
vue-sfc2esm
implements a virtual 📁 file system to record the relationship between files and codes.vue-sfc2esm
be based @ VUE / Compiler-SFC the SFC code intoES Modules
.- The compiled
ES Modules
code can be directly used in modern browsers.
Compile the App.vue
sample code:
<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>
💡 Before using the ES Modules module, you need to introduce Vue
<script type="importmap">
{
"imports": { "vue": "https://cdn.jsdelivr.net/npm/vue@next/dist/vue.esm-browser.js" }
}
</script>
vue-sfc-sandbox
vue-sfc-sandbox
isvue-sfc2esm
, and it is also@vue/compiler-sfc
, providing real-time editing & previewing SFC sandbox components.
Features
🗳️ SFC Sandbox
- 💪 Written based on TypeScript
- 🌳 TreeShakable & SideEffects Free
- 📁 Virtual file system (supports compilation of
.vue/.js
files) - 👬 Friendly error message, based on vue-sfc2esm
- 🧪 Convert Vue SFC files to ES Modules
- 🔌 Support external CDN, such as unpkg , jsdelivr etc.
- 🧩 Load Import Maps .
✏️ editor panel
- 🎨 Code editor based on codemirror 6
- 🧑💻 Friendly to developers, built-in highlight code, interactive panel presents REPL sandbox environment.
👓 Preview Panel
- ⚡️ Compile SFC files in real time
- 🔍 View in full screen
The future and the status quo
✨ Function
- Online real-time compilation & preview of
SFC
files /Vue 3
components - Support incoming external
CDN
- Support incoming
Import Maps
, incoming URL needs to be ESM
💠 Future
- Export SFC components
- Support real-time compilation of
React
components - Editor smart tips
💉 Pain point
- Cannot directly use packages packaged in
CommonJS
orUMD
format - The third party relies on too many requests, and there is an obvious waiting time
🖖 break
CommonJS
ToES Modules
plan- Vite 2 relies on the pre-built scheme
Similar project
Similar to sfc-sandbox
, based on the Vue
technology stack, an editor + demo tool can be provided 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 the browser can now be loaded using ES Modules
, but it still has some above-mentioned pain points problems of.
However, today in 2021, a new batch of front-end construction tools have emerged that can be called the next generation, such as esbuild
, snowpack
, vite
, wmr
and so on.
You can take a look at this article "Comparing the New Generation of Build Tools" , which analyzes and compares the front-end next-generation build tools from the aspects of tool configuration, development services, production construction, and building SSR.
Reference
- JavaScript modules : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
- ES modules: A cartoon deep-dive: https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
- import-maps: https://github.com/WICG/import-maps
- es-module-shims: https://github.com/guybedford/es-module-shims
- Vue 3 Template Explorer: https://vue-next-template-explorer.netlify.app/
- Vue SFC Playground: https://sfc.vuejs.org/
- 《Comparing the New Generation of Build Tools》: https://css-tricks.com/comparing-the-new-generation-of-build-tools/
Mobile reading
Follow my technical public number, you can also find this article.
Original: https://transpile-vue-sfc-to-es-modules.vercel.app
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。