Vue: icons are not displayed when css.extract: false - vue.js

When building a Vue library (component), according to Vue docs, you can set css.extract: false in vue.config.js to avoid the users having to import the CSS manually when they import the library into an app:
vue.config.js
module.exports = {
css: {
extract: false
}
}
However, when you do that, the icons are not displayed in the production build.
In this case I'm using #mdi/font and weather-icons. Neither of them load:
To reproduce
You can reproduce this with this Vue library (component):
Create new Vue project with vue create test
Clone the repo and put in the same directory as the Vue test project
In vue-open-weather-widget set css.extract: false in vue.config.js;
And comment out CSS import:
import 'vue-open-weather-widget/dist/vue-open-weather-widget.css'
Build vue-open-weather-widget with yarn build
Import it into the test Vue app with yarn add "../vue-open-weather-widget";
Serve the test app yarn serve

I have looked at your lib (nice component BTW). I created a build with css: { extract: false } and first looked at the behavior when importing vue-open-weather-widget.umd.js directly into an HTML file. And that worked without any problems.
The thing is that the fonts remain external in the dist after the build. And it seems that there is a problem to find the fonts when your component is loaded in a Webpack project (in our case Vue CLI project). I don't know why the fonts are not referenced correctly. But I have found another, and possibly a better solution.
As it is stated in the MDI docs, the use of the web fonts can negatively affect the page performance. When importing only one icon, all of them are imported, which in turn increases the bundle size. In such a small component this is more than suboptimal, especially for the component users. Therefore here is the alternative solution, also suggested by MDI:
Use #mdi/js instead of #mdi/font
Remove all #mdi/font references in your code and install deps:
npm install #mdi/js #jamescoyle/vue-icon
Replace all icons with SVG(e.g. in MainView.vue). Note that on this way only icons are included in the bundle that are used in your components:
...
<span #click="state.settings.view = 'settings'">
<svg-icon type="mdi" :path="mdiCogOutline"></svg-icon>
</span>
...
import SvgIcon from '#jamescoyle/vue-icon'
import { mdiCogOutline } from '#mdi/js'
...
components: {
SvgIcon
},
data () {
return {
mdiCogOutline: mdiCogOutline
}
},
Adjust vue.config.js:
module.exports = {
css: {
extract: false
}
}
Build component:
# i would also include --formats umd-min
vue-cli-service build --target lib --formats umd-min --name vue-open-weather-widget src/main.js
Now your dist contains only 192.68 KiB vue-open-weather-widget.umd.min.js and the component is ready to use over CDN or in a Vue CLI Project, without importing any CSS or fonts. I have tested both cases. Here is how it looks like:
Hope it helps you! Feel free to ask if you have further questions.

Related

How to bundle tailwind css inside a Vue Component Package

In one of my projects, I build a nice vue3 component that could be useful to several other projects. So I decided to publish it as an NPM package and share it with everyone.
I wrote the isolate component, build it and publish BUT I use Tailwind css to make the style.
When I publish and install the component everything is working BUT without the beauty of the css part.
I tried several configurations and alternative tools to generate the package that automatically add the tailwind as an inner dependency to my package.
Does someone have experience with this? how can build/bundle my component by adding the tailwind CSS instructions into it?
You're almost there
Since you've got your component working, the majority of the part has been done.
For configuring the styling of the component you need to identify the Tailwind CSS classes being used by your Vue component package and retain them in the final CSS that is generated by the Tailwind engine in your project.
Follow below steps in the project where you want to use your tailwind vue component package.
For Tailwind CSS V3
// tailwind.config.js
module.exports = [
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
]
For Tailwind CSS V2
// tailwind.config.js
module.exports = [
//...
purge: {
//...
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/package-name/**/*.{vue,js,ts,jsx,tsx}" // Add this line
// Replace "package-name" with the name of the dependency package
],
//...
//...
}
]
The content property in the tailwind.config.js file defines file path pattern that the tailwind engine should look into, for generating the final CSS file.
For Pro users
You may also try to automate the above setup by writing an install script for your npm package to add this configuration to the tailwind.config.js file
References
Tailwind Docs - 3rd party integration
It's a bit difficult for someone to answer your question as you've not really shared the source code, but thankfully (and a bit incorrectly), you've published the src directory to npm.
The core issue here is that when you're building a component library, you are running npm run build:npm which translates to vue-cli-service build --target lib --name getjvNumPad src/index.js.
The index.js reads as follows:
import component from './components/numeric-pad.vue'
// Declare install function executed by Vue.use()
export function install (Vue) {
if (install.installed) return
install.installed = true
Vue.component('getjv-num-pad', component)
}
// Create module definition for Vue.use()
const plugin = {
install
}
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null
if (typeof window !== 'undefined') {
GlobalVue = window.Vue
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue
}
if (GlobalVue) {
GlobalVue.use(plugin)
}
// To allow use as module (npm/webpack/etc.) export component
export default component
There is no mention of importing any CSS, hence no CSS included in the built version.
The simplest solution would be to include the index.css import in your index.js or the src/components/numeric-pad.vue file under the <style> section.
Lastly, I'm a bit rusty on how components are built, but you might find that Vue outputs the CSS as a separate file. In that case, you would also need to update your package.json to include an exports field.

Vue CLI plugin CSS preprocessor (sass) transpile in parent

We have a pretty standard Vue CLI environment. It currently ingests a vue plugin we created via the install method. The plugin is also a Vue CLI environment and lives as a git submodule in the repo.
Currently the parent uses sass and sass-loader packages to transpile. It's configured in the vue.config.js settings, like so:
module.exports = {
// Other properties and settings removed to simplify
css: {
loaderOptions: {
sass: {
data: `
#import "#/styles/global.scss";
`
}
}
}
The global.scss just houses all our style includes.
The plugin is set up in a similar way, but none of the code is getting ingested into the parent. Which totally makes sense, as there is nothing importing/building the plugins style files. Anyone know how to import and transpile plugin sass style sheets? Thank you!

What is the right way to import vue package in nuxt?

I try to import this package into my nuxt project.
All my coding experiments can be found here. I will refer to different branches.
There are several ways to do so:
Just import it right in the page like here (master branch)
This way worked well. You can go to the uploading page via a button on a home page.
Until you manually refresh the page
Then you will get this error SyntaxError Cannot use import statement outside a module
The same error happens when you try to build it.
Import it via plugins (like in plugin-use branch with or without vendor option in build)
I've got the same error.
Import it via plugins with some options (like in plugin-options branch)
Then the package loads only when you refresh the page and only in dev mode.
If you will go to that button on a home page (referenced before) - there will be an empty page.
Import it through modules (like in modules branch).
Then the nuxt cannot load at all, this error happens SyntaxError: Invalid or unexpected token
Could you comment on each method and why it doesn't work?
How to import it correctly?
The final answer is following (I've looked up the projects which use this package).
There was a project which run on Nuxt.
These are changes which you should add to #tamzid-oronno 's answer
//vue-upload-multiple-image.js
import Vue from 'vue'
import VueLazyload from 'vue-lazyload'
import VueUploadMultipleImage from 'vue-upload-multiple-image'
Vue.use(VueLazyload) // this is from the package installation guide
Vue.component('VueUploadMultipleImage', VueUploadMultipleImage)
And list it in plugins the same way.
//nuxt.config.js
plugins: [
{ src: '~plugins/vue-upload-multiple-image', ssr: false }
]
Thus you will be able to use the package without importing it in pages as tags. This was implemented in plugin_plus_lazy branch.
Both tags will work vue-upload-multiple-image and VueUploadMultipleImage.
//your-index-file.vue
<template>
<div id="my-strictly-unique-vue-upload-multiple-image" style="display: flex; justify-content: center;">
<vue-upload-multiple-image
#upload-success="uploadImageSuccess"
#before-remove="beforeRemove"
#edit-image="editImage"
:data-images="images"
idUpload="myIdUpload"
editUpload="myIdEdit"
dragText = "Drag an image here"
browseText = "(or click here to browse)"
primaryText = "Default Image"
markIsPrimaryText = "Set as default image"
popupText = "This image will be set as default"
dropText = "Drag and drop"
accept = image/jpeg,image/png,image/jpg,image/tif,image/tiff
></vue-upload-multiple-image>
</div>
</template>
<script>
export default {
name: "AppUpload",
data(){
return{
file:"",
images: []
}
},
methods:{
uploadImageSuccess(formData, index, fileList) { },
beforeRemove (index, done, fileList) { },
editImage (formData, index, fileList) { },
}
}
</script>
<style scoped>
</style>
To create a static version and test it on your local machine do the following:
$ npm run generate
# test the project
$ npm install http-server
$ cd dist
$ http-server -p 3000
I still have a question - why does it work? :)
Use it as plugin.
In the plugins folder, make a file named vue-upload-multiple-image.js
//vue-upload-multiple-image.js
import Vue from 'vue'
import {VueUploadMultiple} from 'vue-upload-multiple-image'
Vue.use(VueUploadMultiple)
List it under plugins block on nuxt.config.js
//nuxt.config.js
plugins: [
{ src: '~plugins/vue-upload-multiple-image', ssr: false }
]
Thus you will be able to use the package on any component of your project

css changed after nuxt generate

I'm using Nuxt with Vuetify.
I created a class and assigned it some padding.
The class is defined in a unscoped <style> in layouts/default.vue.
when I'm on development mode (npm run dev) everything looks great as I aimed for.
the class is on container element so the final html looks like
<div class="container container--fluid my-class">
the devtools look like that when I'm on dev mode:
so my-class is applied. But once I build the project (npm run generate) my-class is overridden by the container class rules:
I guess it is happening because of the order in which the classes combined into a single css but not sure it behaves differently for dev and built projects.
How can I fix it?
After some more digging it seems to be a known issue with nuxt.
It happens when declaring styles in non-scoped style tag, and using it somewhere else.
I followed these steps: https://stackoverflow.com/a/60925793/9103301
which is basically integrating Vuetify into nuxt manually and not with #nuxt/vuetify.
then I could control over the order the css is loaded in nuxt.config.js - first vuetify and then my styling (which I moved from the layout the a css file).
a more basic vuetify plugin that worked for me:
import Vue from "vue"
import Vuetify from "vuetify"
version "^2.1.1" ,
Vue.use(Vuetify)
export default (ctx) => {
const vuetify = new Vuetify({
theme: {
dark: false, // From 2.0 You have to select the theme dark or light here
},
})
ctx.app.vuetify = vuetify
ctx.$vuetify = vuetify.framework
}
You'll have to install icons as well, vuetify default is mdi which is installed with npm install #mdi/font -D
managed to fix this by disabling tree shaking for vuetify. Change the following in nuxt.config.js:
buildModules: [
["#nuxtjs/vuetify", { treeShake: false }],
],

Loading vuetify in a package that i use in a vuetify project

What is the correct way of loading vuetify into a package that i use in a vuetify project?
When serving projects it all seems to work fine but when i build the project i've got some issues with the css/sass
things i've tried:
With vuetify loader: the css is loaded twice so i can't overwrite sass variables
Without vuetify loader: the package doesn't have the vuetify css, so it looks horrible
Without vuetify loader with vuetify.min.css: the css is loaded twice so i can't overwrite sass variables, and the loaded css is all the css so it's huge
My package is called vuetify-resource, and this is the source code of the index.js (without the vuetify loader) At this point everything works on npm run serve But when i build the package doesn't have "access" to the vuetify css.
import Vue from 'vue';
import Vuetify from 'vuetify';
import VuetifyResourceComponent from './VuetifyResource.vue';
Vue.use(Vuetify);
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
To solve my issue i had to do a couple of things.
Make peer dependencies of vuetify and vue
add vuetify to the webpack externals, so when someone uses the package, the package uses that projects vuetify
not longer import vue and vuetify in the index.js it's not needed, the project that uses the package imports that
import the specific components that you use in every .vue file
for example:
Vue.config.js
module.exports = {
configureWebpack: {
externals: {'vuetify/lib': 'vuetify/lib'},
},
};
index.js
import VuetifyResourceComponent from './VuetifyResource.vue';
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
part of the component.vue
import { VDataTable } from 'vuetify/lib';
export default {
name: 'vuetify-resource',
components: {
VDataTable
},
Step 4 in Ricardo's answer is not needed if you use vuetify-loader, it will do the job for you.
And I would modify step 2 to also exclude Vuetify's styles/css from your bundle. If you don't exclude them you can run into styling issues when the Vuetify version differ between your library and your application.
Use a regular expression in vue.config.js like this: configureWebpack: { externals: /^vuetify\// }. That way, only your own styles are included in the library bundle.