Implementing Vue SFC with Webpack (no Vue CLI) - vue.js

I've recently switched to Webpack and have all my JS and CSS running perfectly through it now. Here's the relevant piece of webpack.config.js:
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
},
{loader: 'import-glob-loader'}
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
},
{loader: 'sass-loader'},
{loader: 'import-glob-loader'}
]
}
]
I have Vue included from a CDN and with this setup I can do the following no problem:
Vue.component('test-component', {
data: function () {
return {
title: 'Title',
description: 'Description'
};
},
methods: {
created: function () {
console.log('Created');
}
},
template: '<section id="test-component"><h2>{{ title }}</h2>{{ description }}</section>'
});
new Vue({el: '#app'});
And in my HTML:
<div id="app">
<test-component></test-component>
</div>
I'd now like to use Vue single file components instead, and reading the docs it tells me to simply run .vue files through vue-loader, so I changed my rules to the following:
rules: [
// NOTE: This is new
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
},
{loader: 'import-glob-loader'}
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')
]
}
},
// NOTE: This is new too, but commented because it throws errors
// {loader: 'vue-style-loader'},
{loader: 'sass-loader'},
{loader: 'import-glob-loader'}
]
}
]
With that in place my .vue files are picked up and added to dist/main.js so it seems to be working (as long as I don't include a <style> element in the Vue file in which case it fails), but now new Vue({el: '#app'}) does absolutely nothing. Checking the DOM the <test-component> is still in there and not rendered by Vue at all.
If I also try to enable vue-style-loader the build fails entirely saying:
(1:4) Unknown word
> 1 | // style-loader: Adds some css to the DOM by adding a <style> tag
| ^
2 |
3 | // load the styles
What am I doing wrong here?
Edit: Progress. Thanks to Daniel my <style> now works as long as it has lang="scss" set. This is because my webpack config only has rules for scss files and not css files.
I've also figured out the reason the <test-component> won't render is because I never actually register it, simply including the .vue-file is not enough for it to be registered obviously.
The problem I'm having now is trying to glob import all my .vue-files as an array of components. If I do this it works fine:
import TestComponent from "./test-component.vue";
import AnotherComponent from "./another-component.vue";
document.querySelectorAll('[data-vue]').forEach(el => {
new Vue({
el: el,
components: {
'test-component': TestComponent,
'another-component': AnotherComponent
}
});
});
But I'd like to be able to do this some how:
import components from "./**/*.vue";
document.querySelectorAll('[data-vue]').forEach(el => {
new Vue({
el: el,
components: components
});
});
Using import-glob-loader.

Simply importing the vue files is not enough for them to be available for use. You also have to register them with Vue.
So, this is wrong:
import 'component.vue';
new Vue({el: '#app'});
This is right:
import component from 'component.vue';
new Vue({
el: '#app',
components: {
'component': component
}
});
That takes care of making them usable.
The reason the <style> elements don't work is because I don't have a webpack rule for CSS files - only for SCSS files. Thanks to #Daniel for pointing out that I need <style lang="scss">.
vue-style-loader is only needed to inject styles into the DOM as <style> elements which I don't actually use (I use mini-css-extract-plugin to create css-files) also according to the docs:
However, since this is included as a dependency and used by default in vue-loader, in most cases you don't need to configure this loader yourself.
Will create a separate question regarding the glob import.

Make sure you have <style lang="scss"> in your SFC
You can also try deleting the package-lock and node_modules folder and do a clean install. Sometimes that can resolve an issue if the dependencies are not using compatible versions.
Update
To import using glob style imports you may need to use import-glob
https://www.npmjs.com/package/import-glob
You can also achieve similar result using global component registration. This is documented well in the official docs at:
https://v2.vuejs.org/v2/guide/components-registration.html#Automatic-Global-Registration-of-Base-Components

Related

Dynamic Classes in Vue 3 SFC

I try to inject classnames into a Vue 3 component via props and would like to keep the classes defined in the component. Problem is, those classes are not included in the build, since they are "unreferenced".
Is there a way around this?
<script setup lang="ts">
const {className} = defineProps<{className: string;}>();
</script>
<template>
<div :class="className">Hello</div>
</template>
<style lang="scss" scoped>
.some-class {
color: pink;
}
</style>
.some-class is not included in the build since it's not directly referenced :-(
The application is built using webpack 5 with vue-style-loader:
module: {
rules: [
{ test: /\.css$/, use: ["vue-style-loader", "css-loader"] },
{
test: /\.scss$/,
use: [
"vue-style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
additionalData: `#import "scss/_variables.scss";`
}
}
]
}
]
}
EDIT: Added my rather unspectacular webpack5 config
Thanks

How do I edit vue-loader to support audio files?

this is my first question here so please let me know if you need more info.
I am working on a small project using vue CLI 3 and I want to add audio and audio controls but I get the following error:
Module parse failed: Unexpected character '' (1:0) You may need an
appropriate loader to handle this file type, currently no loaders are
configured to process this file. See
https://webpack.js.org/concepts#loaders
I don't really know how to edit webpack. Nonetheless, I found this in the documentation to create a vue.config.js file. But I don't really understand what should I add there.
this is how my component looks:
<template>
<div class="controller-container">
<audio controls>
<source src="#/assets/Catastrophe03music.m4a" type="audio/mp4" />
</audio>
</div>
</template>
<script>
export default {
name: "MusicController",
components: {},
};
</script>
thanks for helping
If you are using Vue App then go to webpack.config.js and add the following code
module: {
rules: [
{
test: /\.mp3$/,
loader: 'file-loader',
exclude: /node_modules(?!\/foundation-sites)|bower_components/,
options: {
name: '[path][name].[ext]'
}
}
]
}
But if you have webpack.mix.js file then add the following code.
mix.webpackConfig({
module: {
rules: [
{
test: /\.mp3$/,
loader: 'file-loader',
exclude: /node_modules(?!\/foundation-sites)|bower_components/,
options: {
name: '[path][name].[ext]'
}
}
]
}
});

Got error when testing vue-echarts component with Jest

I have problem with component testing when using 'vue-echarts'
InfoBoard.spec.ts
import { render } from '#testing-library/vue'
import InfoBoard from '#/components/InfoBoard.vue'
describe('InfoBoard', () => {
test('Should be truthy', () => {
const wrapper = render(InfoBoard, {
stubs: {
'v-charts': true
},
})
expect(wrapper).toBeTruthy()
})
})
InfoBoard.vue
<template>
<div>
<v-chart></v-chart>
</div>
</template>
<script>
import { defineComponent } from '#nuxtjs/composition-api'
import VChart from 'vue-echarts'
export default defineComponent({
components: {
VChart
},
setup() {}
})
</script>
jest.config.js
module.exports = {
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js'
},
moduleFileExtensions: [
'ts',
'js',
'vue',
'json'
],
transform: {
"^.+\\.ts$": "ts-jest",
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest'
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue'
],
transformIgnorePatterns: [
"<rootDir>/node_modules/(?!echarts)",
"<rootDir>/node_modules/(?!echarts\/core)",
"<rootDir>/node_modules/(?!vue-echarts)"
],
testEnvironment: 'jsdom',
setupFilesAfterEnv: ["./jest-setup.js"]
}
Got Error:
FAIL components/InfoBoard.spec.ts
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
/Users/admin/Documents/THIP/node_modules/echarts/core.js:20
export * from './lib/export/core';
^^^^^^
SyntaxError: Unexpected token 'export'
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1479:14)
at Object.<anonymous> (node_modules/vue-echarts/dist/index.cjs.min.js:1:179)
I think the problem is in 'transformIgnorePatterns', maybe I write wrong patterns.
I searching for many day, I tried many anwser like change 'testEnviroment' or add plugin in .babelrc but don't found the solution.
I ran into the same problem and was just able to get it working by adding the following setting to my jest.config.js:
module.exports = {
transformIgnorePatterns: ["/node_modules/(?!(echarts|zrender)/)"],
}
If you wrote your configuration as provided here, I assume you wouldn't have to include the root directory in the ignore-pattern. It didn't work either when having more than one ignore-pattern; but this might be because of the way I wrote the ignore-pattern.

Webpack not loading Vue's single file components CSS

Webpack is compiling single file components but not loading CSS. The HTML and Vue is rendered correctly but without CSS. It seems to be an issue with webpack configuration. Any idea what's wrong?
I'm using webpack-dev-server to load the development server.
src/index.html
<html>
<head>
<title>Vue Hello World</title>
</head>
<body>
<h1>Header</h1>
<div id="app"></div>
</body>
</html>
src/Hello.vue
<template>
<p>{{ greeting }} Test!</p>
</template>
<script>
module.exports = {
data : function () {
return {
greeting: 'Hello'
}
}
}
</script>
<style scoped>
p {
font-size: 18px;
font-family: 'Roboto', sans-serif;
color: blue;
}
</style>
src/main.js
import Vue from 'vue';
import Hello from './Hello.vue';
new Vue({
el: '#app',
render: h => h(Hello),
});
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
{ test: /\.vue$/, exclude: /node_modules/, use: 'vue-loader' },
{ test: /\.css$/, exclude: /node_modules/, use: ['vue-style-loader', 'css-loader']},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
]
};
I just spent the last 4 hours figuring this out. I wanted to use only Webpack because it integrates much nicer into my Gulp pipeline than Vue CLI does. Not sure what version of css-loader you're on and if it's the same cause, but hope this helps you out as well.
TL;DR: instead of just 'css-loader', use:
{
loader: 'css-loader',
options: {
esModule: false
}
}
I use SCSS which has the same problem, but for your example that would be:
{ test: /\.css$/, exclude: /node_modules/, use: ['vue-style-loader', { loader: 'css-loader', options: { esModule: false }}]}
I'm not sure if this is a proper solution or just a workaround. I figured this out because I had an old project that worked before, but as soon as I upgraded all packages it failed. Eventually I traced it down to css-loader, which was version 2.1 before and 4.3 after the update. It worked up to version 3.6, then with 4.0 it failed. Then I just tried toggling the various options and this was the one that fixed it (I never claimed to know what I was doing ;-)).
Edit: seiluv added a link to a comment on vue-style-loader's Github that confirms this as a workaround. It was apparantly posted a month before, but I did not find it in my search for an answer. Thanks seiluv!
I encountered the same problem, so either :
Use style-loader instead of vue-style-loader
As suggested here.
use: ['style-loader', 'css-loader']
Basically the upgrade to css-loader 4.x.x + doesn't work well with vue-style-loader.
or
Disable css-loader's esModule option
The solution by MvRens is also valid
and confirmed by this comment on github.
The problem is css-loader's esModule is set to true by default and should be false in this case.
In addition to the above two answers, here sum up the possible solutions including the new one -- sideEffects. Please edit the list if someone can improve it.
Possible Solutions
in webpack.config.js
Add esModule: false to css-loader's option. (as #MvRens's answer)
Change vue-style-loader to style-loader in webpack.config.js. (as #seiluv's answer)
in package.json
Add "*.vue" to {sideEffects: []}
See: https://github.com/vuejs/vuepress/issues/718#issuecomment-412345448 and https://stackoverflow.com/a/53737250/493168

Using lang="pug" with Quasar CLI

I've started a brand new Quasar CLI project and I'd like to use Pug in the way I'm used to doing: incorporating a lang="pug" tag in some of my .vue files:
<template lang="pug">
q-page(class="flex flex-center")
p Well hello there
...
This throws this error:
Component template requires a root element, rather than just text
The advice given by Quasar is to add this configuration:
// quasar.conf.js
build: {
extendWebpack (cfg) {
cfg.module.rules.push({
test: /\.pug$/,
loader: 'pug-plain-loader'
})
}
}
But that presupposes that all my pug files are in files called .pug, which isn't my preference.
Is there a way to have lang="pug" work like normally in my vue-cli projects?
Quasar CLI........ v0.17.24
Quasar Framework.. v0.17.20
This should work as it is. I have set up a new quasar project and added pug to the webpack rules like so
extendWebpack (cfg) {
cfg.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/
})
cfg.module.rules.push({
test: /\.pug$/,
loader: 'pug-plain-loader'
})
}
My app.vue looks like this
<template lang="pug">
div(id="q-app")
router-view
</template>
And everything works. Im using quasar 1.0 beta and its the default project format created when you create using the CLI.
In my case Ben solution didn't work: what I figured out was to setup that block as follows, in the quasar.conf.js file:
extendWebpack (cfg) {
cfg.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/
})
},
chainWebpack (chain) {
chain.module.rule('pug')
.test(/\.pug$/)
.use('pug-plain-loader')
.loader('pug-plain-loader')
}
With that, I just require to add the atribute lang="pug" in any template where I use the pug syntax.
Ben's solution triggered me a lot of errors with existing Vue files, and js files.