Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-dom): defineCustomElement without shadowDom (#4314) #4404

Closed
wants to merge 11 commits into from

Conversation

gnuletik
Copy link

@gnuletik gnuletik commented Aug 20, 2021

This PR add an optional parameter to defineCustomElement to disable shadow DOM on mount.

This is a first draft, let me know if that sounds good to you and I can add more tests / docs if needed.

Thanks :)

Fixes #4314

@gnuletik
Copy link
Author

There is a current limitation when not using Shadow DOM : using slot inside web components is not working :

<my-comp>
  <div>Some content</div>
</my-comp>

The div is rendered after the element instead of replacing my-comp-defined slot.
This is because slot elements are not natively supported without Shadow DOM.

However, it should be possible to implement slot without shadow DOM inside Vue. Stencil seems to already support this : https://stackoverflow.com/questions/48726904/is-there-a-way-workaround-to-have-the-slot-principle-in-hyperhtml-without-using

@gnuletik
Copy link
Author

I implemented slot without shadow DOM on the same principle as https://github.com/vuejs/vue-web-component-wrapper/ (creating a VNode with innerHTML).

I'm using a local copy of apiCustomElement.ts for the needs of my project.
If anyone is interested, I can share it!

Let me know if you want me to add the slot implementation without shadow DOM to this PR.

Thanks!

@netlify

This comment was marked as outdated.

@netlify

This comment was marked as outdated.

@netlify

This comment was marked as outdated.

@gnuletik gnuletik force-pushed the feat/customElements-shadowRoot branch from 5822d18 to a446bfa Compare January 18, 2022 21:06
@martinmaillard
Copy link

Any chance this will get officially supported?

@chasegiunta
Copy link

chasegiunta commented Mar 19, 2022

Hoping to see this move along! One thing to be mindful of with this option, is how fallthrough attributes behave. For example, currently, class attributes are inherited on single root elements, but they also remain on the custom element leaving duplicate, nested, style declarations. This doesn't present any issues when there's a shadow dom, because nothing falls through...

@chasegiunta
Copy link

@gnuletik Am I correct in that this PR does not support named slots? If your local apiCustomElement.ts does support named slots (please share it!), I would think it needs to be included as part of this PR, just as vue-web-component-wrapper supports them

@gnuletik
Copy link
Author

@chasegiunta You're right, this PR does not support named slots. I don't have remaining local changes that are not part of this PR.

Feel free to create a fork from my own to add support for it :)

@chasegiunta
Copy link

@gnuletik I've added named slot support locally. Getting fallthrough attributes to work for single root elements can be included next to button up this feature, but curious if it's even worth pursuing @LinusBorg @yyx990803 ? Is this the right direction?

@chasegiunta
Copy link

@gnuletik I've PR'd support for named slots into this branch. As for fallthrough attributes onto single root element components, or for slots without parent elements, I've worked out my own solution wrapping the contents in an element with display: contents for now, but I'd suspect that's not the best approach as an official solution to either of those issues. If anyone reading this is interested in that approach until something concrete is put into place, feel free to reach out.

@lcichanowicz
Copy link

lcichanowicz commented Jul 12, 2022

I needed this functionality now so I implemented the PR locally as described here:
#4314 (comment)

However, my Vue project is not using TypeScript, so I used an online TypeScript to JavaScript converter.

My Vue project built fine, via Vue CLI, but I got an error in the browser:

apiCustomElement.js:467 Uncaught ReferenceError: __DEV__ is not defined
    at eval (apiCustomElement.js:467:11)
    at Array.forEach (<anonymous>)
    at VueCustomElement._applyStyles (apiCustomElement.js:460:16)
    at resolve (apiCustomElement.js:296:16)
    at VueCustomElement._resolveDef (apiCustomElement.js:307:9)
    at VueCustomElement._connect (apiCustomElement.js:176:14)
    at VueCustomElement.parsedCallback (apiCustomElement.js:167:14)
    at eval (index.js:18:25)
    at Array.forEach (<anonymous>)
    at upgrade (index.js:15:21)

To resolve it, I had to add a line of code to the converted JavaScript.

const BaseClass =
    typeof HTMLElement !== "undefined" ? HTMLParsedElement : class { }

//add declaration of __DEV__ to resolve error
const __DEV__ = (process.env.NODE_ENV === "development")

export class VueElement extends BaseClass {
    /**
     * @internal

@retail-robot
Copy link

Hi all - are there any specific blockers to moving forward with incorporating this change (aside from resolving the branch conflicts?) I notice this PR hasn't received any updates since last year and was wondering if there's anything I can do to help it along?

@gnuletik
Copy link
Author

gnuletik commented Mar 8, 2023

@retail-robot we are waiting for maintainers feedback.

@jflemingsignifi
Copy link

jflemingsignifi commented Apr 11, 2023

There is an error when trying to build this

D:\vue-next\packages\reactivity\src\index.ts → packages\reactivity\dist\reactivity.global.js...
[!] (plugin rpt2) Error: D:/vue-next/packages/runtime-dom/src/apiCustomElement.ts(25,31): semantic error TS7016: Could not find a declaration file for module 'html-parsed-element'. 'D:/vue-next/packages/runtime-dom/node_modules/html-parsed-element/cjs/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/html-parsed-element` if it exists or add a new declaration (.d.ts) file containing `declare module 'html-parsed-element';`
packages\runtime-dom\src\apiCustomElement.ts
Error: D:/vue-next/packages/runtime-dom/src/apiCustomElement.ts(25,31): semantic error TS7016: Could not find a declaration file for module 'html-parsed-element'. 'D:/vue-next/packages/runtime-dom/node_modules/html-parsed-element/cjs/index.js' 
implicitly has an 'any' type.
  Try `npm i --save-dev @types/html-parsed-element` if it exists or add a new declaration (.d.ts) file containing `declare module 'html-parsed-element';`
    at error (D:\vue-next\node_modules\.pnpm\rollup@2.38.5\node_modules\rollup\dist\shared\rollup.js:5239:30)
    at throwPluginError (D:\vue-next\node_modules\.pnpm\rollup@2.38.5\node_modules\rollup\dist\shared\rollup.js:18179:12) 
    at Object.error (D:\vue-next\node_modules\.pnpm\rollup@2.38.5\node_modules\rollup\dist\shared\rollup.js:18786:24)     
    at Object.error (D:\vue-next\node_modules\.pnpm\rollup@2.38.5\node_modules\rollup\dist\shared\rollup.js:18348:38)     
    at RollupContext.error (D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\src\rollupcontext.ts:37:18)
    at D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\src\print-diagnostics.ts:41:11
    at arrayEach (D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\node_modules\lodash\lodash.js:516:11)
    at Function._.each [as forEach] (D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\node_modules\lodash\lodash.js:9368:14)
    at printDiagnostics (D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\src\print-diagnostics.ts:9:2)
    at Object.transform (D:\vue-next\node_modules\.pnpm\rollup-plugin-typescript2@0.27.3_rollup@2.38.5_typescript@4.5.3\node_modules\rollup-plugin-typescript2\src\index.ts:242:5)

Does something need to be done to fix the html-parsed-element dependency?

@MarcinKarpeta
Copy link

Any update on this? any chance we will see this in official package?

@leonheess
Copy link

leonheess commented May 22, 2023

Are you aware of #7942 and https://github.com/baiwusanyu-c/unplugin-vue-ce?

@haoqunjiang haoqunjiang added the ❗ p4-important Priority 4: this fixes bugs that violate documented behavior, or significantly improves perf. label Aug 28, 2023
@netlify

This comment was marked as outdated.

@github-actions
Copy link

github-actions bot commented Aug 29, 2023

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 87.6 kB (+1.76 kB) 33.2 kB (+590 B) 30 kB (+546 B)
vue.global.prod.js 133 kB (+1.76 kB) 49.9 kB (+600 B) 44.8 kB (+501 B)

Usages

Name Size Gzip Brotli
createApp 48.9 kB (+1.04 kB) 19.3 kB (+457 B) 17.6 kB (+419 B)
createSSRApp 51.7 kB (+1.04 kB) 20.4 kB (+458 B) 18.6 kB (+425 B)
defineCustomElement 52 kB (+1.76 kB) 20.3 kB (+614 B) 18.5 kB (+558 B)
overall 62.3 kB (+1.04 kB) 24.1 kB (+439 B) 22 kB (+447 B)

Not sure if it has broader impacts, but at least it fixes the current
failing e2e tests
`html-parsed-element` schedules `parsedCallback` in rAF, so we have to
wait for at least an animation frame (`nextTick` is not enough)
to ensure the element is fully initialized.

https://github.com/WebReflection/html-parsed-element/blob/11081fc102e2eec745a1a1d32723eef342df2452/index.js#L34
@johnwc
Copy link

johnwc commented Aug 31, 2023

@LinusBorg @sxzz @yyx990803 any chance someone from Vue core team can look at this PR? We need a way to create web components that allows an end-user to be able to customize the style of the component.

Copy link
Member

@haoqunjiang haoqunjiang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to see this feature in Vue.js core.

However, for this PR to move forward, here are some improvements that need to be made:

  • I've copied the html-parsed-element package's source code into the repository to fix some build issues. But as an IIFE, it cannot be tree-shaken, leading to an increase in bundle sizes. It's crucial that this code is refactored to be tree-shakable, ensuring that only users of defineCustomElement experience the bundle size increase.
  • (In my opinion) Introducing an additional argument to defineCustomComponent seems aesthetically inconsistent. Personally, I would prefer a top-level component option alongside the styles option.
  • (Possible caveats?) If this implementation is accepted and merged, we will need a companion PR to the docs repository to
    1. Document this new option
    2. Explain the special parsedCallback trick, which only executes in a requestAnimationFrame callback. This behavior might catch some users by surprise. While most asynchronous operations in Vue.js occur in nextTick, this specific one operates in nextFrame.

@lognaturel
Copy link

Thanks for pushing this forward! @gnuletik any thoughts on @sodatea's comments? Anything eager audience members can do to help? 😊

@EranGrin
Copy link

EranGrin commented Jun 4, 2024

The latest version of https://www.npmjs.com/package/vue-web-component-wrapper can now generate vue web component
without Shadow DOM with slots and name slots and nested web-component
Here is the demo of web component without shadow DOM
https://stackblitz.com/~/github.com/EranGrin/web-component-no-shadow-dom-demo

@yyx990803
Copy link
Member

#4314 (comment)

@yyx990803 yyx990803 closed this Aug 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
❗ p4-important Priority 4: this fixes bugs that violate documented behavior, or significantly improves perf. scope: custom elements version: minor
Projects
Status: Rejected
Development

Successfully merging this pull request may close these issues.

defineCustomElement without shadowDom