DEV Community

hadakadenkyu
hadakadenkyu

Posted on

Importing Style Tag of SFC to Other SFC in Vue.js

This article is in response to issue where scoped style is not applied when extending components.

// Comp.vue
<script>
export default {
  data: () => ({
    text: 'Hello'
  })
};
</script>
<template>
  <p class="text">
    {{ text }}
  </p>
</template>
<style scoped>
.text {
  background-color: yellow;
  padding: 10px;
  font-size: 1.3rem;
}
</style>
Enter fullscreen mode Exit fullscreen mode
// ExtendedComp.vue
<script>
import Comp from './Comp.vue';
export default {
  extends: Comp,
  data: () => ({
    text: 'Hello extended'
  })
};
</script>
Enter fullscreen mode Exit fullscreen mode

Scoped Style Won't Apply

I found that by writing load in the plugins section of vite.config.js, you can modify the SFC before building. I thought if I could bring the style tag of the extended component similarly, it would work, and it did.

Scoped Style Works

Specifically, I defined the following plugin in vite.config.js.

// vite.config.js
import { fileURLToPath, URL } from 'node:url'
import path from 'node:path'
import { readFileSync } from 'fs'
import { JSDOM } from 'jsdom'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    {
      async load(id) {
        if (id.endsWith('.vue')) {
          const source = readFileSync(id).toString()
          // It doesn't matter what parses the SFC.
          // Since no need for window or body, I use a fragment.
          const frag = JSDOM.fragment(source)
          const stls = frag.querySelectorAll(`style[src$=".vue"]`)
          return [...stls].reduce(async (acc, stl) => {
            const src = stl.getAttribute('src')
            const absPath = path.resolve(path.dirname(id), src)
            // `resolve.alias` can be resolved with `this.resolve`,
            // but relative paths are not resolved, so I do it like this.
            const resolved = (await this.resolve(absPath)) || (await this.resolve(src))
            const source = readFileSync(resolved.id).toString()
            const frag = JSDOM.fragment(source)
            const stls = frag.querySelectorAll(`style`)
            // If the style tag that references the .vue is left,
            // a compile error will occur.
            const regex = new RegExp(`\\s+src=(['"])${src}\\1`)
            // It's easier to manipulate the DOM and output innerHTML,
            // but when parsing as HTML, self-closing tags aren't resolved well,
            // and when parsing as XML, the content of style and script is escaped.
            // So I chose to add the styles to the original string.
            return [...stls].reduce(
              (acc, stl) => acc + stl.outerHTML, acc.replace(regex, '')
            )
          }, source)
        }
      },
    },
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

Then specify the vue file with the style you want to refer to from the extended component.

// ExtendedComp.vue
<script>
import Comp from './Comp.vue';
export default {
  extends: Comp,
  data: () => ({
    text: 'Hello extended'
  })
};
</script>
<style src="./Comp.vue"></style>
Enter fullscreen mode Exit fullscreen mode

Note: You can refer to it even without extending.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (1)

Collapse
 
grantorin profile image
Andrii Mostovenko

Wouldn't it be easier to move the style to a separate file and connect it by import in both components?

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay