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`)
|
||||
}
|
||||
const reset = setCurrentInstance(instance)
|
||||
instance.scope.run(() => {
|
||||
instance.scope.run(function componentSetupFn() {
|
||||
const { type: component, props } = instance
|
||||
|
||||
if (__DEV__) {
|
||||
|
|
|
@ -28,7 +28,10 @@ import type { Data } from '@vue/runtime-shared'
|
|||
|
||||
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 &
|
||||
Omit<ObjectComponent, 'setup'> & {
|
||||
displayName?: string
|
||||
|
|
|
@ -3,14 +3,30 @@ import { renderEffect } from '../renderEffect'
|
|||
import { setText } from './prop'
|
||||
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(
|
||||
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)
|
||||
if (block instanceof Node) {
|
||||
parent.insertBefore(block, 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 {
|
||||
getCurrentInstance,
|
||||
type ComponentInternalInstance as ComponentInternalInstance,
|
||||
ComponentInternalInstance,
|
||||
type Component as Component,
|
||||
type ObjectComponent,
|
||||
type FunctionalComponent,
|
||||
|
@ -155,6 +155,10 @@ export {
|
|||
export { createBranch, createIf } from './apiCreateIf'
|
||||
export { createFor, createForSlots } from './apiCreateFor'
|
||||
export { createComponent } from './apiCreateComponent'
|
||||
export {
|
||||
createComponentSimple,
|
||||
renderEffectSimple,
|
||||
} from './apiCreateComponentSimple'
|
||||
export { createSelector } from './apiCreateSelector'
|
||||
export { setInheritAttrs } from './componentAttrs'
|
||||
|
||||
|
|
|
@ -1,79 +1,8 @@
|
|||
<script setup lang="ts" vapor>
|
||||
import {
|
||||
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
|
||||
}
|
||||
import Comp from './Comp.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="red">Counter</h1>
|
||||
<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>
|
||||
<h1>Vapor</h1>
|
||||
<Comp />
|
||||
</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'
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
|
||||
const modules = import.meta.glob<any>('./**/*.(vue|js|ts)')
|
||||
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()
|
||||
function createForSimple(val: () => any, render: (i: number) => any) {
|
||||
const l = val(),
|
||||
arr = new Array(l)
|
||||
for (let i = 0; i < l; i++) {
|
||||
arr[i] = render(i)
|
||||
}
|
||||
})
|
||||
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({
|
||||
build: {
|
||||
target: 'esnext',
|
||||
minify: 'terser',
|
||||
minify: false,
|
||||
terserOptions: {
|
||||
compress: {
|
||||
pure_getters: true,
|
||||
|
|
Loading…
Reference in New Issue