How Tencent Dajia Mini‑Program Boosts Mobile Content Aggregation and User Engagement
The article details the design, features, and technical implementation of the Tencent Dajia WeChat mini‑program, explaining how it aggregates high‑quality Chinese internet content, supports multi‑device data sharing, author and column management, user login, collection, sharing, commenting, and analytics, while providing practical code snippets and development tips for front‑end engineers.
Tencent Dajia Product Background
"Tencent Dajia" is a Chinese‑internet column writing service launched by the company. Because finding valuable information is costly, high‑quality content often gets lost in the information flood. The product aims to present the most valuable information to users as quickly as possible, encouraging deeper and longer‑lasting thinking and expression. It delivers timeless articles and cutting‑edge ideas through columns, e‑books, personal channels, or even mini‑programs.
The name "Dajia" means gathering the works of great Chinese writers to provide the most attractive classic texts for internet users, creating a powerful platform for online discourse and a valuable reading brand.
Tencent Dajia Mini‑Program Use Cases
The mini‑program is built on WeChat mini‑program capabilities and tightly integrated with official accounts to attract users and expand content distribution.
Dajia, a high‑quality column of Tencent Content Production, focuses on quality and produces several premium articles daily, emphasizing efficient and fast content dissemination. Before the mini‑program, Dajia content was mainly recommended individually via the Tencent News client, official accounts, etc., and the PC aggregation page could not adapt to mobile distribution, resulting in low visit counts and loss of historical content value.
To meet the demand for aggregated mobile pages, the Dajia mini‑program enables fast lookup of historical content, allowing push notifications to include author lists or related articles. Users can access the latest and historical content anytime through WeChat article links or mini‑program favorites.
Problems Solved
1. Mobile‑centric content aggregation and historical content browsing for users.
2. Customized author/column following (favorites) so users continuously receive high‑quality content and product teams can more accurately track user interests.
3. Bridging content aggregation pages with WeChat articles, enabling quick access to topic‑specific content via sharing and re‑exposing historical articles.
4. Functional H5 displays (e.g., banner "Dajia Choice" with open‑editor features) to launch diverse user activities and enrich product‑user interaction.
5. Transforming manual recommendation into user‑initiated browsing; the mini‑program preserves and re‑exposes daily premium content that would otherwise be replaced.
The mini‑program presents content through tabs: home aggregation, author list, column aggregation, personal center, and underlying layers for content, authors, columns, and activities.
Feature Analysis
1.1 Multi‑Device Data Sharing
The mini‑program must keep content consistent with the Dajia website (http://dajia.qq.com) while adding a user center for favorites, article rating, and browsing history without affecting the original content platform (http://ninja.webdev.com). Thanks to the Ninja team and contributors for API and documentation support.
1.2 Home Selection Detail Display
Three tips:
Cache the list JSON to keep the selection list and web site data consistent, reducing duplicate requests and allowing data insertion for operational needs.
Utilize the image component; it offers 13 modes (4 scaling, 9 cropping) to display images perfectly.
Use array concat for list pull‑down loading to concatenate data efficiently.
<image mode="aspectFill" src="{{item.n_image}}"></image>The aspectFill mode scales the image while ensuring the short side is fully visible, potentially cropping the longer side.
Api.fetchGet(dajaMorEchoiceUrl, (err, res) => {
...
vm.$set({ firstData: vm.$data().firstData.concat(res.data) })
...
});1.3 Content Underlying Display
The mini‑program relies on a reactive data‑binding system. To render an article, a JSON structure determines whether to render paragraphs, tables, lists, images, or videos.
Dajia's content publishing system outputs standardized JSON for article attributes. Example of a text block:
{tag: "text", value: "对于不认识Ayawawa的人,很难用一两句话介绍她的理论。"},
{tag: "image", src: "//img1.gtimg.com/cul/pics/hv1/92/70/2270/147624692.jpg"},
{tag: "title", level: "H2", value: "婚姻对不同阶层的女性,根本不是一回事"}Corresponding WXML template:
<block wx:if="{{detail.length > 0}}">
<block wx:for="{{detail}}" wx:key="item">
<view wx:if="{{item.tag == 'title' || item.tag == 'text'}}" class="{{item.tag}} {{item.level ? 'h2' : ''}}">{{item.value}}</view>
<block wx:if="{{item.tag == 'image'}}">
<image mode="widthFix" src="{{item.src}}"></image>
<view class="imgalt">{{item.title}}</view>
</block>
</block>
</block>
<block wx:else>
<view class="p"></view>
</block>Rich‑text HTML cannot be displayed directly in the mini‑program and must be transformed.
1.4 Author Display
There are nearly a thousand Dajia authors. Data is periodically fetched, cached, and served with pagination. When loading author data, the user ID is passed to check relationships such as favorites and ratings.
Author list loading strategy:
Load a few authors on first entry.
Use pull‑up loading for pagination.
Refresh favorite status when returning to the author page.
Sample response format (image omitted for brevity).
<view class="oloading" wx:if="{{ready}}" style="height:{{wHeight}}px"></view>
<view class="body" wx:if="{{body}}">
<view class="num_index" wx:if="{{colAutData.length !== 0}}">我的收藏</view>
<view class="box_author" wx:if="{{colAutData.length !== 0}}">
<view class="no_author" wx:if="{{!dataReady}}">暂无收藏</view>
<block wx:for="{{colAutDatas}}" wx:key="item" wx:if="{{colAutData.length !== 0}}">
<view class="colltloading" wx:if="{{!dataReady}}"></view>
<view class="num_author" bindtap="onAuthor" data-id="{{item.id}}">
<image class="num-image" src="{{item.image}}"></image>
<view class="num_name">{{item.name}}</view>
<view class="box_start {{item.have == 1 ? 'on':'out'}}" catchtap="onRecommStart" data-id="{{item.id}}" data-parentid="{{item.first_letter}}" data-have="{{item.have}}"></view>
</view>
</block>
</view>
...
</view>1.5 User Login State Acquisition
Login flow (image omitted) includes:
Calling wx.login() to obtain a code.
Sending the code to the server to exchange for openid and session_key.
Generating a sessionId from session_key and storing it.
Including sessionId in subsequent requests.
Checking login state with wx.checkSession and re‑authenticating if needed.
Login state is required for personalized features such as author/column favorites and for protecting user‑specific data.
1.6 User Information Retrieval
When the app first opens, it prompts the user for authorization to obtain avatar and nickname, which are cached in storage with an expiration determined by the feature.
1.7 Collection Feature (Author & Column)
Buttons toggle collection status. Example button markup:
<view class="writer-collet {{have == false ? 'edd':'cdd'}}" bindtap="writerColletButton" data-have="{{have}}" data-id="{{load.wid}}" data-parentid="{{author.first_letter}}" hover-class="writer-collet-hover"><view class="add" wx:if="{{!have}}"></view>{{allReady}}</view>Collection handling logic (simplified):
ColletButton: function(e) {
let id = e.currentTarget.dataset.id;
let have = e.currentTarget.dataset.have;
let parentid = e.currentTarget.dataset.parentid;
let user = wx.getStorageSync('user') || {};
wx.showLoading({title: '正在处理'});
if (!have) {
Api.fetchPost(Api.collection, {userid: user.openid, id: id, type: 1, have: 1}, (err, res) => {
if (res.ret == 200) {
wx.hideLoading();
vm.$set({allReady: "已经收藏", have: true});
}
});
} else {
Api.fetchPost(Api.collection, {userid: user.openid, id: id, type: 1, have: 0}, (err, res) => {
if (res.ret == 200) {
wx.hideLoading();
vm.$set({allReady: "收藏", have: false});
}
});
}
// Update UI list after request
list[parentid].map(item => {
if (item.id == id) { item.have = !item.have; }
return item;
});
vm.$set({authors: list});
setTimeout(() => { this.getColAutData(); }, 1000);
}1.8 Personal Center
The personal center displays user info, favorite authors/columns statistics, and browsing history. It refreshes favorite statistics on each onShow cycle.
1.9 Browsing History
Browsing records are reported when users read articles; the backend timestamps and deduplicates them. Developers must handle cases where an article is deleted after being recorded.
1.10 Comment Feature
Due to content review and login constraints, the mini‑program uses the Coral Comment service to display comments only.
1.11 Share Feature
Sharing is implemented in onShareAppMessage(). Custom share buttons use open-type="share" with data attributes for title and article ID.
<button class="choice-share-b" catchtap="onShareAppMessage" open-type="share" data-title="{{item.title}}" data-tid="{{item.tid}}"></button>1.12 Rating Feature
Users can rate articles, contributing to article, column, and author rating aggregates. The rating module consists of rendering, user interaction, server callbacks, and data calculations.
1.13 Poster Generation
Poster generation uses the mini‑program canvas component. Images such as backgrounds and QR codes are downloaded via wx.downloadFile(). The final image is saved with wx.canvasToTempFilePath() and can be previewed with wx.previewImage(). Text length calculations are handled by utility functions getTrueLength and cutString.
getTrueLength: function(str) {
let len = str.length, truelen = 0;
for (let x = 0; x < len; x++) {
truelen += str.charCodeAt(x) > 128 ? 2 : 1;
}
return truelen;
},
cutString: function(str, leng) {
let len = str.length, tlen = len, nlen = 0;
for (let x = 0; x < len; x++) {
if (str.charCodeAt(x) > 128) {
if (nlen + 2 < leng) nlen += 2; else { tlen = x; break; }
} else {
if (nlen + 1 < leng) nlen += 1; else { tlen = x; break; }
}
}
return tlen;
}1.14 Message Template (Not Yet Launched)
Message templates are used for low‑frequency push notifications, grouping users by subscribed columns or authors. When an article updates, a template message can be sent.
2 Style Presentation
2.1 Sprite Sheet Merging Tips
WeChat mini‑programs use the responsive pixel unit rpx (750 rpx = 750 px). Sprite sheets can be generated automatically with tools such as https://code.ahthw.com/tools/csssprite/ .
2.2 TabBar Icon Size Recommendations
TabBar icons are typically 81 px × 81 px with a recommended 35 px text area. The Dajia mini‑program omits the text configuration to match the design mockup.
2.3 Clever Use of Ternary Operators in WXML
Using ternary operators simplifies code, e.g.:
<view class="box_start {{item.have == 1 ? 'on':'out'}}" catchtap="onRecommStart">...</view>
<view wx:if="{{item.tag == 'title' || item.tag == 'text'}}" class="{{item.tag}} {{item.level ? 'h2' : ''}}">...</view>2.4 WXSS Tips
text-align:justify;aligns text evenly on both sides.
Use position:fixed for high‑z‑index components like canvas to avoid shadows on long pages.
Apply -webkit-line-clamp to truncate text after a set number of lines with an ellipsis.
Combine local base64 images and CSS3 animations for lightweight loading placeholders.
Pre‑process data when possible and toggle visibility via styles.
Use rpx in background properties for responsive design.
Import shared WXSS files with @import "*.wxss" for reuse.
Provide fallback backgrounds for image views to handle load failures.
Set mode="widthFix" on image components to keep width fixed while height adapts.
3 Code Development & Maintenance
3.1 WxPage Framework
The Dajia mini‑program uses the lightweight WxPage framework ( https://github.com/tvfe/wxpage ), which offers fast page opening, extended lifecycle hooks, component dependency management, and utility methods such as emit and $put.
3.2 Modular Utility Methods
Common utilities are encapsulated in modules for reuse, e.g.:
let Api = {
fun1: function(){...},
fun2: function(){...}
};
module.exports = {fun1: fun1};
function fetchGet(url, callback) {
wx.request({url, success: res => callback(null, res.data), fail: e => callback(e)});
}
function fetchPost(url, data, callback) {
wx.request({url, method:'POST', data, success: res => callback(null, res.data), fail: e => callback(e)});
}
function downFile(url, cb) { wx.downloadFile({url, success: r => cb(r.tempFilePath)}); }3.3 Clever Use of WXML Templates
WXML supports import for sub‑templates, enabling component‑based development. Example:
<import src="/comps/header.wxml" />
<template is="header" data="{{...header}}"></template>4 Data Statistics
4.1 Stay‑time Statistics
Two approaches: using the official analysis API or manually recording timestamps on onLoad and onHide.
4.2 Share Count Statistics
Track share events via onShareAppMessage() and differentiate between menu shares and custom button shares.
4.3 Channel Source Statistics
Parse the scene parameter on onLoad to identify entry channels such as QR code scans or article links.
4.4 Mini‑Program Backend Analytics
The built‑in analytics dashboard provides visual insights (image omitted).
5 Miscellaneous
5.1 Debugging Deep Dive
WeChat mini‑programs run on JavaScriptCore, X5, or NWJS kernels depending on platform. Understanding kernel differences helps troubleshoot issues.
5.2 WebView
WebView currently lacks bidirectional communication beyond URL parameters, making it suitable for static or frequently updated promotional pages.
5.3 Development Beyond Coding
Collecting references, selecting frameworks, outlining project structure, and identifying potential challenges early improves risk control and schedule estimation.
If you find this content useful, please share it with your friends.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
