{"id":19201450,"url":"https://github.com/padcom/vuelit","last_synced_at":"2025-02-23T05:26:17.402Z","repository":{"id":209677627,"uuid":"724667758","full_name":"padcom/vuelit","owner":"padcom","description":null,"archived":false,"fork":false,"pushed_at":"2023-11-30T15:09:10.000Z","size":319,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-04T13:18:22.255Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/padcom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-28T14:50:41.000Z","updated_at":"2023-11-30T13:56:10.000Z","dependencies_parsed_at":"2024-11-09T12:49:30.087Z","dependency_job_id":null,"html_url":"https://github.com/padcom/vuelit","commit_stats":null,"previous_names":["padcom/vuelit"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/padcom%2Fvuelit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/padcom%2Fvuelit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/padcom%2Fvuelit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/padcom%2Fvuelit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/padcom","download_url":"https://codeload.github.com/padcom/vuelit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240273999,"owners_count":19775368,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T12:38:48.147Z","updated_at":"2025-02-23T05:26:17.378Z","avatar_url":"https://github.com/padcom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vuelit - Lit with Vue's reactivity system\n\nThis project is a spin-off off a project initially created as a PoC by Evan You in 2020.\n\nYou can check the original project [here](https://www.npmjs.com/package/@vue/lit)\n\nThe point of this library is to be able to create Lit components using the reactivity system of Vue.js. Currently the industry trend is to add signals to every framework out there. Luckily, Vue.js has added it already years ago. What's even nicer is that the implementation is extracted to a separate package which makes it much easier to integrate in other tools.\n\n## Differences from the original project\n\nThe main differences in this implementation are:\n\n- full TypeScript support\n- hidden state / clean(er) API\n- automatic dereferencing of Vue.js' `ref`\n- 2-way binding\n- `provide`/`inject`\n\n## Getting started\n\nVuelit uses the [from-github](https://npmjs.org/package/create-from-github) generator which just clones the given repository and clears it out to a point where it is usable as a new project:\n\n```\nnpm create from-github padcom/vuelit-template hello-world\n```\n\nThe project is very simple but has everything that is needed to get you started.\n\n## Example component\n\nSince the framework is designed to create [webcomponents](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) there is no application framework that you need to instantiate. All you need is your component and an `index.html` to put the tag in.\n\nHere's how you define a component:\n\n```typescript\nimport { defineComponent, html } from 'vuelit'\n\nconst styles = `\n  :host {\n    font-family: monospace\n  }\n`\n\ndefineComponent(\n  // name of the tag that you will later on use in HTML\n  'hello-world',\n  {\n    // Styles are passed as plain text. You can get them from an external file\n    // or specify them in-line. Your choice\n    styles,\n    // Should the component have a `shadowRoot`? (default: false)\n    shadowRoot: true\n  },\n  // A list of reactive properties that will be added to the instance\n  {\n    counter: 0\n  },\n  // The `props` are the same as defined above\n  ({ props }) =\u003e {\n    function increment() {\n      props.counter++\n    }\n\n    // From here on it's all Lit\n    return () =\u003e html`\n      \u003ch2\u003eNumber of clicks: ${props.counter}\u003c/h2\u003e\n      \u003cbutton @click=\"${increment}\"\u003eIncrement\u003c/button\u003e\n      \u003cp\u003eNow delete me and get to work!\u003c/p\u003e\n    `\n  }\n)\n```\n\n## Using reactive `ref` and 2-way data binding\n\nVuelit understands that Vue's [`ref`](https://vuejs.org/api/reactivity-core.html#ref) has a `.value` field. You don't need to dereference it manually.\n\nIf you want to react to the `@input` event to implement 2-way binding there's a special `update()` function that you'd use as follows:\n\n```typescript\nimport { defineComponent, html, ref, update } from 'vuelit'\n\ndefineComponent('hello-world', {}, {}, () =\u003e {\n  const message = ref('Hello, world!')\n\n  return () =\u003e html`\n    \u003cinput .value=\"${message}\" @input=\"${update(message)}\"\u003e\n    ${message}\n  `\n})\n```\n\n## Lifecycle hooks\n\nJust like Vue.js, Vuelit exposes a number of lifecycle hooks:\n\n### `onBeforeMount(({ component }) =\u003e void)`\n\nThis lifecycle hook is called after the setup function has completed but before the component is mounted to the DOM\n\n### `onMounted(({ component }) =\u003e void)`\n\nThis lifecycle hook is called right after the component is mounted in the DOM\n\n### `onBeforeUpdate(({ component }) =\u003e void)`\n\nThis lifecycle hook is called once a change in props is detected but before the internal state is updated\n\n### `onUpdated(({ component }) =\u003e void)`\n\nThis lifecycle hook is called once a change in props is detected and after the internal state is updated\n\n### `onUnmounted(({ component }) =\u003e void)`\n\nThis lifecycle hook is called after the component has been unmounted from the DOM\n\n## `getComponentInstance(): VuelitComponent` - getting component instance\n\nFirst things first: each Vuelit component is actually a valid DOM element. So if you will `console.log` it, it will be a DOM element.\n\nIn many different places getting access to that instance might be very useful, for example to examine some props and/or attributes or to call a method on that given object.\n\nAll lifecycle hooks are provided the `component` instance so you don't have to get it yourself. However, in composables that don't use the lifecycle hooks you still might want to access the `component` instance. For those rare cases there's the `getComponentInstance()` function exposed from Vuelit.\n\n## Dependency injection\n\nVue.js provides a dependency injection mechanism that's really useful for mitigating prop drilling. [Vue.js](https://vuejs.org/guide/components/provide-inject.html) does it using [provide](https://vuejs.org/api/options-composition.html#provide)/[inject](https://vuejs.org/api/options-composition.html#inject) composition functions.\n\nVuelit is no different. It provides the same functionality using `provide()` and `inject()` composition functions:\n\n### `[component.]provide(key: Symbol, value: any)`\n\nProvides a value for injection using `inject()`. Please note `key` needs to be a `Symbol` because the underlying mechanism storing the provided values is a `WeakMap`.\n\nPlease note that both the component instance, that you can access by destructuring it from the first parameter of your composition function as well as a standalone function exported from the library expose this function. This means that in runtime, if you're using Vuelit, you can also dynamically provide values for later injections.\n\n### `[component.]inject\u003cT\u003e(key: Symbol, defaultValue: T): T | null`\n\nInjects a value provided using `inject()`. Please note `key` needs to be a `Symbol` because the underlying mechanism storing the provided values is a `WeakMap`.\n\nPlease note that both the component instance, that you can access by destructuring it from the first parameter of your composition function as well as a standalone function exported from the library expose this function. This means that in runtime, if you're using Vuelit, you can also dynamically retrieve values from providers.\n\n## Exposing additional properties and methods\n\nThe `expose(things)` function is the ultimate weapon when it comes to defining additional APIs for your component.\n\nAssuming you'd like your `\u003ccounter-component\u003e` to also have the `increment()` method:\n\n```typescript\nimport { defineComponent, html, expose } from 'vuelit'\n\ndefineComponent('counter-component', { shadowRoot: true }, { value: 0 }, ({ props }) =\u003e {\n  function increment() {\n    props.value++\n  }\n\n  expose({ increment })\n\n  return () =\u003e html`\n    \u003cdiv\u003eCurrent value: ${props.value}\u003c/div\u003e\n  `\n})\n```\n\nThen later on in your code you can just access that method on the instance:\n\n```typescript\ndocument.querySelector('counter-component').increment()\n```\n\n### Example\n\nLet's assume we have 2 components:\n\n- `\u003ccounter-provider\u003e` that will provide a reactive counter\n- `\u003ccounter-display\u003e` that will display and allow for manipulation of the counter\n\nThis is how you would implement this:\n\n```typescript\nconst counterSymbol = Symbol('counter')\n\ndefineComponent('counter-provider', {}, {}, () =\u003e {\n  const counter = ref(0)\n\n  provide(counterSymbol, counter)\n\n  return () =\u003e html``\n})\n\ndefineComponent('counter-display', {}, {}, () =\u003e {\n  const counter = inject\u003cRef\u003cnumber\u003e\u003e(counterSymbol)\n\n  function increment() {\n    if (counter) counter.value++\n  }\n\n  return () =\u003e html`\n    \u003cdiv\u003e\n      \u003cp\u003eCurrent counter: ${counter}\u003c/p\u003e\n      \u003cbutton type=\"button\" @click=\"${increment}\"\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n  `\n})\n```\n\nAs you can see you can literally provide any value, including reactive refs. The `provide`/`inject` pair doesn't care.\n\nAlternatively you can use the component instance instead of the imported methods:\n\n```typescript\ndefineComponent('counter-provider', {}, {}, ({ component }) =\u003e {\n  component.provide(counterSymbol, ref(0))\n  ...\n\ndefineComponent('counter-display', {}, {}, ({ component }) =\u003e {\n  const counter = component.inject\u003cRef\u003cnumber\u003e\u003e(counterSymbol)\n```\n\n## Credits\n\nBig thank you to to Evan You and the entire Vue.js team.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpadcom%2Fvuelit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpadcom%2Fvuelit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpadcom%2Fvuelit/lists"}