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

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.

Related

Web3js fails to import in Vue3 composition api project

I've created a brand new project with npm init vite bar -- --template vue. I've done an npm install web3 and I can see my package-lock.json includes this package. My node_modules directory also includes the web3 modules.
So then I added this line to main.js:
import { createApp } from 'vue'
import App from './App.vue'
import Web3 from 'web3' <-- This line
createApp(App).mount('#app')
And I get the following error:
I don't understand what is going on here. I'm fairly new to using npm so I'm not super sure what to Google. The errors are coming from node_modules/web3/lib/index.js, node_modules/web3-core/lib/index.js, node_modules/web3-core-requestmanager/lib/index.js, and finally node_modules/util/util.js. I suspect it has to do with one of these:
I'm using Vue 3
I'm using Vue 3 Composition API
I'm using Vue 3 Composition API SFC <script setup> tag (but I imported it in main.js so I don't think it is this one)
web3js is in Typescript and my Vue3 project is not configured for Typescript
But as I am fairly new to JavaScript and Vue and Web3 I am not sure how to focus my Googling on this error. My background is Python, Go, Terraform. Basically the back end of the back end. Front end JavaScript is new to me.
How do I go about resolving this issue?
Option 1: Polyfill Node globals/modules
Polyfilling the Node globals and modules enables the web3 import to run in the browser:
Install the ESBuild plugins that polyfill Node globals/modules:
npm i -D #esbuild-plugins/node-globals-polyfill
npm i -D #esbuild-plugins/node-modules-polyfill
Configure optimizeDeps.esbuildOptions to use these ESBuild plugins.
Configure define to replace global with globalThis (the browser equivalent).
import { defineConfig } from 'vite'
import GlobalsPolyfills from '#esbuild-plugins/node-globals-polyfill'
import NodeModulesPolyfills from '#esbuild-plugins/node-modules-polyfill'
export default defineConfig({
⋮
optimizeDeps: {
esbuildOptions: {
2️⃣
plugins: [
NodeModulesPolyfills(),
GlobalsPolyfills({
process: true,
buffer: true,
}),
],
3️⃣
define: {
global: 'globalThis',
},
},
},
})
demo 1
Note: The polyfills add considerable size to the build output.
Option 2: Use pre-bundled script
web3 distributes a bundled script at web3/dist/web3.min.js, which can run in the browser without any configuration (listed as "pure js"). You could configure a resolve.alias to pull in that file:
import { defineConfig } from 'vite'
export default defineConfig({
⋮
resolve: {
alias: {
web3: 'web3/dist/web3.min.js',
},
// or
alias: [
{
find: 'web3',
replacement: 'web3/dist/web3.min.js',
},
],
},
})
demo 2
Note: This option produces 469.4 KiB smaller output than Option 1.
You can avoid the Uncaught ReferenceError: process is not defined error by adding this in your vite config
export default defineConfig({
// ...
define: {
'process.env': process.env
}
})
I found the best solution.
The problem is because you lose window.process variable, and process exists only on node, not the browser.
So you should inject it to browser when the app loads.
Add this line to your app:
window.process = {
...window.process,
};

Vue: icons are not displayed when css.extract: false

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.

import global scss variables with Vue CLI and Vuetify

when importing a global scss file into my project via:
import into main.js, didn't let me use the variables i defined but classes and ids work.
import into App.vue, didn't load any styles at all. and i removed the scoped of course
import into vue.config.js as following
module.exports = {
transpileDependencies: [
'vuetify'
],
css: {
loaderOptions: {
sass: {
prependData: '#import "#/scss/_variables.scss";'
}
}
}
}
breaks my whole application and gives me tons of errors in cmd rel. to Vuetify.
can anybody tell me how i can import a global scss into a SPA made with latest Vue CLI and Vuetify?
You don't need to import the variables explicitly. According to the official documentation https://vuetifyjs.com/en/customization/sass-variables vue-cli-plugin-vuetify do this itself. To use global variables just follow these simple steps.
Add Vuetify using command vue add vuetify This will automatically add all the required dependency like vuetify-loader, vue-cli-plugin-vuetify, etc.
Make a variables.scss file in src/{scss, sass or styles} directory. As these are the default directories for global variables Example variable file.
Run the server, you are ready to go.
In general, this seems to work now:
// vue.config.js
module.exports = {
css: {
loaderOptions: {
sass: {
additionalData: '#import "~#/assets/scss/_variables.scss";'
}
}
}
}

bootstrap-vue's `b-tabs` renders differently depending on the Vue build that I import

I'm using bootstrap-vue and I've noticed b-tabs renders differently depending on the Vue build that I import:
If I import vue it renders correctly:
https://codesandbox.io/s/vue-template-77mzg
But if I import vue/dist/vue.common or vue/dist/vue It renders wrongly:
https://codesandbox.io/s/vue-template-y0t15
Also, it doesn't happen with other components, like b-navbar-nav. They render correctly regardless of the vue build I import.
I'd like to understand why does it happen, since I need to import a vue version that includes the compiler because some components need it.
Thanks!
When importing a specific variant of Vue (i.e. commonjs vs ES), you need to set up an alias in webpack to ensure that BootstrapVue (and other dependants such as PortalVue) use the same build of Vue (as BootstrapVue also imports from vue).
See the docs on setting up aliases (so you can just import Vue from 'vue'):
https://bootstrap-vue.js.org/docs#aliasing-vue-import
i.e. for Webpack config
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
}
}

Dedupe CSS in Vue.js development mode

I am working on a Vue.js project that heavily uses single file components. These components have scss styles associated with them.
In production mode the duplicate css that occurs from importing the same component multiple times is filtered out. But in development mode the same scss is imported multiple times.
This leads to slow downs with the chrome debugger when inspecting and modifying the css.
Does anone know a way to dedupe the css/scss attatched to single file components in developlment mode?
Here is my current vue config:
module.exports = {
lintOnSave: false,
configureWebpack: {
resolve: {
alias: require("./aliases.config").webpack
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
_: "lodash"
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
}
Here's how we ended up solving it.
Import only pure SCSS in components (ie. mixins, variables, functions). If a file with CSS is imported in each component the sass loader will NOT dedupe the CSS in development mode.
In your vue config add the following to include you scss variables in every single file component:
module.exports = {
...
css: {
loaderOptions: {
sass: {
data: `
#import "#src/_variables.scss";
`
}
}
},
...
}
Import your global scss in your app entry (main.js or equivalent)
import "bootstrap";
import "#src/global.scss";
In your global.scss file you can import your variables file so that it can also access your scss variables.