Nuxt add global plugins memory leakage issue - vue.js

I'm on Nuxt 2.13 universal mode, and I have serious memory usage and leakage!!
so as I was looking for related issues, I found this in Nuxt Docs Plugins - NuxtJS :
Don't use Vue.use(), Vue.component(), and globally, don't plug anything in Vue inside this function, dedicated to Nuxt injection. It will cause a memory leak on the server-side.
can anyone tell me what that means??
I'm currently using many external plugins and some globally added mixins by vue.component() and vue.use() . may them be the problem?? (i also have an utils.js mixin file that includes many methods and computed data that is added globally to nuxt.config)
some of my plugins and mixins that added globally to nuxt.config.js file :
// Vuetify 2
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import '#mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader version "^2.1.1"
import 'font-awesome/css/font-awesome.min.css'
import '#fortawesome/fontawesome-free/css/all.css'
import colors from "vuetify/es5/util/colors";
import '~/assets/vuetify.scss'
// let siteDirection = process.env.SITE_DIRECTION
Vue.use(Vuetify)
export default ctx => {
const vuetify = new Vuetify({
rtl: process.env.SITE_DIRECTION === 'rtl' ,
customVariables: ['~/assets/variables.scss','~/assets/colors.scss'],
icons: {
iconfont: 'mdi', // default - only for display purposes
},
theme: {
dark: false, // From 2.0 You have to select the theme dark or light here
themes: {
dark: {
primary: colors.deepPurple.lighten3,
accent: colors.deepPurple.accent3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent4
}
}
}
})
ctx.app.vuetify = vuetify
ctx.$vuetify = vuetify.framework
}
// Vue-Glide Package
import Vue from 'vue'
import { Glide, GlideSlide } from 'vue-glide-js'
Vue.component("vue-glide", Glide)
Vue.component("vue-glide-slide", GlideSlide)
// Noty package
import Vue from "vue";
import Noty from "noty";
Vue.prototype.$noty = (type, text) => new Noty({
layout:process.env.SITE_DIRECTION === 'rtl' ? 'bottomLeft' : 'bottomRight',
type,
text,
timeout: 5000
});
// vue-product-zoomer package
import Vue from 'vue'
import ProductZoomer from 'vue-product-zoomer'
Vue.use(ProductZoomer)
my Mixins are also added the default way:
import Vue from 'vue'
const FormattedPrice = {
install(Vue){
Vue.mixin({
methods:{
formattedPrice(price,options){
if(price !== null && price !== undefined){
if(typeof price === 'object'){
let min = price.min ? Number(price.min).toLocaleString() : 0
let max = price.min ? Number(price.max).toLocaleString() : 0
return min + ' - ' + max + (options && options.noSign ? '' : this.currencySign)
}else{
// try {
// return price.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",") + this.currencySign
// // return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + this.currencySign
// } catch (error) {
// return Number(price).toLocaleString() + this.currencySign
// }
return Number(price).toLocaleString() + (options && options.noSign ? '' : this.currencySign)
}
}
return '...' + (options && options.noSign ? '' : this.currencySign)
}
},
computed:{
currencySign(){
return ' ' + this.shopInfo.currency.sign
}
}
})
}
}
Vue.use(FormattedPrice)

For your question:
Don't use Vue.use(), Vue.component(), and globally, don't plug anything in Vue inside this function, dedicated to Nuxt injection. It will cause a memory leak on the server-side.
See this PR, which is the reason for that warning.
A more clear explanation would be that you should not call Vue.use() or Vue.component() from inside an exported plugin function. You should place the calls in the global scope, and simply apply them to the context.
Where I believe you're getting a memory leak is with calling new Vuetify() from inside the exported function. It's entirely possible that they are calling Vue.use() or Vue.component() as a side effect to that call.
You should instead place that call in the global scope. If this doesn't work, then you may want to consider creating a minimum reproducible example and opening an issue on the Nuxt GitHub repo.
// Vuetify 2
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
import '#mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader version "^2.1.1"
import 'font-awesome/css/font-awesome.min.css'
import '#fortawesome/fontawesome-free/css/all.css'
import colors from "vuetify/es5/util/colors";
import '~/assets/vuetify.scss'
// let siteDirection = process.env.SITE_DIRECTION
Vue.use(Vuetify)
const vuetify = new Vuetify({
rtl: process.env.SITE_DIRECTION === 'rtl' ,
customVariables: ['~/assets/variables.scss','~/assets/colors.scss'],
icons: {
iconfont: 'mdi', // default - only for display purposes
},
theme: {
dark: false, // From 2.0 You have to select the theme dark or light here
themes: {
dark: {
primary: colors.deepPurple.lighten3,
accent: colors.deepPurple.accent3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent4
}
}
}
})
export default ctx => {
ctx.app.vuetify = vuetify
ctx.$vuetify = vuetify.framework
}

Related

Vue build didnt include my function to output .js file

Works fine in dev mode, but after build process vue not includes setupToken function (from #/api.js) to app.js output file.
// App.vue
//...
import { setupToken } from '#/api
export default {
mounted () {
this.setupToken() // TypeError: this.setupToken is not a function
}
}
// #/api.js
import { mande } from 'mande'
export const postsAPI = mande(`${process.env.VUE_APP_ROOT_URL}/api/v1/posts`)
export const setupToken = ({ token }) => {
postsAPI.options.headers.Authorization = 'Bearer ' + token
}
I guess the problem with webpack config (im using default one), but not sure how to fix it.
setupToken does not exist on the object you're exporting from App.vue.
You are importing setupToken from #/api so you have to call it directly, i.e. setupToken() and not as an instance method, i.e. this.setupToken()
WebPack sees you are not using the method you're importing from #/api (because you are calling it as an instance method) so it tree-shakes it out.
BTW, try using TypeScript, it would have let you know of that error.
Import api.js in main.js
import Vue from 'vue'
import App from './App.vue'
import api from './api'
Vue.config.productionTip = false
new Vue({
api,
render: function (h) {
return h(App)
},
}).$mount('#app')
Modify api.js:
import Vue from 'vue'
Vue.mixin({
methods: {
setupToken({ token }) {
postsAPI.options.headers.Authorization = 'Bearer ' + token
}
}
}

How can I display the current app version from package.json to the user using Vite?

With create-react-app one could use process.env.REACT_APP_VERSION for this.
Is there an equivalent in Vite?
For React & TypeScript users:
Add a define to your vite.config.ts:
import react from '#vitejs/plugin-react';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react()],
define: {
APP_VERSION: JSON.stringify(process.env.npm_package_version),
},
});
If you haven't got one already, define a vite-env.d.ts or env.d.ts and add a declare:
declare const APP_VERSION: string;
You'll now be able to use the variable APP_VERSION anywhere in your code & Vite will substitute it at compile time.
Note: You may need to restart your TS server for the declaration to be picked up by intellisense:
VSCode MacOS: ⌘ + ⇧ + P > Restart TS Server
VSCode Windows: ctrl + ⇧ + P > Restart TS Server
EDIT: For TypeScript, see Jamie's answer to set the types.
If you want to use plugin, see Adarsh's answer
But it's very easy to implement it yourself.
You can use define in vite.config.js. Read about it here
vite.config.js
export default {
plugins: [vue()],
define: {
'__APP_VERSION__': JSON.stringify(process.env.npm_package_version),
}
}
component.vue
<template>
<div>{{ version }}</div>
</template>
<script>
export default {
data () {
version: __APP_VERSION__
},
}
</script>
or with <script setup>
<script setup>
const version = __APP_VERSION__
</script>
<template>
<div>{{ version }}</div>
</template>
You should be able to change '__APP_VERSION__' as long as it doesn't conflict with javascript syntax or other variables.
If you don't want to use define, there is a vite plugin for just this.
https://www.npmjs.com/package/vite-plugin-package-version
// vite.config.js
import loadVersion from 'vite-plugin-package-version';
export default {
plugins: [loadVersion()],
};
Will inject import.meta.env.PACKAGE_VERSION with the version specified in your package.json.
Vite 4, React, Typescript setup
This worked for me.
I imported package.json in vite.config.ts and defined a PACKAGE_VERSION environment variable.
import { defineConfig } from 'vite'
import react from '#vitejs/plugin-react'
import packageJson from './package.json';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
define: {
'import.meta.env.PACKAGE_VERSION': JSON.stringify(packageJson.version)
}
})
I added "resolveJsonModule": true to the compiler options of tsconfig.node.json.
I added "./package.json" to the include array of tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
"include": ["vite.config.ts", "./package.json"]
}
In order to make intellisense work for PACKAGE_VERSION, I added it to vite-env.d.ts
interface ImportMetaEnv {
readonly PACKAGE_VERSION: string;
// more env variables...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
I could use {import.meta.env.PACKAGE_VERSION} anywhere in my react app to show the package version.
This worked for me:
import { version } from '../../package.json'
In case anyone is interested, this automatically increases the version in package.json and makes it available to the application.
import { defineConfig } from 'vite';
const increasePackageVersion = () => {
try {
const fs = require('fs');
const path = require('path');
const packageFilePath = path.join(__dirname, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageFilePath, 'utf8'));
packageJson.version = packageJson.version.replace(/(\d+)$/, (match, p1) => {
return parseInt(p1) + 1;
}
);
fs.writeFileSync(packageFilePath, JSON.stringify(packageJson, null, 2));
console.log('New version is', packageJson.version);
} catch (error) {
console.log('Error in increasePackageVersion', error);
}
};
export default defineConfig({
build: {
lib: {
entry: 'src/main.js',
formats: ['es']
}
},
plugins: [
increasePackageVersion()],
define: {
'__APP_VERSION__': JSON.stringify(process.env.npm_package_version),
}
});
console.log(__APP_VERSION__);
Below Answer includes
Secure Way of Importing Vue version.
Incrementing semantic versions using npm commands
Secure and Semantic Way of Versioning using npm and env

Import monaco-editor using Vite 2

Currently, I have set up a Vite 2 project with monaco-editor as a dependency.
Whenever I am trying to use it says that the workers are not imported.
editorSimpleWorker.js:454 Uncaught (in promise) Error: Unexpected usage
at EditorSimpleWorker.loadForeignModule (editorSimpleWorker.js:454)
at webWorker.js:38
Since I am using Vite 2 I have assumed that simply specifying the rollup plugin rollup-plugin-monaco-editor in the plugins array. However, I am still getting this issue.
export default defineConfig({
plugins: [
vue(),
monaco({ languages: ['javascript'] }),
],
});
Is there any proper way to import monaco-editor into a Vite 2 project?
With the latest release (2.0.0-beta.59) it is fixed.
You can now add the environment workers without any further configuration (ref: https://github.com/vitejs/vite/discussions/1791)
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new jsonWorker()
}
if (label === 'css' || label === 'scss' || label === 'less') {
return new cssWorker()
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new htmlWorker()
}
if (label === 'typescript' || label === 'javascript') {
return new tsWorker()
}
return new editorWorker()
}
}
The accepted answer is ok in dev build, but in production build at current version (v2.1.2), Uncaught ReferenceError: window is not defined is raised on page load.
So in addition to the accepted answer, build.rollupOptions.output.manualChunks needs to be added to vite.config.js like the following.
// vite.config.js
import { defineConfig } from 'vite';
const prefix = `monaco-editor/esm/vs`;
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
jsonWorker: [`${prefix}/language/json/json.worker`],
cssWorker: [`${prefix}/language/css/css.worker`],
htmlWorker: [`${prefix}/language/html/html.worker`],
tsWorker: [`${prefix}/language/typescript/ts.worker`],
editorWorker: [`${prefix}/editor/editor.worker`],
},
},
},
},
});

How do I mock this Vue injection?

I have a Vue 3 component that, when mounted in tests, cause warnings:
console.warn node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:40
[Vue warn]: injection "Symbol(VueToastification)" not found.
at <ModifyJob ref="VTU_COMPONENT" >
at <VTUROOT>
I assume it's this one complaining https://github.com/Maronato/vue-toastification/blob/master/composition/index.js#L30.
I have nearly 100 of these warnings, so it's kind of hard to read test-run output. I've tried to mock provide for this dependency, but I can't seem to succeed:
let provide = {}
provide[VueToastification] = VueToastification
provide['VueToastification'] = VueToastification
provide[Symbol(VueToastification)] = VueToastification
provide[Symbol('VueToastification')] = VueToastification
provide['Symbol(VueToastification)'] = VueToastification
let options = {
global: {
provide: provide,
}
}
mount(ModifyJob, options)
Is this some Vue2/Vue3 incompatibility or do I just not understand the docs at https://vue-test-utils.vuejs.org/v2/api/#global-provide ? Can someone help me get rid of these warnings, ideally by allowing me to inject a mock so I can test that toasts are made?
That error actually indicates that the plugin isn't installed in the test Vue instance. You could make VueToastification available to the component under test through the global.plugins mounting option:
import { shallowMount } from '#vue/test-utils'
import MyComponent from '#/components/MyComponent.vue'
import VueToastificationPlugin from 'vue-toastification'
it('initializes', () => {
shallowMount(MyComponent, {
global: {
plugins: [VueToastificationPlugin]
}
})
})
Alternatively, if you want to verify that toast() (from VueToastification's useToast()) is called, you could mock vue-toastification:
import { shallowMount } from '#vue/test-utils'
import MyComponent from '#/components/MyComponent.vue'
jest.mock('vue-toastification')
it('should call toast', () => {
const toast = jest.fn()
require('vue-toastification').useToast.mockReturnValueOnce(toast)
shallowMount(MyComponent).vm.showToast()
expect(toast).toHaveBeenCalled()
})
I solved setting a global list of plugins according to https://next.vue-test-utils.vuejs.org/api/#config-global:
// In a jest.setup.js file
import { config } from "#vue/test-utils";
import VueToastificationPlugin from "vue-toastification";
config.global.plugins = [VueToastificationPlugin];
// In your jest.config.js
module.exports = {
...
setupFilesAfterEnv: ["./jest.setup.js"],
};

Using quill modules with vue2-editor and webpack mix

I'm currently using vue2-editor and importing quill modules and registering them as per documentation.But getting error window.Quill is undefined.
I've tried webpack plugin mix to include window.Quill and Quill but still error remains the same.
In my vue component
import { VueEditor } from "vue2-editor";
import { Quill } from "quill";
import { ImageDrop } from "quill-image-drop-module";
import { ImageResize } from "quill-image-resize-module";
Quill.register("modules/imageDrop", ImageDrop);
Quill.register("modules/imageResize", ImageResize);
And in my webpack mix
mix.extend('foo',new class{
webpackPlugins(){
return new webpack.ProvidePlugin({
"window.Quill": "quill/dist/quill.js",
Quill: "quill/dist/quill.js"
});
}
});
Uncaught TypeError: Cannot read property 'imports' of undefined
which is from window.Quill.imports
You need to get REALY working files from https://www.jsdelivr.com/package/npm/quill-image-resize-vue:
Just install npm i --save quill-image-resize-vue
and use another file:
import { VueEditor,Quill } from 'vue2-editor'
import ImageResize from 'quill-image-resize-vue';
import { ImageDrop } from 'quill-image-drop-module';
Quill.register("modules/imageDrop", ImageDrop);
Quill.register("modules/imageResize", ImageResize);
export default {
name: 'MainForm',
components: { VueEditor},
data() {
return {
content: '<h2>I am Example</h2>',
editorSettings: {
modules: {
imageDrop: true,
imageResize: {},
}
}
}
},
//........
}
For me got fixed after changing
import { ImageResize } from "quill-image-resize-module";
to
import ImageResize from "quill-image-resize-module";