From 2231ec1aa1e3a23c32983022c4cad6e4730251b0 Mon Sep 17 00:00:00 2001 From: Pim Date: Wed, 9 Oct 2019 11:43:44 +0200 Subject: [PATCH] fix: try to detect global mixins adding meta info (#467) * feat: try to detect global mixins adding meta info * fix: add find polyfill for ie * fix: only detect global mixins when Vue.devtools: true --- src/shared/mixin.js | 36 +++++++++++++++++++++++++++--------- src/utils/array.js | 13 +++++++++++++ test/unit/utils.test.js | 13 ++++++++++++- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/shared/mixin.js b/src/shared/mixin.js index 255693c3..14061102 100644 --- a/src/shared/mixin.js +++ b/src/shared/mixin.js @@ -1,5 +1,6 @@ import { triggerUpdate } from '../client/update' import { isUndefined, isFunction } from '../utils/is-type' +import { find } from '../utils/array' import { ensuredPush } from '../utils/ensure' import { rootConfigKey } from './constants' import { hasMetaInfo } from './meta-helpers' @@ -18,12 +19,13 @@ export default function createMixin (Vue, options) { const rootKey = '$root' const $root = this[rootKey] const $options = this.$options + const devtoolsEnabled = Vue.config.devtools Object.defineProperty(this, '_hasMetaInfo', { configurable: true, get () { // Show deprecation warning once when devtools enabled - if (Vue.config.devtools && !$root[rootConfigKey].deprecationWarningShown) { + if (devtoolsEnabled && !$root[rootConfigKey].deprecationWarningShown) { warn('VueMeta DeprecationWarning: _hasMetaInfo has been deprecated and will be removed in a future version. Please use hasMetaInfo(vm) instead') $root[rootConfigKey].deprecationWarningShown = true } @@ -41,6 +43,17 @@ export default function createMixin (Vue, options) { if (!$root[rootConfigKey]) { $root[rootConfigKey] = { appId } appId++ + + if (devtoolsEnabled && $root.$options[options.keyName]) { + // use nextTick so the children should be added to $root + this.$nextTick(() => { + // find the first child that lists fnOptions + const child = find($root.$children, c => c.$vnode && c.$vnode.fnOptions) + if (child && child.$vnode.fnOptions[options.keyName]) { + warn(`VueMeta has detected a possible global mixin which adds a ${options.keyName} property to all Vue components on the page. This could cause severe performance issues. If possible, use $meta().addApp to add meta information instead`) + } + }) + } } // to speed up updates we keep track of branches which have a component with vue-meta info defined @@ -83,14 +96,18 @@ export default function createMixin (Vue, options) { $root[rootConfigKey].initialized = this.$isServer if (!$root[rootConfigKey].initialized) { - ensuredPush($options, 'beforeMount', function () { - const $root = this[rootKey] - // if this Vue-app was server rendered, set the appId to 'ssr' - // only one SSR app per page is supported - if ($root.$el && $root.$el.nodeType === 1 && $root.$el.hasAttribute('data-server-rendered')) { - $root[rootConfigKey].appId = options.ssrAppId - } - }) + if (!$root[rootConfigKey].initializedSsr) { + $root[rootConfigKey].initializedSsr = true + + ensuredPush($options, 'beforeMount', function () { + const $root = this + // if this Vue-app was server rendered, set the appId to 'ssr' + // only one SSR app per page is supported + if ($root.$el && $root.$el.nodeType === 1 && $root.$el.hasAttribute('data-server-rendered')) { + $root[rootConfigKey].appId = options.ssrAppId + } + }) + } // we use the mounted hook here as on page load ensuredPush($options, 'mounted', function () { @@ -155,6 +172,7 @@ export default function createMixin (Vue, options) { if (!this.$parent || !hasMetaInfo(this)) { return } + delete this._hasMetaInfo this.$nextTick(() => { if (!options.waitOnDestroyed || !this.$el || !this.$el.offsetParent) { diff --git a/src/utils/array.js b/src/utils/array.js index 41b24560..853b028b 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -11,6 +11,19 @@ // which means the polyfills are removed for other build formats const polyfill = process.env.NODE_ENV === 'test' +export function find (array, predicate, thisArg) { + if (polyfill && !Array.prototype.find) { + // idx needs to be a Number, for..in returns string + for (let idx = 0; idx < array.length; idx++) { + if (predicate.call(thisArg, array[idx], idx, array)) { + return array[idx] + } + } + return + } + return array.find(predicate, thisArg) +} + export function findIndex (array, predicate, thisArg) { if (polyfill && !Array.prototype.findIndex) { // idx needs to be a Number, for..in returns string diff --git a/test/unit/utils.test.js b/test/unit/utils.test.js index 8f897e3b..0568531f 100644 --- a/test/unit/utils.test.js +++ b/test/unit/utils.test.js @@ -1,7 +1,7 @@ /** * @jest-environment node */ -import { findIndex, includes, toArray } from '../../src/utils/array' +import { find, findIndex, includes, toArray } from '../../src/utils/array' import { ensureIsArray } from '../../src/utils/ensure' import { hasGlobalWindowFn } from '../../src/utils/window' @@ -29,6 +29,17 @@ describe('shared', () => { }) /* eslint-disable no-extend-native */ + test('find polyfill', () => { + const _find = Array.prototype.find + Array.prototype.find = false + + const arr = [1, 2, 3] + expect(find(arr, (v, i) => i === 0)).toBe(1) + expect(find(arr, (v, i) => i === 3)).toBe(undefined) + + Array.prototype.find = _find + }) + test('findIndex polyfill', () => { const _findIndex = Array.prototype.findIndex Array.prototype.findIndex = false