Skip to content

Commit db36966

Browse files
committed
feat: template refs
1 parent 30d2814 commit db36966

File tree

5 files changed

+51
-3
lines changed

5 files changed

+51
-3
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
`petite-vue` is an alternative distribution of Vue optimized for progressive enhancement. It provides the same template syntax and reactivity mental model with standard Vue. However, it is specifically optimized for "sprinkling" small amount of interactions on an existing HTML page rendered by a server framework.
44

5-
- Only ~5.7kb
5+
- Only ~5.8kb
66
- DOM-based, mutates in place
77
- Driven by `@vue/reactivity`
88

@@ -332,13 +332,13 @@ Check out the [examples directory](https://github.com/vuejs/petite-vue/tree/main
332332
- `v-cloak`
333333
- `reactive()`
334334
- `nextTick()`
335+
- Template refs
335336

336337
### Not Supported
337338

338339
Some features are dropped because they have a relatively low utility/size ratio in the context of progressive enhancement. If you need these features, you should probably just use standard Vue.
339340

340341
- `ref()`, `computed()` etc.
341-
- Template refs (just use selectors)
342342
- Render functions (`petite-vue` has no virtual DOM)
343343
- Reactivity for Collection Types (Map, Set, etc., removed for smaller size)
344344
- Transition, KeepAlive, Teleport, Suspense
@@ -350,6 +350,6 @@ Some features are dropped because they have a relatively low utility/size ratio
350350
## Relationship with Alpine
351351

352352
- This is indeed addressing a similar scope to Alpine, but aims to be even more minimal.
353-
- `petite-vue` is less than half the size of Alpine.
353+
- `petite-vue` is only half the size of Alpine.
354354
- `petite-vue` has no transition system (maybe this can be an opt-in plugin).
355355
- Alpine is developing its own ecosystem and likely will diverge more from Vue in the future. `petite-vue` aligns with standard Vue behavior whenever possible, so it's less friction moving to standard Vue if needed. It's intended to cover the progressive enhancement use case where standard Vue is less optimized for nowadays.

src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const createApp = (initialData?: any) => {
1515
// global internal helpers
1616
ctx.scope.$s = toDisplayString
1717
ctx.scope.$nextTick = nextTick
18+
ctx.scope.$refs = Object.create(null)
1819

1920
let rootBlocks: Block[]
2021

src/directives/ref.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Directive } from '.'
2+
3+
export const ref: Directive = ({
4+
el,
5+
ctx: {
6+
scope: { $refs }
7+
},
8+
get,
9+
effect
10+
}) => {
11+
let prevRef: any
12+
effect(() => {
13+
const ref = get()
14+
$refs[ref] = el
15+
if (prevRef && ref !== prevRef) {
16+
delete $refs[prevRef]
17+
}
18+
prevRef = ref
19+
})
20+
return () => {
21+
prevRef && delete $refs[prevRef]
22+
}
23+
}

src/walk.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { effect as rawEffect, reactive, ReactiveEffect } from '@vue/reactivity'
1010
import { Block } from './block'
1111
import { queueJob } from './scheduler'
1212
import { checkAttr } from './utils'
13+
import { ref } from './directives/ref'
1314

1415
export interface Context {
1516
scope: Record<string, any>
@@ -84,6 +85,11 @@ export const walk = (node: Node, ctx: Context): ChildNode | null | void => {
8485
inOnce = true
8586
}
8687

88+
// ref
89+
if ((exp = checkAttr(el, 'ref'))) {
90+
applyDirective(el, ref, `"${exp}"`, ctx)
91+
}
92+
8793
// process children first before self attrs
8894
walkChildren(el, ctx)
8995

@@ -167,6 +173,7 @@ const processDirective = (
167173
arg = argIndex > 0 ? raw.slice(argIndex + 1) : undefined
168174
}
169175
if (dir) {
176+
if (dir === bind && arg === 'ref') dir = ref
170177
applyDirective(el, dir, exp, ctx, arg, modifiers)
171178
el.removeAttribute(raw)
172179
} else if (import.meta.env.DEV) {

tests/ref.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script type="module">
2+
import { createApp, reactive } from '../src'
3+
createApp().mount()
4+
</script>
5+
6+
<div
7+
v-scope="{ dynamicRef: 'x', show: true }"
8+
v-effect="console.log({ x: $refs.x, y: $refs.y, input: $refs.input })"
9+
>
10+
<input ref="input" />
11+
<span v-if="show" :ref="dynamicRef">Span with dynamic ref</span>
12+
<p>dynamicRef is {{ dynamicRef }}</p>
13+
<button @click="dynamicRef = dynamicRef === 'x' ? 'y' : 'x'">
14+
change dynamicRef
15+
</button>
16+
<button @click="show = !show">toggle</button>
17+
</div>

0 commit comments

Comments
 (0)