@Akryum
Vue.js Core Team
@Akryum@m.webtoo.ls
<!-- Cars.story.vue -->
<template>
<Story title="Cars">
<Variant title="default">
🚗
</Variant>
<Variant title="Fast">
🏎️
</Variant>
<Variant title="Slow">
🚜
</Variant>
</Story>
</template>
histoire dev
Story FS watcher
.md FS watcher
Vite dev server
(HTTP server)
vite-node server
Generate docs-only stories
<!-- Cars.story.vue -->
<template>
<Story title="Cars">
<Variant title="default">
🚗
</Variant>
<Variant title="Fast">
🏎️
</Variant>
<Variant title="Slow">
🚜
</Variant>
</Story>
</template>
Docs of Story 1
Docs page 1
Docs page 2
Story 1
Story 2
Story 3
...
Collect
Story FS watcher
.md FS watcher
Vite dev server
(HTTP server)
vite-node server
Story 1
Story 2
Story 3
...
Docs of Story 1
Docs page 1
Docs page 2
Vite dev server
(HTTP server)
Browser
import {
files, tree, onUpdate
} from 'virtual:$histoire-stories'
export let files = [{ ... }]
export let tree = { ... }
const handlers = []
export function onUpdate (cb) {
handlers.push(cb)
}
if (import.meta.hot) {
import.meta.hot.accept(newModule => {
files = newModule.files
tree = newModule.tree
handlers.forEach(h => {
h(newModule.files, newModule.tree)
newModule.onUpdate(h)
})
})
}
HMR
async resolveId (id) {
if (id === 'virtual:$histoire-stories') {
return '\0virtual:$histoire-stories'
}
}
async load (id) {
if (id === '\0virtual:$histoire-stories') {
return `export let files = [${JSON.stringify(files)}]
export let tree = ${JSON.stringify(tree)}
const handlers = []
export function onUpdate (cb) {
handlers.push(cb)
}
if (import.meta.hot) {
// Handle HMR...
}`
}
}
Vite dev server
(HTTP server)
Browser
HMR
stories data & tree
resolved config
theme CSS variables
setup code
support plugins
markdown files
full-text search indexes
Vite dev server
(HTTP server)
Browser
View: Story 1
stories data & tree
setup code
support plugin
markdown files
Mount: Story 1
Story1.story.vue
Render: Story 1: Content
Render: Story 1: Controls
support plugin
support plugin
Load
Browser
View: Story 1
stories data & tree
support plugin
Mount: Story 1
Story1Component.vue
Load
Render: Story 1: Content
Render: Story 1: Controls
support plugin
support plugin
Sync state
can be in an iframe
Browser
View: Story 1
stories data & tree
support plugin
Mount: Story 1
Story1Component.vue
Load
Render: Story 1: Content
support plugin
Auto-CodeGen: Story 1
support plugin
slot
<button
color="primary"
disabled
>
Click me!
</button>
Sync state
Browser
stories data & tree
resolved config
theme CSS variables
setup code
support plugins
markdown files
full-text search indexes
Static code
Vite build
export const count = 1
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
if (newModule) {
// newModule is undefined when SyntaxError happened
console.log('updated: count is now ', newModule.count)
}
})
}
Accept an update for current module
import { foo } from './foo.js'
foo()
if (import.meta.hot) {
import.meta.hot.accept('./foo.js', (newFoo) => {
// the callback receives the updated './foo.js' module
newFoo?.foo()
})
}
Accept an update for a dependency
if (import.meta.hot) {
// When current module is replaced
import.meta.hot.dispose((data) => {
// cleanup side effect
})
// when the module is no longer used in the page
import.meta.hot.prune((data) => {
// cleanup side effect
})
}
Cleanup module side-effects
if (import.meta.hot) {
import.meta.hot.accept((module) => {
// You may use the new module instance
// to decide whether to invalidate.
if (cannotHandleUpdate(module)) {
import.meta.hot.invalidate()
}
})
}
Bail out of HMR and propagate the update in parent modules
vitePlugins.push({
name: 'histoire:example',
configureServer (server) {
server.ws.on('some-event', (payload) => {
server.ws.send('some-other-event', somePayload)
})
},
})
if (import.meta.hot) {
import.meta.hot.send('some-event', { message: 'Hello Amsterdam' })
import.meta.hot.on('some-other-event', (somePayload) => {
console.log(somePayload)
})
}
Browser
Vite server
Thank you!