Загрузка файла сценария по требованию с помощью vanilla js

#javascript #loading

#javascript #Загрузка

Вопрос:

Я использую аккордеон или вкладки. При нажатии на вкладку я хочу отобразить карту или что-то, для загрузки чего требуется большой файл javascript.

Чтобы не загружать большой javascript, когда он не нужен, я хочу загружать его только при нажатии на вкладку.

Как я могу это сделать? Я создал хорошую отправную точку с аккордеоном и событием щелчка.

  • Я использую vanilla js (чистый javascript — без jQuery)
  • Я использую его на веб-странице (не nodejs)
  • Я в порядке, если он работает с современными браузерами (IE не нужен)

 window.addEventListener('DOMContentLoaded', () => {
  document.querySelector('[data-trigger]').addEventListener('click', () => {
    console.log('Load script from file');
    // CDN example - https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js
  });
});  
 ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

label {
  display: flex;
  align-items: center;
}

label:before {
  content: '';
  background-image: url("data:image/svg xml,");
  background-repeat: no-repeat;
  width: 24px;
  height: 24px;
}

input[type=checkbox] {
  display: none;
}

input[type=checkbox]:checked ~ h2 label:before {
  transform: rotate(90deg);
}

p {
  display: none;
}

input[type=checkbox]:checked ~ h2 ~ p {
  display: block;
}  
 <ul>
  <li>
    <input type="checkbox" id="faq-1">
    <h2 data-trigger>
      <label for="faq-1">Click to load map</label>
    </h2>
    <p>Gummies marzipan croissant chupa chups.</p>
  </li>

  <li>
    <input type="checkbox" id="faq-2">
    <h2>
      <label for="faq-2">Something else</label>
    </h2>
    <p>Cookie bear claw carrot cake croissant.</p>
  </li>
</ul>  

Комментарии:

1. попробуйте document.write? или пусть MyScript = document.createElement(«script»); MyScript.setAttribute(«src», » example.com/foo.js » ); document.body.appendChild(MyScript);

Ответ №1:

Поскольку вам удобно поддерживать только современные браузеры, вы можете использовать динамическую import() функцию для загрузки файла:

 window.addEventListener('DOMContentLoaded', () => {
  document.querySelector('[data-trigger]').addEventListener('click', async() => {
    const module = await import('https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js');
    console.log(`module is loaded: ${Object.keys(mapboxgl)}`);
  });
});  
 ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

label {
  display: flex;
  align-items: center;
}

label:before {
  content: '';
  background-image: url("data:image/svg xml,");
  background-repeat: no-repeat;
  width: 24px;
  height: 24px;
}

input[type=checkbox] {
  display: none;
}

input[type=checkbox]:checked~h2 label:before {
  transform: rotate(90deg);
}

p {
  display: none;
}

input[type=checkbox]:checked~h2~p {
  display: block;
}  
 <ul>
  <li>
    <input type="checkbox" id="faq-1">
    <h2 data-trigger>
      <label for="faq-1">Click to load map</label>
    </h2>
    <p>Gummies marzipan croissant chupa chups.</p>
  </li>

  <li>
    <input type="checkbox" id="faq-2">
    <h2>
      <label for="faq-2">Something else</label>
    </h2>
    <p>Cookie bear claw carrot cake croissant.</p>
  </li>
</ul>  

import() возвращает обещание, которое разрешается при успешной выборке и немедленно разрешается при последующих вызовах (в моем тестировании), так что вам не придется иметь дело с этим конкретно.

Для вашего конкретного mapbox-gl скрипта возвращаемое значение не будет полезным, поскольку скрипт не написан как модуль ES6, но побочные эффекты запуска скрипта будут на месте (в данном случае, mapboxgl переменная, на которую назначается globalThis ). В сценариях, где загруженный скрипт является модулем ( export при правильном использовании), вы можете использовать разрешенное значение напрямую, чтобы избежать загрязнения глобального пространства имен.

Ответ №2:

Вы можете динамически создавать тег сценария и добавлять его в документ при каждом нажатии кнопки. Это приведет к немедленной загрузке скрипта. С onload помощью обратного вызова вы можете запускать свой код, например, создавать карту, в момент фактической загрузки скрипта.

Добавьте { once: true } в качестве третьего параметра for addEventListener , поскольку это гарантирует, что триггер можно щелкнуть только один раз и он не сможет добавить скрипт более одного раза.

 function createMap() {
  const map = new mapboxgl.Map({
    ...
  });
}

window.addEventListener('DOMContentLoaded', () => {
  document.querySelector('[data-trigger]').addEventListener('click', () => {
    console.log('Load script from file');
    
    const script = document.createElement('script');
    script.id = 'mapboxgljs';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js'
    script.async = true;
    script.onload = function() {
      console.log('script loaded, you can use it now.');
      createMap();
    }
    document.body.append(script);
  }, { once: true }); // Make sure that the button can only be pressed once.
});
  

Комментарии:

1. добавление script.defer = true или async тоже было бы полезно. мы хотим, чтобы страница реагировала во время загрузки скрипта

2. Хорошее дополнение. async будет ли путь, как defer следует, загружать его после каждого другого сценария, который не откладывается.