Vue 2.7. Builded with webpack-stream.
I am try to add another vue template using the same js defenitions.
My folder structure is folowing:
I am including this component via import vue files in js:
import productItemPreview from "Components/product-item-preview/product-item-preview.vue";
And use the components object inside vue constructor to define that
global.catalog = new Vue({
el: "#catalog",
data: {
...
},
computed: {
...
},
components: {
productItemPreview,
},
}
The vue template imports the component js definition and styles(if that needs)
<template>
<div>
</div>
</template>
<script src="./product-item-modal.js"></script>
<style lang="less" scoped src="./product-item-modal.less"></style>
And that forked fine for me.
But now i want to define the another template for some cases.
I know i can just add property to component and switch template with v-if, v-show etc.
But using that import logic i just try to another vue template which imports the same js definition product-item-preview.js.
I did that and that worked fine for new template.
But when i return to the native page with old template i saw that error:
[Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'length')"
found in
---> <ProductItemPreview> at assets/vue/components/product-item-preview/themes/mainpage__product-item-preview.vue
<Root>
Why in the old page with old vue import the component use new vue template?
Is this problem with webpack or with Vue?Maybe that antipattern for vue and i just dont realize how the imports inside vue works.
Webpack-stream config :
webpackStream({
mode: "development",
output: {
filename: "app.js",
},
resolve: {
alias: {
Images: path.resolve(__dirname, "public_html/assets/"),
Components: path.resolve(__dirname, "assets/vue/components/"),
},
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules)/,
loader: "babel-loader",
},
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.less$/,
use: ["vue-style-loader", "css-loader", "less-loader"],
},
{
test: /\.css$/,
use: ["vue-style-loader", "css-loader",],
},
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /internal/,
use: {
loader: "svg-url-loader",
options: { noquotes: false },
},
},
{
resourceQuery: /external/,
use: [
"vue-loader",
{
loader: "svg-url-loader",
},
],
},
],
},
],
},
plugins: [new VueLoaderPlugin()],
externals: {
jquery: "jQuery",
},
})
Related
How can values be passed to Vue.js from within a server-rendered template (i.e. integrate Vue.js app with a django template)?
Use Cases
Enhancement of specific pages in a server-rendered environment: Embed a simple vue application on one specific page of your server-rendered web application (i.e. django) to enhance the frontend user experience for a specific functionality (i.e. a signup form)
Content Management Systems (CMS): In a server-rendered CMS, a vue application can be exposed to the editors as an independent CMS component. The editor can then choose where to place the vue application on specific pages, at specific positions.
Integration Challenges:
Global settings, such as endpoint URLs or other such environment variables
configurations set by the editor: i.e. URL of user profile picture (of course it would be better to fetch such information via a REST API).
translations: If your server-rendered web framework already centrally handles translations (i.e. django's makemessages CLI command) it would be great to include the Vue.js application string translations.
Here is a first solution approach for django + Vue.js v2. It includes some stuff that is only relevant for django CMS. But the general principle applies to any server-side rendering CMS or web framework.
Enable Vue.js support in your Webpack config, something like the below:
frontend/webpack.config.js:
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require(`mini-css-extract-plugin`);
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const isServerMode = process.env.NODE_ENV === 'production';
const config = {
mode: 'development',
entry: {
// here, any number of entrypoints (bundles) can be defined
"marketplace-app": './frontend/blocks/marketplace-app/index.js',
},
output: {
filename: '[name].bundle.js',
path: __dirname + '/dist/',
publicPath: `http://localhost:8090/assets/`,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.svg$/i,
exclude: /fonts/,
loader: 'svg-url-loader',
},
{
test: /\.(sass|scss|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
sourceMap: true,
plugins: () => {
return [
require('precss'),
require('autoprefixer'),
];
},
hmr: true,
}
},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'sass-loader', options: {sourceMap: true}},
]
},
{
// images
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
query: {
hash: 'sha512',
digest: 'hex',
name: '[name].[ext]'
}
}
},
{
loader: 'image-webpack-loader',
options: {
query: {
bypassOnDebug: 'true',
mozjpeg: {progressive: true},
gifsicle: {interlaced: true},
optipng: {optimizationLevel: 7},
}
}
}
]
},
{
test: /\.(svg)(\?[\s\S]+)?$/,
// svg fonts cannot be processed the way we do with svg images above
// therefore they are handled separately here
include: /fonts/,
use: [
'file-loader'
]
},
{
test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
},
{
test: /\.(ttf|eot)(\?[\s\S]+)?$/,
loader: 'file-loader',
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader'],
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader'],
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader'
}, /*{
loader: 'eslint-loader' // You can uncomment this if you want compiling to fail if linting fails
}*/]
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.vue'],
modules: [
path.resolve('frontend'),
'node_modules'
],
alias: {
modernizr$: path.resolve(__dirname, '/frontend/.modernizrrc'),
vue: process.env.NODE_ENV === 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
}
},
devServer: {
contentBase: path.resolve(__dirname, `frontend`),
headers: {'Access-Control-Allow-Origin': '*'},
host: `localhost`,
port: 8090,
hot: true,
inline: true,
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: '[name].css'}),
],
devtool: 'eval-source-map',
optimization: {
// the default config from webpack docs, most of it might be useless
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
if (isServerMode) {
config.mode = 'production';
config.devtool = 'none';
config.output.filename = '[name].js';
config.output.publicPath = '/static/dist/';
}
module.exports = config;
example for the server-side template (i.e. in django this could be something like backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html):
Notes:
This demos how to import one value (an endpoint url) from the django template into the Vue.js application.
supports local dev env mode with hot reloading via webpack dev server based on the value of a global setting variable settings.DJANGO_ENV
settings.WEBPACK_DEV_URL needs to be set to publicPath from the Webpack dev server setting in webpack.config.js
data-is-reload-on-page-edit class needs to be tied to CMS functionality that reloads the js sources upon a frontend editing action, if CMS has such functionality.
{% load i18n staticfiles static thumbnail %}
{% comment %} this is a vue widget from frontend/blocks/marketplace-app/index.js {% endcomment %}
<div id="{{ instance.anchor_id }}">
<div class="marketplace-vue-widget" data-endpoint-url='{% url 'marketplace:article-list' %}'></div>
</div>
{% if settings.DJANGO_ENV.value == 'local' %}
<script data-is-reload-on-page-edit defer src="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.bundle.js"></script>
<link rel="stylesheet" href="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.css">
{% else %}
<script data-is-reload-on-page-edit defer src="{% static 'dist/marketplace-app.js' %}"></script>
<link rel="stylesheet" href="{% static 'dist/marketplace-app.css' %}">
{% endif %}
Frontend setup (frontend/blocks/marketplace-app/index.js):
import Vue from 'vue';
import App from './app.vue'
// this relates to backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html
// CMS plugins could (in theory) be added more than once to a page,
// we take care to allow the vue instance to be mounted on any occurrences
// create a constructor for your widget
let Widget = Vue.extend({
render(h) {
return h(App, {
props: { // load stuff from data attributes in the django template
endpointUrl: this.$el.getAttribute('data-endpoint-url'),
}
})
},
});
// mount the widget to any occurences
let nodes = document.querySelectorAll('.marketplace-vue-widget'); // unique selector!
for (let i = 0; i < nodes.length; ++i) {
new Widget({el: nodes[i]})
}
app.vue:
<template src='./app.html'></template>
<script src="./app.js"></script>
<style lang="scss" scoped src="./app.scss"></style>
app.js:
// example code
export default {
props:["endpointUrl"],
data() {
return {}
},
computed: {},
mounted() {},
watch: {},
methods: {},
components: {},
}
I am using vuejs + webpack + electron.
I have hard time retrieving the path to my asset.
My project structure is the following:
/my-project
/app
/components
componentA.vue
...
App.vue
main.js
/dist
/assets
/icons
/a.png
package.json
webpack.config.js
I load the static asset in the dist folder with file-loader.
My webpack.config.js looks like:
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './app/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
}
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015'],
plugins: ['transform-runtime']
}
}
},
{
test: /\.(jpe?g|png|gif|svg|ico)$/,
exclude: /node_modules/,
loader: "file-loader?name=/assets/icons/[name].[ext]"
},
]
},
resolve: {
alias: {
vue: 'vue/dist/vue.js'
}
},
plugins: [
new webpack.ExternalsPlugin('commonjs', [
'electron'
])
],
node: {
__dirname: true,
fs: 'empty',
module: 'empty',
net: 'empty',
tls: 'empty'
},
externals: ["fs"]
}
In the file componentA.vue I am trying to do the following:
<template>
<div>
<img :src= "link">
</div>
</template>
<script>
import Aa from '../assets/icons/a.png'
export default {
data () {
return {
link: '',
}
},
created() {
this.link = Aa;
},
...
</script>
But I have the following error:
Failed to load resource: net::ERR_FILE_NOT_FOUND file:///a.png
The images appear loaded in the correct path in the dist/assets/icons but electron is trying to load it from
file:///assets/icons/a.png
I'm new to webpack and tried to load it in different ways but i can't make it work, can you help me in resolving this issue ? Thanks a lot !
In my app I'm initializing a Vue app, which uses single file .vue components.
I use Webpack to bundle, and vue-loader + postcss-modules to generate scoped classes.
But for some reason I can't access the generated classes inside my components ($style object is empty). I'll explain the problem below and created this repo as an example.
My hello.vue component looks like this:
<template>
<div :class="$style.hello">
Hello World!
</div>
</template>
<script>
export default {
name: "hello",
created() {
console.log(this.$style); // <- empty object :(
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
hello.vue.json is generated as expected (CSS Modules mapping):
{"hello":"_hello_23p9g_17"}
Scoped styles are appended in the document head, and when using mini-css-extract-plugin it is bundled in app.css:
._hello_23p9g_17 {
background: lime;
}
Does anyone know what the problem is and possibly how to fix this?
Below my config files.
webpack.config.js (trimmed for readability)
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
entry: {
app: "./src/index.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build")
},
resolve: {
extensions: [".js", ".vue", ".json", ".css"],
alias: {
vue: "vue/dist/vue.esm.js"
}
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
},
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.css$/,
use: [
"vue-style-loader",
// MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
// modules: true,
importLoaders: 1
}
},
"postcss-loader"
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
}),
new VueLoaderPlugin()
]
};
postcss.config.js
module.exports = {
ident: "postcss",
plugins: {
"postcss-preset-env": { stage: 0 },
"postcss-modules": {}
}
};
EDIT:
FYI, setting modules: true in the css-loader options works in that it populates the $style object (see docs):
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
But in our app we use postcss-loader (as per docs) that takes care of all transformations including scoping. Enabling both modules: true and postcss-modules conflicts and breaks the classes/mapping (as expected).
In other words, I'm looking for a way to omit the modules: true option and enable css modules using postcss-modules instead.
Found a workaround: manually import the styles from the JSON file.
If anyone knows a better way please let me know :)
hello.vue.json
{"hello":"_hello_fgtjb_30"}
hello.vue
<template>
<div :class="style.hello">
Hello World!
</div>
</template>
<script>
import style from "./hello.vue.json";
export default {
name: "hello",
beforeCreate() {
this.style = style;
},
created() {
console.log(this.style);
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
This only works when using postcss-modules (loaded from config by postcss-loader in my case) instead of using modules: true:
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 1
}
},
"postcss-loader"
]
};
See this PR as a full example.
I am using vue of typescript as well as typescript in Express. Everything worked until i want to use single file component in vue.js.
Here is my component,
<template>
<div>This is a simple component. {{msg}}</div>
</template>
<script lang='ts'>
export default {
data() {
return {
msg: "you"
}
}
}
</script>
My webpack file,
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackPugPlugin = require('html-webpack-pug-plugin');
const Webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
bundle: [
'./node_modules/bootstrap/dist/js/bootstrap.js',
'./dist/web/views/common/client/ts/_main.js',
'./web/views/common/client/style/main.scss'
]
},
output: {
path: path.resolve(__dirname, 'public'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
ts: 'ts-loader'
},
esModule: true
}
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
minimize: false
}
}, {
loader: 'sass-loader'
}]
})
},
{
test: require.resolve('jquery'),
loader: 'expose-loader?$!expose-loader?jQuery'
}
]
},
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js'
}
},
plugins: [
new ExtractTextPlugin('styles.css'),
new HtmlWebpackPlugin({
template: './web/views/common/_layout.pug',
filename: '../web/views/common/layout.pug',
filetype: 'pug'
}),
new HtmlWebpackPugPlugin(),
new Webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
Popper: 'popper.js'
})
]
};
If I don't use single file component, everything works, but when i introduce .vue file, it will show this error,
ERROR in ./dist/web/views/about/client/ts/_aboutController.js
Module not found: Error: Can't resolve './components/mycomponent.vue' in '/Users/george/github/bochure/dist/web/views/about/client/ts'
# ./dist/web/views/about/client/ts/_aboutController.js 4:24-63
# ./dist/web/views/common/client/ts/_main.js
# multi ./node_modules/bootstrap/dist/js/bootstrap.js ./dist/web/views/common/client/ts/_main.js ./web/views/common/client/style/main.scss
Can anyone help me? You can also download my source at github and help me out. Many thanks.
git#github.com:geforcesong/bochure.git
I'd suggest you to check the component path first.
If you are embedding one vue component into another then check if both are on the same directory level.
If they are then you can use './component-name.vue' and you will be good to go.
Try to change
'./components/mycomponent.vue'
'../components/mycomponent.vue'
This might work in some cases.
when I'm using ...mapState in vue.js, I ran into an error when bundling files with webpack. The error is
Module build failed: SyntaxError: Unexpected token.
I've tried kinds of babel plugins such as stage-0 and transform-object-rest-spread.
Howerver, none seems to be ok for me. Would you please so kind tell me how to solve it?
the source code is
<script type="text/babel">
import { mapState } from 'vuex';
let [a, b, ...other] = [1,2,3,5,7,9]; // this line is ok
console.log(a);
console.log(b);
console.log(other);
export default{
computed:{
localComputed(){
return 10;
},
...mapState({ //this line caused the error
count: state => state.count
})
},
methods: {
increment() {
this.$store.commit('increment');
},
decrement() {
this.$store.commit('decrement');
}
}
}
</script>
and this is the webpack config fragment
{
test: /\.(js|es|es6|jsx)$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
['react'],
['es2015', {modules: false, loose: true}],
['stage-2']
],
plugins: [
['transform-runtime'],
// https://github.com/JeffreyWay/laravel-mix/issues/76
['transform-object-rest-spread'],
['transform-es2015-destructuring']
],
comments: false,
cacheDirectory: true
}
},
{
loader: 'eslint-loader',
options: {
configFile: eslintConfigPath
}
}
],
exclude: excludeReg
}
I had a similar problem a while ago. As far as I can see, your issue is that your babel-loader does not currently work on .vue files (which is correct as such).
The vue-loader, which handles .vue files, uses babel internally as well, but it won't use webpack's babel-loader config. The easiest way to provide a config for babel in the vue-loader is (unfortunately) creating a separate .babelrc file with your babel config in the root folder of your project:
.babelrc
{
presets: [
["react"],
["es2015", { "modules": false, "loose": true}],
["stage-2"]
],
plugins: [
["transform-runtime"],
["transform-object-rest-spread"],
["transform-es2015-destructuring"]
]
}
Note that .babelrc requires valid JSON.