or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asset-resolution.mdbuiltin-components.mdcomponents.mdcomposition-helpers.mddependency-injection.mderror-handling.mdhydration.mdindex.mdinternal-render-helpers.mdlifecycle.mdreactivity.mdscheduler-timing.mdssr-context.mdvdom-rendering.mdwatch-effects.md

asset-resolution.mddocs/

0

# Asset Resolution

1

2

Vue's asset resolution system enables dynamic resolution of components and directives at runtime, supporting flexible component registration and usage patterns.

3

4

## Capabilities

5

6

### Component Resolution

7

8

Resolve registered components by name at runtime.

9

10

```typescript { .api }

11

/**

12

* Resolves a globally registered component by name

13

* @param name - Component name to resolve

14

* @returns Resolved component or component name string if not found

15

*/

16

function resolveComponent(name: string): ConcreteComponent | string;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { defineComponent, resolveComponent, h } from "@vue/runtime-core";

23

24

// Basic component resolution

25

const DynamicComponentExample = defineComponent({

26

props: {

27

componentName: { type: String, required: true }

28

},

29

30

setup(props) {

31

return () => {

32

// Resolve component by name

33

const Component = resolveComponent(props.componentName);

34

35

// Check if component was found

36

if (typeof Component === 'string') {

37

console.warn(`Component "${props.componentName}" not found`);

38

return h('div', `Component "${props.componentName}" not registered`);

39

}

40

41

return h(Component, { message: 'Hello from dynamic component' });

42

};

43

}

44

});

45

46

// Usage with component registry

47

const app = createApp({

48

components: {

49

'my-button': MyButtonComponent,

50

'my-input': MyInputComponent,

51

'my-modal': MyModalComponent

52

},

53

54

setup() {

55

const currentComponent = ref('my-button');

56

57

return () => {

58

const Component = resolveComponent(currentComponent.value);

59

return h(Component);

60

};

61

}

62

});

63

64

// Conditional component rendering

65

const ConditionalRenderer = defineComponent({

66

props: {

67

showAdvanced: Boolean

68

},

69

70

setup(props) {

71

return () => {

72

const componentName = props.showAdvanced ? 'advanced-form' : 'simple-form';

73

const Component = resolveComponent(componentName);

74

75

return typeof Component !== 'string'

76

? h(Component)

77

: h('div', 'Component not available');

78

};

79

}

80

});

81

```

82

83

### Directive Resolution

84

85

Resolve registered directives by name at runtime.

86

87

```typescript { .api }

88

/**

89

* Resolves a globally registered directive by name

90

* @param name - Directive name to resolve (without v- prefix)

91

* @returns Resolved directive or undefined if not found

92

*/

93

function resolveDirective(name: string): Directive | undefined;

94

```

95

96

**Usage Examples:**

97

98

```typescript

99

import { defineComponent, resolveDirective, withDirectives, h } from "@vue/runtime-core";

100

101

// Dynamic directive application

102

const DynamicDirectiveExample = defineComponent({

103

props: {

104

directiveName: String,

105

directiveValue: String

106

},

107

108

setup(props) {

109

return () => {

110

const element = h('input', { type: 'text' });

111

112

if (props.directiveName) {

113

const directive = resolveDirective(props.directiveName);

114

115

if (directive) {

116

return withDirectives(element, [

117

[directive, props.directiveValue]

118

]);

119

}

120

}

121

122

return element;

123

};

124

}

125

});

126

127

// Conditional directive usage

128

const ConditionalDirective = defineComponent({

129

props: {

130

enableTooltip: Boolean,

131

tooltipText: String

132

},

133

134

setup(props) {

135

return () => {

136

let element = h('button', 'Hover me');

137

138

if (props.enableTooltip) {

139

const tooltipDirective = resolveDirective('tooltip');

140

141

if (tooltipDirective) {

142

element = withDirectives(element, [

143

[tooltipDirective, props.tooltipText]

144

]);

145

}

146

}

147

148

return element;

149

};

150

}

151

});

152

153

// Multiple directive resolution

154

const MultiDirectiveComponent = defineComponent({

155

setup() {

156

const directives = ['focus', 'tooltip', 'ripple'];

157

158

return () => {

159

const resolvedDirectives = directives

160

.map(name => ({ name, directive: resolveDirective(name) }))

161

.filter(item => item.directive);

162

163

let element = h('button', 'Multi-directive button');

164

165

if (resolvedDirectives.length > 0) {

166

const directiveBindings = resolvedDirectives.map(({ directive }) => [

167

directive,

168

true // Simple binding value

169

]);

170

171

element = withDirectives(element, directiveBindings);

172

}

173

174

return element;

175

};

176

}

177

});

178

```

179

180

### Dynamic Component Resolution

181

182

Resolve components dynamically with support for various input types.

183

184

```typescript { .api }

185

/**

186

* Resolves dynamic component reference to actual component

187

* @param component - Component reference (string, component object, or function)

188

* @returns Resolved component type

189

*/

190

function resolveDynamicComponent(component: unknown): VNodeTypes;

191

```

192

193

**Usage Examples:**

194

195

```typescript

196

import { defineComponent, resolveDynamicComponent, h } from "@vue/runtime-core";

197

198

// Flexible component resolution

199

const FlexibleRenderer = defineComponent({

200

props: {

201

component: [String, Object, Function]

202

},

203

204

setup(props) {

205

return () => {

206

const resolvedComponent = resolveDynamicComponent(props.component);

207

return h(resolvedComponent);

208

};

209

}

210

});

211

212

// Component factory pattern

213

const ComponentFactory = defineComponent({

214

props: {

215

type: { type: String, required: true },

216

config: Object

217

},

218

219

setup(props) {

220

const componentMap: Record<string, any> = {

221

'button': () => import('./ButtonComponent.vue'),

222

'input': () => import('./InputComponent.vue'),

223

'select': () => import('./SelectComponent.vue')

224

};

225

226

return () => {

227

const componentSpec = componentMap[props.type];

228

const resolvedComponent = resolveDynamicComponent(componentSpec);

229

230

return h(resolvedComponent, props.config);

231

};

232

}

233

});

234

235

// Plugin-based component resolution

236

const PluginRenderer = defineComponent({

237

props: {

238

pluginName: String,

239

componentName: String

240

},

241

242

setup(props) {

243

return () => {

244

// Resolve from plugin registry

245

const pluginComponent = window.plugins?.[props.pluginName]?.[props.componentName];

246

const resolvedComponent = resolveDynamicComponent(pluginComponent || 'div');

247

248

return h(resolvedComponent);

249

};

250

}

251

});

252

```

253

254

### Advanced Resolution Patterns

255

256

```typescript

257

// Asset resolution with fallbacks

258

function useComponentWithFallback(primaryName: string, fallbackName: string) {

259

return computed(() => {

260

const primary = resolveComponent(primaryName);

261

if (typeof primary !== 'string') {

262

return primary;

263

}

264

265

const fallback = resolveComponent(fallbackName);

266

return typeof fallback !== 'string' ? fallback : 'div';

267

});

268

}

269

270

// Lazy directive resolution

271

function useLazyDirective(name: string) {

272

const directive = ref<Directive | null>(null);

273

274

onMounted(() => {

275

directive.value = resolveDirective(name) || null;

276

});

277

278

return directive;

279

}

280

281

// Dynamic asset registry

282

class DynamicAssetRegistry {

283

private components = new Map<string, Component>();

284

private directives = new Map<string, Directive>();

285

286

registerComponent(name: string, component: Component) {

287

this.components.set(name, component);

288

}

289

290

registerDirective(name: string, directive: Directive) {

291

this.directives.set(name, directive);

292

}

293

294

resolveComponent(name: string) {

295

return this.components.get(name) || resolveComponent(name);

296

}

297

298

resolveDirective(name: string) {

299

return this.directives.get(name) || resolveDirective(name);

300

}

301

}

302

303

const registry = new DynamicAssetRegistry();

304

305

// Theme-based component resolution

306

function useThemeComponent(baseName: string) {

307

const theme = inject('theme', 'default');

308

309

return computed(() => {

310

const themedName = `${baseName}-${theme}`;

311

const themedComponent = resolveComponent(themedName);

312

313

// Fallback to base component if themed version not found

314

if (typeof themedComponent === 'string') {

315

return resolveComponent(baseName);

316

}

317

318

return themedComponent;

319

});

320

}

321

322

// Async component resolution

323

async function resolveAsyncComponent(name: string): Promise<Component | null> {

324

const syncComponent = resolveComponent(name);

325

326

if (typeof syncComponent !== 'string') {

327

return syncComponent;

328

}

329

330

// Try to load async

331

try {

332

const module = await import(`./components/${name}.vue`);

333

return module.default;

334

} catch {

335

return null;

336

}

337

}

338

```

339

340

### Error Handling and Validation

341

342

```typescript

343

// Safe component resolution with validation

344

function useSafeComponent(name: string, validator?: (component: any) => boolean) {

345

return computed(() => {

346

const component = resolveComponent(name);

347

348

if (typeof component === 'string') {

349

console.warn(`Component "${name}" not found`);

350

return null;

351

}

352

353

if (validator && !validator(component)) {

354

console.warn(`Component "${name}" failed validation`);

355

return null;

356

}

357

358

return component;

359

});

360

}

361

362

// Directive resolution with type checking

363

function useSafeDirective(name: string): Ref<Directive | null> {

364

const directive = ref<Directive | null>(null);

365

366

watchEffect(() => {

367

const resolved = resolveDirective(name);

368

369

if (resolved && typeof resolved === 'object') {

370

directive.value = resolved;

371

} else {

372

console.warn(`Directive "v-${name}" not found`);

373

directive.value = null;

374

}

375

});

376

377

return directive;

378

}

379

380

// Resolution with dependency tracking

381

function useTrackedResolution() {

382

const resolvedComponents = reactive(new Map<string, Component>());

383

const resolvedDirectives = reactive(new Map<string, Directive>());

384

385

const getComponent = (name: string) => {

386

if (!resolvedComponents.has(name)) {

387

const component = resolveComponent(name);

388

if (typeof component !== 'string') {

389

resolvedComponents.set(name, component);

390

}

391

}

392

return resolvedComponents.get(name);

393

};

394

395

const getDirective = (name: string) => {

396

if (!resolvedDirectives.has(name)) {

397

const directive = resolveDirective(name);

398

if (directive) {

399

resolvedDirectives.set(name, directive);

400

}

401

}

402

return resolvedDirectives.get(name);

403

};

404

405

return {

406

getComponent,

407

getDirective,

408

resolvedComponents: readonly(resolvedComponents),

409

resolvedDirectives: readonly(resolvedDirectives)

410

};

411

}

412

```

413

414

## Types

415

416

```typescript { .api }

417

type ConcreteComponent = ComponentOptions | FunctionalComponent;

418

419

type VNodeTypes =

420

| string

421

| VNode

422

| Component

423

| typeof Text

424

| typeof Static

425

| typeof Comment

426

| typeof Fragment;

427

428

interface Directive<T = any, V = any> {

429

created?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;

430

beforeMount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;

431

mounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;

432

beforeUpdate?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;

433

updated?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;

434

beforeUnmount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;

435

unmounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;

436

}

437

438

interface DirectiveBinding<V = any> {

439

instance: ComponentPublicInstance | null;

440

value: V;

441

oldValue: V | null;

442

arg?: string;

443

modifiers: Record<string, boolean>;

444

dir: ObjectDirective<any, V>;

445

}

446

447

type FunctionalComponent<P = {}, E extends EmitsOptions = {}> = {

448

(props: P, ctx: Omit<SetupContext<E>, 'expose'>): any;

449

props?: ComponentPropsOptions<P>;

450

emits?: E | (keyof E)[];

451

inheritAttrs?: boolean;

452

displayName?: string;

453

};

454

```

455

456

## Usage Guidelines

457

458

### Best Practices

459

460

1. **Cache resolved assets** for performance in frequently called render functions

461

2. **Validate resolved components** before using them in render functions

462

3. **Provide fallbacks** for missing components to avoid runtime errors

463

4. **Use type guards** when working with dynamically resolved assets

464

465

### Common Patterns

466

467

1. **Dynamic form builders** using component resolution for field types

468

2. **Plugin systems** with runtime component registration

469

3. **Theme systems** with themed component variants

470

4. **Micro-frontends** with dynamic asset loading

471

472

### Performance Considerations

473

474

- Asset resolution happens during render, so cache results when possible

475

- Use `computed` to memoize resolution results

476

- Consider lazy loading for components that may not be used