WebGetTest의 Visual Studio 코드 확장을 구축하는 방법

Stack Overflow 2019 Developer Survey에 따르면 VisualStudio 코드는 가장 인기 있는 개발자 환경 도구로 꼽혔으며 87317명 중 50.7%가 사용했다고 보고했다.
우리가 이 확장을 구축한 주요 원인은 개발자가 코드를 작성할 때 사이트의 성능을 향상시키는 데 도움을 주기 때문이다. 우리가 일찍 문제를 발견할수록 쉽게 해결되지 않겠는가?
일반적으로 VS 코드에서 코드를 작성하는 개발자는 그들의 코드 편집기를 떠나서 그들이 개발한 전단 코드의 성능을 검사해야 하기 때문에 우리는 자신에게 묻는다
VS 코드에서 성능을 테스트하는 것은 어떻습니까?그들은 어디에서 코드를 작성합니까?
좋은 생각인 것 같은데 어떻게 할까요?
그래, 타. 우리가 어떻게 했는지 보여줘.

1단계: 확장을 위한 기본 템플릿 생성


VS 코드는 템플릿 코드를 제공하여 구축 확장 과정을 간소화하고 우리가 필요로 하는 노드를 생성한다.js 설치 후 다음 명령을 실행하여 설치YeomanVS Code Extension Generator할 수 있습니다.npm install -g yo generator-codeVS 코드 확장 생성기는 개발할 준비가 된 TypeScript 또는 JavaScript 프로젝트를 구축합니다.이제 생성기를 실행하고 프로젝트에 대한 몇 개의 필드를 작성합니다.yo code
JavaScript 확장을 생성하고 있습니다.네, 좋습니다. WebGetTest 기능을 모두 추가할 수 있는 확장이 있습니다.

2단계: 설정 추가


VisualStudio 코드는 Github의 Electron 위에서 웹 기술(HTML, CSS, 자바스크립트)을 사용하여 구축된 것을 아십니까?
따라서 다양한 설정을 통해 원하는 대로 VisualStudio 코드를 구성하는 것이 쉬워집니다.VS 코드의 편집기, 사용자 인터페이스, 기능 동작의 거의 모든 부분에 수정할 수 있는 옵션이 있다. 
우리는 테스트를 실행하기 위해 약간의 속성이 필요하기 때문에 이러한 속성을 간단한 설정으로 받아들이는 것은 의미가 있다.API 키, 위치, URL 등을 수락합니다.트리거 테스트.다음은 설정의 객체 예입니다.json
// Your WebPageTest API key. REQUIRED
"wpt_extension.apiKey": "YOUR_API_KEY",

// The URL to test. If left out of settings.json, the extension will prompt you for a URL when run.
"wpt_extension.urlToTest": null,

// The location to test from. The location is comprised of the location of the testing agent, the browser to test on, and the connectivity in the following format: location:browser.connectivity.
"wpt_extension.location": "Dulles:Chrome.Cable",

// The number of tests to run
"wpt_extension.runs": 1,

// The interval (in seconds) to poll the API for test results
"wpt_extension.pollResults": 5,

// The maximum time (in seconds) to wait for test results
"wpt_extension.timeout": 240,
지원되는 모든 옵션을 추가할 수 있습니다WebPageTest Node API wrapper.이상은 단지 기본적인 문제일 뿐이다. 

3단계: 네트워크 뷰 구축


웹뷰 API는 VisualStudio 코드에서 사용자 정의 뷰를 만들 수 있도록 확장할 수 있습니다.웹뷰를 확장 제어의 VS 코드의 iframe으로 간주합니다.웹뷰는 이 프레임워크에서 거의 모든 HTML 내용을 보여 주고 메시지 전달과 확장을 사용하여 통신할 수 있습니다.
우리에 대해 말하자면, 우리는 웹뷰가 지표, 화면 캡처, 폭포 등 테스트의 세부 사항을 제공하기를 바란다.
테스트 실행 시 다음과 같은 5가지 응답 유형이 표시됩니다.

  • 테스트 커밋 성공 – 테스트 커밋 성공 시

  • URL 없음 – URL이 추가되지 않은 경우

  • 오류 – 테스트를 실행하는 동안 오류가 발생한 경우

  • 크롬 기반 테스트 - 테스트가 크롬에 특정하고 크롬망 생명체징이 포함될 때

  • 비크롬기 테스트 - 비크롬 특정 테스트로 테스트할 때
  • 하나하나 자세히 봅시다.

    3.1 테스트를 성공적으로 제출


    다음은 테스트를 성공적으로 제출한 후 표시되는 HTML 예로, 테스트 중인 URL을 보여 줍니다.
     
    exports.getContentForTestSubmission = (url) =>{
        return `<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>WebPageTest Results</title>
            <style>
              h1 {text-align: center;}
              h3 {text-align: center;}
            </style>
        </head>
        <body>
              <h1>WebPageTest Results</h1>
              <h3>Test Submitted for <a href="${url}">${url}</a></h3>
              <h3>Please wait until we fetch your results....</h3>
          </body>
        </html>`
    }
    

    3.2 URL 없음


    다음은 HTML 예시입니다. 테스트 제출에 URL을 제공하지 않으면 이 예시를 보여 줍니다. 이 예시에서 우리는 정보를 추가하는 방법을 보여 줍니다.
     
    exports.getContentForNoUrl = ()=>{
    
        return `<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>WebPageTest Results</title>
            <style>
              h1 {text-align: center;}
              h3 {text-align: center;}
              h4 {text-align: center;}
            </style>
        </head>
        <body>
              <h1>WebPageTest Results</h1>
              <h3>Please enter a URL to test</h3>
              <h4>You can add URL in settings.json file for vscode or enter it in the input field</h4>
          </body>
        </html>`
    }
    

    3.3 오류


    다음은 HTML 예시입니다. 테스트를 실행할 때 오류가 발생하면 이 예시를 보여 줍니다. WebGetTest에서 보낸 상태 메시지를 보여 줍니다.예를 들어, api 키가 올바르지 않은 경우
     
    exports.getContentForError = (wptResponse)=>{
    
        return `<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>WebPageTest Results</title>
            <style>
              h1 {text-align: center;}
              h3 {text-align: center;}
              h4 {text-align: center;}
            </style>
        </head>
        <body>
              <h1>WebPageTest Results</h1>
              <h3>${wptResponse.statusText}</h3>
          </body>
        </html>`
    }
    

    3.4 크롬 기반 테스트 결과


    다음은 크롬 기반 테스트의 HTML 예입니다.
     
    exports.getContentForChromeBasedSubmission = (wptResponse) =>{
    
        return `<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>WebPageTest Results</title>
            <style>
            //Lets see this later
            </style>
        </head>
        <body>
              <h1>WebPageTest Results</h1>
              <h3>Test result for <a href="${wptResponse.result.data.url}">${wptResponse.result.data.url}</a></h3>
              <h3>Find detailed results at <a href="${wptResponse.result.data.summary}">${wptResponse.result.data.summary}</a></h3>
              <h4><b>From :</b> ${wptResponse.result.data.from} </h4>
    
              <div>
                  <table>
                      <tbody>
                          <tr>
                                <th colspan="4" class="bordernone"></th>
                              <th colspan="3">Web Vitals</th>
                              <th colspan="3">Document Complete</th>
                              <th colspan="4">Fully Loaded</th>  
                          </tr>
                          <tr>
                              <th>First Byte</th>
                              <th>Start Render</th>
                              <th>First Contentful Page</th>
                              <th>Speed Index</th>
                              <th>Largest Contentful Paint</th>
                              <th>Cumulative Layout Shift</th>
                              <th>Total Blocking Time</th>
                              <th>Time</th>
                              <th>Requests</th>
                              <th>Bytes In</th>
                              <th>Time</th>
                              <th>Requests</th>
                              <th>Bytes In</th>  
                          </tr>
                          <tr>
                                <td>${wptResponse.result.data.median.firstView.TTFB/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.render/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.firstContentfulPaint/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.SpeedIndex/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.chromeUserTiming.LargestContentfulPaint/1000}s</td>
                              <td>${wptResponse.result.data.median.firstView.chromeUserTiming.CumulativeLayoutShift}</th>
                              <td>>= ${wptResponse.result.data.median.firstView.TotalBlockingTime/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.docTime/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.requestsDoc}</th>
                              <td>${Math.round(wptResponse.result.data.median.firstView.bytesInDoc/1024)}KB</th>
                              <td>${wptResponse.result.data.median.firstView.fullyLoaded/1000}s</th>
                              <td>${wptResponse.result.data.median.firstView.requestsFull}</th>
                              <td>${Math.round(wptResponse.result.data.median.firstView.bytesIn/1024)}KB</th>  
                          </tr>
                      </tbody>
                  </table>
              </div>
    
              <div class="row" align="center">
                  <div class="column">
                      <h4>Waterfall</h4>
                        <img src="${wptResponse.result.data.median.firstView.images.waterfall}"/>
                  </div>
                  <div class="column">
                      <h4>Screenshot</h4>
                        <img src="${wptResponse.result.data.median.firstView.images.screenShot}"/>
                  </div>
              </div>
    
          </body>
        </html>`;
    
    }
    

    3.5 비크롬기 시험 결과


    다음은 크롬 기반 테스트가 아닌 HTML 예입니다.
     
    exports.getContentForNonChromeBasedSubmission = (wptResponse) =>{
    
            return `<!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>WebPageTest Results</title>
                <style>
                  // Hang on, lets see this in a bit
                </style>
            </head>
            <body>
                  <h1>WebPageTest Results</h1>
                  <h3>Test result for <a href="${wptResponse.result.data.url}">${wptResponse.result.data.url}</a></h3>
                  <h3>Find detailed results at <a href="${wptResponse.result.data.summary}">${wptResponse.result.data.summary}</a></h3>
                  <h4><b>From :</b> ${wptResponse.result.data.from} </h4>
    
                  <div>
                      <table>
                          <tbody>
                              <tr>
                                    <th colspan="4" class="bordernone"></th>
                                  <th colspan="1">Web Vitals</th>
                                  <th colspan="3">Document Complete</th>
                                  <th colspan="4">Fully Loaded</th>  
                              </tr>
                              <tr>
                                  <th>First Byte</th>
                                  <th>Start Render</th>
                                  <th>First Contentful Page</th>
                                  <th>Speed Index</th>
                                  <th>Total Blocking Time</th>
                                  <th>Time</th>
                                  <th>Requests</th>
                                  <th>Bytes In</th>
                                  <th>Time</th>
                                  <th>Requests</th>
                                  <th>Bytes In</th>  
                              </tr>
                              <tr>
                                    <td>${wptResponse.result.data.median.firstView.TTFB/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.render/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.firstContentfulPaint/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.SpeedIndex/1000}s</th>
                                  <td>>= ${wptResponse.result.data.median.firstView.TotalBlockingTime/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.docTime/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.requestsDoc}</th>
                                  <td>${Math.round(wptResponse.result.data.median.firstView.bytesInDoc/1024)}KB</th>
                                  <td>${wptResponse.result.data.median.firstView.fullyLoaded/1000}s</th>
                                  <td>${wptResponse.result.data.median.firstView.requestsFull}</th>
                                  <td>${Math.round(wptResponse.result.data.median.firstView.bytesIn/1024)}KB</th>  
                              </tr>
                          </tbody>
                      </table>
                  </div>
    
                  <div class="row" align="center">
                      <div class="column">
                          <h4>Waterfall</h4>
                            <img src="${wptResponse.result.data.median.firstView.images.waterfall}"/>
                      </div>
                      <div class="column">
                          <h4>Screenshot</h4>
                            <img src="${wptResponse.result.data.median.firstView.images.screenShot}"/>
                      </div>
                  </div>
    
              </body>
            </html>`;
    
    }
    
    크롬 및 비크롬 테스트 결과를 기준으로 한 스타일 레이블:
    <style>
                  h1 {text-align: center;}
                  h2 {text-align: center;}
                  .row {
                      display: flex;
                    }
    
                    .column {
                      flex: 33.33%;
                      padding: 5px;
                    }
                    table {
                      font-family: arial, sans-serif;
                      border-collapse: collapse;
                      width: 100%;
                    }
                    td, th {
                      border: 1px solid silver;
                      padding: 8px; 
                      text-align: center;
                    }
                    .bordernone{
                        border: none;
                    }   
     </style>
    

    4단계: WebGetTest 포장 방법


    유지보수에 편리하도록 코드를 모듈화하는 것을 시종 건의합니다.다음은 WebPageTest Node API wrapper가 제공하는runTest 방법을 포장했습니다. 이것은 리셋을 바탕으로 하는 방법이고 이를 약속을 바탕으로 하는 방법으로 전환합니다.
     
    exports.runTest = (wpt, url, options) => {
    
        const tempOptions = JSON.parse(JSON.stringify(options));
        return new Promise((resolve, reject) => {
            wpt.runTest(url, tempOptions, async(err, result) => {
                try {
                    if (result) {
                        return resolve({'result':result,'err':err});
                    } else {
                        return reject(err);
                    }
                } catch (e) {
                    console.info(e);
                }
            })
        });
    }
    

    5단계: 확장


    Ufff, 길지만, 이제 확장을 위한 모든 선결 조건이 생겼습니다.우리 는 결국 그것 을 건설할 것 이다

    스트레칭 해부학


    WebGetTest 확장에는 다음과 같은 3가지 기능이 있습니다.
  • 등록onCommandActivation Event:onCommand: 확장명.웹 테스트.WebGetTest 명령을 실행할 때 확장을 활성화하려면 wpt를 사용하십시오. 
  • 사용contributes.commandsContribution Point 명령 WebGetTest는 명령 팔레트에서 사용할 수 있으며 명령 ID 확장에 바인딩됩니다.웹 테스트. 
  • commands.registerCommandVS Code API로 등록된 명령 ID에 함수를 바인딩하여 확장합니다.웹 테스트. 
  • 이 세 가지 개념을 이해하는 것은 VS 코드에서 확장을 작성하는 데 매우 중요하다.

  • Activation Events: 사용자의 분기 활성화 이벤트입니다. 

  • Contribution Points: 가방에서 진행된 정적 성명.jsonExtension Manifest으로 VS 코드를 확장합니다. 

  • VS Code API: 확장 코드에서 호출할 수 있는 JavaScript API 그룹입니다. 
  • 아래 코드에는 WebGetTest, VS 코드 모듈(1행과 2행), 그리고 이전에 구축된 지원 방법(3행과 4행)이 포함되어 있습니다.

  • wpt 어시스턴트 - WebGetTest 패키지 및 약속으로 변환

  • 웹 뷰 - 결과에 표시되는 HTML 컨텐트입니다. 
  • 명령을 등록하고 이전에 추가된 설정 (18 줄과 22 줄) 을 가져오면, 우리는api 키 (24 줄) 를 전달해서 WebGetTest의 실례를 설정합니다. 
    설정(settings.json)에 URL이 전달되지 않으면 VS 코드 API(vscode.window.showInputBox)를 사용하여 URL을 가져옵니다(27줄).이것은 마지막으로 당신의 URL에 로그인한 것입니다. 
    설정에 추가되지 않으면 필요한 모든 설정이 설정됩니다.json(29줄 – 33줄)
     
    const vscode = require('vscode'); //line #1
    const WebPageTest = require("webpagetest"); //line #2
    const wptHelpers = require('./wpt-helpers'); //line #3
    const webViews = require('./utils/web-views'); //line #4
    let options = {
        "firstViewOnly": true,
        "runs": 1,
        "location": 'Dulles:Chrome.Cable',
        "pollResults": 5,
        "timeout": 240
    }
    
    /**
     * @param {vscode.ExtensionContext} context
     */
    async function activate(context) {
    
        let disposable = vscode.commands.registerCommand('webpagetest.wpt', async function () {  //line #18
    
            try {
    
                const wpt_extension_config = JSON.parse(JSON.stringify(vscode.workspace.getConfiguration('wpt_extension')))  //line #22
                const WPT_API_KEY = wpt_extension_config.apiKey;
                const wpt = new WebPageTest('www.webpagetest.org', WPT_API_KEY); //line #24
                let url = wpt_extension_config['urlToTest'];
                if (!url)
                    url = await vscode.window.showInputBox({"prompt": "Enter the URL you want to test."}) //line #27
    
                wpt_extension_config['firstViewOnly'] = wpt_extension_config['firstViewOnly'] === false ? false : options['firstViewOnly']; //line #29
                wpt_extension_config['location'] = wpt_extension_config['location'] || options['location'];
                wpt_extension_config['pollResults'] = wpt_extension_config['pollResults'] || options['pollResults'];
                wpt_extension_config['timeout'] = wpt_extension_config['timeout'] || options['timeout'];
                wpt_extension_config['runs'] = wpt_extension_config['runs'] || options['runs'];  //line #33
    
                var panel = vscode.window.createWebviewPanel(
                    'webpagetest',
                    'WebPageTest',
                    vscode.ViewColumn.One
                );
    
                if (!url) {
                    panel.webview.html = webViews.getContentForNoUrl();
                    return;
                }
    
    다음 그림에서 vscode를 입력합니다.창문.createWebviewPanel 함수는 편집기에서 웹뷰를 만들고 표시합니다. 
    최종 호출에 URL을 추가하지 않으면 contentForNoURL 웹뷰 (8 줄) 가 표시되고, 추가하면 최종 결과에 두 개의 다른 웹뷰가 생성됩니다.

  • 크롬기(24행)

  • 비크롬기(27행)
     
  •             var panel = vscode.window.createWebviewPanel(  //line #1
                    'webpagetest',
                    'WebPageTest',
                    vscode.ViewColumn.One
                );
    
                if (!url) {
                    panel.webview.html = webViews.getContentForNoUrl(); //line #8
                    return;
                }
                panel.webview.html = webViews.getContentForTestSubmission(url);
                const wptResponse = await wptHelpers.runTest(wpt, url.toString(), wpt_extension_config);
                const chromeUserTiming = wptResponse.result.data.median.firstView.chromeUserTiming;
                if (chromeUserTiming) {
                    for (let i = 0; i < chromeUserTiming.length; i++) {
                        if (chromeUserTiming[i].name == 'firstContentfulPaint')
                            wptResponse.result.data.median.firstView.firstContentfulPaint = chromeUserTiming[i].time;
                        if (chromeUserTiming[i].name == 'LargestContentfulPaint')
                            wptResponse.result.data.median.firstView.chromeUserTiming.LargestContentfulPaint = chromeUserTiming[i].time;
                        if (chromeUserTiming[i].name == 'CumulativeLayoutShift')
                            wptResponse.result.data.median.firstView.chromeUserTiming.CumulativeLayoutShift = chromeUserTiming[i].value.toFixed(3);
                    }
    
                    panel.webview.html = webViews.getContentForChromeBasedSubmission(wptResponse);  //line #24
                }
                else {
                    panel.webview.html = webViews.getContentForNonChromeBasedSubmission(wptResponse);  //line #27
                }
    
    Full Code for reference can be found here

    4단계: 확장 실행


    길이 멀지 않습니까?지금 우리에게 분기기를 운행하라고 한다. 
    디버거 모드에서 확장을 실행하려면 다음 절차를 따르십시오.
    4.1 F5 키를 눌러 디버거를 트리거합니다.그러면 명령이 등록된 다른 VS 코드 창이 열립니다.
    4.2 명령 팔레트 열기(⇧⌘P) 그런 다음 WebGetTest 입력을 시작합니다.

    4.3 이전에 설정에 URL을 입력하지 않은 경우 명령을 실행합니다.입력할 수 있습니다. (우리가 전에 토론한 마지막 호출)테스트를 커밋하면 다음과 같은 응답이 표시됩니다.

    다음은 Webview의 결과 예입니다.

    아직도 나랑 같이 있어?또한 VS Code extension marketplace에 이 확장을 게시하여 플러그 앤 플레이 방식으로 사용할 수 있습니다.
    이전과 같이 우리는 당신의 피드백을 중시하고 당신과 전 세계 수백만 명의 개발자들이 이러한 체험을 개선하도록 돕습니다.너는 향상PRs on the repository을 통해 우리가 향상하는 것을 도울 수 있다.
    최초 발표WebPageTest.WebGetTestthis is the way에 대해 더 알고 싶으세요?

    좋은 웹페이지 즐겨찾기