Skip to content

Commit 6fd0b58

Browse files
committed
WIP guides
1 parent 9d953eb commit 6fd0b58

9 files changed

+1192
-43
lines changed

guides/advanced_features.md

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Advanced Features
2+
3+
This guide covers advanced LiveVue features and customization options.
4+
5+
## Using ~V Sigil
6+
7+
The `~V` sigil provides an alternative to the standard LiveView DSL, allowing you to write Vue components directly in your LiveView:
8+
9+
```elixir
10+
defmodule MyAppWeb.CounterLive do
11+
use MyAppWeb, :live_view
12+
13+
def render(assigns) do
14+
~V"""
15+
<script setup lang="ts">
16+
import {ref} from "vue"
17+
const props = defineProps<{count: number}>()
18+
const diff = ref<number>(1)
19+
</script>
20+
21+
<template>
22+
Current count: {{ props.count }}
23+
<input v-model="diff" type="range" min="1" max="10">
24+
<button
25+
phx-click="inc"
26+
:phx-value-diff="diff"
27+
class="button">
28+
Increment by {{ diff }}
29+
</button>
30+
</template>
31+
"""
32+
end
33+
34+
def mount(_params, _session, socket) do
35+
{:ok, assign(socket, count: 0)}
36+
end
37+
38+
def handle_event("inc", %{"diff" => diff}, socket) do
39+
{:noreply, update(socket, :count, &(&1 + String.to_integer(diff)))}
40+
end
41+
end
42+
```
43+
44+
## Lazy Loading Components
45+
46+
Enable lazy loading by returning a function that returns a promise in your components configuration:
47+
48+
```javascript
49+
// assets/vue/index.js
50+
const components = {
51+
Counter: () => import("./Counter.vue"),
52+
Modal: () => import("./Modal.vue")
53+
}
54+
55+
// Using Vite's glob import
56+
const components = import.meta.glob(
57+
'./components/*.vue',
58+
{ eager: false, import: 'default' }
59+
)
60+
```
61+
62+
When SSR is enabled, related JS and CSS files will be automatically preloaded in HTML.
63+
64+
## Customizing Vue App Instance
65+
66+
You can customize the Vue app instance in `assets/vue/index.js`:
67+
68+
```javascript
69+
import { createPinia } from "pinia"
70+
const pinia = createPinia()
71+
72+
export default createLiveVue({
73+
setup: ({ createApp, component, props, slots, plugin, el, ssr }) => {
74+
const app = createApp({ render: () => h(component, props, slots) })
75+
app.use(plugin)
76+
app.use(pinia) // Add your plugins
77+
78+
if (ssr) {
79+
// SSR-specific initialization
80+
}
81+
82+
app.mount(el)
83+
return app
84+
}
85+
})
86+
```
87+
88+
Available setup options:
89+
90+
| Property | Description |
91+
|------------|------------------------------------------------|
92+
| createApp | Vue's createApp or createSSRApp function |
93+
| component | The Vue component to render |
94+
| props | Props passed to the component |
95+
| slots | Slots passed to the component |
96+
| plugin | LiveVue plugin for useLiveVue functionality |
97+
| el | Mount target element |
98+
| ssr | Boolean indicating SSR context |
99+
100+
## Server-Side Rendering (SSR)
101+
102+
LiveVue provides two SSR strategies:
103+
104+
### Development (ViteJS)
105+
```elixir
106+
# config/dev.exs
107+
config :live_vue,
108+
ssr_module: LiveVue.SSR.ViteJS
109+
```
110+
Uses Vite's ssrLoadModule for efficient development compilation.
111+
112+
### Production (NodeJS)
113+
```elixir
114+
# config/prod.exs
115+
config :live_vue,
116+
ssr_module: LiveVue.SSR.NodeJS
117+
```
118+
Uses elixir-nodejs for optimized production SSR with an in-memory server bundle.
119+
120+
### SSR Performance
121+
122+
Vue SSR is compiled into string concatenation for optimal performance. The SSR step:
123+
- Only runs during "dead" renders
124+
- Skips during live navigation
125+
- Can be disabled per-component with `v-ssr={false}`
126+
127+
## Client-Side Hooks
128+
129+
Access Phoenix hooks from Vue components using `useLiveVue`:
130+
131+
```vue
132+
<script setup>
133+
import {useLiveVue} from "live_vue"
134+
135+
const hook = useLiveVue()
136+
137+
// Access all Phoenix hook methods
138+
hook.pushEvent("hello", {value: "world"})
139+
hook.handleEvent("response", (payload) => {
140+
console.log(payload)
141+
})
142+
</script>
143+
```
144+
145+
## TypeScript Support
146+
147+
LiveVue provides full TypeScript support:
148+
149+
1. Use the example tsconfig.json from the example project
150+
2. Check `example_project/assets/ts_config_example` for TypeScript versions of:
151+
- LiveVue entrypoint file
152+
- Tailwind configuration
153+
- Vite configuration
154+
155+
For app.js TypeScript support:
156+
```javascript
157+
// app.js
158+
import {initApp} from './app.ts'
159+
initApp()
160+
```
161+
162+
## Next Steps
163+
164+
- Check out the [FAQ](faq.html) for implementation details and optimization tips
165+
- Visit the [Deployment Guide](deployment.html) for production setup
166+
- Join our [GitHub Discussions](https://github.com/Valian/live_vue/discussions) for questions and ideas

guides/basic_usage.md

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Basic Usage
2+
3+
This guide covers the fundamental patterns for using Vue components within LiveView.
4+
5+
## Component Organization
6+
7+
By default, Vue components should be placed in either:
8+
- `assets/vue` directory
9+
- Colocated with your LiveView files in `lib/my_app_web`
10+
11+
You can configure these paths by:
12+
1. Modifying `assets/vue/index.js`
13+
2. Adjusting the LiveVue.Components configuration:
14+
```elixir
15+
use LiveVue.Components, vue_root: ["your/vue/dir"]
16+
```
17+
18+
## Rendering Components
19+
20+
### Basic Syntax
21+
22+
To render a Vue component from HEEX, use the `<.vue>` function:
23+
24+
```elixir
25+
<.vue
26+
v-component="Counter"
27+
v-socket={@socket}
28+
count={@count}
29+
/>
30+
```
31+
32+
### Required Attributes
33+
34+
| Attribute | Example | Required | Description |
35+
|--------------|------------------------|-----------------|------------------------------------------------|
36+
| v-component | `v-component="Counter"`| Yes | Component name or path relative to vue_root |
37+
| v-socket | `v-socket={@socket}` | Yes in LiveView| Required for SSR and reactivity |
38+
39+
### Optional Attributes
40+
41+
| Attribute | Example | Description |
42+
|--------------|----------------------|------------------------------------------------|
43+
| v-ssr | `v-ssr={true}` | Override default SSR setting |
44+
| v-on:event | `v-on:inc={JS.push("inc")}` | Handle Vue component events |
45+
| prop={@value}| `count={@count}` | Pass props to the component |
46+
47+
### Component Shortcut
48+
49+
Instead of writing `<.vue v-component="Counter">`, you can use the shortcut syntax:
50+
51+
```elixir
52+
<.Counter
53+
count={@count}
54+
v-socket={@socket}
55+
/>
56+
```
57+
58+
Function names are generated based on `.vue` file names. For files with identical names, use the full path:
59+
```elixir
60+
<.vue v-component="helpers/nested/Modal" />
61+
```
62+
63+
## Passing Props
64+
65+
Props can be passed in three equivalent ways:
66+
67+
```elixir
68+
# Individual props
69+
<.vue
70+
count={@count}
71+
name={@name}
72+
v-component="Counter"
73+
v-socket={@socket}
74+
/>
75+
76+
# Map spread
77+
<.vue
78+
v-component="Counter"
79+
v-socket={@socket}
80+
{%{count: @count, name: @name}}
81+
/>
82+
83+
# Using shortcut
84+
<.Counter
85+
count={@count}
86+
name={@name}
87+
v-socket={@socket}
88+
/>
89+
```
90+
91+
## Handling Events
92+
93+
### Phoenix Events
94+
95+
All standard Phoenix event handlers work inside Vue components:
96+
- `phx-click`
97+
- `phx-change`
98+
- `phx-submit`
99+
- etc.
100+
101+
### Vue Events
102+
103+
For Vue-specific events, use the `v-on:` syntax:
104+
105+
```elixir
106+
<.vue
107+
v-on:submit={JS.push("submit")}
108+
v-on:close={JS.hide()}
109+
v-component="Form"
110+
v-socket={@socket}
111+
/>
112+
```
113+
114+
Special case: When using `JS.push()` without a value, it automatically uses the emit payload:
115+
```elixir
116+
# In Vue
117+
emit('inc', {value: 5})
118+
119+
# In LiveView
120+
<.vue v-on:inc={JS.push("inc")} />
121+
# Equivalent to: JS.push("inc", value: 5)
122+
```
123+
124+
## Slots Support
125+
126+
Vue components can receive slots from LiveView templates:
127+
128+
```elixir
129+
<.Card title="Example Card" v-socket={@socket}>
130+
<p>This is the default slot content!</p>
131+
<p>Phoenix components work too: <.icon name="hero-info" /></p>
132+
133+
<:footer>
134+
This is a named slot
135+
</:footer>
136+
</.Card>
137+
```
138+
139+
```vue
140+
<template>
141+
<div>
142+
<!-- Default slot -->
143+
<slot></slot>
144+
145+
<!-- Named slot -->
146+
<slot name="footer"></slot>
147+
</div>
148+
</template>
149+
```
150+
151+
Important notes about slots:
152+
- Each slot is wrapped in a div (technical limitation)
153+
- Slots are passed as raw HTML
154+
- Phoenix hooks in slots won't work
155+
- Slots stay reactive and update when their content changes
156+
157+
## Dead Views vs Live Views
158+
159+
Components can be used in both contexts:
160+
- Live Views: Full reactivity with WebSocket updates
161+
- Dead Views: Static rendering, no reactivity
162+
- `v-socket={@socket}` not required
163+
- SSR still works for initial render
164+
165+
## Client-Side Hooks
166+
167+
Access Phoenix hooks from Vue components using `useLiveVue`:
168+
169+
```vue
170+
<script setup>
171+
import {useLiveVue} from "live_vue"
172+
173+
const hook = useLiveVue()
174+
hook.pushEvent("hello", {value: "from Vue"})
175+
</script>
176+
```
177+
178+
The hook provides all methods from [Phoenix.LiveView JS Interop](https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook).
179+
180+
## Next Steps
181+
182+
Now that you understand the basics, you might want to explore:
183+
184+
- [Advanced Features](advanced_features.html) to learn about:
185+
- Using the `~V` sigil for inline Vue components
186+
- Lazy loading components
187+
- Customizing the Vue app instance
188+
- SSR configuration and optimization
189+
- [FAQ](faq.html) for:
190+
- Understanding how LiveVue works under the hood
191+
- Performance optimizations
192+
- TypeScript setup
193+
- Comparison with LiveSvelte

0 commit comments

Comments
 (0)