慕爾名,我的“知乎”小程式(1)

mosa發表於2018-06-11

慕爾名,我的“知乎”小程式(1)

前言

不知道有多少人跟我一樣愛上“知乎”的呢?一直喜歡“知乎”所帶來的使用者體驗效果和一些新穎的資訊傳送。所以在最近的小程式學習中,開始的第一個專案實訓,就是“知乎”了。
開始做了才知道,“知乎”小程式的工程量之大,我這個前端新手不知應該需要多少個工作日,才能把“知乎”完整做成小程式。不過程式設計師的路本就是邊學習邊成長的,學無止境。小人不才,最近做了一些,忍不住先開始一波經驗和問題的分享了。

“知乎”小程式開跑

成果分享

一、首頁

1. tab欄切換

首頁由三個tab項組成,”關注”,“推薦”,“熱榜”,這幾個頁面的切換功能分別設定了“點選切換”和“滑屏切換”:

慕爾名,我的“知乎”小程式(1)
剛開始做點選事件的時候一股腦門的簡單勁,簡單粗暴,直接給三個tab設定data-index,然後判斷每次事件觸發取到的index值,然後轉到相應內容,”if…elseif…else…”,如果有很多個tab怎麼辦?if…到何年何月啊~喔得天!就覺得自己太low了,一直覺得能以最短的程式碼寫最好的功能就很讓人敬佩,好吧我還是嘗試了一下的:

home.js部分程式碼:
switchTab (e) {//tab點選事件
    let index = parseInt(e.target.dataset.index)
    this.setData({
      currentIndex: index
    })
  },
  handleChangeTab (e) {//tab滑屏事件
    const p = 33.3
    this.setData({
      lineStyle: `left: ${p * e.detail.current}%`
    })
  }
複製程式碼

直接將index值parseInt,賦值到每次轉到的當前tab的index值:currentIndex,數值的比較相對於字串的比較就輕鬆許多了不是嗎? 而後滑屏事件裡自動會引用點選事件中的結果,這個時候不得不又為parseInt點個贊,我只需要將那條“選中線”在每次觸發該事件時自動乘當前的currentIndex,得到它在不同當前tab下的位置值。
沒錯就沒用if…else!用煩了,還好還有點餘地讓我換口味,沒辦法的時候該low還不是得low,這個時候就得安慰自己是‘走走基層’體驗生活了hhhhh

2. 圖片佔位問題

在進行首頁的三個tab頁時,都存在一個問題,頁面中有些分享的文中,插入了圖片,而有些則沒有插入。但在我們的程式碼中,一般初始都是加了這個圖片元素的,至於資料中加不加入圖片,是發文使用者自己的事兒了吧。
但是呢,小程式起步,我就遇到這樣一個問題,不管有沒有加入圖片,頁面上仍然會給這個圖片元素留了空白佔位,導致其它內容全部被擠下去了。
所以我就在想:怎麼能讓這裡有圖片就顯示,沒圖片就不留佔位呢?

解決:
wx:if => 條件渲染
加入wx:if的元素,頁面會自動判斷是否該渲染該元素所包含的程式碼塊,wx:if:”{{繫結的資料}}”,繫結的資料有值時渲染,無值時不渲染。

3. 熱榜:簡便進行多列布局

看到熱榜頁面的樣式就感覺它就像個多行三列的網格,所以撇開了繁瑣的頁面設計與標籤類名間的位置樣式值設定,果斷用了柵格佈局:

home.wxml部分程式碼:
<view class="container-item" wx:for="{{hotList}}" wx:key="{{item.id}}">
          <view class="row">
            <view class="col">
              <view class="col-1">{{item.rank}}</view>
              <view class="col-7">
                <view class="title">{{item.title}}</view>
                <view class="status">{{item.status}}</view>
              </view>
              <view class="col-4">
               <image wx:if="{{item.titleImage}}" class="title-image" src="{{item.titleImage}}"></image>
              </view>
            </view>
         </view>
       </view>
複製程式碼

當然,小程式暫時還沒有col-number的固定值,所以直接設定類名並不會發生變化,沒辦法直接像使用api一樣直接使用,所以,就在app.wxss全域性樣式中定義了每個col塊的寬度:

.col>.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12{
  overflow: hidden;
}
.col-1{
  width: 8.33333333333333%;
}
.col-2{
  width: 16.66666666666666%;
}
.col-3{
  width: 25%;
}
.col-4{
  width: 33.33333333333333%;
}
.col-5{
  width: 41.66666666666666%;
}
.col-6{
  width: 50%;
}
.col-7{
  width: 58.33333333333333%;
}
.col-8{
  width: 66.66666666666666%;
}
.col-9{
  width: 75%;
}
.col-10{
  width: 83.33333333333333%;
}
.col-11{
  width: 91.66666666666666%;
}
.col-12{
  width: 100%;
}
複製程式碼

這樣後面這個程式中若還想再建這種佈局就能直接簡便地引用了。
當然,這個頁面中,在設定這個之前,還少不了flex佈局的實現,“彈性佈局”真是我這段時間學習css中最喜歡的東西了。相關知識大家可以看看阮一峰老師的文章

二、“想法”頁

1.header欄fix及滾動fix功能

該介面完成了一個☝️個人覺得平時刷知乎的時候比較妙的使用者體驗,:頭部nav在下滑到一定距離時仍然fix其主要“功能轉到點”在頭部,如果使用者在下滑到比較長距離的時候想要去到頭部的導航,不用重新上滑很長的距離到頂部去實現這個動作。
先看一波效果:

慕爾名,我的“知乎”小程式(1)

前端框架semantic ui
在這方面的效果就做得好棒,看人家主頁semantic ui
所以在做這個專案的時候特意也挑了這個效果去實現。
作為一個前端初學者,還是得依靠我們的“主角”scroll-view,

配置項 作用
scroll-top 設定豎向滾動條位置
scroll-y 允許縱向滾動
bindscroll 滾動時觸發的回撥函式

我在這裡用了scroll事件進行兩個小頁面(也就是頁面滾動前與滾動到 一定距離顯示市顯示的不同top nav頂部導航條)的切換,配合小程式的”wx:if else “框架進行條件渲染,當頁面滾動到設定的目標距離時,切換新生成的頂部導航。

thought.wxml部分程式碼:
切換前的nav:
<view wx:if="{{!display}}" class="nav">
      <view class="title">想法</view>
      <view class="message">
        <image class="messageImg" src="{{messageImg}}" wx:if="{{messageImg}}"></image>
        <text class="messageTitle" >訊息</text>
      </view>
      <view class="myThought">
        <image class="thoughtImg" src="{{thoughtImg}}" wx:if="{{thoughtImg}}"></image>
        <text class="thoughtTitle">我的想法</text>
      </view>
    </view>
切換後的nav:
<view wx:else class="nav1">
        <view class="title">想法</view>
        <view class="message">
          <image class="messageImg" src="{{messageImg}}" wx:if="{{messageImg}}"></image>
        </view>
        <view class="myThought">
          <image class="thoughtImg" src="{{thoughtImg}}" wx:if="{{thoughtImg}}"></image>
        </view>
    </view>
複製程式碼

分別給兩個nav設定不同樣式。

scroll事件觸發nav轉換:
Scroll事件接收兩個引數:
1.scroll-top;
2.display屬性

scroll: function(e){
    if (e.detail.scrollTop > 200) {
      this.setData({
        display: true
      })
    } else {
      this.setData({
        display: false
      })
    }
  }
複製程式碼

在這個地方就有幾個開始動手時就踩到的坑:
1.使用豎向滾動條時必須為元件設定一個固定高度,否則bindscroll事件無法觸發;
2.scroll-top在設定豎向滾動條位置時,如果設定的值沒有變化,元件不會渲染!

這種功能呢,用我們老師的話來說… 沒錯,就是很有“質感”!

2 .輪播內容

h5寫過輪播的都只有寫過的知道,相對還是比較麻煩的,並沒有一個輪播圖元件,有個ViewPage也需要自己定製,小程式中swiper元件封裝的相對還是方便的,使用方式也相對容易些。

主要屬性:

配置項 作用
indicator-dots 是否顯示皮膚指示點
autoplay 是否自動切換
current 當前所在頁面的index
interval 自動切換時間間隔
duration 滑動動畫時長
bindchange current改變時觸發change事件

屬性只需要設定就行了 也可以抽到js檔案的data中進行資料繫結,監聽使用bindchange,在js中做業務處理。
wxml程式碼塊:

<swiper class="swiper" autoplay="true" interval="2000" duration="1000" circular="true" >
        <block wx:for="{{discussion}}" wx:for-index="index">  
              <swiper-item class="discuss">
                <image wx:if="{{item.url}}" src="{{item.url}}" class="swiper-url"/>
                <view class="discuss-desc">
                  <view class="discussing">{{item.discussing}}</view>
                  <view class="desc-title">{{item.title}}</view>
                  <view class="desc-question"> {{item.descQuestion}}
                    <!-- <swiper class="desc-question" autoplay="true" interval="3000" duration="1000" vertical="true">
                      <block wx:for="{{item.descQuestion}}" wx:if="{{item.descQuestion}}">
                        <swiper-item class="question">
                          <view class="question-item">{{item.questionItem}}</view>
                        </swiper-item>
                      </block>
                    </swiper> -->
                  </view> 
                </view>
              </swiper-item>
          </block>
        </swiper>
複製程式碼

慕爾名,我的“知乎”小程式(1)
其實這個頁面裡的輪播中,還巢狀了其中一個項的縱向輪播,剛開始我直接在swiper中另巢狀了一個swiper,然後設定verticle=“true”,但只能將外層swiper的輪播切換時間增加,才能給裡面的縱向輪播預留時間顯示出來,所以體驗效果並不好,有知道怎麼做的可以傳授下一下(๑`・ᴗ・´๑)ヾ(●´∇`●)ノ哇~。

三、搜尋頁

1.“回車確定”搜尋內容

每次輸入搜尋內容後,點選回車確定,轉到詳情頁,搜尋歷史欄也隨之新增一條新記錄,同時將記錄儲存到本地快取,頁面重新整理之後仍然存在歷史記錄。

慕爾名,我的“知乎”小程式(1)
這裡用到了input元件的bindconfirm事件,也就是每次輸入搜尋內容回車確定時觸發的事件,這裡我用wx:for迴圈設定了一個historyRecord陣列,該陣列接收兩個引數,每次輸入內容的id值和recordItem內容,key值id區分不同一行不同內容的歷史記錄,recordItem是輸出在搜尋歷史欄的記錄value:

search.wxml

<view class="search-history">
            <text class="zhhs">搜尋歷史</text>
            <view class="search-history-item" wx:for="{{historyRecord}}" wx:key="item.id">
              <image class="search-history-icon" src="/assets/icons/shizhong.png"></image>
              <text>{{item.recordItem}}</text>
            </view>
          </view>

複製程式碼

在進行bindconfirm事件處理上,剛開始就生生踩雷了,剛開始學小程式都得了解的事,我就犯了錯,我們都應該知道,要改變data裡的資料,只能用setData,剛開始我直接在外部使用了this.data.historyRecord.push({id:’’,recordItem:e.detail.value}); 然後就報錯顯示沒有push這個方法,然後我當然就去找度娘問清楚啦,被提示data裡的資料只能用setData改變,然後一敲腦袋,最後就是下面的樣子了:

search.is

bindconfirm: function(e){
    console.log(e);
    var historyRecord = this.data.historyRecord;
    var recordItem = e.detail.value;
    historyRecord.unshift({
      id:'0',
      recordItem: recordItem
    });
    this.setData({
      historyRecord:historyRecord
    });
  }

複製程式碼

要使用setData之外的方法,就要借用變數來賦值啦,將陣列賦到外部定義的變數,就可以使用setdata之外的方法了。
而該變數本身就是陣列,陣列的方法之多,夠用的了!起先我用的是push()方法,後來又去摸了下知乎搜尋頁,發現歷史記錄的最新記錄都是直接插入首行,好的,陣列方法unshift()滿足你!

2.“點選搜尋結果”搜尋內容

每次輸入搜尋內容後,出現搜尋結果條頁,點選結果條,轉到詳情頁,搜尋歷史欄也隨之新增一條新記錄。

慕爾名,我的“知乎”小程式(1)
這裡用到了模糊查詢,關鍵字查詢,利用了陣列的filter過濾方法,遍歷資料庫(我這裡暫時用了假資料,後期掌握後端方法之後補上),選出包含輸入詞的相關結果,羅列在結果條目上供選擇。

Search.wxml部分程式碼
<view wx:else class="search-like">
      <view class="search-like-item" data-param="{{item.text}}" wx:for="{{searchLikeList}}" wx:key="{{index}}" bindtap="turnTo">
          <image class="search-like-icon" src="/assets/icons/sousuo.png"></image>
          <text>{{item.text}}</text>
          <image data-index="{{index}}" class="turn" src="/assets/icons/turn.png"></image>
      </view>
    </view>

複製程式碼
Search.js部分程式碼
changeSearch (e) {
    let value = e.detail.value
    if (value === '') {
      this.setData({
        haveSerachLike: false
      })
      return
    }
    let arr = this.data.searchLikeAllList.filter(item => item.text.indexOf(value) > -1)
    console.log(arr)
    this.setData({
      haveSerachLike: true,
      searchLikeList: arr,
    })
  },
turnTo: function(e){
    this.saveHistory({
      id: 0,
      recordItem: e.target.dataset.param
    })
    wx.navigateTo({
      url: '../searchDetail/searchDetail'
    })
  },

複製程式碼

選擇結果條目上的某條內容後,點選進入詳情頁,同樣,在歷史記錄中新增一條記錄內容,記錄儲存在本地快取中。

3.清除搜尋記錄

效果圖:

慕爾名,我的“知乎”小程式(1)
既然有了儲存記錄功能,當然也少不了清楚搜尋記錄的功能。
感覺自己抱緊了陣列方法的大腿,這裡再次用到過濾filter,先給要賦予清除事件的元素通過設定data - index 的方法來標識要傳遞的值,然後在deleteRecord事件中將歷史記錄中被該事件選中的index值進行過濾,即刪除所傳遞的index值,然後返回過濾後的陣列,即沒被過濾掉的記錄。

Search.wxml部分程式碼:
<view class="search-history">
          <text class="zhhs">搜尋歷史</text>
          <view class="search-history-item" wx:for="{{historyRecord}}" wx:key="{{index}}">
            <image class="search-history-icon" src="/assets/icons/shizhong.png"></image>
            <text>{{item.recordItem}}</text>
            <image data-index="{{index}}" class="delete" src="/assets/icons/delete.png" bindtap="deleteRecord"></image>
          </view>

複製程式碼
search.js部分程式碼:
deleteRecord: function(e){
    console.log(e);
    let filterArr = this.data.historyRecord.filter((item, index) => {
      return index !== e.target.dataset.index
    })

    this.setData({
      historyRecord: filterArr
    })

    wx.setStorage({
      key: 'historyRecord',
      data: filterArr
    })
  },

複製程式碼

4. 熱搜詞

效果圖:

慕爾名,我的“知乎”小程式(1)
搜尋頁的熱搜詞模組list出來的是最近熱門搜尋,排在第一個的就是最熱搜尋,依次按照熱門程度排序。
(1)佈局:
佈局方式採用彈性佈局,熱搜詞橫向排序,並且在設定固定百分比寬度的情況下用了彈性佈局下flex-wrap:wrap;進行超出則換行操作。
(2)功能實現:
在繫結的熱搜詞資料中給它加入hotstatus引數,表示熱度情況,型別為number:

<view class="search-item">
                <view class="hot-search-item" wx:for="{{hots}}" wx:key="{{item.id}}">
                  <view class="hot-item">
                    <view class="text">
                      <image class="hot-img" src="{{item.hotImg}}" wx:if="{{item.hotImg}}"></image>
                      <text>{{item.text}}</text>
                    </view>
                    <view class="hot-status" >{{hotStatus}}</view>
                  </view>
                </view>         
            </view>
複製程式碼

然後在頁面載入事件onload中新增排序方法,檢索熱詞陣列中每個hotstatus值,按從大到小排序排列在“知乎熱搜”塊中:

onLoad: function (options) {
    var hots = this.data.hots;
    var hots2 = hots.sort((x, y) => y.hotStatus - x.hotStatus);
    // reverse()方法會反轉陣列項的順序
    // hots.reverse();
    console.log(hots2);
    this.setData({
      hots: hots2
    })
複製程式碼

5 .程式碼優化

做了好幾個搜尋頁的功能,發現“儲存歷史記錄並加入本地快取”這個功能在好幾個地方都用到了,每個事件中都寫一遍,程式碼繁瑣,邏輯可讀性略差,所以我將該功能封裝成一個方法,每次需要用到的時候,直接帶著相應引數引用即可:

saveHistory (param) {
    let arr = this.data.historyRecord
    arr.unshift(param)
    wx.setStorage({
      key: 'historyRecord',
      data: arr
    })
    this.setData({
      historyRecord: arr
    })
  }
複製程式碼

隨即,上面第一條的bindconfirm事件函式則變為:

bindconfirm: function(e){
    console.log(e);
    var recordItem = e.detail.value;
        this.saveHistory({
      id: 0,
      recordItem
    })
turnTo事件函式:
turnTo: function(e){
    this.saveHistory({
      id: 0,
      recordItem: e.target.dataset.param
    })
    wx.navigateTo({
      url: '../searchDetail/searchDetail'
    })
  },

複製程式碼

程式碼是不是變得更加簡潔了?邏輯更加清晰了?這就是我們一直追求的“用最短的程式碼寫最棒的功能!”

四、附頁:下拉重新整理上拉載入更多

原生App開發中,下拉重新整理和上拉載入是使用得比較多的一個功能了。
小程式開發中,小程式只提供了下拉重新整理的介面。
Bug & Tip

  • 在滾動 scroll-view 時會阻止頁面回彈,所以在 scroll-view 中滾動,是無法觸發 onPullDownRefresh
  • 若要使用下拉重新整理,請使用頁面的滾動,而不是 scroll-view ,這樣也能通過點選頂部狀態列回到頁面頂部,在這裡其實也就說了在使用scroll-view時是不能使用onPullDownRefresh了。

我這裡直接用了scroll-view實現下拉重新整理上拉載入更多,scroll-view有三個event事件:

配置項 作用
bindscrolltoupper 滾動到頂部觸發的回撥函式
bindscrolltolower 滾動到底部觸發回撥函式
bindscroll 滾動時觸發的回撥函式

這裡js程式碼裡面其實就是處理邏輯:
上拉:我們需要在陣列container-list的後面拼接資料和處理請求的頁碼;
首先需要封裝一個獲取頁碼資料的方法

getPage:
getPage: function(){
    var that = this;
    var pageIndex = that.data.currentPage;
    wx.request({
      url: '',
      data: {
        page: pageIndex
      },
      success: function(res){
        if(pageIndex != 1){ // 載入更多
          console.log('載入更多');
          var tempArray = that.data.articles;
          tempArray = tempArray.concat(that.data.articles);
          that.setData({
            allPages: that.data.allPages,
            articles: tempArray,
            hideBottom: true
          })
        }
      }

複製程式碼

然後判斷當前頁是否是最後一頁:

if (that.data.currentPage == that.data.allPages){
      that.setData({
        loadMoreData: '已經到頂'
      })
      return;
    }

複製程式碼

這裡加了一個定時,是為了延長上拉下拉檢視的顯示時間:

setTimeout(function(){
      console.log('上拉載入更多');
      var currentPage = that.data.currentPage;
      currentPage = currentPage + 1;
      that.setData({
        currentPage: currentPage,
        hideBottom: false  
      })
      that.getPage();  
    },300);
複製程式碼

下拉:我們需要把當前頁碼設定成1,articles取當前網路請求的資料。網路請求getData函式上拉下拉的區分是通過當前頁碼值區分的。

refresh: function(event){
    var that =  this;
    page = 1;  
    that.setData({  
      articles: [{這裡加入傳入的重新整理的資料}]   
      scrollTop: 0,
      hidden:true  
    });  
    that.getPage();
    // GetList(this) 
  },
複製程式碼

來一波成品圖:

慕爾名,我的“知乎”小程式(1)
這個功能各網頁、app上都用得很廣,我做得還不太完善,後續慢慢修改完善。

結語:

因為時間比較短,知乎也是個大專案,頁面、功能還需要慢慢完善,釋出了的功能也有一些待改進的地方,後續慢慢把這個專案做下去,慢慢打磨技術。愛程式碼,愛知乎! 歡迎同樣志同道合的碼友們多多指教和交流。ヾ(❀╹◡╹)ノ~

順便附上我的專案地址:仿“知乎”微信小程式
icon“贊助地”:icon

相關文章