Vue loads and renders well, but then stops working - javascript

I have an application using Vue and Webpack. I have this configuration from the webpack docs:
webpack.common.js
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: path.resolve(__dirname, 'src/app.js')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Production',
template: 'src/index.html'
})
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
};
webpack.dev.js
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
})
In the front I have some Vue code that ran when I developed it using webpack-dev-server.
When I run it with that configuration the page loads with the parameters interpolated and the directives rendered (i.e. v-for) and all stops working (except for a setInterval), the ui is not updated, the events does not trigger the handlers, nothing.
index.html
<div id="app" class="container">
<div class="row">
<div class="col">
<div class="jumbotron">
<h1 class="display-4">Title</h1>
<p class="lead">
Lorem ipsum faciebat <i><b>'{{randomWord}}'</b></i>?
</p>
</div>
</div>
</div>
</div>
app.js
import Vue from 'vue';
import {mockedData} from './mocked-data';
import './components/search-bar';
import './components/word-cta';
var app = new Vue({
el: "#app",
data: function(){
const words = ["foo", "bar", "baz", "faz", "boo", "foz", "bor"]
let i = 0
const getRandomWord = function(){
if(i == words.length - 1){
i = 0
} else {
i += 1
}
return words[i]
}
const data = {
randomWord: words[0],
lastWords: mockedData,
result: ""
}
setInterval(function(){
data.randomWord = getRandomWord()
}, 1700)
return data
},
methods: {
onSearch: function(result){
this.result = result;
}
}
})
I have no idea of what is happening... The console does not help. Below is the output:
vue.esm.js:8439 Download the Vue Devtools extension for a better
development experience: https://github.com/vuejs/vue-devtools
vue.esm.js:8449 You are running Vue in development mode. Make sure to
turn on production mode when deploying for production. See more tips
at https://vuejs.org/guide/deployment.html
EDIT:
I just realized that the problem is to import Vue using webpack. If I remove the import Vue from 'vue';
lines that I have on my .js files and put
<script src="https://unpkg.com/vue"></script>
on my index.html, the problem is fixed and JS behaves as expected.
Can someone explain why?

My assumption is that u missed babel-loader in your webpack config file, so "import" syntax cant be recognized by webpack, and also u should change 'vue/dist/vue.esm.js' to 'vue/dist/vue.common.js'.

Related

Error when compiling javascript with Vue using Rollup

I'm having issues trying to compile my Vue scripts with rollup. The error I'm getting is
[!] Error: 'openBlock' is not exported by
node_modules/vue/dist/vue.runtime.esm.js, imported by
src/js/components/TestButton.vue?vue&type=template&id=543aba30&lang.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
app.js
import Vue from 'vue/dist/vue'
import Buefy from 'buefy'
Vue.use(Buefy)
import ButtonTest from './components/TestButton.vue'
Vue.component('ssm-button', ButtonTest);
var app = new Vue({
el: '#app',
data: {
message: 'You loaded this page on ' + new Date().toLocaleString()
}
})
TestButton.vue
<template>
<div>
asdf
</div>
</template>
<script>
export default {}
</script>
rollup.config.js
'use strict'
const path = require('path')
const { babel } = require('#rollup/plugin-babel')
const banner = require('./banner.js')
const { nodeResolve } = require('#rollup/plugin-node-resolve')
import multi from '#rollup/plugin-multi-entry'
import vuePlugin from 'rollup-plugin-vue'
import replace from '#rollup/plugin-replace'
import commonjs from '#rollup/plugin-commonjs'
let fileDest = 'app.js'
const external = ['jquery']
const plugins = [
vuePlugin(),
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify( 'production' )
}),
babel({
// Only transpile our source code
exclude: 'node_modules/**',
// Include the helpers in the bundle, at most one copy of each
babelHelpers: 'bundled'
}),
nodeResolve(),
multi(),
commonjs(),
]
const globals = {
jquery: 'jQuery', // Ensure we use jQuery which is always available even in noConflict mode
'popper.js': 'Popper'
}
module.exports = [
{
input: [path.resolve(__dirname, '../js/app.js'), path.resolve(__dirname, '../js/custom.js')],
output: {
banner,
file: path.resolve(__dirname, `../../assets/js/${fileDest}`),
format: 'umd',
globals,
name: 'main-javascript'
},
external,
plugins,
},
]
I've tried a lot of different things, but nothing seems to help. However, if I load commonjs before vuePlugin in the config, I get a different error
[!] (plugin commonjs) SyntaxError: Unexpected token (2:4) in
/Users/xxx/Dev/self-storage-manager/wordpress_data/wp-content/themes/Bulmascores/src/js/components/TestButton.vue?vue&type=template&id=543aba30&lang.js
src/js/components/TestButton.vue?vue&type=template&id=543aba30&lang.js
(2:4) 1: 2:
^ 3: asdf 4:
Does anybody have an idea of what is going on? I've been working on this for the past two days now and really can't seem to find a solution.
Finally found a solution
I changed the rollup vue plugin from #rollup/plugin-vue to rollup-plugin-vue2 https://github.com/thgh/rollup-plugin-vue2 and now it works.

How to split all import statements of a lazy loaded child component from the main bundle file in react (using webpack 4)

I am not sure whether the title is appropriate or not. But this is what I want to achieve -
I have created a react reusable component which will return an environment specific header. When I say environment specific, I mean, in boomerang environment, I need to use some boomerang specific packages which are not supported in other environments.
I have created two separate header components for that, one is for the boomerang environment which has some boomerang UI shell (not supported in other environments), and another component for other environments.
Now, from my CustomHeader component, I am rendering the headers based on the environment props.
renderDynamicHeader() {
let {project, headerText, productName, customHeaderStyle, logoutLink, onSwitcherItemClick, headerPanel, onIconClick, logoLink, headerIcons, navigation, baseURL, features, platform, bmrgCustomIcons} = this.props;
let {environment} = this.props;
if(environment !== 'boomerang' && environment !== 'production' && environment !== undefined) {
return <NonBoomerangHeader environment={environment} headertxt={headerText} selectedProject={project} productName={productName} customHeaderStyle={customHeaderStyle} logoutLink={logoutLink} onSwitcherItemClick={onSwitcherItemClick} onSwitcherItemClick={onSwitcherItemClick} headerPanel={headerPanel} onIconClick={onIconClick} logoLink={logoLink} headerIcons={headerIcons}/>
} else {
const BoomerangHeader = lazy(() => import(/* webpackChunkName: "BoomerangHeader" */ "../BoomerangHeader/index"));
return (
<Suspense fallback={<NonBoomerangHeader environment={environment} headertxt={headerText} selectedProject={project} productName={productName} customHeaderStyle={customHeaderStyle} logoutLink={logoutLink} onSwitcherItemClick={onSwitcherItemClick} onSwitcherItemClick={onSwitcherItemClick} headerPanel={headerPanel} onIconClick={onIconClick} logoLink={logoLink} headerIcons={headerIcons}/>}>
<BoomerangHeader environment={environment} headertxt={headerText} selectedProject={project} productName={productName} customHeaderStyle={customHeaderStyle} logoutLink={logoutLink} navigation={navigation ? navigation : undefined} baseURL={baseURL? baseURL : undefined} features={features? features : undefined} platform={platform ? platform : undefined} bmrgCustomIcons={bmrgCustomIcons} onIconClick={onIconClick}/>
</Suspense>
);
}
}
I have this configuration in my webpack.config.js file:
const path = require('path')
const {
CleanWebpackPlugin
} = require('clean-webpack-plugin')
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
externals: [nodeExternals()],
output: {
filename: 'index.js',
publicPath: 'dist/',
path: path.resolve(__dirname, 'dist'),
library: '',
libraryTarget: 'commonjs',
chunkFilename: '[name].js'
},
plugins: [new CleanWebpackPlugin()],
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', {
loader: 'sass-loader',
options: {
implementation: require('sass'),
},
}],
include: path.resolve(__dirname, './src')
}
]
}
}
But when I am doing build, the internal dependencies of the boomerang header component is getting appended/bundled in the main index.js file
This is the import statement in my BoomerangHeader.js file
import React, {Component} from 'react'
import {UIShell} from '#boomerang/carbon-addons-boomerang-react';
import axios from 'axios';
import './boomerangHeader.scss';
And this I can see as an export statement in the dist folder's index.js file
(r=[{key:"renderDynamicHeader",value:function(){console.log("inside renderDynamicHeader library");var t=this.props,e=t.project,r=t.headerText,a=t.productName,l=t.customHeaderStyle,b=t.logoutLink,s=t.onSwitcherItemClick,x=t.headerPanel,d=t.onIconClick,c=t.logoLink,p=t.headerIcons,f=t.navigation,g=t.baseURL,h=t.features,_=t.platform,u=t.bmrgCustomIcons,w=this.props.environment;if("boomerang"!==w&&"production"!==w&&void 0!==w)return console.log("inside non boomerang if block"),n.a.createElement(m,{environment:w,headertxt:r,selectedProject:e,productName:a,customHeaderStyle:l,logoutLink:b,onSwitcherItemClick:s,onSwitcherItemClick:s,headerPanel:x,onIconClick:d,logoLink:c,headerIcons:p});console.log("trying to lazy load boomerang file");var v=Object(i.lazy)((function(){return o.e(0).then(o.bind(null,9))}));return console.log("successfully imported boomerang file"),console.log(v),n.a.createElement(i.Suspense,{fallback:n.a.createElement(m,{environment:w,headertxt:r,selectedProject:e,productName:a,customHeaderStyle:l,logoutLink:b,onSwitcherItemClick:s,onSwitcherItemClick:s,headerPanel:x,onIconClick:d,logoLink:c,headerIcons:p})},n.a.createElement(v,{environment:w,headertxt:r,selectedProject:e,productName:a,customHeaderStyle:l,logoutLink:b,navigation:f||void 0,baseURL:g||void 0,features:h||void 0,platform:_||void 0,bmrgCustomIcons:u,onIconClick:d}))}},{key:"render",value:function(){var t=this.renderDynamicHeader();return n.a.createElement("div",null,t)}}])&&_(e.prototype,r),a&&_(e,a),b}(i.Component)},function(t,e){t.exports=require("#boomerang/carbon-addons-boomerang-react")},function(t,e){t.exports=require("axios")}]));
I want this require statement to be kept separately in a different file. Because of lazy loading/ code-splitting the boomerangHeader chunk is also getting created separately, but this require statement is not getting splitted into a different file.
I tried with splitChunks plugin of webpack, but that also did not separate it from the main bundle.
Is there any possible way that we can remove this require statement from this bundle and keep it separate in some other files so that I can ignore that file while working in non boomerang environment. Or is there any way to ignore "module not found" error in the client application ?
It would be really good if anyone can help me on this.
I could not find any proper solution as to how to split the import statements from the webpack bundle into a separate file, because I guess that is how webpack works.
And I wanted to split it so that I can ignore that file based on the condition.
But It seems that ignoring the package itself in the client application solved this issue.
if (isnotBoomerang) {
var newClientPlugins = [];
newClientPlugins.push(
new webpack.IgnorePlugin(/#boomerang/)
);
newClientPlugins.push(
new HtmlWebPackPlugin({
template: APP_DIR + '/client/index.html',
})
);
client['plugins'] = newClientPlugins;
}

How can Include both commonJs require syntax and es modules import syntax in the same bundle - webpack

before tagging as a duplicate, I searched for an answer before I asked and I found this one:
Is it possible to use both "require" and "import" together with Webpack?
The problem is that the accepted answer directs to the webpack documentation which has different content now.
The issue:
I have a project with Webpack version 4.41.2, React as a front and approximately 600 js/jsx files all written with the old require and module.exports syntax and I want to update them to the import/export syntax (not all of them at one time because this will take forever). When I try to update even one simple file like this:
From this-
const React = require('react');
const Spinner = () => {
return (
<div className="vertical-align" style={{ "width": "100%", "height": "100%" }}>
<div className="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
);
};
module.exports = Spinner;
To this-
import React from 'react';
const Spinner = () => {
return (
<div className="vertical-align" style={{ "width": "100%", "height": "100%" }}>
<div className="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
);
};
export default Spinner;
The app gets crashed and It gives me the following errors:
ContactList is the component that renders the Spinner component.
My webpack.config.js looks like this:
"use strict";
var WebpackNotifierPlugin = require('webpack-notifier');
var webpack = require('webpack');
var path = require('path');
module.exports = [
{
mode: 'development',
context: __dirname + "/app",
entry: {
'app': ["./main.jsx"],
'widget': ["./Widget/Widget.jsx"],
'admin': ["./Admin/Main.jsx"],
'dashboard': ["./Dashboard/Main.jsx"],
'signin': ["./SignIn/Main.jsx"],
'surveys': ["./Surveys/Main.jsx"]
},
output: {
filename: "[name].entry.js",
path: __dirname + "/dist",
publicPath: '/'
},
devtool: "eval-source-map",
devServer: {
hot: true,
contentBase: './dist',
host: "localhost",
inline: true,
port: 58852
},
module: {
rules: [
{
test: /\.jsx?$/,
use: ['babel-loader'],
exclude: /node_modules/
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new WebpackNotifierPlugin(),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
performance: {
maxEntrypointSize: 10000000,
maxAssetSize: 10000000
}
}
]
I will be extremely thankful for any suggestion of how can I solve this problem.
It's how the latest babel works when transpiling es module to commonjs.
Take this code for example:
const Spinner = () => {
return (
<div></div>
);
};
export default Spinner;
Babel will transpile it into:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var Spinner = function Spinner() {
return /*#__PURE__*/React.createElement("div", null);
};
var _default = Spinner;
exports.default = _default;
As you can see from the transpiled code above, there's no module.exports, only exports.default, that means you have to use const {default: Spinner} = require('./path/to/Spinner') instead of const Spinner = require('./path/to/Spinner').
But sure you can work around it with help of https://www.npmjs.com/package/babel-plugin-add-module-exports.

Adding Vue to existing old website, removing the div instead of displaying old html

im trying to wrap my old website with Vue, so all the old jQuery scripts should still run and all the old HTML should display, however for some reason the el and everything inside it is being removed.
This is when using the webpacked version of the script, so for example:
<html>
<head></head>
<body>
<div id="wrapper">
<p>dsadfasfasfasfas</p>
<p>dfasdsadasdasdas</p>
</div>
</body>
<script src="/assets/js/app.min.js"></script>
</html>
Would display nothing within the body tag. But if I import Vue at the top of the website, and put the below code in place of the webpacked version it works fine!
window.app = new Vue({
el: '#wrapper',
data: {
test: 'hello world'
},
created() {
console.log('Vue Running');
}
});
Edit
Also this is the app.js which gets compiled:
import Vue from 'vue';
// window.EventBus = new Vue();
window.app = new Vue({
el: '#wrapper',
data: {
test: 'hello world'
},
created() {
console.log('Vue Running');
}
});
Edit
Here is my webpack.config.js:
const path = require('path');
module.exports = {
mode: 'production',
entry: [
'./resources/js/vue/app.js'
],
output: {
filename: 'app.min.js',
path: path.resolve(__dirname, 'public_html/assets/js/vue')
},
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
};
Edit 2
Just removed the import vue from 'vue', and imported it using the normal script src=... way and it works. I was under the impression if I import vue and compile then this would include vue in my website?
That's because your Webpack config is incomplete.
If you'd open browser console, you'll see there:
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
Your html in #wrapper is a template, but Vue can't compile (and use it), because your build includes runtime-only version of Vue.
Simply add this to webpack.config.js:
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
},
That will include runtime+compiler build of Vue.
The default build exported by the NPM package is the runtime-only build. It doesn’t bring the template compiler. So you would either need to import the full build version of vue in your javascript or create a webpack alias (if you are using webpack).
This issue won't occur when you define templates with the render function or Single File Component.
NOTE:
You don't need babel-loader if you dont expect to support older browsers and if you dont use latest ES features and you won't need vue-loader if you are not using Single File Components.
Also note that you must include the meta tag in your HTML <meta charset="utf-8">
This would work -
// webpack
module.exports = {
mode: "production",
entry: ["./app.js"],
output: { filename: "./app.min.js" },
resolve: { alias: { vue: "vue/dist/vue.js" } },
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader"
},
{
test: /\.vue$/,
use: "vue-loader"
}
]
}
};
// app.js
import Vue from "vue";
// OR You do this if you dont want to use webpack alias
// import Vue from 'vue/dist/vue.js';
window.app = new Vue({
el: "#app",
data: { message: "Hello Vue!" },
created() {
console.log("<><><> Vue Created");
}
});
<!-- index.html -->
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="app">
<p>{{message}}</p>
</div>
<script src="app.min.js"></script>
</body>
</html>
Ok so,
It's normal because your compiled version looks like this:
import Vue from 'vue';
// window.EventBus = new Vue();
window.app = new Vue({
el: '#wrapper',
data: {
test: 'hello world'
},
created() {
console.log('Vue Running');
}
});
But how the browser know how to import 'vue' ?
Your compiled version is not browser ready,
I think your build configuration is not correct
Can you provide it ?

Browser cache problems with webpack chunks and vue.js components

The Problem
I have a problem with cached Vue.js components and I can't reproduce this problem on my devices but every client-side update we get users complains about broken interfaces and only clearing browser cache helps.
I use Laravel + Vue.js and it's multipage app.
Strategy
All components described in one file which included in app.js and it looks like this:
Vue.component('task-feed', () => import('./components/task/task-feed'/* webpackChunkName: "/js/components/task-feed" */));
Vue.component('task-item', () => import('./components/task/task-item'/* webpackChunkName: "/js/components/task-item" */));
There are vue.js async components.
And then i have configured webpack.mix like this:
let mix = require('laravel-mix');
const webpack = require('webpack');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
mix.disableNotifications();
let config = {
watchOptions: {
ignored: /node_modules/
},
resolve: {
alias: {
'vue$': mix.inProduction() ? 'vue/dist/vue.runtime.min.js' : 'vue/dist/vue.runtime.js',
}
},
output: {
chunkFilename: mix.inProduction() ? '[name].[chunkhash].js' : '[name].js',
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
new ChunkManifestPlugin({
filename: '/js/chunk-manifest.json',
manifestVariable: 'webpackManifest',
inlineManifest: true,
}),
],
};
mix.webpackConfig(config);
I'm using ChunkManifestPlugin here, that plugin creates chunk-manifest.json and i load it in the main layout like this:
window.webpackManifest = JSON.parse(#json(file_get_contents(public_path('/js/chunk-manifest.json'))));
Questions
What could be wrong here? Can anyone suggest the way to solve this?
In webpack.mix.js change it to this:
mix.config.webpackConfig.output = {
chunkFilename: 'scripts/[name].[chunkhash].js',
publicPath: '/',
};
Refer to this article for more information.
Configure webpack.mix.js to version your files
let version = 0;
mix.config.webpackConfig.output = {
chunkFilename: 'v/' + version + '/scripts/[name].js',
publicPath: '/',
};
mix.js('resources/js/app.js', 'public/v/'+version+'/js')
.sass('resources/sass/app.scss', 'public/v/'+version+'/css');

Categories

Resources