vuejs / core-vapor Goto Github PK
View Code? Open in Web Editor NEWVue Vapor is a variant of Vue that offers rendering without the Virtual DOM.
Home Page: https://vapor-repl.netlify.app
License: MIT License
Vue Vapor is a variant of Vue that offers rendering without the Virtual DOM.
Home Page: https://vapor-repl.netlify.app
License: MIT License
core-vapor/packages/runtime-core/src/directives.ts
Lines 92 to 94 in 597eae4
Should be consistent with the existing runtime, returning a proxy object for the component instance
related: #99
We need two kinds of tests: test each single functions (something like test patchProp function) and you mentioned integration test (e2e): test API that the user will be using. (like createApp + ref...)
// setup
const map = new Map()
const weakMap = new WeakMap()
const node = document.querySelector('#title')
// 85M ops/s ± 1.33%
// 87.99 % slower
map.set(node, { a: 'b' })
const a = map.get(node)
// 708M ops/s ± 0.47%
// Fastest
node.$$a = 'b'
const a = node.$$a
// 56M ops/s ± 0.98%
// 92.1 % slower
weakMap.set(node, { a: 'b' })
const a = weakMap.get(node)
metadata
dirs
related: #47 (comment)
I implemented Component's Proxy in the PR that implements props, but found that it was erased in this commit.
Without it, I don't know how to access props from the render function, since the ctx I receive as argument to the render function does not include props.
this issue blocks #81, #42, #47, #54
I am assuming the following code, or am I wrong? 🤔
const Comp = defineComponent({
props: ['foo'],
setup() {
const __returned__ = {}
Object.defineProperty(__returned__, '__isScriptSetup', {
enumerable: false,
value: true,
})
return __returned__
},
render(_ctx: any) {
const t0 = template('<div></div>')
const n0 = t0()
const {
0: [n1],
} = children(n0)
watchEffect(() => {
setText(n1, void 0, _ctx.foo) // here!
})
return n0
},
})
children
985d4c7
const node = children(node, ...paths)
const n1 = children(t0, 1, 2, 3)
-> t0.childNodes[1].childNodes[2].childNodes[3]
template
d10a1a3withKeys
/ withModifers
inside of on
_renderEffect(() => {
_setText(n9, _ctx.remaining)
})
v-on
modifiersFunctionally unified with vue/core, we can use it directly
The withModifiers method and withKeys method in packages/runtime-dom/src/directives/vOn.ts
wrap the handler function to implement the function.
The compiler compiles separately for the categories of modifiers
<h1 @click.stop ="msg = 'a'">{{msg}}</h1>
->
_effect(() => {
_on(n7, "click", _withModifiers( _ctx.inc , ["stop"]));
});
<input @keyup.enter="onEnter" />
->
_effect(() => {
_on(n7, "keyup", _withKeys( _ctx.inc , ["enter"]));
});
<input @click.once="onEnter" />
->
_effect(() => {
_on(n7, "click", _ctx.inc , { once: true });
});
I will add unit tests for custom directives
Setup a CI job to publish packages on npm after each push
event.
See packages/vue/examples/composition/todomvc.html
Currently, we cannot run Vue Vapor on browsers yet. Wait #108
Let's copy the compilation code and inline it temporarily.
Taken by @LittleSound
Maybe I can help to support it
v-memo
in vapor mode is completely different, compared to vue-core. Since in vapor mode, we use effect
(aka watchEffect
) to track & update DOM nodes.
So to implement it, I think watch
function is enough.
<div v-memo="[msg]">{{ msg }}{{ count }}</div>
Compiles to
watch([msg], () => {
setText(...)
})
watch
functionIn the todo list, I noticed that the modifiers for v-bind are not being checked. If no one's already on it, I'd be willing to try and help implement it.
The current implementation of effect
within the vue-vapor project does not fully conform to the standard watchEffect
behavior, particularly lacking in comprehensive flush
timing options.
We aim to align vue-vapor's watchEffect
with the standard version by introducing detailed flush
options (pre
, post
, sync
). A fully realized flush
mechanism will facilitate the implementation of the beforeUpdate
directive lifecycle hook.
In addition, we plan to introduce a new onEffectCleanup
API analogous to onScopeDispose
, which will aid in implementing dynamic arguments for v-on
. first, extend watchEffect
in the runtime-vapor package. If necessary later, extract it to the reactivity package.
I've noticed in several PRs that there are reviews mentioning not to import runtime-core or compiler-core into vapor.
Indeed, it seems this isn't written anywhere, so we should compile the constraints and either lint the imports or include them in the contribution guide.
Even if we introduce linting, I think it might not be clear how to address it, so documentation is probably better. (Something along the lines of 'please implement by copying').
Related docs: https://vuejs.org/guide/reusability/custom-directives.html#directive-hooks
Wait for #67
related #4
I've submitted a PR concerning Props
, so I'll proceed with the implementation of Emits
based on that branch.
Wait for #67
I want to write test for this module. Just like this. Can i?
I will try to improve the unit test of existing features.
related: #4
https://vuejs.org/guide/reusability/custom-directives.html#introduction
updated
hookCurrently TODO
I'm currently working on the implementation.
branch: https://github.com/Ubugeeei/vuejs-core/tree/feat/v-on-modifier
In the v-show directive, I noticed an issue regarding the incorrect parameter being used.
core-vapor/packages/runtime-vapor/src/directives/vShow.ts
Lines 5 to 9 in 597eae4
It appears that the source
value returned in the beforeMount function is a function (_ctx.visible). Therefore, it is suggested to use the value parameter, similar to how it is used in the updated function.
We should extract transformer logic. See packages/compiler-core/src/transforms
core-vapor/packages/compiler-ssr/src/index.ts
Lines 57 to 84 in 503615a
<template>
<button @click="inc">inc</button>
<div v-once :id="count"></div>
</template>
The above use case id will be dynamically updated
i am trying to do the test for the module
import { ref } from '@vue/reactivity'
import { children, on, template, vModelDynamic, withDirectives } from '../src'
import { makeRender } from './_utils'
import { nextTick } from '@vue/runtime-dom'
const define = makeRender()
const triggerEvent = (type: string, el: Element) => {
const event = new Event(type)
el.dispatchEvent(event)
}
describe('directive: v-show', () => {
it('should work with text input', async () => {
const spy = vi.fn()
const data = ref<string | null>(null)
const { host } = define(() => {
const t0 = template('<input />')
const n0 = t0()
const n1 = children(n0, 0) as HTMLInputElement
withDirectives(n1, [[vModelDynamic, () => data.value]])
on(n1, 'update:modelValue', val => (data.value = val))
on(n1, 'input', () => () => spy(data.value))
return n0
}).render()
const input = host.querySelector('input')!
expect(input.value).toEqual('')
input.value = 'foo'
triggerEvent('input', input)
await nextTick()
expect(data.value).toEqual('foo')
expect(spy).toHaveBeenCalledWith('foo')
})
})
the last expect expect(spy).toHaveBeenCalledWith('foo')
got error. Because the event 'input', () => () => spy(data.value)
added before v-model
's input
event. I think custom input
event should add after v-model
' s input
event. Maybe cache these custom event, after add v-model
's input event, add them all.
import {
template,
children,
withDirectives,
effect,
setText,
render,
getCurrentInstance,
on,
ref,
unmountComponent
} from '../src'
import type { DirectiveBinding, DirectiveHook, ComponentInternalInstance } from '../src'
import {afterEach, beforeEach, describe, expect} from "vitest";
import { defineComponent, nextTick } from "@vue/runtime-core";
let host: HTMLElement
const initHost = () => {
host = document.createElement('div')
host.setAttribute('id', 'host')
document.body.appendChild(host)
}
beforeEach(() => {
initHost()
})
afterEach(() => {
host.remove()
})
describe('test', () =>{
test('unmountComponent', async () => {
let _instance: ComponentInternalInstance | null = null
const Comp = defineComponent({
__name: "directive",
setup() {
_instance = getCurrentInstance()
const count = ref(0);
const __returned__ = { count };
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
return __returned__;
},
render(ctx: any){
const t0 = template("<div></div>");
const n0 = t0();
const { 0: [n1] } = children(n0 as ChildNode);
effect(() => {
setText(n1 as Element, void 0, ctx.count);
});
return n0;
}
});
render(Comp as any, {}, '#host')
await nextTick()
expect(host.innerHTML).toBe('<div>0</div>')
unmountComponent(_instance!)
})
})
run the unit test code and I get an error
NotFoundError: The node to be removed is not a child of this node.
Most of the basic APIs related to component runtime have been basically implemented or are waiting for merging. I think it's time to complete the basic implementation of the component Compiler part.
related: #4
@vue/compiler-vapor
now cannot run on browsers (mode === 'function'
).
related: #4
I'm going to work on props/emit from now on (I plan to separate them into separate PRs).
Regarding the design, there are already some points that I'm concerned about.
First, let's consider the design of propsOptions.
Based on some comments in previous PRs, I feel that components should be expressed as functions that return Block
.
Now, in order to implement props, we need to store the runtime types, default values, and user-defined props definitions somewhere.
It is likely that the interface will be defined through defineProps for now, but I'm wondering if we are assuming that users will use Props Option in the Options API format.
How should we store the options information of user-defined props in the component?
Since this is an internal matter, I think it will be stored in ComponentInternalInstance
.
The problem is how to store it.
Currently, in the compiler,
export default (props, { expose }) => {
// .
// .
return t0;
}; // satisfies BlockFn
code like this is planned to be outputted. (Currently, the output is represented as setup or render options, but referring to the interface Block
in runtime-vapor/render, it seems to be temporary.)
And ComponentInternalInstance
is generated in the form of receiving BlockFn
as follows:
export function render(
comp: BlockFn,
container: string | ParentNode
): ComponentInternalInstance {
const instance = createComponentInstance(comp);
// .
// .
}
So, we need to discuss how to store user-defined props options.
define internal
https://github.com/sxzz/vue-simple-props
// how provide propsOptions? (e.g. default value)
export default ({ expose }) => {
const props = useProps();
// ...
return t0;
};
export default ({ expose }) => {
const props = useProps({ p1: { type: String, default: "v" } });
// ...
return t0;
};
const useProps = (options: PropsOptions) => {
const i = getCurrentInstance()
i.propsOptions = option
// or no use options, and refer to attrs like https://github.com/sxzz/vue-simple-props
}
export internal options
export default {
props: { p1: { type: String, default: "v" } },
blockFn: (props, { expose }) => {
// ...
return t0;
},
};
export const props = { p1: { type: String, default: "v" } };
export default (props, { expose }) => {
// ...
return t0;
};
high impact performance
related: #4
Currently, we are working on Props in parallel with #25,
but it seems that we can proceed with LifeCycles on a separate axis, so we will continue with it during the discussion as well.
Some implementations seem to be able to start, while others cannot be started yet or require discussion.
If you have editing rights for this comment, please add the situation to the Todo.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.