Webb1an's Technical Blog

website·Indexed 29 days ago

https://webb1an.github.io/

A collection of front-end development guides and notes covering Vue 2 and Vue 3 source code analysis, JavaScript event loops, prototype chains, and project architecture using Pnpm, Vite, and TypeScript. Includes practical tutorials on Docker deployment, MongoDB integration, Nginx configuration, and CSS techniques, alongside interview preparation for JS, CSS, and Vue.

Tokens
34K
Snippets
180
Records
201
Agent score
25%

What's inside Webb1an Blog

  1. Understand the JavaScript Event Loop execution order

    The Event Loop is the core mechanism that allows single-threaded JavaScript to handle asynchronous operations without blocking the main thread. It follows a strict execution cycle:

    1. Execute one Macro Task (e.g., the initial script or a setTimeout callback).
    2. Execute all pending Micro Tasks (e.g., Promise.then callbacks).
    3. Perform UI Rendering (if necessary).
    4. Repeat from step 1.

    Crucially, the entire microtask queue must be cleared after every single macro task before the next macro task can begin.

  2. Understand the JavaScript Prototype Chain mechanism

    In JavaScript, every object has a prototype object. When accessing a property of an object, the engine first looks at the object itself. If the property is not found, it searches the object's prototype, then the prototype's prototype, and so on. This sequence of linked objects is called the prototype chain. The search continues until a matching property is found or the end of the chain (null) is reached. This mechanism allows objects to inherit properties and methods defined in other objects.
  3. Use beforeUpdate and updated hooks for data changes

    • beforeUpdate: Called when data changes and the component is about to re-render. Data is updated, but the DOM still reflects the old state. Useful for capturing snapshots of the DOM before it changes.
    • updated: Called after the DOM has been re-rendered and updated. Used for DOM operations dependent on the latest state. Warning: Avoid changing data inside this hook to prevent infinite update loops.
  4. Reference JavaScript global prototype relationships

    The following rules define the fundamental prototype relationships in JavaScript:

    • __proto__ acts as the bridge pointing to the prototype object of the constructor that created the instance.
    • All objects eventually inherit from Object.prototype.
    • All functions (including the Object constructor) are instances of Function and inherit from Function.prototype.
    • The end of the prototype chain is null (Object.prototype.__proto__ === null).
    • The Function object is unique in that its __proto__ points to its own prototype (Function.__proto__ === Function.prototype).
    // The root of the chain
    Object.prototype.__proto__ === null;
    
    // Function inheritance
    Object.__proto__ === Function.prototype;
    Function.__proto__ === Function.prototype;
  5. Analyze the Vue constructor and mixin architecture

    In src/core/instance/index.js, the Vue constructor is defined. Rather than containing all logic, it serves as a shell that calls this._init(options). The functionality of the Vue instance is built by passing the Vue constructor to several mixin functions that populate the prototype.
    function Vue (options) {
      this._init(options)
    }
    
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    export default Vue
  6. Understand JavaScript Garbage Collection (GC) and Reachability

    JavaScript engines (like V8) use an automatic Garbage Collection (GC) mechanism to release memory that is no longer needed. The core concept is Reachability: an object is 'reachable' if it can be accessed from a root (e.g., global variables, variables in the current execution function). If an object is 'unreachable', it is eligible for GC. The primary algorithm used is Mark-and-Sweep, which consists of two phases: 1. Mark: The engine traverses from the roots and marks all reachable objects. 2. Sweep: Any object not marked is considered unreachable and its memory is reclaimed.
  7. Set up commit linting and standardization

    Standardize git commit messages using commitlint, husky, and cz-git. This ensures all commits follow the conventional commit format.
    git init
    pnpm install @commitlint/cli @commitlint/config-conventional cz-git czg husky lint-staged @esbuild-kit/cjs-loader -D
  8. Define component options in <script setup> using unplugin-vue-define-options

    Since <script setup> does not natively support defining component options (like name), you can use the unplugin-vue-define-options plugin to enable the defineOptions macro.
    // vite.config.ts
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import DefineOptions from 'unplugin-vue-define-options/vite'
    
    export default defineConfig({
      plugins: [vue(), DefineOptions()],
    })
    <script setup lang="ts">
      defineOptions({
        name: 'Layout'
      })
    </script>
    <template>
      <div>layout</div>
    </template>
  9. Configure commit linting with commitlint, husky, and cz-git

    Set up a standardized commit message workflow using commitlint for rule enforcement, husky for git hooks, and cz-git for interactive commit prompts. The configuration includes automatic scope detection based on modified files in the src directory.
    // commitlint.config.ts
    import { execSync } from 'node:child_process'
    
    const scopes = ['views', 'components', 'router', 'store', 'utils', 'styles', 'assets', 'types', 'other']
    
    const gitStatus = execSync('git status --porcelain || true').toString().trim().split('\n')
    const scopeComplete = gitStatus.find(r => ~r.indexOf('M  src'))?.replace(/(\/)/g, '%%')?.match(/src%%((\w|-)*)/)?.[1];
    const subjectComplete = gitStatus.find(r => ~r.indexOf('M  src'))?.replace(/\//g, '%%')?.match(/src%%((\w|-)*)/)?.[1];
    
    export default {
      rules: {
        'scope-enum': [2, 'always', scopes],
        'body-leading-blank': [1, 'always'],
        'footer-leading-blank': [1, 'always'],
        'header-max-length': [2, 'always', 72],
        'scope-case': [2, 'always', 'lower-case'],
        'subject-case': [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
        'subject-empty': [2, 'never'],
        'subject-full-stop': [2, 'never', '.'],
        'type-case': [2, 'always', 'lower-case'],
        'type-empty': [2, 'never'],
        'type-enum': [2, 'always', ['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'release', 'style', 'test', 'improvement']],
      },
      prompt: {
        defaultScope: scopeComplete,
        customScopesAlign: !scopeComplete ? 'top' : 'bottom',
        defaultSubject: subjectComplete && `[${subjectComplete}] `,
        allowCustomIssuePrefixs: false,
        allowEmptyIssuePrefixs: false,
      },
    }
    // package.json
    {
      "scripts": {
        "cz": "czg"
      },
      "config": {
        "commitizen": {
          "path": "./node_modules/cz-git"
        }
      },
      "lint-staged": {
        "*.{vue,js,ts,jsx,tsx,md,json}": "eslint --fix"
      }
    }
    # Initialize husky
    npx husky-init
    
    # Add commit-msg hook
    npx husky add .husky/commit-msg 'pnpm exec commitlint --config commitlint.config.cjs --edit "${1}"'
  10. Install and configure Tailwind CSS in a Vite project

    Standard installation of Tailwind CSS, PostCSS, and Autoprefixer for a Vite-based Vue project.
    npm install -D tailwindcss postcss autoprefixer
    npx tailwindcss init -p
    // tailwind.config.js
    module.exports = {
      content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
      theme: { extend: {} },
      plugins: [],
    }
    /* main.css */
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
  11. Scroll an element to top while accounting for fixed navigation

    Standard scrollIntoView({ behavior: 'smooth' }) can be obscured by fixed navigation bars. To fix this, calculate the element's offsetTop and subtract the navigation bar's height before using window.scrollTo.
    const scrollTo = (id: string) => {
      const el = document.getElementById(id)
      if (el) {
        const height = el.offsetTop
        const navHeight = document.querySelector('.nav')?.clientHeight || 0
        document.documentElement.scrollTo({
          top: height - navHeight,
          behavior: 'smooth',
        })
      }
    }
  12. Configure ESLint for a Pnpm UI component library

    To set up ESLint in a Pnpm Monorepo for a UI library, install the shared config package at the workspace root and create a root configuration file that extends it.
    # Install the config package at the workspace root
    pnpm install @pnpm-ui/eslint-config -D -w

    .eslintrc.json

    { "root": true, "extends": ["@pnpm-ui/eslint-config"] }