Detailed explanation of Vue3 sandbox mechanism

Detailed explanation of Vue3 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. In theory, 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 fact, 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.

refer to

Proxy handler.has
Talk about the sandbox in JS and write js sandbox by hand

This is the end of this article about the detailed explanation of the vue3 sandbox mechanism. For more relevant vue3 sandbox mechanism content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

<<:  Decompression, installation, backup and restore of MySQL under Windows environment

>>:  Troubleshooting process for Docker container suddenly failing to connect after port mapping

Recommend

How to use html table (to show the visual effect of web page)

We know that when using HTML on NetEase Blog, we ...

A brief discussion on group by in MySQL

Table of contents 1. Introduction 2. Prepare the ...

mysql obtains statistical data within a specified time period

mysql obtains statistical data within a specified...

CSS and JS to achieve romantic meteor shower animation

1. Rendering 2. Source code HTML < body > &...

Small problem with the spacing between label and input in Google Browser

Code first, then text Copy code The code is as fol...

Three steps to solve the IE address bar ICON display problem

<br />This web page production skills tutori...

Javascript to achieve the effect of closing advertisements

Here is a case study on how to close ads using Ja...

Summary of Spring Boot Docker packaging tools

Table of contents Spring Boot Docker spring-boot-...

CSS implements the web component function of sliding the message panel

Hello everyone, I wonder if you have the same con...

How to install nginx on win10

Because the company asked me to build a WebServic...

Detailed explanation of MySQL replication principles and practical applications

This article uses examples to illustrate the prin...

Linux firewall status check method example

How to check the status of Linux firewall 1. Basi...

Detailed explanation of Docker basic network configuration

External Access Randomly map ports Using the -P f...

CSS3 realizes particle animation effect when matching kings

When coding, you will find that many things have ...

Using shadowsocks to build a LAN transparent gateway

Table of contents Install and configure dnsmasq I...