Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

WeChat Mini Program Development: Architecture, Components, and API Integration

Tech 1

WeChat Mini Programs operate within a structured environment requiring specific directory conventions. Upon initialization, the framework establishes the following hierarchy:

project-root/
├── pages/                 # Route containers
│   └── index/
│       ├── index.js       # Page logic controller
│       ├── index.json     # Page-level configuration
│       ├── index.wxml     # View template
│       └── index.wxss     # Scoped styles
├── components/            # Reusable UI modules
├── utils/                 # Service utilities
├── app.js                 # Application lifecycle
├── app.json               # Global manifest
├── app.wxss               # Global stylesheet
└── project.config.json    # Development environment

Configuration Architecture

Global behavior is governed through app.json, which registers routes and defines window presentation defaults:

{
  "pages": [
    "pages/landing/landing",
    "pages/catalog/catalog",
    "pages/cart/cart",
    "pages/account/account"
  ],
  "window": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTitleText": "Commerce App",
    "navigationBarTextStyle": "black",
    "enablePullDownRefresh": false
  },
  "tabBar": {
    "color": "#999999",
    "selectedColor": "#ff3300",
    "backgroundColor": "#fafafa",
    "borderStyle": "white",
    "list": [
      {
        "pagePath": "pages/landing/landing",
        "text": "Home",
        "iconPath": "/static/tab-home.png",
        "selectedIconPath": "/static/tab-home-active.png"
      },
      {
        "pagePath": "pages/catalog/catalog",
        "text": "Browse",
        "iconPath": "/static/tab-browse.png",
        "selectedIconPath": "/static/tab-browse-active.png"
      }
    ]
  }
}

Individual pages override global settings via their local page.json files, enabling granular control over navigation appearance and gesture behaviors.

WXML Templating System

The view layer employs WXML, a declarative syntax supporting data binding and directiev-based rendering:

// landing.js
Page({
  data: {
    greeting: "Hello WeChat",
    categories: ['electronics', 'apparel', 'home'],
    isAuthenticated: false
  }
})
<!-- landing.wxml -->
<view class="container">
  <text class="header">{{greeting}}</text>
  
  <view class="category-list">
    <block wx:for="{{categories}}" wx:key="*this" wx:for-item="category">
      <view class="tag">{{category}}</view>
    </block>
  </view>
  
  <view wx:if="{{isAuthenticated}}" class="member-badge">
    Premium Member
  </view>
  <view wx:else class="guest-notice">
    Sign in for benefits
  </view>
</view>

Template reusability is achieved through definition blocks:

<template name="productCard">
  <view class="card">
    <image src="{{thumbnail}}" mode="aspectFill"/>
    <view class="meta">
      <text class="name">{{productName}}</text>
      <text class="price">${{unitPrice}}</text>
    </view>
  </view>
</template>

<template is="productCard" data="{{...selectedItem}}"/>

Template scope management utilizes import for encapsulated references or include for code injection without template logic duplicasion.

Application Lifecycle

The global application instance manages state across sessions:

// app.js
App({
  globalState: {
    apiBaseUrl: 'https://service.example.com',
    userSession: null,
    cartCache: []
  },
  
  onLaunch(launchOptions) {
    // Initialize analytics and validation
    this.validateSession()
  },
  
  onShow() {
    // Resume background tasks
  },
  
  onHide() {
    // Persist temporary state
  },
  
  validateSession() {
    // Authentication logic
  }
})

Access the application context from any module:

const appInstance = getApp()
console.log(appInstance.globalState.apiBaseUrl)

Page Lifecycle Management

Pages implement specific hooks for state transitions:

Page({
  data: {
    contentList: [],
    paginationCursor: 1,
    hasMoreContent: true
  },
  
  onLoad(routeParameters) {
    // Initial data population
    this.fetchInitialData()
  },
  
  onReady() {
    // First render completion
  },
  
  onShow() {
    // Re-activation from background
  },
  
  onHide() {
    // Deactivation
  },
  
  onUnload() {
    // Cleanup resources
  },
  
  onPullDownRefresh() {
    // Reset pagination and refresh
    this.setData({ paginationCursor: 1 })
    this.fetchInitialData().finally(() => {
      wx.stopPullDownRefresh()
    })
  },
  
  onReachBottom() {
    // Infinite scroll implementation
    if (this.data.hasMoreContent) {
      this.loadAdditionalItems()
    }
  },
  
  onShareAppMessage() {
    return {
      title: 'Check this collection',
      path: '/pages/landing/landing'
    }
  }
})

Network Abstraction Layer

Centralize API communication for maintainability:

// utils/apiService.js
const BASE_ENDPOINT = 'https://api.service.io'

class ApiService {
  request(path, payload = {}, method = 'GET') {
    return new Promise((resolve, reject) => {
      wx.request({
        url: `${BASE_ENDPOINT}${path}`,
        method,
        data: payload,
        header: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.getToken()}`
        },
        success: (response) => resolve(response.data),
        fail: (error) => reject(error)
      })
    })
  }
  
  fetchFeed(page) {
    return this.request('/feed', { page })
  }
  
  fetchItemDetail(id) {
    return this.request(`/items/${id}`)
  }
  
  getToken() {
    return wx.getStorageSync('accessToken') || ''
  }
}

export default new ApiService()

Integration within page logic:

import apiService from '../../utils/apiService.js'

Page({
  async fetchInitialData() {
    try {
      const response = await apiService.fetchFeed(1)
      this.setData({
        contentList: response.items
      })
    } catch (err) {
      wx.showToast({ title: 'Load failed', icon: 'error' })
    }
  }
})

Component Development

Custom components encapsulate complex UI patterns:

// components/mediaGrid/mediaGrid.js
Component({
  properties: {
    mediaItems: {
      type: Array,
      value: [],
      observer(newVal) {
        // React to data changes
        this.processMedia(newVal)
      }
    },
    layoutMode: {
      type: String,
      value: 'grid'
    }
  },
  
  data: {
    processedItems: []
  },
  
  methods: {
    processMedia(rawData) {
      const formatted = rawData.map(item => ({
        ...item,
        displayUrl: item.cdnUrl + '?thumbnail=300'
      }))
      this.setData({ processedItems: formatted })
    },
    
    handleTap(event) {
      const index = event.currentTarget.dataset.index
      this.triggerEvent('mediaselect', { 
        index,
        item: this.data.processedItems[index]
      })
    }
  }
})

Registration requires declaration in the consuming page's configuration:

{
  "usingComponents": {
    "media-grid": "/components/mediaGrid/mediaGrid"
  }
}

Communication Patterns

Parent components transmit data via attribute binding:

<media-grid 
  mediaItems="{{galleryPhotos}}" 
  layoutMode="masonry"
  bind:mediaselect="onPhotoSelected" />

Child components emit events upward:

// Parent handler
onPhotoSelected(event) {
  const { item } = event.detail
  wx.navigateTo({
    url: `/pages/viewer/viewer?url=${encodeURIComponent(item.url)}`
  })
}

Navigation Strategies

Declarative routing maintains history stack:

<navigator 
  url="/pages/detail/detail?sku={{productCode}}" 
  open-type="navigate"
  hover-class="navigator-hover">
  <view class="product-row">
    <text>View Details</text>
  </view>
</navigator>

Imperative navigation enables dynamic routing:

navigateToDetail(sku) {
  wx.navigateTo({
    url: `/pages/detail/detail?sku=${sku}`,
    events: {
      // Listen for data from target page
      acceptDataFromDetail(data) {
        console.log('Received:', data)
      }
    },
    success: (res) => {
      // Pass data to target page
      res.eventChannel.emit('sendDataToDetail', { preview: true })
    }
  })
}

switchToMainTab() {
  wx.switchTab({ url: '/pages/account/account' })
}

replaceCurrentPage() {
  wx.redirectTo({ url: '/pages/login/login' })
}

navigateBackTo(delta = 1) {
  wx.navigateBack({ delta })
}

Interaction Enhancements

Implemant smooth scrolling behaviors:

returnToTop() {
  wx.pageScrollTo({
    scrollTop: 0,
    duration: 400,
    easingFunction: 'easeInOut'
  })
}

Enable pull-to-refresh via page configuration:

{
  "enablePullDownRefresh": true,
  "backgroundTextStyle": "dark",
  "backgroundColor": "#f5f5f5"
}

Handle pagination in scroll events:

async loadAdditionalItems() {
  const nextPage = this.data.paginationCursor + 1
  
  try {
    const response = await apiService.fetchFeed(nextPage)
    
    if (response.items.length === 0) {
      this.setData({ hasMoreContent: false })
      wx.showToast({ title: 'End of list', icon: 'none' })
      return
    }
    
    this.setData({
      contentList: [...this.data.contentList, ...response.items],
      paginationCursor: nextPage
    })
  } catch (err) {
    wx.showToast({ title: 'Error loading', icon: 'error' })
  }
}

User Authorization and Media

Request user profile data:

<button 
  open-type="getUserInfo" 
  bindgetuserinfo="processUserInfo">
  Authenticate
</button>
processUserInfo(event) {
  const profile = event.detail.userInfo
  if (profile) {
    this.setData({
      nickname: profile.nickName,
      avatar: profile.avatarUrl
    })
    wx.setStorageSync('userProfile', profile)
  }
}

Geolocation requires manifest permissions:

{
  "permission": {
    "scope.userLocation": {
      "desc": "Location services enable delivery tracking"
    }
  }
}

Image selection and preview capabilities:

selectProfilePicture() {
  wx.chooseMedia({
    count: 1,
    mediaType: ['image'],
    sourceType: ['album', 'camera'],
    success: (res) => {
      const tempPath = res.tempFiles[0].tempFilePath
      this.uploadAvatar(tempPath)
    }
  })
}

previewImageGallery(event) {
  const currentUrl = event.currentTarget.dataset.src
  const allUrls = this.data.gallery.map(img => img.highResUrl)
  
  wx.previewImage({
    current: currentUrl,
    urls: allUrls,
    showmenu: true
  })
}

Utility Access

Open system settings for revoked permissions:

openAuthorizationSettings() {
  wx.openSetting({
    success: (res) => {
      console.log('Updated permissions:', res.authSetting)
      if (res.authSetting['scope.userLocation']) {
        this.initializeLocationServices()
      }
    }
  })
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.