Detailed explanation of Vue3's sandbox mechanism

Detailed explanation of Vue3's sandbox mechanism

Preface

There are two main types of vue3 sandboxes

  1. The browser compiled version uses the with syntax plus proxy interception
  2. The local precompiled version uses the transformExpression plugin to hang the non-whitelist identifier under the component proxy object during the template precompilation phase.

Browser compiled version

Render function compilation result

<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 */
      )
    );
  }
};

From the above code, we can find that the variable identifier has no prefix added, but is just wrapped with the with syntax to extend the scope chain. So how is js sandbox interception achieved? For example, the variable test. Theoretically, there is no test variable in the current scope chain. The variable will be searched from the previous scope until the global scope is found. However, in practice, it will only be searched on _ctx. The principle is very simple. _ctx is a proxy object. So how do we use Proxy to intercept? The sample code is as follows:

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, receiver) {
    if (hasOwn(target, key)) {
      Reflect.get(target, key, receiver);
    } else {
      console.warn(
        `Property ${JSON.stringify(key)} was accessed during render ` +
          `but is not defined on instance.`
      );
    }
  },
  has(target, key) {
    // If it is a global object, return false, do not trigger get interception, and search for variables from the previous scope. // If it is not a global object, return true, trigger get interception. return !isGloballyWhitelisted(key);
  },
});

The code is very simple, why can such a simple code achieve interception? Because the with statement triggers the has interception, when has returns true, it triggers the proxy object get interception. If it returns false, the proxy object get interception will not be triggered, and the variable will not be searched in the current proxy object, but directly searched in the upper scope.

Local precompiled version

<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 */
    )
  );
}

From the above code, we can see that the non-whitelist identifiers are prefixed with the _ctx variable. So how is this done? When compiling the template locally, the variable expression node NodeTypes.SIMPLE_EXPRESSION will be prefixed during the conversion phase. The sample code is as follows:

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
  }

Of course, the above code is just a simplified version. The original plugin also makes it precise to __props $setup, shortens the variable query path, improves performance, and compiles complex expressions such as arrow functions through babel.

Summarize

The entire vue3 js sandbox mechanism is explained. The browser compiled version bothered me for a long time because I didn’t know that has could intercept with statement variable queries.

The above is a detailed explanation of the sandbox mechanism of vue3. For more information about the sandbox mechanism of vue3, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • springboot+vue+docking Alipay interface+QR code scanning payment function (sandbox environment)
  • Vue references multiple ways of js files (recommended)
  • Detailed explanation of four ways of vue routing jump (with parameters)
  • Example of communication between parent and child components of Vue (props, $ref, $emit)
  • Detailed explanation of Vue props usage (summary)
  • Three ways to refresh the current page in the Vue project
  • Simple understanding of Props attributes in Vue
  • Hiding and showing VUE elements (v-show directive)
  • Vue implements file upload function
  • Three ways to upload pictures using Vue
  • Summary of common Vue.js instructions (v-if, v-for, etc.)

<<:  The magic of tr command in counting the frequency of English words

>>:  Detailed explanation of possible problems in converting floating point data to character data in MySQL

Recommend

How to make your own native JavaScript router

Table of contents Preface Introduction JavaScript...

Analysis of MySql index usage strategy

MySql Index Index advantages 1. You can ensure th...

SQL ROW_NUMBER() and OVER() method case study

Syntax format: row_number() over(partition by gro...

URL Rewrite Module 2.1 URL Rewrite Module Rule Writing

Table of contents Prerequisites Setting up a test...

JavaScript Regular Expressions Explained

Table of contents 1. Regular expression creation ...

A detailed explanation of the subtle differences between Readonly and Disabled

Readonly and Disabled both prevent users from chan...

Several ways to submit HTML forms_PowerNode Java Academy

Method 1: Submit via the submit button <!DOCTY...

MySQL download and installation details graphic tutorial

1. To download the MySQL database, visit the offi...

CSS pseudo-class: empty makes me shine (example code)

Anyone who has read my articles recently knows th...

How to use jsx syntax correctly in vue

Table of contents Preface Virtual DOM What is Vir...

Windows 2016 Server Security Settings

Table of contents System update configuration Cha...

Summary of problems that may occur when using JDBC to connect to Mysql database

First, clarify a few concepts: JDBC: Java databas...

Automatic backup of MySQL database using shell script

Automatic backup of MySQL database using shell sc...