원형 체인 납치 기반의 전단 코드 삽입 실천

7398 단어
코드 플러그 기술은 우리로 하여금 기존의 원본 코드를 바꾸지 않는 전제에서 외부에서 각종 자정 논리를 주입하고 차단하게 할 수 있다.이것은 각종 흑마법을 펼치기 위해 거대한 상상 공간을 제공했다.다음은 브라우저 환경에서 말뚝 삽입 기술의 원리와 응용 실천을 소개할 것이다.

말뚝삽입기초개념


앞부분 말뚝의 기본 이념은 이 문제로 표현할 수 있다. 만약에 업무에 광범위하게 사용되는 함수가 있다면 우리는 그 함수를 호출하는 업무 코드도 바꾸지 않고 이 함수 코드를 바꾸지 않는 전제에서 그 집행 전후에 우리의 정의로운 논리를 주입할 수 있을까?
더욱 구체적인 예를 들어 만약에 업무 논리에 많은 console.log 로그 코드가 있다면 우리는 이 코드를 바꾸지 않는 전제에서 이log 내용을 네트워크를 통해 보고할 수 있습니까?하나의 간단한 사고방식은 다음과 같다.
  • 사용자 정의 논리를 먼저 실행한 다음에 원래log 방법을 실행하는 함수를 봉인합니다.
  • 는 원생console.log을 이 함수로 대체한다.

  • 만일 우리의 해법이 통용성을 갖추기를 원한다면 첫 번째 단계의 조작을 고급 함수로 범용하기 어렵지 않다.
    function withHookBefore (originalFn, hookFn) {
      return function () {
        hookFn.apply(this, arguments)
        return originalFn.apply(this, arguments)
      }
    }
    

    그래서 우리의 삽입 코드는 매우 간결하게 되었다.이렇게 하면 됩니다.
    console.log = withHookBefore(console.log, (...data) => myAjax(data))
    

    원생적console.log은 우리가 삽입한 논리 이후에 계속될 것이다.다음은 이 문제를 고려한다. 우리가 외부로부터console.log의 집행을 차단할 수 있을까?고급 함수가 생기면 이것도 마찬가지로 식은 죽 먹기다.
    function withHookBefore (originalFn, hookFn) {
      return function () {
        if (hookFn.apply(this, arguments) === false) {
          return
        }
        return originalFn.apply(this, arguments)
      }
    }
    

    갈고리 함수false만 되돌아오면 원 함수는 실행되지 않는다.예를 들어 다음과 같이 콘솔을 깔끔하게 조작할 수 있습니다.
    console.log = withHookBefore(console.log, () => false)
    

    이것이 바로 브라우저에서 하늘과 태양을 훔치는 기본 원리다.

    DOM API에 대한 삽입


    단순한 함수 교체로는 비교적 큰 HACK 조작을 완성하기에는 부족하다.다음은 브라우저의 모든 사용자 이벤트를 어떻게 포착합니까?
    너는 당연히 맨 윗부분 document.body 에 각종 이벤트listener를 추가해서 이 요구를 달성할 수 있다.그러나 이때의 문제는 하위 요소e.stopPropagation()를 사용하여 사건의 거품을 막으면 맨 윗부분 노드에서 이 사건을 받을 수 없다는 것이다.설마 우리가 모든 DOM의 요소를 훑어보고 그 사건 감청기를 고쳐야 하는 건 아니겠지?폭력보다 원형 체인에서 글을 쓸 수 있다.
    DOM 요소에 대해 addEventListener를 사용하여 이벤트 리셋을 추가하는 것은 더 이상 정상적이지 않은 작업이다.이 방법은 사실 공공의 원형 체인에 위치하고 우리는 앞의 고급 말뚝 함수를 통해 그것을 납치할 수 있다.
    EventTarget.prototype.addEventListener = withHookBefore(
      EventTarget.prototype.addEventListener,
      myHookFn //         
    )
    

    하지만 그것만으로는 부족하다.이런 방식을 통해 진정으로 추가된listener 매개 변수는 바뀌지 않았기 때문이다.그럼,listener 파라미터를 납치할 수 있습니까?이때 우리는 실제로 이런 고급 함수가 필요하다.
  • 원 함수의 매개 변수를 사용자 정의 갈고리에 전송하여 일련의 새로운 매개 변수를 되돌려줍니다.
  • 마법이 바뀐 새로운 매개 변수로 원 함수를 호출합니다.

  • 이 함수는 대략 이렇게 생겼다.
    function hookArgs (originalFn, argsGetter) {
      return function () {
        var _args = argsGetter.apply(this, arguments)
        //      arguments
        for (var i = 0; i < _args.length; i++) arguments[i] = _args[i]
        return originalFn.apply(this, arguments)
      }
    }
    

    이 고급 함수와 기존withHookBefore을 결합하면 우리는 완전한 납치 방안을 설계할 수 있다.
  • 전송hookArgs의 매개 변수를 addEventListener로 대체합니다.
  • 교체된 매개 변수 중 두 번째 매개 변수는 진정한 listener 리셋이다.이 리셋을 withHookBefore 맞춤형 버전으로 바꾸십시오.
  • 저희가 listener에 추가한 갈고리에서 맞춤형 이벤트 수집 코드를 실행합니다.

  • 이 시나리오의 기본 논리 구조는 다음과 같습니다.
    EventTarget.prototype.addEventListener = hookArgs(
      EventTarget.prototype.addEventListener,
      function (type, listener, options) {
        const hookedListener = withHookBefore(listener, e => myEvents.push(e))
        return [type, hookedListener, options]
      }
    )
    

    위의 코드가 addEventListener를 포함하는 모든 실제 업무 코드를 실행하기 전에 실행된다면 우리는 이벤트 거품의 제한을 초월하여 우리가 흥미를 느끼는 모든 사용자 이벤트를 수집할 수 있습니다:)

    전단 프레임에 대한 말뚝 꽂기


    우리가 DOM API에 말뚝을 꽂는 원리를 이해한 후에 앞부분 프레임의 API에 대해 고양이 그림처럼 할 수 있게 되었다.예를 들어 우리는 Vue에서 심지어 모든 this.$emit 정보를 수집할 수 있습니까?또한 원형 체인 납치를 통해 간단하게 실현할 수 있다.
    import Vue from 'vue'
    
    Vue.prototype.$emit = withHookBefore(Vue.prototype.$emit, (name, payload) => {
      //          
      console.log('emitting', name, payload)
    })
    

    물론 API 인터페이스가 완벽하게 봉인된 프레임워크를 이런 방식으로 맞춤형으로 제작하는 것은 최선의 실천에 어긋날 수 있다.그러나 기초 라이브러리나 개발자 도구를 개발해야 할 때 이 기술이 쓸모가 있다고 믿는다.예를 들면 다음과 같습니다.
  • 콘솔 기반log의 플러그는 크로스 스크린 로그 수집을 실현할 수 있습니다(예를 들어 기기에서 다른 장치의 조작 로그를 실시간으로 볼 수 있습니다)
  • DOM API에 대한 삽입을 바탕으로 우리는 업무에 침입하지 않는 매립점과 사용자 행위의 녹화와 재생을 실현할 수 있다.
  • 부품의 생명주기 갈고리에 대한 삽입말뚝을 바탕으로 더욱 정확하고 통증이 없는 성능을 수집하고 분석할 수 있다.
  • ……

  • 총결산


    지금까지 우리는 말뚝 꽂기 기술의 기본 개념과 약간의 실천을 소개했다.만약 당신이 흥미가 있다면, 좋은 소식은 우리가 자주 사용하는 말뚝 삽입 고급 함수를 상자를 열면 바로 사용할 수 있는 NPM 기초 라이브러리 runtime-hooks 로 봉인했다는 것입니다. 이 말뚝 삽입 함수들은 다음과 같습니다.
  • withHookBefore - 함수에 before 갈고리 추가
  • withHookAfter - 함수에 애프터 갈고리 추가
  • hookArgs-마개함수 매개 변수
  • hookOutput-마개함수 반환값
  • GitHub에서 우리 회사의 이 개원 프로젝트를 맛보는 것을 환영하며, 이 전단 칼럼에 관심을 가져주시는 것도 환영합니다:)
    P.S. 저희 베이스 샤먼의 전단팀 활약 구인 중, 이력서 요청xuebiat gaoding.com이야~

    좋은 웹페이지 즐겨찾기