CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tarojs--runtime

Cross-platform runtime for Taro framework providing DOM/BOM polyfills and mini-program bridge functionality

Pending
Overview
Eval results
Files

utilities.mddocs/

Utilities & Performance APIs

Comprehensive utility functions, performance monitoring tools, event management, and optimization helpers for cross-platform Taro development.

Overview

The utilities module provides essential tools for building performant cross-platform applications:

  • Performance Monitoring: Timing, profiling, and performance measurement
  • Function Utilities: Throttling, debouncing, and execution control
  • DOM Utilities: Element type checking and manipulation helpers
  • Event Management: Global event center and communication
  • Async Operations: Next tick scheduling and update coordination
  • Hydration System: DOM to mini-program data conversion
  • Router Utilities: URL manipulation and navigation helpers
  • Cache Management: Context-aware data caching
  • Options Configuration: Runtime behavior configuration

Performance APIs

Performance Monitoring

import { perf } from '@tarojs/runtime'

interface Performance {
  // Timing methods
  start(id: string): void
  stop(id: string, now?: number): void
  delayStop(id: string, delay?: number): void
  
  // Internal timing storage
  timers: Map<string, number>
}

const perf: Performance

Key Features

  • High-resolution Timing: Uses performance.now() when available, falls back to Date.now()
  • Automatic Logging: Logs timing results to console with formatted output
  • Delayed Stopping: Debounced timing for frequent operations
  • Memory Management: Automatic cleanup of completed timers

Usage Examples

// Basic performance timing
perf.start('component-render')
// ... component rendering logic
perf.stop('component-render')  // Logs: "component-render took X ms"

// Manual timing with custom timestamp
const startTime = performance.now()
// ... some operation
perf.stop('custom-operation', startTime)

// Delayed timing (debounced)
perf.start('frequent-operation')
perf.delayStop('frequent-operation', 100)  // Waits 100ms before logging

// Multiple timing sessions
perf.start('data-fetch')
const data = await fetchData()
perf.stop('data-fetch')

perf.start('data-process')  
const processed = processData(data)
perf.stop('data-process')

// Nested timing
perf.start('full-operation')
perf.start('sub-operation-1')
// ... work
perf.stop('sub-operation-1')

perf.start('sub-operation-2')
// ... more work  
perf.stop('sub-operation-2')
perf.stop('full-operation')

Function Utilities

throttle

import { throttle } from '@tarojs/runtime'

function throttle<T extends (...args: any[]) => any>(
  fn: T,
  threshold?: number,
  scope?: any
): T

// Default threshold: 250ms

Key Features

  • Leading and Trailing: Executes immediately and after threshold
  • Context Preservation: Maintains this context when scope provided
  • Return Value: Returns result of first execution during threshold period

Usage Examples

// Basic throttling
const expensiveFunction = (data: string) => {
  console.log('Processing:', data)
  // Expensive computation
}

const throttledFn = throttle(expensiveFunction, 1000)

// Multiple rapid calls - only executes every 1000ms
throttledFn('call 1')  // Executes immediately
throttledFn('call 2')  // Ignored
throttledFn('call 3')  // Ignored
// ... 1000ms later, executes with last arguments

// Scroll event throttling
const handleScroll = throttle((event: Event) => {
  const scrollTop = (event.target as Element).scrollTop
  console.log('Scroll position:', scrollTop)
}, 100)

window.addEventListener('scroll', handleScroll)

// Resize handler with context
class ResponsiveComponent {
  threshold = 768
  
  handleResize = throttle(function(this: ResponsiveComponent) {
    if (window.innerWidth < this.threshold) {
      this.enterMobileMode()
    } else {
      this.enterDesktopMode()
    }
  }, 250, this)
  
  enterMobileMode() { /* ... */ }
  enterDesktopMode() { /* ... */ }
}

debounce

import { debounce } from '@tarojs/runtime'

function debounce<T extends (...args: any[]) => any>(
  fn: T,
  ms?: number,
  scope?: any
): T

// Default delay: 250ms

Key Features

  • Trailing Execution: Only executes after delay period with no new calls
  • Context Preservation: Maintains this context when scope provided
  • Cancellation: Automatically cancels previous delayed executions

Usage Examples

// Basic debouncing
const searchFunction = (query: string) => {
  console.log('Searching for:', query)
  // API call
}

const debouncedSearch = debounce(searchFunction, 500)

// Rapid typing - only searches after 500ms of no input
debouncedSearch('a')      // Cancelled
debouncedSearch('ap')     // Cancelled  
debouncedSearch('app')    // Cancelled
debouncedSearch('apple')  // Executes after 500ms

// Form validation
const validateForm = debounce((formData: FormData) => {
  // Validate form fields
  console.log('Validating form...')
}, 300)

// API call debouncing
const saveData = debounce(async (data: any) => {
  try {
    await api.saveUserData(data)
    console.log('Data saved successfully')
  } catch (error) {
    console.error('Save failed:', error)
  }
}, 1000)

// Method debouncing with context
class AutoSave {
  data: any = {}
  
  updateData(key: string, value: any) {
    this.data[key] = value
    this.debouncedSave()
  }
  
  debouncedSave = debounce(function(this: AutoSave) {
    console.log('Auto-saving data:', this.data)
    // Save to storage
  }, 2000, this)
}

DOM Utilities

Element Type Checking

import { isElement, isText, isComment } from '@tarojs/runtime'

// Element type checks
function isElement(node: TaroNode): node is TaroElement
function isText(node: TaroNode): node is TaroText  
function isComment(node: TaroNode): boolean

Usage Examples

// Type-safe DOM manipulation
function processNode(node: TaroNode) {
  if (isElement(node)) {
    // TypeScript knows this is TaroElement
    console.log('Element tag:', node.tagName)
    console.log('Element attributes:', node.attributes)
    node.setAttribute('processed', 'true')
  } else if (isText(node)) {
    // TypeScript knows this is TaroText
    console.log('Text content:', node.textContent)
    node.textContent = node.textContent.trim()
  } else if (isComment(node)) {
    console.log('Comment node found')
    // Handle comment node
  }
}

// Filter node collections
function getElements(nodes: TaroNode[]): TaroElement[] {
  return nodes.filter(isElement)
}

function getTextNodes(nodes: TaroNode[]): TaroText[] {
  return nodes.filter(isText)
}

// DOM traversal
function walkDOM(node: TaroNode, callback: (node: TaroNode) => void) {
  callback(node)
  
  if (isElement(node)) {
    node.childNodes.forEach(child => walkDOM(child, callback))
  }
}

Property Utilities

import { 
  isHasExtractProp, 
  isParentBinded, 
  shortcutAttr 
} from '@tarojs/runtime'

// Property extraction check
function isHasExtractProp(element: TaroElement): boolean

// Event binding check  
function isParentBinded(element: TaroElement, eventType: string): boolean

// Attribute shortcuts
function shortcutAttr(key: string): string

Usage Examples

// Check if element has extractable properties
const element = document.createElement('custom-component')
element.setAttribute('custom-prop', 'value')

if (isHasExtractProp(element)) {
  console.log('Element has non-standard properties')
  // Handle custom property extraction
}

// Check event delegation
const child = document.createElement('button')
const parent = document.createElement('view')
parent.addEventListener('tap', handleTap)
parent.appendChild(child)

if (isParentBinded(child, 'tap')) {
  console.log('Parent handles tap events')
  // Skip adding duplicate listener
} else {
  child.addEventListener('tap', handleTap)
}

// Attribute shortcuts
console.log(shortcutAttr('className'))  // 'class'
console.log(shortcutAttr('htmlFor'))    // 'for'

ID Generation

import { incrementId } from '@tarojs/runtime'

function incrementId(): () => string

Key Features

  • Unique IDs: Generates unique identifiers using A-Z, a-z sequence
  • Deterministic: Consistent ID generation across sessions
  • Performance: Fast generation without collision checking

Usage Examples

// Create ID generator
const getId = incrementId()

// Generate unique IDs
const id1 = getId()  // 'A'
const id2 = getId()  // 'B'  
const id3 = getId()  // 'C'
// ... continues through alphabet

// Use in component creation
function createUniqueElement(tagName: string): TaroElement {
  const element = document.createElement(tagName)
  element.id = getId()
  return element
}

// Multiple generators for different purposes
const nodeIdGen = incrementId()
const componentIdGen = incrementId()

const nodeId = nodeIdGen()      // Independent sequence
const componentId = componentIdGen()  // Independent sequence

Event Management

Event Center

import { eventCenter, Events } from '@tarojs/runtime'

interface EventsType {
  // Event registration
  on(eventName: string, listener: Function, context?: any): EventsType
  once(eventName: string, listener: Function, context?: any): EventsType
  
  // Event emission  
  trigger(eventName: string, ...args: any[]): EventsType
  emit(eventName: string, ...args: any[]): EventsType
  
  // Event removal
  off(eventName?: string, listener?: Function, context?: any): EventsType
}

const eventCenter: EventsType

Key Features

  • Global Communication: Cross-component and cross-page event communication
  • Context Binding: Automatic this binding for event handlers
  • One-time Events: Support for single-execution event handlers
  • Flexible Removal: Remove specific listeners or all listeners for an event

Usage Examples

// Basic event communication
eventCenter.on('user-login', (user: User) => {
  console.log('User logged in:', user.name)
  updateUserInterface(user)
})

eventCenter.trigger('user-login', { name: 'John', id: '123' })

// Cross-component communication
// Component A
class ComponentA extends React.Component {
  sendMessage = () => {
    eventCenter.trigger('message-sent', {
      from: 'ComponentA',
      message: 'Hello from A'
    })
  }
}

// Component B  
class ComponentB extends React.Component {
  componentDidMount() {
    eventCenter.on('message-sent', this.handleMessage, this)
  }
  
  componentWillUnmount() {
    eventCenter.off('message-sent', this.handleMessage, this)
  }
  
  handleMessage = (data: { from: string, message: string }) => {
    console.log(`Message from ${data.from}: ${data.message}`)
  }
}

// One-time events
eventCenter.once('app-initialized', () => {
  console.log('App initialization complete')
  // This handler will only run once
})

// Event cleanup
class EventfulComponent {
  listeners = new Map()
  
  componentDidMount() {
    // Store listeners for cleanup
    this.listeners.set('data-update', this.handleDataUpdate)
    this.listeners.set('user-action', this.handleUserAction)
    
    // Register events
    eventCenter.on('data-update', this.handleDataUpdate, this)
    eventCenter.on('user-action', this.handleUserAction, this)
  }
  
  componentWillUnmount() {
    // Clean up all listeners
    this.listeners.forEach((handler, eventName) => {
      eventCenter.off(eventName, handler, this)
    })
  }
  
  handleDataUpdate = (data: any) => { /* ... */ }
  handleUserAction = (action: any) => { /* ... */ }
}

Async Operations

nextTick

import { nextTick } from '@tarojs/runtime'

function nextTick(
  callback: Function, 
  context?: Record<string, any>
): void

Key Features

  • Update Coordination: Executes callback after next DOM update cycle
  • Context Binding: Supports custom execution context
  • Timeout Fallback: Uses setTimeout if update queue unavailable

Usage Examples

// Wait for DOM updates
function updateComponent() {
  // Modify DOM
  element.textContent = 'Updated content'
  element.className = 'new-class'
  
  // Execute after DOM update
  nextTick(() => {
    console.log('DOM updates completed')
    // Safe to read computed styles, dimensions, etc.
    const rect = element.getBoundingClientRect()
    console.log('Element dimensions:', rect)
  })
}

// React integration
class ReactComponent extends React.Component {
  updateData = () => {
    this.setState({ data: newData }, () => {
      // Execute after React update
      nextTick(() => {
        // Execute after Taro DOM update
        console.log('Both React and Taro updates complete')
      })
    })
  }
}

// Custom context
const context = { name: 'CustomContext' }
nextTick(function() {
  console.log('Context:', this.name)  // 'CustomContext'
}, context)

// Chained operations
function performComplexUpdate() {
  // Step 1: Update data
  updateApplicationData()
  
  nextTick(() => {
    // Step 2: Update DOM after data update
    updateDOMElements()
    
    nextTick(() => {
      // Step 3: Final operations after DOM update
      performFinalOperations()
    })
  })
}

Hydration System

import { hydrate } from '@tarojs/runtime'

function hydrate(node: TaroElement | TaroText): MiniData

interface MiniData {
  [key: string]: any
  // Converted mini-program data structure
}

Key Features

  • DOM to Data: Converts DOM nodes to mini-program data structure
  • Component Aliasing: Maps generic components to platform-specific ones
  • View Optimization: Optimizes view types for performance
  • Property Filtering: Filters out non-standard properties

Usage Examples

// Hydrate DOM tree for mini-program
const element = document.createElement('view')
element.className = 'container'
element.textContent = 'Hello World'
element.style.color = 'red'

const miniData = hydrate(element)
console.log('Mini-program data:', miniData)
// Output: { cn: [{ v: 'Hello World' }], st: { color: 'red' }, ... }

// Hydrate complex component tree
const complexElement = document.createElement('scroll-view')
complexElement.setAttribute('scroll-y', 'true')

const listItem = document.createElement('view')
listItem.className = 'list-item'
listItem.textContent = 'Item 1'
complexElement.appendChild(listItem)

const hydratedData = hydrate(complexElement)
console.log('Complex hydration:', hydratedData)

// Use in custom rendering
function customRender(virtualDOM: TaroElement) {
  const miniData = hydrate(virtualDOM)
  
  // Send to mini-program
  if (getCurrentInstance().page) {
    getCurrentInstance().page.setData({
      customComponent: miniData
    })
  }
}

Router Utilities

URL Utilities

import { 
  addLeadingSlash, 
  hasBasename, 
  stripBasename, 
  stripTrailing, 
  stripSuffix 
} from '@tarojs/runtime'

// URL formatting
function addLeadingSlash(path: string): string
function stripTrailing(path: string): string
function stripSuffix(path: string, suffix: string): string

// Basename handling  
function hasBasename(path: string, basename: string): boolean
function stripBasename(path: string, basename: string): string

Usage Examples

// URL formatting
console.log(addLeadingSlash('path/to/page'))     // '/path/to/page'
console.log(addLeadingSlash('/path/to/page'))    // '/path/to/page'

console.log(stripTrailing('/path/to/page/'))     // '/path/to/page'
console.log(stripTrailing('/path/to/page'))      // '/path/to/page'

console.log(stripSuffix('/page.html', '.html'))  // '/page'
console.log(stripSuffix('/page', '.html'))       // '/page'

// Basename handling
const basename = '/app'
const fullPath = '/app/pages/home'

console.log(hasBasename(fullPath, basename))     // true
console.log(stripBasename(fullPath, basename))   // '/pages/home'

// Route processing
function processRoute(rawPath: string, appBasename: string = ''): string {
  let path = addLeadingSlash(rawPath)
  path = stripTrailing(path)
  
  if (appBasename && hasBasename(path, appBasename)) {
    path = stripBasename(path, appBasename)
  }
  
  return path
}

console.log(processRoute('pages/home/', '/myapp'))  // '/pages/home'

Page Utilities

import { getHomePage, getCurrentPage } from '@tarojs/runtime'

// Get application home page
function getHomePage(): string

// Get current page path  
function getCurrentPage(): string

Usage Examples

// Navigation helpers
function navigateHome() {
  const homePage = getHomePage()
  wx.navigateTo({ url: homePage })
}

function refreshCurrentPage() {
  const currentPage = getCurrentPage()
  wx.redirectTo({ url: currentPage })
}

// Breadcrumb generation
function generateBreadcrumb(): string[] {
  const current = getCurrentPage()
  const home = getHomePage()
  
  const breadcrumb = [home]
  if (current !== home) {
    breadcrumb.push(current)
  }
  
  return breadcrumb
}

Cache Management

RuntimeCache

import { RuntimeCache } from '@tarojs/runtime'

class RuntimeCache<T = any> {
  // Cache operations
  set(key: string, value: T): void
  get(key: string): T | undefined  
  has(key: string): boolean
  delete(key: string): boolean
  clear(): void
  
  // Iteration
  keys(): IterableIterator<string>
  values(): IterableIterator<T>
  entries(): IterableIterator<[string, T]>
  
  // Properties
  readonly size: number
}

Key Features

  • Context-aware: Automatic cleanup based on page context
  • Type-safe: Generic type support for cached values
  • Memory Management: Automatic cleanup to prevent memory leaks

Usage Examples

// Create cache instance
const pageCache = new RuntimeCache<PageData>()
const componentCache = new RuntimeCache<ComponentState>()

// Cache page data
interface PageData {
  userInfo: User
  settings: AppSettings
}

pageCache.set('user-profile', {
  userInfo: { id: '123', name: 'John' },
  settings: { theme: 'dark', lang: 'en' }
})

// Retrieve cached data
const cachedData = pageCache.get('user-profile')
if (cachedData) {
  console.log('Cached user:', cachedData.userInfo.name)
}

// Cache management
console.log('Cache size:', pageCache.size)
console.log('Has user data:', pageCache.has('user-profile'))

// Iterate cache
for (const [key, value] of pageCache.entries()) {
  console.log(`Cached ${key}:`, value)
}

// Cleanup
pageCache.delete('old-data')
pageCache.clear()  // Clear all cache

// Component state caching
class CacheableComponent extends React.Component {
  cache = new RuntimeCache<ComponentState>()
  
  componentDidMount() {
    const cached = this.cache.get('component-state')
    if (cached) {
      this.setState(cached)
    }
  }
  
  componentWillUnmount() {
    this.cache.set('component-state', this.state)
  }
}

Runtime Configuration

Options

import { options } from '@tarojs/runtime'

interface Options {
  prerender: boolean    // Enable prerendering (default: true)
  debug: boolean       // Enable debug mode (default: false)
}

const options: Options

Usage Examples

// Configure runtime behavior
options.prerender = false  // Disable prerendering
options.debug = true      // Enable debug logging

// Conditional behavior
if (options.debug) {
  console.log('Debug mode enabled')
  // Additional logging
}

if (options.prerender) {
  // Prerender optimization enabled
  performPrerenderOptimizations()
}

// Environment-based configuration
if (process.env.NODE_ENV === 'development') {
  options.debug = true
}

if (process.env.NODE_ENV === 'production') {
  options.prerender = true
  options.debug = false
}

Polyfills

handlePolyfill

import { handlePolyfill } from '@tarojs/runtime'

function handlePolyfill(): void

Key Features

  • Conditional Polyfills: Only applies needed polyfills based on environment
  • Standard APIs: Polyfills for Object.assign, Array.find, etc.
  • Web-specific: IntersectionObserver polyfill for web platforms

Usage Examples

// Apply polyfills (usually done automatically)
handlePolyfill()

// Manual polyfill check
if (typeof Object.assign !== 'function') {
  handlePolyfill()  // Will apply Object.assign polyfill
}

// Environment-specific polyfills
if (typeof window !== 'undefined') {
  // Web environment - full polyfills applied
  handlePolyfill()
}

Helper Functions

General Utilities

import { extend, getComponentsAlias, convertNumber2PX } from '@tarojs/runtime'

// Object extension
function extend(target: any, ...sources: any[]): any

// Component mapping
function getComponentsAlias(): Record<string, string>

// Unit conversion  
function convertNumber2PX(value: number | string): string

Usage Examples

// Object extension (like Object.assign)
const base = { a: 1, b: 2 }
const extended = extend(base, { b: 3, c: 4 }, { d: 5 })
console.log(extended)  // { a: 1, b: 3, c: 4, d: 5 }

// Component aliasing
const aliases = getComponentsAlias()
console.log(aliases)  // { 'scroll-view': 'ScrollView', ... }

// Convert to px units
console.log(convertNumber2PX(16))     // '16px'
console.log(convertNumber2PX('16'))   // '16px'  
console.log(convertNumber2PX('16px')) // '16px'

// Use in style processing
function normalizeStyleValue(value: any): string {
  if (typeof value === 'number') {
    return convertNumber2PX(value)
  }
  return String(value)
}

Environment & Runtime APIs

Environment Detection

import { env } from '@tarojs/runtime'

interface Env {
  window: Window | {}
  document: Document | {}
}

const env: Env

Usage Examples

// Check platform capabilities
if (env.window && 'addEventListener' in env.window) {
  // Web platform - use native APIs
  env.window.addEventListener('resize', handleResize)
} else {
  // Mini-program platform - use alternatives
  // Handle resize through Taro lifecycle
}

// Safe document access
if (env.document && 'createElement' in env.document) {
  // Native DOM available
  const element = env.document.createElement('div')
} else {
  // Use Taro DOM polyfills
  const element = document.createElement('view')
}

URL Parsing

import { parseUrl } from '@tarojs/runtime'

function parseUrl(url?: string): {
  href: string
  origin: string
  protocol: string
  hostname: string
  host: string
  port: string
  pathname: string
  search: string
  hash: string
}

Usage Examples

// Parse complete URLs
const parsed = parseUrl('https://example.com:8080/path?query=value#section')
console.log(parsed.hostname)  // 'example.com'
console.log(parsed.port)      // '8080'
console.log(parsed.pathname)  // '/path'
console.log(parsed.search)    // '?query=value'
console.log(parsed.hash)      // '#section'

// Handle partial URLs
const relative = parseUrl('/api/users?page=1')
console.log(relative.pathname)  // '/api/users'
console.log(relative.search)    // '?page=1'

// Safe URL parsing
function getUrlParts(url: string) {
  try {
    const parts = parseUrl(url)
    return parts.pathname + parts.search
  } catch (error) {
    return '/'
  }
}

Hooks System

import { hooks } from '@tarojs/runtime'

interface Hooks {
  call<T>(hookName: string, ...args: any[]): T
  tap(hookName: string, callback: Function): void
}

const hooks: Hooks

Usage Examples

// Register hook handlers
hooks.tap('beforeMount', (component) => {
  console.log('Component mounting:', component)
})

hooks.tap('afterUpdate', (instance) => {
  console.log('Component updated:', instance)
})

// Call hooks programmatically
const eventCenter = hooks.call('getEventCenter', Events)
const router = hooks.call('getRouter')

// Custom hook integration
hooks.tap('customEvent', (data) => {
  // Handle custom events
  processCustomData(data)
})

// Trigger custom hooks
hooks.call('customEvent', { type: 'user-action', payload: data })

The utilities module provides essential tools for building performant, maintainable cross-platform applications with comprehensive performance monitoring, event management, and optimization capabilities.

Install with Tessl CLI

npx tessl i tessl/npm-tarojs--runtime

docs

bom.md

dom.md

framework-integration.md

index.md

types.md

utilities.md

tile.json