[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-71292":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":28,"discoverSource":29},71292,"petite-vue","vuejs\u002Fpetite-vue","vuejs","6kb subset of Vue optimized for progressive enhancement","",null,"TypeScript",9683,398,74,20,0,2,17,6,38.8,"MIT License",false,"main",[],"2026-06-12 02:02:50","# petite-vue\n\n`petite-vue` is an alternative distribution of [Vue](https:\u002F\u002Fvuejs.org) optimized for [progressive enhancement](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FGlossary\u002FProgressive_Enhancement). It provides the same template syntax and reactivity mental model as standard Vue. However, it is specifically optimized for \"sprinkling\" a small amount of interactions on an existing HTML page rendered by a server framework. See more details on [how it differs from standard Vue](#comparison-with-standard-vue).\n\n- Only ~6kb\n- Vue-compatible template syntax\n- DOM-based, mutates in place\n- Driven by `@vue\u002Freactivity`\n\n## Status\n\n- This is pretty new. There are probably bugs and there might still be API changes, so **use at your own risk.** Is it usable though? Very much. Check out the [examples](https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fpetite-vue\u002Ftree\u002Fmain\u002Fexamples) to see what it's capable of.\n\n- The issue list is intentionally disabled because I have higher priority things to focus on for now and don't want to be distracted. If you found a bug, you'll have to either workaround it or submit a PR to fix it yourself. That said, feel free to use the discussions tab to help each other out.\n\n- Feature requests are unlikely to be accepted at this time - the scope of this project is intentionally kept to a bare minimum.\n\n## Usage\n\n`petite-vue` can be used without a build step. Simply load it from a CDN:\n\n```html\n\u003Cscript src=\"https:\u002F\u002Funpkg.com\u002Fpetite-vue\" defer init>\u003C\u002Fscript>\n\n\u003C!-- anywhere on the page -->\n\u003Cdiv v-scope=\"{ count: 0 }\">\n  {{ count }}\n  \u003Cbutton @click=\"count++\">inc\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\n- Use `v-scope` to mark regions on the page that should be controlled by `petite-vue`.\n- The `defer` attribute makes the script execute after HTML content is parsed.\n- The `init` attribute tells `petite-vue` to automatically query and initialize all elements that have `v-scope` on the page.\n\n### Manual Init\n\nIf you don't want the auto init, remove the `init` attribute and move the scripts to end of `\u003Cbody>`:\n\n```html\n\u003Cscript src=\"https:\u002F\u002Funpkg.com\u002Fpetite-vue\">\u003C\u002Fscript>\n\u003Cscript>\n  PetiteVue.createApp().mount()\n\u003C\u002Fscript>\n```\n\nOr, use the ES module build:\n\n```html\n\u003Cscript type=\"module\">\n  import { createApp } from 'https:\u002F\u002Funpkg.com\u002Fpetite-vue?module'\n  createApp().mount()\n\u003C\u002Fscript>\n```\n\n### Production CDN URLs\n\nThe short CDN URL is meant for prototyping. For production usage, use a fully resolved CDN URL to avoid resolving and redirect cost:\n\n- Global build: `https:\u002F\u002Funpkg.com\u002Fpetite-vue@0.2.2\u002Fdist\u002Fpetite-vue.iife.js`\n  - exposes `PetiteVue` global, supports auto init\n- ESM build: `https:\u002F\u002Funpkg.com\u002Fpetite-vue@0.2.2\u002Fdist\u002Fpetite-vue.es.js`\n  - Must be used with `\u003Cscript type=\"module\">`\n\n### Root Scope\n\nThe `createApp` function accepts a data object that serves as the root scope for all expressions. This can be used to bootstrap simple, one-off apps:\n\n```html\n\u003Cscript type=\"module\">\n  import { createApp } from 'https:\u002F\u002Funpkg.com\u002Fpetite-vue?module'\n\n  createApp({\n    \u002F\u002F exposed to all expressions\n    count: 0,\n    \u002F\u002F getters\n    get plusOne() {\n      return this.count + 1\n    },\n    \u002F\u002F methods\n    increment() {\n      this.count++\n    }\n  }).mount()\n\u003C\u002Fscript>\n\n\u003C!-- v-scope value can be omitted -->\n\u003Cdiv v-scope>\n  \u003Cp>{{ count }}\u003C\u002Fp>\n  \u003Cp>{{ plusOne }}\u003C\u002Fp>\n  \u003Cbutton @click=\"increment\">increment\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\nNote `v-scope` doesn't need to have a value here and simply serves as a hint for `petite-vue` to process the element.\n\n### Explicit Mount Target\n\nYou can specify a mount target (selector or element) to limit `petite-vue` to only that region of the page:\n\n```js\ncreateApp().mount('#only-this-div')\n```\n\nThis also means you can have multiple `petite-vue` apps to control different regions on the same page:\n\n```js\ncreateApp({\n  \u002F\u002F root scope for app one\n}).mount('#app1')\n\ncreateApp({\n  \u002F\u002F root scope for app two\n}).mount('#app2')\n```\n\n### Lifecycle Events\n\nYou can listen to the special `vue:mounted` and `vue:unmounted` lifecycle events for each element (the `vue:` prefix is required since v0.4.0):\n\n```html\n\u003Cdiv\n  v-if=\"show\"\n  @vue:mounted=\"console.log('mounted on: ', $el)\"\n  @vue:unmounted=\"console.log('unmounted: ', $el)\"\n>\u003C\u002Fdiv>\n```\n\n### `v-effect`\n\nUse `v-effect` to execute **reactive** inline statements:\n\n```html\n\u003Cdiv v-scope=\"{ count: 0 }\">\n  \u003Cdiv v-effect=\"$el.textContent = count\">\u003C\u002Fdiv>\n  \u003Cbutton @click=\"count++\">++\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\nThe effect uses `count` which is a reactive data source, so it will re-run whenever `count` changes.\n\nAnother example of replacing the `todo-focus` directive found in the original Vue TodoMVC example:\n\n```html\n\u003Cinput v-effect=\"if (todo === editedTodo) $el.focus()\" \u002F>\n```\n\n### Components\n\nThe concept of \"Components\" are different in `petite-vue`, as it is much more bare-bones.\n\nFirst, reusable scope logic can be created with functions:\n\n```html\n\u003Cscript type=\"module\">\n  import { createApp } from 'https:\u002F\u002Funpkg.com\u002Fpetite-vue?module'\n\n  function Counter(props) {\n    return {\n      count: props.initialCount,\n      inc() {\n        this.count++\n      },\n      mounted() {\n        console.log(`I'm mounted!`)\n      }\n    }\n  }\n\n  createApp({\n    Counter\n  }).mount()\n\u003C\u002Fscript>\n\n\u003Cdiv v-scope=\"Counter({ initialCount: 1 })\" @vue:mounted=\"mounted\">\n  \u003Cp>{{ count }}\u003C\u002Fp>\n  \u003Cbutton @click=\"inc\">increment\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\n\u003Cdiv v-scope=\"Counter({ initialCount: 2 })\">\n  \u003Cp>{{ count }}\u003C\u002Fp>\n  \u003Cbutton @click=\"inc\">increment\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\n### Components with Template\n\nIf you also want to reuse a piece of template, you can provide a special `$template` key on a scope object. The value can be the template string, or an ID selector to a `\u003Ctemplate>` element:\n\n```html\n\u003Cscript type=\"module\">\n  import { createApp } from 'https:\u002F\u002Funpkg.com\u002Fpetite-vue?module'\n\n  function Counter(props) {\n    return {\n      $template: '#counter-template',\n      count: props.initialCount,\n      inc() {\n        this.count++\n      }\n    }\n  }\n\n  createApp({\n    Counter\n  }).mount()\n\u003C\u002Fscript>\n\n\u003Ctemplate id=\"counter-template\">\n  My count is {{ count }}\n  \u003Cbutton @click=\"inc\">++\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003C!-- reuse it -->\n\u003Cdiv v-scope=\"Counter({ initialCount: 1 })\">\u003C\u002Fdiv>\n\u003Cdiv v-scope=\"Counter({ initialCount: 2 })\">\u003C\u002Fdiv>\n```\n\nThe `\u003Ctemplate>` approach is recommended over inline strings because it is more efficient to clone from a native template element.\n\n### Global State Management\n\nYou can use the `reactive` method (re-exported from `@vue\u002Freactivity`) to create global state singletons:\n\n```html\n\u003Cscript type=\"module\">\n  import { createApp, reactive } from 'https:\u002F\u002Funpkg.com\u002Fpetite-vue?module'\n\n  const store = reactive({\n    count: 0,\n    inc() {\n      this.count++\n    }\n  })\n\n  \u002F\u002F manipulate it here\n  store.inc()\n\n  createApp({\n    \u002F\u002F share it with app scopes\n    store\n  }).mount()\n\u003C\u002Fscript>\n\n\u003Cdiv v-scope=\"{ localCount: 0 }\">\n  \u003Cp>Global {{ store.count }}\u003C\u002Fp>\n  \u003Cbutton @click=\"store.inc\">increment\u003C\u002Fbutton>\n\n  \u003Cp>Local {{ localCount }}\u003C\u002Fp>\n  \u003Cbutton @click=\"localCount++\">increment\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\n### Custom Directives\n\nCustom directives are also supported but with a different interface:\n\n```js\nconst myDirective = (ctx) => {\n  \u002F\u002F the element the directive is on\n  ctx.el\n  \u002F\u002F the raw value expression\n  \u002F\u002F e.g. v-my-dir=\"x\" then this would be \"x\"\n  ctx.exp\n  \u002F\u002F v-my-dir:foo -> \"foo\"\n  ctx.arg\n  \u002F\u002F v-my-dir.mod -> { mod: true }\n  ctx.modifiers\n  \u002F\u002F evaluate the expression and get its value\n  ctx.get()\n  \u002F\u002F evaluate arbitrary expression in current scope\n  ctx.get(`${ctx.exp} + 10`)\n\n  \u002F\u002F run reactive effect\n  ctx.effect(() => {\n    \u002F\u002F this will re-run every time the get() value changes\n    console.log(ctx.get())\n  })\n\n  return () => {\n    \u002F\u002F cleanup if the element is unmounted\n  }\n}\n\n\u002F\u002F register the directive\ncreateApp().directive('my-dir', myDirective).mount()\n```\n\nThis is how `v-html` is implemented:\n\n```js\nconst html = ({ el, get, effect }) => {\n  effect(() => {\n    el.innerHTML = get()\n  })\n}\n```\n\n### Custom Delimiters (0.3+)\n\nYou can use custom delimiters by passing `$delimiters` to your root scope. This is useful when working alongside a server-side templating language that also uses mustaches:\n\n```js\ncreateApp({\n  $delimiters: ['${', '}']\n}).mount()\n```\n\n## Examples\n\nCheck out the [examples directory](https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fpetite-vue\u002Ftree\u002Fmain\u002Fexamples).\n\n## Features\n\n### `petite-vue` only\n\n- `v-scope`\n- `v-effect`\n- `@vue:mounted` & `@vue:unmounted` events\n\n### Has Different Behavior\n\n- In expressions, `$el` points to the current element the directive is bound to (instead of component root element)\n- `createApp()` accepts global state instead of a component\n- Components are simplified into object-returning functions\n- Custom directives have a different interface\n\n### Vue Compatible\n\n- `{{ }}` text bindings (configurable with custom delimiters)\n- `v-bind` (including `:` shorthand and class\u002Fstyle special handling)\n- `v-on` (including `@` shorthand and all modifiers)\n- `v-model` (all input types + non-string `:value` bindings)\n- `v-if` \u002F `v-else` \u002F `v-else-if`\n- `v-for`\n- `v-show`\n- `v-html`\n- `v-text`\n- `v-pre`\n- `v-once`\n- `v-cloak`\n- `reactive()`\n- `nextTick()`\n- Template refs\n\n### Not Supported\n\nSome features are dropped because they have a relatively low utility\u002Fsize ratio in the context of progressive enhancement. If you need these features, you should probably just use standard Vue.\n\n- `ref()`, `computed()` etc.\n- Render functions (`petite-vue` has no virtual DOM)\n- Reactivity for Collection Types (Map, Set, etc., removed for smaller size)\n- Transition, KeepAlive, Teleport, Suspense\n- `v-for` deep destructure\n- `v-on=\"object\"`\n- `v-is` & `\u003Ccomponent :is=\"xxx\">`\n- `v-bind:style` auto-prefixing\n\n## Comparison with standard Vue\n\nThe point of `petite-vue` is not just about being small. It's about using the optimal implementation for the intended use case (progressive enhancement).\n\nStandard Vue can be used with or without a build step. When using a build setup (e.g. with Single-File Components), we pre-compile all the templates so there's no template processing to be done at runtime. And thanks to tree-shaking, we can ship optional features in standard Vue that doesn't bloat your bundle size when not used. This is the optimal usage of standard Vue, but since it involves a build setup, it is better suited when building SPAs or apps with relatively heavy interactions.\n\nWhen using standard Vue without a build step and mounting to in-DOM templates, it is much less optimal because:\n\n- We have to ship the Vue template compiler to the browser (13kb extra size)\n- The compiler will have to retrieve the template string from already instantiated DOM\n- The compiler then compiles the string into a JavaScript render function\n- Vue then replaces existing DOM templates with new DOM generated from the render function.\n\n`petite-vue` avoids all this overhead by walking the existing DOM and attaching fine-grained reactive effects to the elements directly. The DOM _is_ the template. This means `petite-vue` is much more efficient in progressive enhancement scenarios.\n\nThis is also how Vue 1 worked. The trade-off here is that this approach is coupled to the DOM and thus not suitable for platform agnostic rendering or JavaScript SSR. We also lose the ability to work with render functions for advanced abstractions. However as you can probably tell, these capabilities are rarely needed in the context of progressive enhancement.\n\n## Comparison with Alpine\n\n`petite-vue` is indeed addressing a similar scope to [Alpine](https:\u002F\u002Falpinejs.dev), but aims to be (1) even more minimal and (2) more Vue-compatible.\n\n- `petite-vue` is around half the size of Alpine.\n\n- `petite-vue` has no transition system (maybe this can be an opt-in plugin).\n\n- Although Alpine largely resembles Vue's design, there are various cases where the behavior is different from Vue itself. It may also diverge more from Vue in the future. This is good because Alpine shouldn't have to restrict its design to strictly follow Vue - it should have the freedom to develop in a direction that makes sense for its goals.\n\n  In comparison, `petite-vue` will try to align with standard Vue behavior whenever possible so that there is less friction moving to standard Vue if needed. It's intended to be **part of the Vue ecosystem** to cover the progressive enhancement use case where standard Vue is less optimized for nowadays.\n\n## Security and CSP\n\n`petite-vue` evaluates JavaScript expressions in the templates. This means **if** `petite-vue` is mounted on a region of the DOM that contains non-sanitized HTML from user data, it may lead to XSS attacks. **If your page renders user-submitted HTML, you should prefer initializing `petite-vue` using [explicit mount target](#explicit-mount-target) so that it only processes parts that are controlled by you**. You can also sanitize any user-submitted HTML for the `v-scope` attribute.\n\n`petite-vue` evaluates the expressions using `new Function()`, which may be prohibited in strict CSP settings. There is no plan to provide a CSP build because it involves shipping an expression parser which defeats the purpose of being lightweight. If you have strict CSP requirements, you should probably use standard Vue and pre-compile the templates.\n\n## License\n\nMIT\n","petite-vue 是一个为渐进增强优化的 Vue.js 子集，体积仅约 6kb。它支持与标准 Vue 相同的模板语法和响应式模型，但特别适用于在由服务器框架渲染的现有 HTML 页面上添加少量交互。项目基于 `@vue\u002Freactivity` 实现，采用 DOM 原地修改的方式工作，无需构建步骤即可直接通过 CDN 加载使用。适合用于需要轻量级前端交互增强的场景，如静态网站或服务端渲染页面的局部动态化处理。由于项目较新，可能存在一些未发现的问题，在实际应用中需自行评估风险。","2026-06-11 03:37:02","high_star"]