WordPress의 JavaScript는 지옥입니다... 그리고 여기에 이유가 있습니다

게시 됨: 2022-05-05

WordPress 개발 스택은 최근 몇 년 동안 많이 변경되었습니다. Gutenberg의 등장 이후로 우리가 가장 좋아하는 CMS에서 JavaScript의 역할은 그 어느 때보다 중요해졌습니다. 이 블로그에서 우리는 이미 개발자에게 수반되는 이점에 대해 길게 이야기했지만(몇 가지 예를 들자면 Gutenberg의 확장, TypeScript 및 React에 대한 조언, 개발 도구, 플러그인 예제 등에 대해 이야기했습니다) 모든 이야기에는 어두운 면 이 있습니다. 그리고 그것이 오늘 우리가 여기서 이야기할 내용입니다.

오늘의 포스트에서는 워드프레스 플러그인 개발자가 앞으로 직면하게 될 3가지 주요 문제 에 대해 알려드리겠습니다. 그리고 재미있는 부분은 그들 각각이 WordPress 자체, 다른 개발자 또는 자신과 같은 다른 범인을 가지고 있다는 것입니다. 자, 여기에서 우리가 겪을 수 있는 가장 일반적인 JavaScript 골칫거리와 이를 방지/수정하기 위해 할 수 있는 일에 대해 알아보겠습니다.

플러그인과 사이트를 망가뜨릴 #1 WPO 플러그인

Nelio: WPO 플러그인에서 많은 티켓의 출처였던 문제부터 시작하겠습니다.

나는 당신이 빨리 로드되는 가벼운 웹사이트를 갖는 것의 중요성을 설명하는 많은 기사와 블로그 포스트를 읽었을 것이라고 확신합니다. 내 말은, 우리도 그것에 대해 여러 번 썼습니다! 그들이 일반적으로 제공하는 팁 중에는 더 나은 호스팅 제공업체로 전환, 캐시 플러그인 및 CDN 사용, 서버와 WordPress를 최신 상태로 유지, 또는 (여기서 첫 번째 문제가 있음) WPO 플러그인 설치와 같은 것들을 찾을 수 있습니다. 후자의 몇 가지 예는 다음과 같습니다.

  • 1백만 개 이상의 설치가 있는 W3 Total Cache
  • SiteGround Optimizer도 100만 회 이상 설치되었습니다(그 중 하나는 웹사이트에서 사용)
  • 우리의 좋은 친구 Fernando Tellado의 WordPress WPO Tweaks & Optimizations

이 플러그인은 일반적으로 "모든 WordPress 사이트가 혜택을 받을 수 있는" 일련의 합리적인 최적화를 통해 웹사이트 속도를 높일 것을 약속합니다. 이러한 최적화에는 다음이 포함됩니다.

  • 이모티콘이나 대시콘과 같은 프론트엔드에서 불필요한 스크립트를 대기열에서 빼기
  • 페이지 및 데이터베이스 쿼리 캐싱
  • 헤더에 포함된 정보의 양 줄이기
  • JavaScript 스크립트와 CSS 스타일 결합 및 축소
  • HTML 축소
  • 정적 자산의 URL에서 버전 쿼리 인수 제거
  • JavaScript 스크립트 지연 및/또는 비동기식 로드

내가 말했듯이 이러한 유형의 최적화는 일반적으로 유익할 수 있습니다. 그러나 경험상 워드프레스 웹사이트의 모든 JS 최적화 는 더 많은 문제 를 야기하는 경향이 있어 개선된 것으로 추정되는 것을 쓸모없게 만듭니다. 내가 수년 동안 본 실제 예는 다음과 같습니다.

  • 스크립트 결합. 브라우저에서 요청해야 하는 스크립트는 적을수록 좋습니다. 그렇기 때문에 모든 스크립트를 하나로 결합하는 것이 합리적입니다. 그러나 이것은 문제가 될 수 있습니다. 일반적으로 JavaScript 스크립트가 충돌하면 실행이 종료되고 브라우저 콘솔에 오류가 보고됩니다. 그러나 해당 스크립트의 실행만 중지됩니다. 다른 스크립트는 정상적으로 실행됩니다. 그러나 그것들을 모두 결합하면… 글쎄요, 한 스크립트가 실패하자마자 다른 스크립트(아마도 당신의 스크립트 포함)가 실행되지 않고 사용자는 예상대로 작동하지 않는 플러그인이라고 생각할 것입니다.
  • 스크립트 축소. 믿거 나 말거나, 정규식을 깨고 구문 오류가 있는 JS 스크립트를 생성하는 일부 축소 프로세스를 보았습니다. 물론, 이것을 마지막으로 만난 이후로 시간이 많이 흘렀지만… :-/
  • 쿼리 인수. WordPress에서 스크립트를 대기열에 추가할 때 버전 번호로 그렇게 할 수 있습니다(그런데 @wordpress/scripts 에 의해 자동 생성되었을 것입니다). 버전 번호는 매우 유용합니다. 플러그인을 업데이트하고 스크립트가 변경된 경우 이 새 버전 번호는 모든 방문자가 다른 URL을 볼 수 있도록 보장하므로 브라우저에서 새 버전을 요청할 것입니다. 불행히도 WPO 플러그인이 쿼리 문자열을 제거하면 방문자는 스크립트가 변경되었음을 깨닫지 못하고 해당 스크립트의 캐시된 사본을 사용하게 될 것입니다… 의도하지 않은 결과를 초래할 수도 있고 그렇지 않을 수도 있습니다. 제길!

완전 재앙이죠? 그러나 다음 소리가 들릴 때까지 기다리십시오.

스크립트 연기

Nelio에서는 방문자를 추적하고 어떤 디자인과 콘텐츠가 가장 많은 전환을 가져오는지 알아내는 A/B 테스팅 플러그인을 구현했습니다. 상상할 수 있듯이 추적 스크립트는 다음과 유사합니다.

 window.NelioABTesting = window.NelioABTesting || {}; window.NelioABTesting.init = ( settings ) => { // Add event listeners to track visitor events... console.log( settings ); };

즉, 현재 실행 중인 테스트에 대해 스크립트에 알리기 위해 호출해야 하는 init 함수를 노출합니다. 이 메서드를 호출하기 위해 다음과 같이 PHP에서 인라인 스크립트를 대기열에 넣습니다.

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTesting.init( %s );', wp_json_encode( nab_get_tracking_settings() ) ) ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

그 결과 다음 HTML 태그가 생성됩니다.

 <head> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

그러나 WPO 플러그인이 스크립트에 defer 속성을 추가하면 어떻게 될까요?

 <head> ... <script defer <!-- This delays its loading... --> type="text/javascript" src="https://.../dist/tracking.js" ></script> <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ...

글쎄, 스크립트는 이제 연기되었습니다… 이것은 이전 스니펫이 다음 스니펫과 동일하다는 의미입니다.

 <head> ... <script type="text/javascript" > NelioABTesting.init( {"experiments":[...],...} ); </script> ... </head> <body> ... <script type="text/javascript" src="https://.../dist/tracking.js" ></script> </body> </html>

결과적으로 nab-tracking-js 는 로드되어야 할 때 더 이상 로드되지 않으므로 이후에 와서 이에 의존하는 인라인 스크립트는 단순히 실패합니다. nab-tracking-js-afterNelioABTesting.init 를 사용합니다. , defer 지시문 덕분에 아직 사용할 수 없습니다. 끔찍한!

해결책

가장 효과적인 솔루션은 명확합니다. 사용자에게 스크립트 최적화를 비활성화하고 하루라고 하십시오. 결국 JavaScript의 종속성 관리는 일반적으로 매우 복잡하며(특히 deferasync 지시문을 사용하는 경우) WordPress도 예외는 아닙니다. 이 주제에 대한 12년 간의 토론을 살펴보십시오!

그러나 그것이 가능하지 않다면(그리고 그것이 불가능하다는 것을 알고 있습니다), 저는 우리가 했던 것과 같은 일을 할 것을 권장합니다. init 메소드를 제거하고 일반 및 인라인 스크립트의 책임을 반대로 하십시오. 즉, 일반 스크립트 앞에 인라인 스크립트를 추가하고 이를 사용하여 필수 설정으로 전역 변수를 정의합니다.

 function nab_enqueue_tracking_script() { wp_enqueue_script( 'nab-tracking', ... ); wp_add_inline_script( 'nab-tracking', sprintf( 'NelioABTestingSettings = %s;', wp_json_encode( nab_get_tracking_settings() ) ), 'before' ); } add_action( 'wp_enqueue_scripts', 'nab_enqueue_tracking_script' );

결과 HTML이 다음과 같이 보이도록 합니다.

 <head> ... <script type="text/javascript" > NelioABTestingSettings = {"experiments":[...],...}; </script> <script type="text/javascript" src="https://.../dist/tracking.js" ></script> ... </head> <body> ...

따라서 외부 스크립트의 실행이 지연되는지 여부는 중요하지 않습니다. 항상 인라인 스크립트 뒤에 나타나서 이들 간의 종속성을 만족시킵니다.

마지막으로, 아무도 설정을 수정하지 못하도록 하려면 변수를 const 로 선언하고 Object.freeze 로 값을 고정합니다.

 ... sprintf( 'const NelioABTestingSettings = Object.freeze( %s );', wp_json_encode( nab_get_tracking_settings() ) ), ...

모든 최신 브라우저에서 지원됩니다.

# 2 작동하거나 작동하지 않을 수 있는 WordPress의 종속성…

종속성 관리는 특히 WordPress의 내장 스크립트에 대해 이야기할 때 WordPress에서 문제가 될 수 있습니다. 설명하겠습니다.

예를 들어 여기에서 설명한 대로 Gutenberg에 대한 작은 확장을 생성한다고 상상해 보십시오. 플러그인의 소스 코드에는 다음과 같은 import 문이 있을 것입니다.

 import { RichTextToolbarButton } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { registerFormatType } from '@wordpress/rich-text'; // ...

이 JS 소스 코드가 변환되면 Webpack(또는 사용하는 도구)은 모든 종속성과 고유한 소스 코드를 단일 JS 파일로 패키징합니다. 이것은 모든 것이 예상대로 작동하도록 나중에 WordPress에서 대기열에 추가할 파일입니다.

@wordpress/scripts 를 사용하여 이러한 파일을 만든 경우 기본 제공 프로세스에서 패키지를 전역 범위에서 사용할 수 있다고 가정하기 때문에 일부 종속성이 출력 파일에 포함되지 않습니다. 이는 이전 가져오기가 다음과 유사한 것으로 변환됨을 의미합니다.

 const { RichTextToolbarButton } = window.wp.blockEditor; const { __ } = window.wp.i18n; const { registerFormatType } = window.wp.richText; // ...

스크립트의 종속성을 놓치지 않도록 @wordpress/scripts 는 JS 코드를 변환할 뿐만 아니라 WordPress 종속성을 포함하는 PHP 파일도 생성합니다.

 <?php return array( 'dependencies' => array('wp-block-editor','wp-i18n','wp-rich-text'), 'version' => 'a12850ccaf6588b1e10968124fa4aba3', );

꽤 깔끔하죠? 문제가 무엇입니까? 이 WordPress 패키지는 지속적인 개발 중이며 새로운 기능과 개선 사항을 추가하여 자주 변경됩니다. 따라서 최신 버전의 WordPress를 사용하여 플러그인을 개발하면 "이전" WordPress 버전이 아닌 최신 버전(따라서 모든 것이 제대로 작동함)에서 사용할 수 있는 기능이나 기능을 실수로 사용하게 될 수 있습니다.

어떻게 알 수 있습니까?

해결책

제 조언은 매우 간단합니다. 최신 WordPress 버전을 사용하여 플러그인을 개발하되 이전 버전에서 릴리스를 테스트하십시오. 특히, 최소한 플러그인이 지원해야 하는 최소 WordPress 버전으로 플러그인을 테스트하는 것이 좋습니다. 플러그인의 readme.txt 에서 찾을 수 있는 최소 버전:

 === Nelio Content === ... Requires PHP: 7.0 Requires at least: 5.4 Tested up to: 5.9 ...

한 WordPress 버전에서 다른 버전으로 전환하는 것은 다음 WP CLI 명령을 실행하는 것만큼 쉽습니다.

 wp core update --version=5.4 --force

#3 화살표 기능은 생각보다 까다롭습니다

마지막으로, 제가 며칠 전에 만났고 저를 미치게 만든 최신 문제 중 하나를 공유하겠습니다. 간단히 말해서 다음과 유사한 JavaScript 파일이 있습니다.

 import domReady from '@wordpress/dom-ready'; domReady( () => [ ...document.querySelectorAll( '.nelio-forms-form' ) ] .forEach( initForm ) ); // Helpers // ------- const initForm = ( form ) => { ... } // ...

프론트 엔드에서 Nelio Forms를 초기화합니다. 스크립트는 매우 간단하지 않습니까? DOM이 준비되면 호출되는 익명 함수를 정의합니다. 이 함수는 initForm 이라는 도우미(화살표) 함수를 사용합니다. 글쎄요, 그런 간단한 예가 충돌할 수 있다는 것이 밝혀졌습니다! 그러나 특정 상황에서만(즉, defer 속성을 사용하여 WPO 플러그인에 의해 스크립트가 "최적화"된 경우).

JS가 이전 스크립트를 실행하는 방법은 다음과 같습니다.

  1. domReady 내부의 익명 함수가 정의됩니다.
  2. domReady 실행
  3. DOM이 아직 준비되지 않은 경우(그리고 일반적으로 스크립트가 로드될 때 준비되지 않은 경우) domReady 는 콜백 함수를 실행하지 않습니다. 대신 나중에 호출할 수 있도록 단순히 추적합니다.
  4. JavaScript는 파일을 계속 구문 분석하고 initForm 함수를 로드합니다.
  5. DOM이 준비되면 마지막으로 콜백 함수가 호출됩니다.

이제 세 번째 단계에 도달할 때 DOM이 준비되어 domReady 가 익명 함수를 직접 호출하면 어떻게 될까요? 음, 이 경우 initForm 은 여전히 undefined 이므로 스크립트는 undefined 오류를 트리거합니다.

사실, 이 모든 것에 대해 가장 흥미로운 점은 이 두 솔루션이 동일하다는 것입니다.

 domReady( aux ); const aux = () => {};
 domReady( () => aux() ); const aux = () => {}

JavaScript 린터는 첫 번째 것에서만 오류를 던질 것이고 최신 것은 아닐 것입니다.

해결책

두 가지 가능한 솔루션이 있습니다. function 키워드를 사용하여 도우미 함수를 정의하고 화살표 함수를 잊어버리거나, 모든 도우미 함수가 정의된 후 끝에 domReady 문을 이동합니다.

 domReady( aux ); function aux() { // ... }
 const aux = () => { // ... }; domReady( aux );

첫 번째 솔루션이 우리가 가진 원래 솔루션과 분명히 동일하다면 왜 작동하는지 궁금하다면 JavaScript 호이스팅이 작동하는 방식에 관한 것입니다. 간단히 말해서 JavaScript에서는 정의 전에 함수( function 으로 정의됨)를 사용할 수 있지만 변수와 상수(따라서 화살표 함수)에 대해서는 동일한 작업을 수행할 수 없습니다.

요약하자면

JavaScript에는 잘못될 수 있는 많은 것들이 있습니다. 운 좋게도, 특히 우리가 하는 일에 주의를 기울이면 모두 해결책이 있습니다. 오늘 새로운 것을 배우셨기를 바라며, 제가 과거에 저지른 실수와 실수 덕분에 앞으로는 여러분이 몸으로 겪는 고통을 피할 수 있으리라 믿습니다.

Unsplash의 Ian Stauffer 추천 이미지.