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
import SVG files into the rendered HTML to be able to customize it with CSS
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:
lacks of an "on-the-fly include" system for HTML/SVG/XML-like asset files that can benefit from attribute inheritance
has performance issues for this common task : even if functional components are more performant than stateful components, it still have an unecessary cost for simple HTML/SVG/XML partials that are just created to split the markup and that just need attribute inheritance
CSS scope issues with functional components
CSS scope issues with v-html
performance again: props are unneededly passed while they could just be "copied/included" at build-time from in the parent's context
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.
A compile-tile/build-time component is basically just an "include" that is rendered during build in the regular runtime parent component.
It could have an attribute like a path to an asset file or a static XML-like string that would be transformed to render functions inside the parent and not as a runtime component
Attributes would just be copied on the root node like static attributes and props could be transformed to expressions being part of the parent
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
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:
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
props: ['static', 'dynamic'] would be set on the component
all the code is evaluated in the parent's scope because all this code is just copied to the parent
<template build-time>
<section class="my-own-class">
<!-- some long markup -->
<p>hello {{ props.static/* or just static */ }}, nice to meet {{ props.dynamic /* or just dynamic */ }}</p>
<slot />
<!-- more long markup -->
</section>
</template>
<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.
Props are transformed to either static text or an expression in the parent's scope
Slots are just copied/included as render functions
PROS :
CSS scope hashed could be copied on each markup element as we are doing includes here and replacing instead of instantiating
Performance: no more instantiation or function execution at build-time, it just breaks down to render functions each time
Performance: code can be hoisted since it's all static
DX: long static templates can be shortened to smaller chunks for better readibility and teamwork without sacrificing performance
DX: possibility to include easily HTML/SVG/XML-like partials without external loaders and the issues it brings
DX: no more repetitive functional component creation with attribute inheritance boilerplate to re-create each time
CONS:
API being a little bit different from a runtime component, it may be confusing, but I feel this kind of decision was already made by making functional components not inheriting attributes like regular components, so I guess it's not really an issue here
bundle would become bigger if people use partial many times. This is the Bundle size VS browser performance/calculation dilema that has all build-time frameworks. Same issue for Svelte. I guess this decision will just be upto the end user.
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
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
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:
see: https://github.com/vuejs/vue/issues/7554
3. automatically create functional components on-the-fly with vue-template-loader
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)
App.vue (our "real" runtime parent component file)
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:
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)
App.vue would be transformed to a render function for the runtime that matches this markup:
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.