I am currently coding a small vue component library for company's internal use. However, I faced some difficulties on building and deploy the project. This component library contains several components inside. And it will finally build by vue-cli-service build --target lib. The problem I am facing right now is that I have a component (i.e. called Audio.vue), and it will import a .mp3 file inside the component. The component is look like this
<template>
<audio controls :src="soundSrc" />
</template>
<script>
import { defineComponent } from 'vue';
import sound from './assets/sound.mp3';
export default defineComponent({
name: 'Audio',
props: {},
setup() {
return { soundSrc: sound };
},
});
</script>
<style scoped></style>
However, I use this component by serving (vue-cli-service serve") my project is fine. But if I build this project by running vue-cli-service build --target lib --name project-name-here. And I use this built version as a git submodule of my library in another vue project by importing project-name-here.common built before. The sound.mp3 import from './assets/sound.mp3' could not be found. It seems it is using a relative path of my another vue project (i.e. localhost:8080) instead of the library project
Here is my vue.config.js
const path = require('path');
module.exports = {
css: {
extract: false,
},
lintOnSave: false,
productionSourceMap: false,
configureWebpack: {
resolve: {
alias: {
mediaAsset: path.resolve(__dirname, './src/components/Audio/assets'),
},
},
},
chainWebpack: (config) => {
const imgRule = config.module.rule('images');
imgRule
.use('url-loader')
.loader('url-loader')
.tap((options) => Object.assign(options, { limit: Infinity }));
const svgRule = config.module.rule('svg');
svgRule.uses.clear();
svgRule
.test(/\.svg$/)
.use('svg-url-loader') // npm install --save-dev svg-url-loader
.loader('svg-url-loader');
config.module
.rule('raw')
.test(/\.txt$/)
.use('raw-loader')
.loader('raw-loader');
config.module
.rule('media')
.test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
.use('url-loader')
.loader('url-loader')
.tap((options) =>
Object.assign(options, {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]',
},
},
})
);
},
};
Appreciated for answering this question. Thanks.
Related
Hello everyone I'm using vue 3 with storybook 6.5.16 and when i import the SVGs as a component using svg-inline-loader i get the following error in storybook app:
enter image description here
(Failed to execute 'createElement' on 'Document' svg is not a valid name)
Storybook main.js
const path = require('path');
module.exports = {
stories: [
'../src/**/*.stories.mdx',
'../src/**/*.stories.#(js|jsx|ts|tsx)',
],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'#storybook/addon-interactions',
],
framework: '#storybook/vue3',
core: {
builder: '#storybook/builder-webpack5',
},
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
config.module.rules.push({
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: "sass-loader",
options: {
additionalData: `
#import "#/assets/scss/main.scss";
`,
implementation: require('sass'),
},
},
],
});
(() => {
const ruleSVG = config.module.rules.find(rule => {
if (rule.test) {
const test = rule.test.toString();
const regular = /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
const regularString = regular.toString();
if (test === regularString) {
return rule;
}
}
});
ruleSVG.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
})();
config.module.rules.push({
test: /\.svg$/,
use: ['svg-inline-loader'],
});
config.resolve.alias['#'] = path.resolve('src');
return config;
},
}
package.json file
enter image description here
SVG Vue components
<template>
<div
ref="icon"
class="v-icon"
#click="onClick"
>
<component
:is="iconName"
class="v-icon__svg"
/>
</div>
</template>
<script>
import Cards from '#/assets/icons/Cards.svg';
export default {
name: 'VIcon',
components: {
Cards,
},
props: {
iconName: {
type: String,
required: true,
},
},
};
</script>
.babelrc file
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
i tried to use vue-svg-loader to replace svg-inline-loader but it didn't work and I got another error while building the app
ModuleBuildError: Module build failed: Error: Cannot find module './Block'
I also tried to use babel-loader in conjunction with vue-svg-loader but unfortunately I also got an error:
enter image description here
has anyone come across this or can you show your use cases of using SVGs components in Storybook and Vue 3?
I have created a component as part of my component library that I am building with Vue3 and Vite. Everything works well, except when I try to use environment variables. I want the app that consumes this component library to be able to provide the component with environment specific data.
I have played around and found that if I have a .env file as part of the component library project, I am able to access those variables, but I want to be able to provide that during runtime and not during build time.
Here is my vite.config.ts
import { defineConfig } from "vite";
import { resolve } from "path";
import vue from "#vitejs/plugin-vue";
import dts from "vite-plugin-dts";
export default ({ mode }) => {
return defineConfig({
optimizeDeps: {
exclude: ["vue-demi"],
},
plugins: [
vue(),
dts({
insertTypesEntry: true,
}),
],
server: {
open: true,
},
build: {
lib: {
entry: resolve(__dirname, "src/lib.ts"),
name: "complib",
fileName: "complib",
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
exports: "named",
},
},
},
});
};
The entry looks like:
import { App, install } from "vue-demi";
import TestComp from "./components/TestComp.vue";
import "./tailwind.css";
install();
export default {
install: (app: App) => {
app.component("TestComp", TestComp);
},
};
export { Header };
And here is a minimal component TestComp.vue:
<script setup lang="ts">
import { onMounted } from "vue";
onMounted(() => {
console.log(import.meta.env.VITE_TEST_VAR);
});
</script>
<template>
<span>Test Comp</span>
</template>
I am creating a Vue component library with Rollup, but when I use slots it gives me the following error:
Uncaught (in promise) TypeError: currentRenderingInstance is null
I made a very simple component in my library:
<script setup></script>
<template>
<button>
<slot></slot>
</button>
</template>
<style scoped></style>
Then I simply use it like this:
<ExampleComponent>
Text
</ExampleComponent>
If I remove the slot and replace it with a prop or hard-coded text, everything works fine.
This is my rollup.config.js:
import { defineConfig } from 'rollup';
import path from 'path';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import vue from 'rollup-plugin-vue';
// the base configuration
const baseConfig = {
input: 'src/entry.js',
};
// plugins
const plugins = [
vue(),
resolve({
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
}),
// process only `<style module>` blocks.
postcss({
modules: {
generateScopedName: '[local]___[hash:base64:5]',
},
include: /&module=.*\.css$/,
}),
// process all `<style>` blocks except `<style module>`.
postcss({ include: /(?<!&module=.*)\.css$/ }),
commonjs(),
];
const external = ['vue'];
const globals = {
vue: 'Vue',
};
export default [
// esm
defineConfig({
...baseConfig,
input: 'src/entry.esm.js',
external,
output: {
file: 'dist/vue-my-lib.esm.js',
format: 'esm',
exports: 'named',
},
plugins,
}),
// cjs
defineConfig({
...baseConfig,
external,
output: {
compact: true,
file: 'dist/vue-my-lib.ssr.js',
format: 'cjs',
name: 'VueMyLib',
exports: 'auto',
globals,
},
plugins,
}),
// iife
defineConfig({
...baseConfig,
external,
output: {
compact: true,
file: 'dist/vue-my-lib.min.js',
format: 'iife',
name: 'VueMyLib',
exports: 'auto',
globals,
},
plugins,
}),
];
Any idea about the problem?
After a whole day of searching, I found the solution (here and here). It's a problem with using a library locally (e.g., through npm link) where it seems there are two instances of Vue at the same time (one of the project and one of the library). So, the solution is to tell the project to use specifically its own vue through webpack.
In my case, I use Jetstream + Inertia, so I edited webpack.mix.js:
const path = require('path');
// ...
mix.webpackConfig({
resolve: {
symlinks: false,
alias: {
vue: path.resolve("./node_modules/vue"),
},
},
});
Or if you used vue-cli to create your project, edit the vue.config.js:
const { defineConfig } = require("#vue/cli-service");
const path = require("path");
module.exports = defineConfig({
// ...
chainWebpack(config) {
config.resolve.symlinks(false);
config.resolve.alias.set("vue", path.resolve("./node_modules/vue"));
},
});
Thanks to #mikelplhts
On vite + esbuild I used:
export default defineConfig({
...
resolve: {
alias: [
...
{
find: 'vue',
replacement: path.resolve("./node_modules/vue"),
},
],
},
...
I want to use Storybook in combination with Vue 3, SCSS and TailwindCSS.
I achieved to configure storybook that TailwindCSS is loading but it fails loading the appropriate component style.
In the .storybook Folder I added the following webpack.config.js:
const path = require('path');
module.exports = ({ config, mode }) => {
config.module.rules.push({
test: /\.scss$/,
use: [
require.resolve("vue-style-loader"),
require.resolve("css-loader"),
require.resolve("sass-loader")
],
});
config.module.rules.push({
test: /\.(css|scss)$/,
loaders: [
{
loader: 'postcss-loader',
options: {
sourceMap: true,
config: {
path: './.storybook/',
},
},
},
],
include: path.resolve(__dirname, '../storybook/'),
});
return config;
};
In the preview.js File in .storybook Folder I added the "Global" CSS to initialise Tailwind. (works fine tho)
import '../src/index.css'
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
And I added a postcss.config.js in the .storybook Folder.
var tailwindcss = require('tailwindcss');
module.exports = {
plugins: [
require('postcss-import')(),
tailwindcss('../tailwind.config.js'),
require('autoprefixer'),
],
};
This setup is able to show / build stories but without the corresponding Vue Component Style.
An example component which I wanna preview in storybook is "Fact.vue".
<template>
<div class="fact"><slot /></div>
</template>
<script>
export default {};
</script>
<style lang="scss">
#use "./src/scss/atoms/fact.scss";
</style>
As you can see I #use the corresponding style
fact.scss:
.fact {
#apply font-bold text-4xl;
}
But how can I make sure that the style of the component is loaded correctly? According to Google Inspector, the #apply is not resolved correctly.
I'm currently building some kind of UI Component library on top of vue.js and vuetify. One component is some pdf viewer, which uses pdf.js to render the pdf. All external libraries hould be excluded from the bundle and will be instead installed in the "consuming" project.
I'm loading pdfjs like this in my SFC:
import pdfJs from 'pdfjs-dist/webpack'
function getDocument(url) {
return pdfJs.getDocument(url).promise
}
To build the npm module I've setup the project using vue-cli and changed the webpack config in vue.config.js like this:
module.exports = {
configureWebpack: {
resolve: {
symlinks: false
}
},
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.externals({
vuetify: 'vuetify',
lodash: 'lodash',
axios: 'axios',
pdfJs: 'pdfjs-dist/webpack'
})
}
},
css: {
loaderOptions: {
sass: {
implementation: require('sass')
}
}
}
}
For all the listed libraries like vuetify, lodash and axios the externalization works without problems. Only pdf.js and its pdf.worker.js still appears in the bundle. How can I externalize pdf.js from the bundle?
WebpackConfig:
{
optimization: {
splitChunks: {
cacheGroups: {
pdf: {
name: `chunk-pdf`,
test: /[\\/]pdfjs-dist[\\/]/,
chunks: 'initial'
}
}
}
}
}