Estoy en Nuxt v2.13 y Vuetify v2, y también uso keep-alive en mi diseño predeterminado. A medida que mi aplicación se hizo más y más grande, noté el problema de la memoria cada vez más, por lo que mi aplicación necesita al menos alrededor de 4 GB de RAM en el servidor de la nube para construirse y funcionar correctamente. Busqué y encontré piezas dispersas, así que decidí compartirlas y discutir las soluciones.
Por favor responda cada uno de acuerdo a sus #números
#1 - Fuga de memoria de NuxtLink (vue-router): otros descubrieron que puede haber una fuga en vue-router; también debido a que el DOM asociado con el enlace nuxt se precargará, también puede haber un alto uso en la memoria. Entonces, alguien sugirió usar el ancla html en lugar de un enlace nuxt como este:
<template> <a href="/mypage" @click.prevent="goTo('mypage')">my page link</a> </template> <script> export default{ methods:{ goTo(link){ this.$router.push(link) } } } </script> que opinas de este enfoque?? ¿Y qué hay de Vuetify to accesorios, ya que funcionan como nuxt-link?
<template> <v-card to="/mypage" ></v-card> </template> #2 - Carga dinámica de componentes: como mi aplicación es bidireccional y personalizable por archivo .env , tuve que cargar de forma diferida muchos de mis componentes de forma dinámica y condicional de esta manera:
<template> <component :is="mycomp" /> </template> <script> export default{ computed:{ mycomp(){ return import()=>(`@/components/${process.env.SITE_DIR}/mycomp.vue`) } } } </script>¿Esto causará un alto uso/pérdida de memoria?
# 3 - Nuxt Event Bus: además de lo normal this.$emit() en mis componentes, a veces tuve que usar $nuxt.$emit() . los elimino todos en beforeDestroy hook :
<script> export default{ created:{ this.$nuxt.$on('myevent', ()=>{ // do something } }, beforeDestroy(){ this.$nuxt.$off('myevent') } } </script> pero alguien me dijo que los oyentes en el gancho created serán SSR y no se eliminarán en CSR antes del gancho beforeDestroy . ¿entonces qué debo hacer? agregar if(process.client){} a created ??
# 4 - Complementos globales: Encontré este problema y también este documento . agregué mis complementos/paquetes globalmente como se menciona en esta pregunta . Entonces, ¿el vue.use() es un problema? ¿Debería usar inject en su lugar? ¿Cómo?
// vue-product-zoomer package import Vue from 'vue' import ProductZoomer from 'vue-product-zoomer' Vue.use(ProductZoomer)# 5 - Fuga de validación de Vee: Leí aquí sobre esto, ¿es esto realmente la causa de la fuga? Estoy usando Vee Validate v3:
mi veevalidate.js que se agregó globalmente a nuxt.config.js
import Vue from 'vue' import { ValidationObserver, ValidationProvider, setInteractionMode } from 'vee-validate' import { localize } from 'vee-validate'; import en from 'vee-validate/dist/locale/en.json'; import fa from 'vee-validate/dist/locale/fa.json'; localize({ en, fa }); setInteractionMode('eager') let LOCALE = "fa"; Object.defineProperty(Vue.prototype, "locale", { configurable: true, get() { return LOCALE; }, set(val) { LOCALE = val; localize(val); } }); Vue.component('ValidationProvider', ValidationProvider); Vue.component("ValidationObserver", ValidationObserver); mi mezcla vevalidate que se agregó a cada página/componente tenía uso vevalidate . (Usé un mixin porque necesitaba usar mi estado de vuex lang )
import { required, email , alpha , alpha_spaces , numeric , confirmed , password } from 'vee-validate/dist/rules' import { extend } from 'vee-validate' export default { mounted() { extend("required", { ...required, message: `{_field_} ${this.lang.error_required}` }); extend("email", { ...email, message: `{_field_} ${this.lang.error_email}` }); extend("alpha", { ...alpha, message: `{_field_} ${this.lang.error_alpha}` }); extend("alpha_spaces", { ...alpha_spaces, message: `{_field_} ${this.lang.error_alpha_spaces}` }); extend("numeric", { ...numeric, message: `{_field_} ${this.lang.error_numeric}` }); extend("confirmed", { ...confirmed, message: `{_field_} ${this.lang.error_confirmed}` }); extend("decimal", { validate: (value, { decimals = '*', separator = '.' } = {}) => { if (value === null || value === undefined || value === '') { return { valid: false }; } if (Number(decimals) === 0) { return { valid: /^-?\d*$/.test(value), }; } const regexPart = decimals === '*' ? '+' : `{1,${decimals}}`; const regex = new RegExp(`^[-+]?\\d*(\\${separator}\\d${regexPart})?([eE]{1}[-]?\\d+)?$`); return { valid: regex.test(value), }; }, message: `{_field_} ${this.lang.error_decimal}` }) } }# 6 - Keep-Alive: como mencioné antes, estoy usando keep-alive en mi aplicación y almacena en caché muchas cosas y es posible que no destruya / elimine complementos y detectores de eventos.
# 7 - setTimeout: ¿hay alguna necesidad de usar clearTimeout para borrar datos?
# 8 - Eliminar complementos/paquetes: en este documento se menciona que algunos complementos/paquetes no se eliminarán incluso después de que se destruya el componente, ¿cómo puedo encontrarlos?
aquí están mis paquetes y nuxt.config
// package.json { "name": "nuxt", "version": "1.0.0", "private": true, "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "generate": "nuxt generate" }, "dependencies": { "@nuxt/http": "^0.6.0", "@nuxtjs/auth": "^4.9.1", "@nuxtjs/axios": "^5.11.0", "@nuxtjs/device": "^1.2.7", "@nuxtjs/google-gtag": "^1.0.4", "@nuxtjs/gtm": "^2.4.0", "chart.js": "^2.9.3", "cookie-universal-nuxt": "^2.1.4", "jquery": "^3.5.1", "less-loader": "^6.1.2", "nuxt": "^2.13.0", "nuxt-user-agent": "^1.2.2", "v-viewer": "^1.5.1", "vee-validate": "^3.3.7", "vue-chartjs": "^3.5.0", "vue-cropperjs": "^4.1.0", "vue-easy-dnd": "^1.10.2", "vue-glide-js": "^1.3.14", "vue-persian-datetime-picker": "^2.2.0", "vue-product-zoomer": "^3.0.1", "vue-slick-carousel": "^1.0.6", "vue-sweetalert2": "^3.0.5", "vue2-editor": "^2.10.2", "vuedraggable": "^2.24.0", "vuetify": "^2.3.9" }, "devDependencies": { "@fortawesome/fontawesome-free": "^5.15.1", "@mdi/font": "^5.9.55", "@nuxtjs/dotenv": "^1.4.1", "css-loader": "^3.6.0", "flipclock": "^0.10.8", "font-awesome": "^4.7.0", "node-sass": "^4.14.1", "noty": "^3.2.0-beta", "nuxt-gsap-module": "^1.2.1", "sass-loader": "^8.0.2" } } //nuxt.config.js const env = require('dotenv').config() const webpack = require('webpack') export default { mode: 'universal', loading: { color: 'green', failedColor: 'red', height: '3px' }, router: { // base: process.env.NUXT_BASE_URL || '/' }, head: { title: process.env.SITE_TITLE + ' | ' + process.env.SITE_SHORT_DESC || '', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'keywords', name: 'keywords', content: process.env.SITE_KEYWORDS || '' }, { hid: 'description', name: 'description', content: process.env.SITE_DESCRIPTION || '' }, { hid: 'robots', name: 'robots', content: process.env.SITE_ROBOTS || '' }, { hid: 'googlebot', name: 'googlebot', content: process.env.SITE_GOOGLE_BOT || '' }, { hid: 'bingbot', name: 'bingbot', content: process.env.SITE_BING_BOT || '' }, { hid: 'og:locale', name: 'og:locale', content: process.env.SITE_OG_LOCALE || '' }, { hid: 'og:type', name: 'og:type', content: process.env.SITE_OG_TYPE || '' }, { hid: 'og:title', name: 'og:title', content: process.env.SITE_OG_TITLE || '' }, { hid: 'og:description', name: 'og:description', content: process.env.SITE_OG_DESCRIPTION || '' }, { hid: 'og:url', name: 'og:url', content: process.env.SITE_OG_URL || '' }, { hid: 'og:site_name', name: 'og:site_name', content: process.env.SITE_OG_SITENAME || '' }, { hid: 'theme-color', name: 'theme-color', content: process.env.SITE_THEME_COLOR || '' }, { hid: 'msapplication-navbutton-color', name: 'msapplication-navbutton-color', content: process.env.SITE_MSAPP_NAVBTN_COLOR || '' }, { hid: 'apple-mobile-web-app-status-bar-style', name: 'apple-mobile-web-app-status-bar-style', content: process.env.SITE_APPLE_WM_STATUSBAR_STYLE || '' }, { hid: 'X-UA-Compatible', 'http-equiv': 'X-UA-Compatible', content: process.env.SITE_X_UA_Compatible || '' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: process.env.SITE_FAVICON }, // { rel: 'shortcut icon', type: 'image/x-icon', href: process.env.SITE_FAVICON }, { rel: 'canonical', href: process.env.SITE_REL_CANONICAL }, // { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/font-awesome@4.x/css/font-awesome.min.css' }, ] }, css: [ '~/assets/scss/style.scss', '~/assets/scss/media.scss', '~/assets/scss/customization.scss', '~/assets/scss/sweetalert.scss', '~/assets/scss/noty.scss', '~/assets/scss/flipclock.scss', '~/assets/scss/glide.scss', '~/assets/scss/sorting.scss', '~/assets/scss/cropper.scss', '~/assets/scss/transitions.scss', '~/assets/scss/product-zoom.scss', 'vue-slick-carousel/dist/vue-slick-carousel.css' ], plugins: [ 'plugins/mixins/reqerrors.js', 'plugins/mixins/user.js', 'plugins/mixins/language.js', 'plugins/mixins/shopinfo.js', 'plugins/mixins/formattedprice.js', 'plugins/mixins/utils.js', 'plugins/mixins/cms.js', 'plugins/mixins/client.js', 'plugins/mixins/cart.js', 'plugins/axios.js', 'plugins/veevalidate.js', 'plugins/noty.js', 'plugins/glide.js', '@plugins/vuetify', '@plugins/vuedraggable', '@plugins/vuedraggable', '@plugins/vue-slick-carousel.js', {src: 'plugins/vuepersiandatepicker.js', mode: 'client'}, {src: 'plugins/cropper.js', mode: 'client'}, {src: 'plugins/vue-product-zoomer.js', mode: 'client'}, {src: 'plugins/vueeditor.js', mode: 'client'}, ], buildModules: [ '@nuxtjs/dotenv', 'nuxt-gsap-module' ], modules: [ '@nuxtjs/axios', '@nuxtjs/auth', '@nuxtjs/device', ['vue-sweetalert2/nuxt', { confirmButtonColor: '#29BF12', cancelButtonColor: '#FF3333' } ], 'cookie-universal-nuxt', '@nuxtjs/gtm', '@nuxtjs/google-gtag', 'nuxt-user-agent', ], gtm: { id: process.env.GOOGLE_TAGS_ID, debug: false }, 'google-gtag': { id: process.env.GOOGLE_ANALYTICS_ID, debug: false }, gsap: { extraPlugins: { cssRule: false, draggable: false, easel: false, motionPath: false, pixi: false, text: false, scrollTo: false, scrollTrigger: false }, extraEases: { expoScaleEase: false, roughEase: false, slowMo: true, } }, axios: { baseURL: process.env.BASE_URL, }, auth: { strategies: { local: { endpoints: { login: { url: 'auth/login', method: 'post', propertyName: 'token' }, logout: { url: 'auth/logout', method: 'post' }, user: { url: 'auth/info', method: 'get', propertyName: '' } } } }, redirect: { login: '/login', home: '', logout: '/login' }, cookie: { prefix: 'auth.', options: { path: '/', maxAge: process.env.AUTH_COOKIE_MAX_AGE } } }, publicRuntimeConfig: { gtm: { id: process.env.GOOGLE_TAGS_ID }, 'google-gtag': { id: process.env.GOOGLE_ANALYTICS_ID, } }, build: { transpile: ['vee-validate/dist/rules'], plugins: [ new webpack.ProvidePlugin({ '$': 'jquery', jQuery: "jquery", "window.jQuery": "jquery", '_': 'lodash' }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) ], postcss: { preset: { features: { customProperties: false, }, }, }, loaders: { scss: { prependData: `$theme_colors: ("theme_body_color":"${process.env.THEME_BODY_COLOR}","theme_main_color":"${process.env.THEME_MAIN_COLOR}","theme_main_color2":"${process.env.THEME_MAIN_COLOR2}","theme_side_color":"${process.env.THEME_SIDE_COLOR}","theme_side_color2":"${process.env.THEME_SIDE_COLOR2}","theme_link_color":"${process.env.THEME_LINK_COLOR}");` } }, } }Creo que es hora de compartir mi entendimiento (aunque sea poco):
N.° 1 como precarga de uso vue-router , puede haber un uso intensivo de la memoria dependiendo de la cantidad de enlaces. en mi caso, no hay muchos, así que los dejo estar, también hay una opción para deshabilitar la captación previa en nuxt, por lo que si su aplicación está muy ocupada o tiene cientos de enlaces en una sola página, es mejor deshabilitar la captación previa:
// locally <nuxt-link to="/" no-prefetch>link</nuxt-link> // globally in nuxt.config.js router: { prefetchLinks: false }#2 No encontré ningún problema con los componentes dinámicos
# 3 no con $nuxt.$on pero lo experimenté (no se eliminó el detector de eventos) cuando usé window.addEventListener en el gancho created . así que es mejor mover a todos los oyentes al lado del cliente (antes de montar o montar) tanto como sea posible
# 4 como mencioné en un comentario anterior, eliminé los complementos/css globales tanto como pude para un inicio más ligero y los usé localmente, pero sobre la fuga de memoria de Vue.use() , ¡ese fue mi malentendido! en nuxt doc se dice que:
No use Vue.use(), Vue.component() y, globalmente, no conecte nada en Vue dentro de esta función, dedicada a la inyección de Nuxt. Provocará una pérdida de memoria en el lado del servidor.
Por lo tanto, usar Vue.use() dentro de la función de inyección puede causar una pérdida de memoria, no Vue.use() en sí.
En cuanto a los demás todavía no hay respuesta.