Tampermonkey Learn Guide

repository·main·Indexed 41 days ago

https://github.com/scriptscat/tampermonkey-learn-guide

A comprehensive educational resource for beginners to learn Tampermonkey (Userscript) development. The guide covers basic concepts, userscript structure, metadata headers, DOM automation (simulating clicks and form filling), and advanced topics such as sandbox environments, @grant permissions, and the use of unsafeWindow.

Tokens
93.9K
Snippets
270
Records
335
Agent score
52%

What's inside Tampermonkey Learn Guide

  1. What is a hook and how does it work?

    main

    A hook (also known as 劫持 or hijacking) is a technique used in Tampermonkey scripts to intercept function calls. By replacing an existing method with your own custom function, you can control the execution flow. This allows you to:

    1. Modify arguments before they reach the original function.
    2. Change the return value of the original function.
    3. Prevent the original function from running entirely by implementing your own logic.

    This concept is conceptually similar to Middleware in web backend development or a Man-in-the-Middle (MITM) attack in network security.

  2. What is UMD (Universal Module Definition)

    main

    UMD (Universal Module Definition) is a pattern used to make JavaScript modules compatible with multiple module loading environments. It works by wrapping the source code in a self-executing function that detects the current runtime environment (AMD, CommonJS, or Browser Globals) and executes the module logic accordingly.

    UMD typically supports three main environments:

    1. AMD: Registers as an anonymous module using define.
    2. CommonJS: Uses exports and require (common in Node.js).
    3. Browser Globals: Attaches the module to the global object (e.g., window or self).
    (function (root, factory) {
      if (typeof define === "function" && define.amd) {
        // AMD. Register as an anonymous module.
        define(["exports", "b"], factory);
      } else if (
        typeof exports === "object" &&
        typeof exports.nodeName !== "string"
      ) {
        // CommonJS
        factory(exports, require("b"));
      } else {
        // Browser globals
        factory((root.myModuleName = {}), root.b);
      }
    })(typeof self !== "undefined" ? self : this, function (exports, b) {
      // Use b in some fashion.
    
      // attach properties to the exports object to define
      // the exported module properties.
      exports.action = function () {};
    });
  3. Understand the execution order of Synchronous, Microtask, and Macrotask code

    main

    JavaScript execution follows a specific priority order based on the type of task. Understanding this order is essential for predicting how asynchronous code behaves.

    1. Synchronous Code (Main Thread): Executed immediately in order.
    2. Microtasks: Tasks that are executed immediately after the current synchronous execution stack is empty, but before the next Macrotask. Examples include Promise.then, nextTick, and MutationObserver.
    3. Macrotasks: Tasks that are scheduled to run after the microtask queue is cleared. Examples include setTimeout and setInterval.

    Execution Priority Rule: Synchronous Code $\rightarrow$ Microtasks $\rightarrow$ Macrotasks

    console.log("主线程1");
    new Promise(function (resolve, reject) {
      console.log("创建Promise,也属于主线程");
      resolve("1");
    }).then(function (data2) {
      console.log("then属于微任务");
    });
    console.log("主线程2");
    setTimeout(() => {
      console.log("宏任务");
    }, 0);
  4. Determine `this` binding in objects and functions

    main

    The value of this changes depending on how a function is invoked:

    1. Object Methods: When a function is called as a method of an object, this refers to the object itself.

      var testThis = {
        showThis: function () {
          console.log(this); // 'this' is testThis
        },
      };
      testThis.showThis();
    2. Standalone Functions: When a function is called directly (not as a method), this defaults to the window object.

      function getThis() {
        console.log(this); // 'this' is window
      }
      getThis();
    var testThis = {
      showThis: function () {
        console.log("我的this是对象", this);
      },
    };
    testThis.showThis();
    
    function getThis() {
      console.log(this);
    }
    getThis();
  5. Locate Vue3 component data in the instance

    main

    When working with a Vue3 component instance (often accessed via this in mixins), internal data is stored under the _ property. Use the following mapping to find common data locations:

    PropertyLocation
    setupthis['_'].setupState
    datathis['_'].$data
    renderthis['_'].render
    propsthis['_'].props

    Modifying Data:

    • setupState: Since these are typically ref objects, modify them by setting the .value property (e.g., instance.setupState.message.value = 'new value').
    • props: To trigger reactivity, you must replace the top-level object rather than a sub-property (e.g., instance.props.info = { ...instance.props.info, title: '123' };).
  6. Access ShadowRoot content based on mode

    main

    How you access the contents of a Shadow DOM depends on the mode used during creation:

    1. open mode: You can access the shadow root directly from the host element using the .shadowRoot property.
    2. closed mode: The .shadowRoot property on the host element will return null. You can only interact with the shadow DOM if you have saved the reference returned by the initial attachShadow() call.
    // If mode was 'open'
    console.log(document.querySelector(".wrap").shadowRoot.innerHTML);
    
    // If mode was 'closed'
    // document.querySelector(".wrap").shadowRoot is null
    // You must use the original reference:
    console.log(savedShadowReference.innerHTML);
  7. Avoid reinventing the wheel: Use existing libraries

    main

    When developing Tampermonkey scripts, do not attempt to build complex, general-purpose tools from scratch if they already exist. Using established libraries saves time and reduces complexity.

    Recommended alternatives for common tasks:

  8. How Vuex state management works in Vue components

    main

    Vue components often interact with a global state tree using Vuex. This allows components to access and modify shared data.

    Accessing State via mapState

    Components use mapState to map global state properties to local component properties, shortening the code. For example, mapState('visitUserInfo', ['isTaskUser']) allows the component to use this.isTaskUser instead of this. $store.state.visitUserInfo.isTaskUser.

    Modifying State via commit

    To change the state, components call this.$store.commit(mutationType, payload). This triggers a mutation function that updates the state tree.

    Accessing State via mapGetters

    mapGetters is used to map computed properties from the Vuex store to the component, such as permission checks (e.g., canCopy).

  9. Understand the package.json structure

    main

    The package.json file serves as the manifest for your project, declaring dependencies and basic metadata. Key fields include:

    • name: The name of the package.
    • version: The current version of the package.
    • description: A description of the package.
    • main: The entry point file (e.g., index.js). This file is automatically loaded when using require().
    • scripts: Custom command definitions for running tasks (e.g., tests).
    • author: The author's name.
    • license: The open-source license (e.g., ISC).
    {
      "name": "pack",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC"
    }
  10. Understand the essence of function invocation

    main

    Every function call is essentially an implicit use of call.

    • A standalone function call getThis() is equivalent to getThis.call(window).
    • An object method call testThis.showThis() is equivalent to showThis.call(testThis), where the engine automatically sets this to the object preceding the dot.
  11. Challenges with anonymous functions

    main

    Anonymous functions defined within a local scope are difficult to hook because they are not accessible via the window object or any global reference.

    Workarounds:

    • Rewrite the parent function: If the anonymous function is inside a named function, you can rewrite the entire parent function.
    • Indirect Hooking (Curveball approach): Hook a fundamental API that the anonymous function calls (e.g., console.log or document.querySelector) to influence the logic indirectly.

    Warning: Indirectly hooking fundamental APIs can cause significant performance degradation and side effects (pollution) across the entire page. Use this only as a last resort.