vuejs / rfcs

RFCs for substantial changes / feature additions to Vue core
4.88k stars 546 forks source link

Ability to create compile-time/build-time components to include HTML/SVG/XML-like partials without losing scope #171

Open darkylmnx opened 4 years ago

darkylmnx commented 4 years ago

What problem does this feature solve?

Not sure if it should be done here or in the vue-loader repo but here it is:

In many project we often need to

  1. import SVG files into the rendered HTML to be able to customize it with CSS
  2. split a page that has a lot of markup into smaller chunks for better visiblity and team work

But... The current Vue components and Vue API:

Whether it is an HTML partial or SVG code, most of the time it will have a root node and be static. Creating a component is the way to go but is most of the time repetitive and will be instantiated and rendered at runtime, which has uneeded performance cost.

Of course, by nature, these build-time/compile-time components are purely static

What does the proposed API look like?

Currently, if an Vue app needs to import HTML/SVG/XML-like assets, there are 3 known solutions:

1. using a webpack plugin + a require to the file and include it with v-html

<div v-html="require('@/assets/illustration.svg?include')" />
<div v-html="require('@/assets/random-partial.html?include')" />

Issues: impossible to add HTML attributes at the root of the SVG, CSS scope issues that forces to use the deep selector, CSS scope hash not copied to the rendered root element see: https://github.com/vuejs/vue-loader/issues/1259

2. manually create a component for each asset file to include

Issues: unneededly repetitive, component instantiation cost, functional components need a repetitive boilerplate to inherit attributes since it does not work like regular components:

<template functional>
  <section :class="[data.staticClass, data.class]" v-bind="data.attrs" :style="[data.staticStyle, data.style]">
    ...
  </section>
</template>

see: https://github.com/vuejs/vue/issues/7554

3. automatically create functional components on-the-fly with vue-template-loader

import MySVG from 'my.svg?vue-template'

Issues: caveats of functionnal components, performance, uneeded component instantiation See: https://codeburst.io/managing-svg-images-in-vue-js-applications-88a0570d8e88

Solution and API

1. The include solution

Use a "build-time" and a "src" prop on the core dynamic component

partial.html (our asset file, could be an SVG or any XML-like with a single root node inside, on not single with Vue 3)

<p class="my-own-class">
  hello i'ma a partial
</p>

App.vue (our "real" runtime parent component file)

<div class="parent">
  <component src="@/assets/partial.html" built-time class="inherited-attribute" />
</div>

No :is prop needed since it's a file include, example would work same with a require( ... ).

App.vue would be transformed to a render function for the runtime that matches this markup:

<div class="parent">
  <p class="inherited-attribute my-own-class">
    hello i'ma a partial
  </p>
</div>

2. The manual component solution

Use a "build-time" propr on a regular imported component or set the component's definition to buildTime: true just like for functionnal components functional: true

Partial.vue

App.vue (our "real" runtime parent component file)

<div class="parent">
  <Partial class="inherited-attribute" static="world" :dynamic="name" built-time>
    <p>This is a slot</p>
  </Partial>
</div>

App.vue would be transformed to a render function for the runtime that matches this markup:

<div class="parent">
  <section class="inherited-attribute my-own-class">
    <!-- some long markup -->
    <p>hello world, nice to meet {{ name }}</p>
    <p>This is a slot</p>
    <!-- more long markup -->
  </section>
</div>

This would be the API and implementation. Globally, this is just like doing SSR for components with static markup at build-time.

PROS :

CONS:

Hope this is all clear.

posva commented 4 years ago

Transferred to RFCs to follow its process