vue3의 모래상자 메커니즘을 상세히 풀다

5264 단어 vue모래상자

앞말


모래상자는 주로 두 가지로 나뉜다
  • 브라우저 컴파일 버전, 브라우저 버전은 with 문법에 프록시 에이전트 차단을 사용합니다
  • 로컬 프리컴파일 버전, 템플릿 프리컴파일 단계에서 변환 플러그인transformExpression을 사용하여 비 화이트리스트 식별자를 구성 요소 프록시 대상에 걸기
  • 브라우저 컴파일 버전


    render 함수 컴파일 결과
    
    <div>{{test}}</div>
    <div>{{Math.floor(1)}}</div>
    
    to
    
    const _Vue = Vue;
    
    return function render(_ctx, _cache, $props, $setup, $data, $options) {
      with (_ctx) {
        const {
          toDisplayString: _toDisplayString,
          createVNode: _createVNode,
          Fragment: _Fragment,
          openBlock: _openBlock,
          createBlock: _createBlock,
        } = _Vue;
    
        return (
          _openBlock(),
          _createBlock(
            _Fragment,
            null,
            [
              _createVNode("div", null, _toDisplayString(test), 1 /* TEXT */),
              _createVNode(
                "div",
                null,
                _toDisplayString(Math.floor(1)),
                1 /* TEXT */
              ),
            ],
            64 /* STABLE_FRAGMENT */
          )
        );
      }
    };
    
    위의 코드에서 우리는 변수 표지부호가 접두사를 추가하지 않고 with 문법으로 감싸서 작용역 체인을 연장한 것을 발견할 수 있다. 그러면 어떻게 js 샌드박스를 차단할 수 있을까?예를 들어 변수test, 이론적으로 말하자면 현재 작용역 체인에test변수가 없다. 변수는 이전 작용역에서 전역 작용역을 찾을 때까지 찾지만 실제로는_ctx에서 찾기, 원리는 간단합니다,_ctx는 프록시 대상입니다. 그러면 프록시를 어떻게 차단하는지 예시 코드는 다음과 같습니다.
    
    const GLOBALS_WHITE_LISTED =
      "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI," +
      "decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array," +
      "Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt";
    
    const isGloballyWhitelisted = (key) => {
      return GLOBALS_WHITE_LISTED.split(",").includes(key);
    };
    
    const hasOwn = (obj, key) => {
      return Object.prototype.hasOwnProperty.call(obj, key);
    };
    
    const origin = {};
    const _ctx = new Proxy(origin, {
      get(target, key, reciever) {
        if (hasOwn(target, key)) {
          Reflect.get(target, key, reciever);
        } else {
          console.warn(
            `Property ${JSON.stringify(key)} was accessed during render ` +
              `but is not defined on instance.`
          );
        }
      },
      has(target, key) {
        //      false, get  , 
        //      true, get  
        return !isGloballyWhitelisted(key);
      },
    });
    
    코드는 매우 간단한데, 왜 이렇게 간단한 코드로 차단할 수 있습니까?with 문장은has 차단을 촉발하기 때문에has가true로 돌아오면 프록시 대상 get 차단을 촉발하고false로 돌아오면 프록시 대상 get 차단은 촉발되지 않습니다. 변수는 현재 프록시 대상에서 찾지 않고 더 높은 역할 영역을 직접 찾습니다.

    로컬 사전 컴파일 버전

    
    <div>{{test}}</div>
    <div>{{Math.floor(1)}}</div>
    
    to
    
    import {
      toDisplayString as _toDisplayString,
      createVNode as _createVNode,
      Fragment as _Fragment,
      openBlock as _openBlock,
      createBlock as _createBlock,
    } from "vue";
    
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
      return (
        _openBlock(),
        _createBlock(
          _Fragment,
          null,
          [
            _createVNode("div", null, _toDisplayString(_ctx.a), 1 /* TEXT */),
            _createVNode(
              "div",
              null,
              _toDisplayString(Math.floor(1)),
              1 /* TEXT */
            ),
          ],
          64 /* STABLE_FRAGMENT */
        )
      );
    }
    
    위의 코드에서 알 수 있듯이 비 화이트리스트 식별자가 추가되었습니다_ctx 변수 접두사, 그럼 어떻게 하는 거예요?template를 로컬로 컴파일할 때 변환 단계에 있을 때 변수 표현식 노드 NodeTypes.SIMPLE_EXPRESSION은 다음과 같은 예제 코드를 사용하여 접두사를 추가합니다.
    
    const GLOBALS_WHITE_LISTED =
      "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI," +
      "decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array," +
      "Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt";
    
    const isGloballyWhitelisted = (key) => {
      return GLOBALS_WHITE_LISTED.split(",").includes(key);
    };
    const isLiteralWhitelisted = (key)=>{
      return 'true,false,null,this'.split(',').includes(key)
    }
    export function processExpression(
      node
    ) {
      const rewriteIdentifier = (raw) => {
        return `_ctx.${raw}`
      }
      const rawExp = node.content
      if (isSimpleIdentifier(rawExp)) {
        const isAllowedGlobal = isGloballyWhitelisted(rawExp)
        const isLiteral = isLiteralWhitelisted(rawExp)
        if (!isAllowedGlobal && !isLiteral) {
          node.content = rewriteIdentifier(rawExp)
        }
        return node
      }
    
    물론 위의 코드는 간소화 버전일 뿐, 원본 플러그인은 __props $setup, 변수 조회 경로를 줄이고 성능을 향상시키며 babel을 통해 복잡한 표현식을 컴파일합니다. 예를 들어 화살표 함수입니다.

    총결산


    전체 vue3 js 샌드박스 메커니즘 설명이 끝났습니다. 당초 브라우저 컴파일 버전은 저를 오랫동안 괴롭혔습니다. 왜냐하면has가 with 문장 변수 조회를 차단할 수 있는지 몰랐기 때문입니다.
    이상은 vue3의 샌드박스 메커니즘에 대한 상세한 내용입니다. vue3 샌드박스 메커니즘에 대한 더 많은 자료는 저희 다른 관련 글을 주목해 주십시오!

    좋은 웹페이지 즐겨찾기