mirror of https://github.com/vuejs/core.git
wip: save
This commit is contained in:
parent
a3edc274e4
commit
0acafc7b4d
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
EffectScope,
|
||||||
|
ReactiveEffect,
|
||||||
|
pauseTracking,
|
||||||
|
proxyRefs,
|
||||||
|
resetTracking,
|
||||||
|
} from '@vue/reactivity'
|
||||||
|
import {
|
||||||
|
type Component,
|
||||||
|
type ComponentInternalInstance,
|
||||||
|
createSetupContext,
|
||||||
|
} from './component'
|
||||||
|
import { EMPTY_OBJ, isFunction } from '@vue/shared'
|
||||||
|
import { type SchedulerJob, queueJob } from '../../runtime-core/src/scheduler'
|
||||||
|
|
||||||
|
export function createComponentSimple(component: any, rawProps?: any): any {
|
||||||
|
const instance = new ComponentInstance(
|
||||||
|
component,
|
||||||
|
rawProps,
|
||||||
|
) as any as ComponentInternalInstance
|
||||||
|
pauseTracking()
|
||||||
|
let prevInstance = currentInstance
|
||||||
|
currentInstance = instance
|
||||||
|
instance.scope.on()
|
||||||
|
const setupFn = isFunction(component) ? component : component.setup
|
||||||
|
const setupContext = setupFn.length > 1 ? createSetupContext(instance) : null
|
||||||
|
const node = setupFn(
|
||||||
|
// TODO __DEV__ ? shallowReadonly(props) :
|
||||||
|
instance.props,
|
||||||
|
setupContext,
|
||||||
|
)
|
||||||
|
instance.scope.off()
|
||||||
|
currentInstance = prevInstance
|
||||||
|
resetTracking()
|
||||||
|
node.__vue__ = instance
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
let uid = 0
|
||||||
|
let currentInstance: ComponentInstance | null = null
|
||||||
|
|
||||||
|
export class ComponentInstance {
|
||||||
|
type: any
|
||||||
|
uid: number = uid++
|
||||||
|
scope: EffectScope = new EffectScope(true)
|
||||||
|
props: any
|
||||||
|
constructor(comp: Component, rawProps: any) {
|
||||||
|
this.type = comp
|
||||||
|
// init props
|
||||||
|
this.props = rawProps ? proxyRefs(rawProps) : EMPTY_OBJ
|
||||||
|
// TODO init slots
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderEffectSimple(fn: () => void): void {
|
||||||
|
const updateFn = () => {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
const effect = new ReactiveEffect(updateFn)
|
||||||
|
const job: SchedulerJob = effect.runIfDirty.bind(effect)
|
||||||
|
job.i = currentInstance as any
|
||||||
|
job.id = currentInstance!.uid
|
||||||
|
effect.scheduler = () => queueJob(job)
|
||||||
|
effect.run()
|
||||||
|
|
||||||
|
// TODO lifecycle
|
||||||
|
// TODO recurse handling
|
||||||
|
// TODO measure
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ export function setupComponent(instance: ComponentInternalInstance): void {
|
||||||
startMeasure(instance, `init`)
|
startMeasure(instance, `init`)
|
||||||
}
|
}
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
instance.scope.run(() => {
|
instance.scope.run(function componentSetupFn() {
|
||||||
const { type: component, props } = instance
|
const { type: component, props } = instance
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
|
|
@ -28,7 +28,10 @@ import type { Data } from '@vue/runtime-shared'
|
||||||
|
|
||||||
export type Component = FunctionalComponent | ObjectComponent
|
export type Component = FunctionalComponent | ObjectComponent
|
||||||
|
|
||||||
export type SetupFn = (props: any, ctx: SetupContext) => Block | Data | void
|
export type SetupFn = (
|
||||||
|
props: any,
|
||||||
|
ctx: SetupContext,
|
||||||
|
) => Block | Data | undefined
|
||||||
export type FunctionalComponent = SetupFn &
|
export type FunctionalComponent = SetupFn &
|
||||||
Omit<ObjectComponent, 'setup'> & {
|
Omit<ObjectComponent, 'setup'> & {
|
||||||
displayName?: string
|
displayName?: string
|
||||||
|
|
|
@ -3,14 +3,30 @@ import { renderEffect } from '../renderEffect'
|
||||||
import { setText } from './prop'
|
import { setText } from './prop'
|
||||||
import { type Block, normalizeBlock } from '../block'
|
import { type Block, normalizeBlock } from '../block'
|
||||||
|
|
||||||
|
// export function insert(
|
||||||
|
// block: Block,
|
||||||
|
// parent: ParentNode,
|
||||||
|
// anchor: Node | null = null,
|
||||||
|
// ): void {
|
||||||
|
// const nodes = normalizeBlock(block)
|
||||||
|
// for (let i = 0; i < nodes.length; i++) {
|
||||||
|
// parent.insertBefore(nodes[i], anchor)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
export function insert(
|
export function insert(
|
||||||
block: Block,
|
block: Block,
|
||||||
parent: ParentNode,
|
parent: ParentNode,
|
||||||
anchor: Node | null = null,
|
anchor: Node | null = null,
|
||||||
): void {
|
): void {
|
||||||
const nodes = normalizeBlock(block)
|
if (block instanceof Node) {
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
parent.insertBefore(block, anchor)
|
||||||
parent.insertBefore(nodes[i], anchor)
|
} else if (isArray(block)) {
|
||||||
|
for (let i = 0; i < block.length; i++) {
|
||||||
|
insert(block[i], parent, anchor)
|
||||||
|
}
|
||||||
|
} else if (block) {
|
||||||
|
insert(block.nodes, parent, anchor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ export const warn = (__DEV__ ? _warn : NOOP) as typeof _warn
|
||||||
export { nextTick } from './scheduler'
|
export { nextTick } from './scheduler'
|
||||||
export {
|
export {
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
type ComponentInternalInstance as ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
type Component as Component,
|
type Component as Component,
|
||||||
type ObjectComponent,
|
type ObjectComponent,
|
||||||
type FunctionalComponent,
|
type FunctionalComponent,
|
||||||
|
@ -155,6 +155,10 @@ export {
|
||||||
export { createBranch, createIf } from './apiCreateIf'
|
export { createBranch, createIf } from './apiCreateIf'
|
||||||
export { createFor, createForSlots } from './apiCreateFor'
|
export { createFor, createForSlots } from './apiCreateFor'
|
||||||
export { createComponent } from './apiCreateComponent'
|
export { createComponent } from './apiCreateComponent'
|
||||||
|
export {
|
||||||
|
createComponentSimple,
|
||||||
|
renderEffectSimple,
|
||||||
|
} from './apiCreateComponentSimple'
|
||||||
export { createSelector } from './apiCreateSelector'
|
export { createSelector } from './apiCreateSelector'
|
||||||
export { setInheritAttrs } from './componentAttrs'
|
export { setInheritAttrs } from './componentAttrs'
|
||||||
|
|
||||||
|
|
|
@ -1,79 +1,8 @@
|
||||||
<script setup lang="ts" vapor>
|
<script setup lang="ts" vapor>
|
||||||
import {
|
import Comp from './Comp.vue'
|
||||||
ref,
|
|
||||||
computed,
|
|
||||||
onMounted,
|
|
||||||
onBeforeMount,
|
|
||||||
getCurrentInstance,
|
|
||||||
onBeforeUpdate,
|
|
||||||
onUpdated,
|
|
||||||
onRenderTracked,
|
|
||||||
onRenderTriggered,
|
|
||||||
} from 'vue/vapor'
|
|
||||||
|
|
||||||
const instance = getCurrentInstance()!
|
|
||||||
const count = ref(1)
|
|
||||||
const double = computed(() => count.value * 2)
|
|
||||||
const html = computed(() => `<button>HTML! ${count.value}</button>`)
|
|
||||||
|
|
||||||
const inc = () => count.value++
|
|
||||||
const dec = () => count.value--
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
console.log('onBeforeMount', instance.isMounted)
|
|
||||||
})
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('onMounted', instance.isMounted)
|
|
||||||
})
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
count.value++
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
|
||||||
console.log('before updated')
|
|
||||||
})
|
|
||||||
onUpdated(() => {
|
|
||||||
console.log('updated')
|
|
||||||
})
|
|
||||||
|
|
||||||
onRenderTracked(e => {
|
|
||||||
console.log(`Render Tracked:`, e.target)
|
|
||||||
})
|
|
||||||
onRenderTriggered(e => {
|
|
||||||
console.log(`Render trigger:`, e.target)
|
|
||||||
})
|
|
||||||
|
|
||||||
const log = (arg: any) => {
|
|
||||||
console.log('callback in render effect')
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<h1>Vapor</h1>
|
||||||
<h1 class="red">Counter</h1>
|
<Comp />
|
||||||
<div>The number is {{ log(count) }}.</div>
|
|
||||||
<div>{{ count }} * 2 = {{ double }}</div>
|
|
||||||
<div style="display: flex; gap: 8px">
|
|
||||||
<button @click="inc">inc</button>
|
|
||||||
<button @click="dec">dec</button>
|
|
||||||
</div>
|
|
||||||
<div v-html="html" />
|
|
||||||
<div v-text="html" />
|
|
||||||
<div v-once>once: {{ count }}</div>
|
|
||||||
<div v-pre>{{ count }}</div>
|
|
||||||
<div v-cloak>{{ count }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
|
||||||
.red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,18 +1,79 @@
|
||||||
/// <reference types="vite/client" />
|
import {
|
||||||
|
createComponentSimple,
|
||||||
|
// createFor,
|
||||||
|
createVaporApp,
|
||||||
|
delegate,
|
||||||
|
delegateEvents,
|
||||||
|
ref,
|
||||||
|
renderEffectSimple,
|
||||||
|
template,
|
||||||
|
} from 'vue/vapor'
|
||||||
|
|
||||||
import { createVaporApp } from 'vue/vapor'
|
function createForSimple(val: () => any, render: (i: number) => any) {
|
||||||
import { createApp } from 'vue'
|
const l = val(),
|
||||||
import './style.css'
|
arr = new Array(l)
|
||||||
|
for (let i = 0; i < l; i++) {
|
||||||
const modules = import.meta.glob<any>('./**/*.(vue|js|ts)')
|
arr[i] = render(i)
|
||||||
const mod = (modules['.' + location.pathname] || modules['./App.vue'])()
|
|
||||||
|
|
||||||
mod.then(({ default: mod }) => {
|
|
||||||
const app = (mod.vapor ? createVaporApp : createApp)(mod)
|
|
||||||
app.mount('#app')
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
globalThis.unmount = () => {
|
|
||||||
app.unmount()
|
|
||||||
}
|
}
|
||||||
})
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
const t0 = template('<h1>Vapor</h1>')
|
||||||
|
const App = {
|
||||||
|
vapor: true,
|
||||||
|
__name: 'App',
|
||||||
|
setup() {
|
||||||
|
return (_ctx => {
|
||||||
|
const n0 = t0()
|
||||||
|
const n1 = createForSimple(
|
||||||
|
() => 10000,
|
||||||
|
(i: number) => createComponentSimple(Comp, { count: i }),
|
||||||
|
)
|
||||||
|
return [n0, createComponentSimple(Counter), n1]
|
||||||
|
})()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Counter = {
|
||||||
|
vapor: true,
|
||||||
|
__name: 'Counter',
|
||||||
|
setup() {
|
||||||
|
delegateEvents('click')
|
||||||
|
const count = ref(0)
|
||||||
|
const button = document.createElement('button')
|
||||||
|
button.textContent = '++'
|
||||||
|
delegate(button, 'click', () => () => count.value++)
|
||||||
|
return [
|
||||||
|
button,
|
||||||
|
createComponentSimple(Comp, {
|
||||||
|
// if ref
|
||||||
|
count,
|
||||||
|
// if exp
|
||||||
|
get plusOne() {
|
||||||
|
return count.value + 1
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// TODO dynamic props: merge with Proxy that iterates sources on access
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const t0$1 = template('<div></div>')
|
||||||
|
const Comp = {
|
||||||
|
vapor: true,
|
||||||
|
__name: 'Comp',
|
||||||
|
setup(props: any) {
|
||||||
|
return (_ctx => {
|
||||||
|
const n = t0$1()
|
||||||
|
renderEffectSimple(() => {
|
||||||
|
n.textContent = props.count + ' / ' + props.plusOne
|
||||||
|
})
|
||||||
|
return n
|
||||||
|
})()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = performance.now()
|
||||||
|
const app = createVaporApp(App)
|
||||||
|
app.mount('#app')
|
||||||
|
console.log((performance.now() - s).toFixed(2))
|
||||||
|
|
|
@ -5,7 +5,7 @@ import * as CompilerSFC from '@vue/compiler-sfc'
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
minify: 'terser',
|
minify: false,
|
||||||
terserOptions: {
|
terserOptions: {
|
||||||
compress: {
|
compress: {
|
||||||
pure_getters: true,
|
pure_getters: true,
|
||||||
|
|
Loading…
Reference in New Issue