웹팩에서 스크립트를 평가하지 않고 어떻게 가져올 수 있습니까?


9

최근에 일부 웹 사이트 최적화 작업을 수행하고 있으며 다음과 같이 import 문을 사용하여 webpack에서 코드 분할을 사용하기 시작합니다.

import(/* webpackChunkName: 'pageB-chunk' */ './pageB')

pageB-chunk.js를 올바르게 작성하면 이제 pageA에서이 청크 를 프리 페치 하고 싶다고 가정 하고 pageA에 다음 명령문을 추가하여 수행 할 수 있습니다.

import(/* webpackChunkName: 'pageB-chunk' */ /* webpackPrefetch: true */ './pageB')

결과는

<link rel="prefetch" href="pageB-chunk.js">

HTML의 머리에 추가되면 브라우저는 HTML을 미리 가져옵니다.

문제는 내가 여기에서 사용 하는 import 문은 js 파일을 프리 페치 할뿐만 아니라 js 파일을 평가한다는 것입니다 .js 파일의 코드가 구문 분석 및 바이트 코드로 컴파일됨을 의미합니다 .JS의 최상위 코드가 실행됩니다.

이것은 모바일 장치에서 매우 시간 소모적 인 작업이며 최적화하고 싶습니다. 프리 페치 부분 만 원합니다. 평가 및 실행 부분을 원하지 않습니다. 나중에 일부 사용자 상호 작용이 발생하면 구문 분석 을 트리거하기 때문에 & 나 자신을 평가

여기에 이미지 설명을 입력하십시오

↑↑↑↑↑↑↑↑ 처음 두 단계 만 트리거하고 싶습니다. https://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/ ↑↑↑↑↑↑↑↑↑ ↑↑

물론 프리 페치 링크를 직접 추가하여이 작업을 수행 할 수 있지만 프리 페치 링크에 어떤 URL을 넣어야하는지 알아야합니다. webpack은이 URL을 확실히 알고 있습니다. 어떻게 웹팩에서 얻을 수 있습니까?

웹팩에이를 달성하는 쉬운 방법이 있습니까?


if (false) import(…)-Webpack이 데드 코드 분석을 의심합니까?
베르 기

어디 / 때 않는 당신은 실제로 모듈을 평가하려면? 그것이 바로 동적 import코드가가는 곳입니다.
베르 기

나는 지금 너무 혼란 스럽다. 평가가 중요한 이유 마지막으로 JS 파일은 클라이언트 브라우저 장치에 의해 평가되어야하기 때문입니다. 또는 질문을 올바르게 이해하지 못합니다.
AmerllicA

@AmerllicA는 결국 js를 평가해야하지만이 경우를 생각하십시오. 내 웹 사이트에 A, B 두 페이지가 있고 A 페이지의 방문자가 A 페이지의 "일을 마치고"페이지 B를 자주 방문합니다. 그러면 B 페이지를 프리 페치하는 것이 합리적입니다 JS이지만이 B의 JS가 평가되는 시간을 제어 할 수 있다면 방문자가 A 페이지에서 "작업을 수행"하려고 할 때 글리치를 생성하는 주 스레드를 차단하지 않을 것이라고 100 % 확신 할 수 있습니다. 방문자가 B 페이지를 가리키는 링크를 클릭 한 후 B의 JS이지만 B의 JS가 다운로드 될 가능성이 가장 높으므로 평가하는 데 약간의 시간이 필요합니다.
migcoder

물론 크롬 V8의 블로그에 따르면 : v8.dev/blog/cost-of-javascript-2019 , 그들은 여기에서 자세한 내용을 작업자 스레드와 다른 많은 기술자를 활용하여, 시간을 구문 분석 타오르는 빠른 JS를 달성하기 위해 많은 최적화를 수행 youtube.com / 시계? V = D1UJgiG4_NI . 그러나 다른 브라우저는 아직 그러한 최적화를 구현하지 않습니다.
migcoder

답변:


2

최신 정보

html -webpack-plugin 과 함께 preload-webpack-plugin 을 사용할 수 있습니다 . 구성에서 사전로드 할 항목을 정의하고 청크를 사전로드하기 위해 자동으로 태그를 삽입합니다

현재 webpack v4를 사용하는 경우 다음을 사용 하여이 플러그인을 설치해야합니다. preload-webpack-plugin@next

plugins: [
  new HtmlWebpackPlugin(),
  new PreloadWebpackPlugin({
    rel: 'preload',
    include: 'asyncChunks'
  })
]

chunk.31132ae6680e598f8879.jsand 와 같이 동적으로 생성 된 이름으로 두 개의 비동기 스크립트를 생성하는 프로젝트의 chunk.d15e7fdfc91b34bb78c4.js경우 다음과 같은 사전로드가 문서에 삽입됩니다.head

<link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
<link rel="preload" as="script" href="chunk.d15e7fdfc91b34bb78c4.js">

업데이트 2

모든 비동기 청크를 미리로드하지 않으려면 한 번만 특정로드하면됩니다.

migcoder의 babel 플러그인을 사용 하거나 preload-webpack-plugin다음 과 같이 사용할 수 있습니다

  1. 먼저 webpack magic comment예제를 사용 하여 비동기 청크의 이름을 지정해야합니다

    import(/* webpackChunkName: 'myAsyncPreloadChunk' */ './path/to/file')
  2. 그런 다음 플러그인 구성에서 다음과 같은 이름을 사용하십시오

    plugins: [
      new HtmlWebpackPlugin(),   
      new PreloadWebpackPlugin({
        rel: 'preload',
        include: ['myAsyncPreloadChunk']
      }) 
    ]

먼저 스크립트를로드하기 위해 script태그 또는 link태그를 지정할 때 브라우저의 동작을 살펴 보겠습니다

  1. 브라우저가 script태그를 발견 할 때마다 이를로드하여 구문 분석하고 즉시 실행합니다.
  2. 당신은 단지 파싱의 도움으로 평가 지연시킬 수 asyncdefer태그 까지만DOMContentLoaded 이벤트
  3. 스크립트 태그를 삽입하지 않는 경우 실행 (평가)를 지연시킬 수 있습니다 (단지와 함께 미리로드 link)

이제는 권장되지 않는 다른 해키 방법이 있습니다. 전체 스크립트를 배송하고 ( string또는 comment주석 또는 문자열의 평가 시간이 거의 무시할 수 있기 때문에) 실행해야 할 때 사용할 수 Function() constructor있거나 eval둘 다 권장하지 않습니다


다른 접근 서비스 작업자 : (이는 페이지를 다시로드 한 후 캐시 이벤트를 유지하거나 캐시가로드 된 후 사용자가 오프라인 상태가되도록합니다)

최신 브라우저에서는 service workerrecourse (JavaScript, image, css anything)를 가져오고 캐시하는 데 사용할 수 있으며 해당 recourse에 대한 기본 스레드 요청이 있으면 요청을 가로 채고 캐시에서 recourse를 반환 할 수 있습니다.이 방법으로 스크립트를 구문 분석하고 평가하지 않을 때 서비스 작업자에 대한 자세한 내용은 여기를 캐시에로드 하십시오.

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});

self.addEventListener('fetch', function(event) {
  event.respondWith(caches.match(event.request).then(function(response) {
    // caches.match() always resolves
    // but in case of success response will have value
    if (response !== undefined) {
      return response;
    } else {
      return fetch(event.request).then(function (response) {
        // response may be used only once
        // we need to save clone to put one copy in cache
        // and serve second one
        let responseClone = response.clone();

        caches.open('v1').then(function (cache) {
          cache.put(event.request, responseClone);
        });
        return response;
      }).catch(function () {
        // any fallback code here
      });
    }
  }));
});

당신이 볼 수 있듯이 이것은 webpack 의존적 이지 않습니다. 이것은 webpack 의 범위를 벗어납니다.하지만 webpack의 도움으로 번들을 분할하여 서비스 작업자를 더 잘 활용할 수 있습니다


그러나 여전히 내 고통은 웹 패키지에서 파일의 URL을 쉽게 얻을 수 없다는 것입니다 .SW와 함께가더라도 SW에 미리 캐시 해야하는 파일을 알려야합니다 ... 웹 팩 매니페스트 플러그인은 매니페스트 정보를 생성 할 수 있습니다 SW, 그러나 그것은 모두 작동 중입니다. SW는 선택 사항이 없다는 것을 의미합니다. 매니페스트에 나열된 모든 파일을 미리 캐시하십시오.
migcoder

이상적으로는 webpack이 / * webpackOnlyPrefetch : true * /와 같은 또 다른 마술 주석을 추가 할 수 있기를 바랍니다. 그래서 모든 게으른로드 가능한 청크에 대해 import 문을 두 번 호출하고 프리 페치를 위해 하나씩, 코드 평가를 위해 하나를 호출 할 수 있으며 모든 것이 필요할 때 발생합니다.
migcoder

1
유효한 포인트 인 @migcoder (런타임에 동적으로 생성되기 때문에 파일 이름을 얻을 수 없음)는 내가 찾을 수 있으면 어떤 해결책을 찾게 될 것입니다.
Tripurari Shankar

@migcoder 답변을 업데이트했습니다. 문제를 해결하는 방법을 확인하십시오.
Tripurari Shankar

문제의 일부를 해결하고 좋은 비동기 청크를 필터링 할 수 있지만 최종 목표는 비동기 청크를 프리 페치하는 것입니다. 나는 현재이 플러그인 github.com/sebastian-software/babel-plugin-smart-webpack-import를 보고 있는데 , 모든 import 문을 수집하고 마술 주석에 대한 코드 수정 기반을 만드는 방법을 보여줍니다. 아마도 만들 수 있습니다. 'webpackOnlyPrefetch : true'마술 주석이있는 import 문에 프리 페치 코드를 삽입하는 비슷한 플러그인.
migcoder

1

업데이트 : 모든 것을 npm 패키지에 포함 시켰습니다. https://www.npmjs.com/package/webpack-prefetcher


며칠간의 연구 끝에, 사용자 정의 babel 플러그인을 작성하게되었습니다 ...

간단히 말해서 플러그인은 다음과 같이 작동합니다.

  • 코드에서 모든 import (args) 문을 수집하십시오.
  • import (args) 에 / * 프리 페치가 포함 된 경우 : true * / comment
  • import () 문 에서 chunkId 찾기
  • 로 교체 Prefetcher.fetch (chunkId)

프리 페처는 webpack 출력의 매니페스트를 포함하는 도우미 클래스이며 프리 페치 링크를 삽입하는 데 도움이 될 수 있습니다.

export class Prefetcher {
  static manifest = {
    "pageA.js": "/pageA.hash.js",
    "app.js": "/app.hash.js",
    "index.html": "/index.html"
  }
  static function fetch(chunkId) {
    const link = document.createElement('link')
    link.rel = "prefetch"
    link.as = "script"
    link.href = Prefetcher.manifest[chunkId + '.js']
    document.head.appendChild(link)
  }
}

사용 예 :

const pageAImporter = {
  prefetch: () => import(/* prefetch: true */ './pageA.js')
  load: () => import(/* webpackChunkName: 'pageA' */ './pageA.js')
}

a.onmousehover = () => pageAImporter.prefetch()

a.onclick = () => pageAImporter.load().then(...)

이 플러그인의 세부 사항은 다음에서 찾을 수 있습니다.

프리 페치-웹팩에서 제어

다시 말하지만, 이것은 정말로 해킹 된 방법이며, 웹팩 팀이 이것을 구현하기를 원한다면 pls가 여기에 투표하십시오.

기능 : 주문형 동적 가져 오기 프리 페치


0

달성하려는 것을 이해했다고 가정하면 주어진 이벤트 후에 모듈을 구문 분석하고 실행하려고합니다 (예 : 버튼 클릭). 해당 이벤트 안에 import 문을 넣을 수 있습니다.

element.addEventListener('click', async () => {
  const module = await import("...");
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.