Version
3.0.5
Reproduction link
https://github.com/clark-cui/vue3-problem
Steps to reproduce
yarn
npm run dev
What is expected?
devServer work successfully.
What is actually happening?
Module not found: Error: Can't resolve 'vue' in 'F:\workspace_github\vue3-problem'
I didn't use vue-cli or vite to build this reposity.
so I use "vue-loader": "^16.1.2" and "#vue/compiler-sfc": "^3.0.0" to resolve '.vue'.
If i use cdn to import vue.Then it occur error like this.
If i use npm to import vue.This problem is solved.
This hadn't happen in vue2.I guess it's vue-compiler 's falut.
I want to use vue with cdn.How to solve this?
In order to work with vue from CDN you have to configure externals to tell webpack uses the external one. Additionally you have to refine a few things as following to make it work properly:
// webpack.config.js
module.exports = {
// ...
externals: {
// tell `webpack` to resolve `vue` from root (window)
vue: "Vue",
},
devServer: {
// ...
// might have to turn of this option since it throws error
// not sure where it comes from though :(
hot: false,
}
}
Refine the template a bit:
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Move the script to here to ensure load `Vue` before your script -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.0/vue.global.prod.js"></script>
<title>Vue demo</title>
</head>
<body>
<noscript>
<strong>error</strong>
</noscript>
<div id="app"></div>
</body>
</html>
Related
I am using Vue with single-spa and I decided to try to reduce the bundle size.
To do this, first I started by trying to set Vue as an external dependency by setting it in webpack.config.js of each Micro Front-end
externals: ['single-spa', 'vue'],
and setting in the root config the following:
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa#5.9.0/lib/system/single-spa.min.js",
"vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.34/vue.global.min.js"
}
}
</script>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/single-spa#5.9.0/lib/system/single-spa.min.js" as="script"
crossOrigin="anonymous">
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.34/vue.global.min.js" as="script"
crossOrigin="anonymous">
but when I try to load, it crashes with the following:
Uncaught TypeError: application '#workspace/foo' died in status LOADING_SOURCE_CODE: (0 , t.defineComponent) is not a function
at vue-router.esm-bundler.js:2123:38
at main.ts:32:38
at Object.execute (main.ts:32:38)
at doExec (system.js:469:34)
at postOrderExec (system.js:465:12)
at system.js:422:14
and also for another
Uncaught TypeError: application '#workspace/bar' died in status LOADING_SOURCE_CODE: (0 , o.ref) is not a function
at Object.9828 (quasar.esm.prod.jss:6:510)
at s (bootstrapp:19:32)
at 1.jss:3:16
at main.tss:54:28
at Object.execute (main.tss:54:28)
at doExec (system.js:469:34)
at postOrderExec (system.js:465:12)
at system.js:422:14
You are using a vue3 Version in your externals and Vue3's API is different from vue2.
I guess you are using quasar and vue-router in a version built for vue2, or your full Application might be built on vue2, so the different APIs will clash.
If you have Vue2 and Vue3 Microfrontends you can define two externals:
"vue": "https://vue2location.example",
"vue3": "https://vue3location.example"
And in your vue.config.js you can use named externals like this to rename from vue3 to vue:
config.externals({
vue: 'vue3'
})
(in theory you could use any naming you want)
I really don't know why, but using exactly this made it work
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa#5.9.0/lib/system/single-spa.min.js",
"vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.29/vue.global.prod.min.js",
"vue-router": "https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.12/vue-router.global.prod.min.js",
"vue-i18n": "https://cdnjs.cloudflare.com/ajax/libs/vue-i18n/9.2.0-beta.35/vue-i18n.global.prod.min.js"
}
}
</script>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/single-spa#5.9.0/lib/system/single-spa.min.js" as="script"
crossOrigin="anonymous">
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.29/vue.global.prod.min.js" as="script"
crossOrigin="anonymous">
I'm trying to achieve a 'live' workflow for a website i'm about to build.
It's based on this boilerplate.
I currently have this workflow working:
Write code -> Save -> 'Rollup watch' rebuilds build/main.js from src/main.js -> 'live-server' refreshes browser.
I'm new to a lot of this, so i'll be honest and say that the 8s it takes per build is a hell of a lot faster than my old workflow, which involved manual fileZilla and a noobier me developing on a password protected subdomain.
Is there a way I should be doing this so that I'm not waiting for the builds to happen - it seems unnecessary? E.g. use a dummy index.html that temporarily links to the src/main.js until i'm ready to build and deploy the bundled version on to my domain?
This is the current index.html of the boilerplate:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example for Three JS</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<div id="homepage"></div>
<script src='build/main.js'></script>
<script>
const app = new LIB.App;
app.init();
</script>
</body>
</html>
And here is what my rollup config file looks like:
import resolve from '#rollup/plugin-node-resolve'; // locate and bundle dependencies in node_modules (mandatory)
import { terser } from "rollup-plugin-terser"; // code minification (optional)
export default {
input: 'src/main.js',
output: [
{
format: 'umd',
name: 'LIB',
file: 'build/main.js'
}
],
plugins: [ resolve(), terser() ]
};
I tried just switching out the script source from
<script src='build/main.js'></script>
to
<script src='src/main.js'></script>
and removing:
<script>
const app = new LIB.App;
app.init();
</script>
But this didn't work - so i'm here looking for input.
The 'answer' is that I should have been using a dev and prod version of my rollup builds. The dev version should have minification removed.
From Documentation:
You can, if you like, specify a different config file from the default rollup.config.js:
rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js
Basically, I want achieve this index.html structure:
<html>
<head>
<!-- titles, metas and other "static" stuff -->
<link rel="preload/prefetch" ...> <!-- injected by webpack (ok) -->
<!-- By default compiled styles are injected here, in head, I want them in body -->
</head>
<body>
<div>
My static loading animation
All possible styling is inlined
It doesn't depend on anything!
It also could be anything, even just a plain "Loading..." text.
You still WON'T see it until all style's in head are loaded.
</div>
<div id="app">Vue application goes here</div>
<link rel="stylesheet" href="..."> <!-- Styles injected by webpack (WANTED!) -->
<script src="..."></script> <!-- Scripts injected by webpack (by default, OK) -->
</body>
The reason I want this, is that my html is completely capable of displaying initial loading animation to the user and I want it to render instantly as soon as index.html is loaded and not depend on any other resources. Really, I think this is everybody want, just to say...
But Vue by debault is configured to include it's compiled styles to the <head> tag, which blocks rendering of the page until these styles are loaded. I cannot find any docs of how I could change it.
Update: Pictures!
So, I've managed to manually simulate two variants:
styles are injected in <head> (default)
styles are injected in <body> (wanted)
Here are the pictures of the visual difference:
1) styles are injected in <head> (default):
2) styles are injected in <body> (wanted):
The label "html rendering starts" on pictures means that a user actually sees loading animation, defined completely inside html (small piece of svg and styling in my case, could be anything in general case) and doesn't depend on any other external resources for it to render.
Solution
vue.config.js
class InjectStylesInBody {
apply(compiler) {
compiler.hooks.compilation.tap('inject-styles-in-body', (compilation) => {
if (!compilation.hooks.htmlWebpackPluginAlterAssetTags) return;
compilation.hooks.htmlWebpackPluginAlterAssetTags.tap('inject-styles-in-body', function(pluginArgs) {
const { head, body } = pluginArgs;
head
.filter(asset => asset.tagName === 'link' && asset.attributes && asset.attributes.rel === 'stylesheet')
.forEach(asset => {
head.splice(head.indexOf(asset), 1);
body.push(asset);
});
});
});
}
}
module.exports = {
// ...
chainWebpack: config => {
// ...
config
.plugin('inject-styles-in-body')
.use(InjectStylesInBody)
;
// ...
}
// ...
};
Notes
Eventually, this has nothing to do with Vue.js. One could easily use this with other frameworks or with naked webpack.
It could be much easier if HtmlWebpackPlugin had some inject-css option for styles as it has for scripts.
See: https://github.com/jantimon/html-webpack-plugin/blob/e2c6990e94b298ff66bcd885c9a03a78221479f6/index.js#L548
I have the following code for a menu and menu button. I am using Vue CLI 3 and Vuetify
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
...
<v-navigation-drawer fixed app v-model="drawer">
<MyMenu/>
</v-navigation-drawer>
<v-toolbar fixed app>
<v-toolbar-title class="headline text-uppercase">
<v-toolbar-side-icon #click.stop="drawer = !drawer"/>
</v-toolbar-title>
</v-toolbar>
The code works great when the computer is online. However when the computer is offline the menu button icon doesn't show up. Instead it is just replaced with the text 'MENU'. I have looked into installing (vue-material-design-icons, material-design-icons and material-design-icons-iconfont) via npm but have not had any luck getting the icon to display when the computer is offline. I'm not sure if there's a special way to wire it together that I'm unaware of. Can anyone provide insight as to how to solve this issue?
I've read a bunch of links such as https://github.com/vuetifyjs/cordova/issues/11 and How to host material icons offline? but I could not get them to work for me.
Vuetify address this in their documentation:
https://vuetifyjs.com/en/framework/icons#installing-fonts
Essentially:
npm install material-design-icons-iconfont -D
Then:
// main.js
import 'material-design-icons-iconfont/dist/material-design-icons.css' // Ensure you are using css-loader
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify, {
iconfont: 'md'
})
ok, I finally got it working in VS Code.
npm install material-design-icons-iconfont
COPY the folder from the node_modules into you public/css folder (This is what I didn't do before)
Modify the material-design-icons.css file by changing the urls to start with
url('/css/material-design-icons-iconfont/dist/fonts/MaterialIcons-Regular
- in the index.html page of your project, add
<link rel="stylesheet" href="css/material-design-icons-iconfont/dist/material-design-icons.css">
my friends,
https://vuetifyjs.com/en/features/icon-fonts/#material-design-icons
first install mdi with npm install #mdi/font -D or yarn add #mdi/font -D
import in vuetify file import '#mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader
and set in config export default new Vuetify({ icons: { iconfont: 'mdi', // default - only for display purposes }, })
I'm COMPLETLY LOST about including Google Fonts into my project.
I've installed google-fonts-webpack-plugin and tried to configure it properly, but the html is not being injected. Or maybe I'm not thinking about it right. Regardless, how to I include Google Webfonts?
Code in my vue.config.js:
const GoogleFontsPlugin = require("google-fonts-webpack-plugin")
module.exports = {
configureWebpack: {
plugins: [
new google-fonts-webpack-plugin({
fonts: [
{ family: "IBM Plex Sans" }
]
})
]
}
}
Go to the end of app.vue and add below code:
<style lang="scss">
#import url("https://fonts.googleapis.com/icon?family=Material+Icons");
</style>
That plugin is not currently compatible with the version of Webpack used by vue-cli.
What I've usually done in the past is just include the fonts via <link> tags in the index.html file, eg
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>vue</title>
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans" rel="stylesheet">
You could also edit the <style> block in your App.vue component to include
#import url("https://fonts.googleapis.com/css?family=IBM+Plex+Sans");