您现在的位置: 微信小程序 > 微信小程序开发 > 教程 >

《腾讯大家》小程序开发总结

来源:微信小程序 编辑:Yiyongtong.com 发布时间:2018-05-16 15:51热度:

腾讯大家产品背景

《腾讯大家》是公司推出的中文互联网专栏写作服务产品。由于寻找有效信息的成本是非常大的,一些真正具有传播价值的内容,却往往淹没于信息洪流之中。如何将最有价值的信息以最快的速度呈现给用户,正是《大家》产品设计的初衷。《大家》更关注互联网用户更深入、更持久的思考与表达。我们希望呈现给用户的,是经得起时间考验的文章,是时代最前沿的思想。它的表现,可能是一个专栏、一部电子书、一个属于个人的频道,甚至是一款小程序

所谓“大家”,意在集华语写作之大家手笔,为中文互联网用户提供最具魅力的经典文字,打造最有力量的互联网言论阵地、最有价值的网络阅读品牌。

腾讯大家小程序使用场景

腾讯大家小程序根植于微信小程序功能,与公众号精密结合运营,通过传播引发网友关注,扩大腾讯大家内容的出口。

腾讯大家是腾讯内容出品部的优质栏目,内容以质量取胜,每日产出数篇精品内容,因此,腾讯大家更加注重每一篇文章的传播效率以及传播速度。 
在小程序之前,腾讯大家内容主要通过单篇推荐的方式在腾讯新闻客户端、微信公众号等途径传播,而PC端的聚合页面(栏目、作者文章列表等)难以适应目前移动传播的形式,因此访问数据量较少,历史内容价值缺失。

基于聚合页面的传播需求,腾讯大家小程序解决了在移动端聚合及快速查找历史内容的需求。绑定小程序以后,在推送单篇文章时,可以配合推送作者文章列表、相关文章列表等定制页面。

用户可以通过微信文章入口、小程序收藏等功能,可以随时随地查看腾讯大家的最新内容及历史内容。

解决的实际问题

1.解决了基于移动端的内容聚合、历史内容查询,便于用户浏览;

2.增加了作者(栏目)关注定制(收藏感兴趣的作者/栏目),使得用户能持续接收高质量作者的内容,也便于产品侧对于用户关注的把握更精准;

3.解决了内容聚合页面与微信文章的互通,将特定专题的文章聚合页通过微信分享后,可以让用户快速地浏览到感兴趣专题的内容,也能够让历史文章得到再次浏览和传播;

4.增加了功能性H5的展示(比如在首页最上方的banner 大家之选内的开放编辑部玩法,号召用户参与内容制作),可以通过H5发起多样的用户活动,让产品与用户的交互更丰富;

5.将人工推荐变为用户主动查阅。腾讯大家的每天精品内容,通过web发出后,需要去各个媒体平台进行推荐(例如腾讯新闻客户端、QQ minisite、QQ.com首页,微信公众号),而且昨天的文章被推荐之后,今天会被新文章取代,网友们只能看到今天的新文章,而通过小程序的交互,用户点击最新的文章之后也查阅到历史文章,使得沉淀的内容得到再次曝光。

腾讯大家小程序呈现内容包含:首页首页聚合(tab)、作者列表(tab)、专栏聚合(tab)、个人中心(tab)、内容底层、作者底层、栏目底层、活动底层(战队底层,专题底层)。

一、功能分析

1.1 多端数据共享

腾讯大家小程序要与大家官网( http://dajia.qq.com)呈现给用户内容保持一致,新的开发在不影响原有内容原创平台(http://ninja.webdev.com)的基础上,增加小程序用户中心,开发用户对作者(栏目)的收藏、对文章评分、对文章历史浏览记录等功能。

感谢Ninja团队在开发过程中的支持,感谢saturnzhao(赵冬明)、杰哥jillywang(王杰)对接口数据及标准文档的指导。

1.2 首页精选详情内容展示

三处小技巧:

1.为了保证精选列表和web站点数据一致,将列表json数据缓存,这样做的好处1是避免了多次重复请求,二是缓存了的数据可以根据实际运营要求做数据插入,满足日常运营。

2.善用image组件,组件mode 有 13 种模式,其中 4 种是缩放模式,9 种是裁剪模式。利用组件特性,可以让原有内容配图完美的展现。

3.列表下拉加载过程利用数组特性concat进行数组拼接(利用小程序数据驱动特征)。


<image class="choice-image" mode="aspectFill" src="{{item.n_image}}"></image>

mode=”aspectFill”模式纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。


Api.fetchGet(dajaMorEchoiceUrl, (err, res) => {
    ...
    vm.$set({ firstData: vm.$data().firstData.concat(res.data) })
    ...
});

父组件注册onReachBottom方法使用下拉加载后,将每次请求的返回数据使用concat与原数据拼接。

1.3 内容底层展示

小程序的核心是一个响应的数据绑定系统,所以我们要展示一篇资讯详情,需要有一份数据,通过这份数据来判断这篇资讯是要渲染段落、表格、列表、图片、还是视频。

腾讯大家的内容原创发布系统对底层的文章属性有良好的标准json数据输出能力。举例一篇正文部分输出:


{tag: "text", value: "对于不认识Ayawawa的人,很难用一两句话介绍她的理论。"}, {tag: "text", value: "简单来说,她是个情感网红,真名叫杨冰阳。"},
{tag: "text", value: "对于不认识Ayawawa的人,很难用一两句话介绍她的理论。"}
{tag: "text", value: "前些天她接受姜思达采访的视频播出后, 激进派女权主义者们对她开始了新一轮的批评:说她跪舔“男权”、固化性别不平等、“物化”婚恋关系、“直男癌”。"}
{tag: "text", value: "但我觉得Ayawawa不应该被骂得那么惨,而且重点是:她被骂的角度也不够准。"}
{tag: "image", title: "", src: "//img1.gtimg.com/cul/pics/hv1/92/70/2270/147624692.jpg"}
{tag: "title", level: "H2", value: "婚姻对不同阶层的女性,根本不是一回事"}

对应展示模板:


<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 class="{{item.tag}}" mode="widthFix" src="{{item.src}}"></image>
        <view class="imgalt">{{item.title}}</view>
    </block>
  </block>
</block>
<block wx:else>
  <view class="p"></view>
</block>

另外,常规模式下资讯内容从技术角度看是带有html标签的富文本内容。在小程序中是不能将这些带有html标签的富文本内容直接展示的。

1.4 作者展示

腾讯大家作者有近千名,而作者的新增并不频繁,在数据端做以下处理:

1.定时抓取已有作者全部数据,然后进行缓存,输出带分页参数接口。 
2.在拉取数据时传入用户id,检测用户与作者对应关系(如收藏,打分,分别对应的栏目等。

在作者列表展示上做如下处理:

1.首次进入作者页加载数名作者数据。 
2.上拉加载做分页传参处理。 
3.重新回到作者页面会刷新用户收藏数据(包含已经收藏及列表中的标记)。

响应到的数据格式:

作者模板结构:


 <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>
        <block wx:for="{{authors}}" wx:key="item">
            <view class="num_index">
                {{index}}
            </view>
            <view class="box_author">
                <block wx:for="{{item}}" wx:key="author">
                    <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>
        </block>
    </view>

下拉加载时数据数组处理方法


extendKey:function(array) {
    let arr = [];
    for(let p in array){
        arr.push(p);
    };
        return arr;
},
getAuthorDataMore: function() {
    ...
    wx.showNavigationBarLoading();
    Api.fetchPost(Api.getAllAuthor, {userid: user.openid, perpage: 20, page: (vm.$data().page += 1) }, (err, res) => {        
         if (res.ret == 200) {
            let list = res.data;
            let alist = vm.$data().
            let keyArr = that.extendKey(list);authors;
            let akeyArr = that.extendKey(alist);
            keyArr.map(item => {
                akeyArr.map(eitem => {                    

                    if (item == eitem) {
                        alist[eitem] = alist[eitem].concat(list[item]);
                        keyArr.shift();
                    } else {}
                });
                keyArr.map(_item => {
                    alist[_item] = list[_item]
                })
            });
            vm.$set({ authors: alist });
            wx.hideNavigationBarLoading();
        } else {}
    });
    ...
}

1.5 用户登录态获取

引用小程序官方文档的登录流程图

流程简而言之:

1.在小程序上通过wx.login()获取code。

2.将code传到自己的服务器,然后将小程序的secret和appid与微信服务器交换openid和session_key。

3.将session_key加上随机数生成sessionId,然后openid和session_key存在session里。

4.小程序将sessionId存起来,每次访问都带上这个sessionId。

5.小程序请求登陆区内接口,通过wx.checksession检查登陆态,如果失效重新走上登录流程,否则待上3rd_session到后台进行登陆验证。

为什么有用户登录态:

1.小程序有以用户为个人中心的功能应用,比如作者栏目收藏、关注等。 
2.用户敏感数据,只对用户可见。

获取用户数据示例:


getUsrAppId: function() {
    let user = wx.getStorageSync('user') || {};
    let userInfo = wx.getStorageSync('userInfo') || {};
    wx.getUserInfo({
        success: function(res) {
            let objz = {};
            objz.avatarUrl = res.userInfo.avatarUrl;
            objz.nickName = res.userInfo.nickName;
            wx.setStorageSync('userInfo', objz); 
            //userInfo  
        },
        fail: function() {
            console.log('用户拒绝');
            wx.setStorageSync('allow', { 'user': 'notallow' });
        }
    });
    wx.login({
        success: function(res) {
            if (res.code) {
                Api.fetchPost(Api.getOpenid, { code: res.code }, (err, res) => {
                    let _userObj = JSON.parse(res.data);
                    let obj = {};
                    obj.openid = _userObj.openid;
                    obj.expires_in = _userObj.expires_in;
                    wx.setStorageSync('user', obj);
                });
            } else {
                console.log('获取用户登录态失败!' + res.errMsg)
            }
        }
    });    
    if ((!user.openid || (user.expires_in || Date.now()) < (Date.now() + 600)) && (!userInfo.nickName)) {
        ...
    };
}

1.6 用户信息获取

因为文章的评分、个人中心的头像和昵称都需要用到用户信息。所以大家小程序在第一次打开后会自动弹出授权窗口。当用户授权后,信息缓存在Storage里,缓存的过期时间由具体的功能场景来控制。

1.7 收藏功能(含作者及栏目收藏)

首先将含有收藏功能的地方标注出来:

1处收藏按钮为作者收藏与栏目收藏,当用户按下按钮后会变为已收藏、再按下去则为取消收藏。在开发过程中,主要是对按钮状态的判断

模板结构:


<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>

处理逻辑:


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 {...}
        });
    } 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 })
            } else {...}
        });
    };
}

2处和 3处 的收藏逻辑基本相同,只是3会判断是否已经收藏此作者

模板结构: