DanSnow / vue-recaptcha

Google ReCAPTCHA component for Vue.js
http://dansnow.github.io/vue-recaptcha/
MIT License
862 stars 135 forks source link

Multiple library loads #1532

Open mateusfmello opened 5 months ago

mateusfmello commented 5 months ago

Description

On my website page there are 4 forms, two of which are components within the project, that is, each form/component is instantiated twice.

This is to be expected, as we want the same forms in different locations.

However, vue-recaptcha is instantiating the reCaptcha for each component, and this only needs to be done once, as it can be used globally.

Captura de tela de 2024-02-11 13-41-55

This overloads page loading, even Page Spees Web DEV reports this as a problem, which in fact it is.

Captura de tela de 2024-02-11 13-52-47 Captura de tela de 2024-02-11 13-53-23 Captura de tela de 2024-02-11 13-53-53

Faced with these problems, I turned to the vue recaptcha source code to analyze what might be happening. I noticed that in the vue-recaptcha/src/script-manager/head.ts file, the creation of the reCaptcha script tag was assigned in onMounted, everything was fine until the first execution.

I would like to make a PR with an improvement, but I would like to know what the maintainers think about this and two possible solutions.

First option: Add a flag to the Window called window.vueRecaptchaInserted and mark it as true, using it within onMounted in an if return so that the method is not executed more than once.

This is my preferred option because it is very objective, clear and more performant than the second option.

// vue-recaptcha/src/script-manager/head.ts::onMounted

if (window.vueRecaptchaInserted)
    return;

window.vueRecaptchaInserted = true;

Second option: When running the onMounted method, perform a search in the DOM checking whether the TAG already exists, if it does not exist, you must continue executing the method, otherwise the method is terminated.

// vue-recaptcha/src/script-manager/head.ts::onMounted

const elementExists = document.querySelector(`script[src="${options.recaptchaApiURL}?${toQueryString(options.params)}"]`);

if (elementExists)
    return;

Do you think this is something that needs to be resolved? Do you suggest another solution? Which option do you prefer?

Minimal Reproducible Example

1 - Create a component (MyForm) that uses <vue-recaptcha />; 2 - Instantiate this component several times:

<MyForm />
// Other content
<MyForm />
// Other content
<MyForm />
// Other content
<MyForm />

3 - Open the website and notice that Google reCaptcha is loaded the same number of times as you instantiated <MyForm />, for this you need to open the F12 developer tools and go to the network tab.

System info

// package.json

"dependencies": {
    "@builder.io/partytown": "^0.9.1",
    "@fortawesome/fontawesome-svg-core": "^6.5.1",
    "@fortawesome/free-brands-svg-icons": "^6.5.1",
    "@fortawesome/free-regular-svg-icons": "^6.5.1",
    "@fortawesome/free-solid-svg-icons": "^6.5.1",
    "@fortawesome/vue-fontawesome": "^3.0.5",
    "@vercel/analytics": "^0.1.11",
    "axios": "^1.6.5",
    "pinia": "^2.1.7",
    "sass": "^1.69.7",
    "vue": "^3.2.45",
    "vue-recaptcha": "^2.0.3",
    "vue-router": "^4.2.5"
  },
  "devDependencies": {
    "@rushstack/eslint-patch": "^1.6.1",
    "@types/node": "^18.19.6",
    "@vitejs/plugin-vue": "^4.6.2",
    "@vitejs/plugin-vue-jsx": "^3.1.0",
    "@vue-leaflet/vue-leaflet": "^0.10.1",
    "@vue/eslint-config-prettier": "^7.1.0",
    "@vue/eslint-config-typescript": "^11.0.3",
    "@vue/tsconfig": "^0.1.3",
    "eslint": "^8.56.0",
    "eslint-plugin-vue": "^9.20.1",
    "leaflet": "^1.9.4",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.8.8",
    "typescript": "~4.7.4",
    "vite": "^4.5.1",
    "vite-plugin-sitemap": "^0.5.3",
    "vue-tsc": "^1.8.27"
  }
DanSnow commented 3 months ago

Hi @mateusfmello, Thanks for reporting this, and sorry for the late reply. I'd prefer option 2 with a little improvement. We can add an id or data-vue-recaptcha to the script we inserted to head.

<script id="vue-recaptcha" src="..."></script>

So when we search for an existing tag, we can use document.querySelector('#vue-recaptcha')

What do you think about this?

mateusfmello commented 3 months ago

Perfect, I think it's efficient.

I'll put it as one of the next tasks to do, as soon as possible I'll implement it and make a pull request