現學現賣微信小程式開發(二)

接灰的電子產品發表於2017-01-03

現學現賣微信小程式開發(一)
現學現賣微信小程式開發(三):引入Rx,為小程式插上翅膀

一個Todo應用的小程式版

好的,那麼下一步我們就先照貓畫虎,新建一個todos資料夾,然後一套四樣同名檔案準備齊全

現學現賣微信小程式開發(二)
新建todos目錄和相關檔案

先在app.json中報個到,在pages中加入 "pages/todos/todos"。接下來把首頁 index.js 中的導航改為 ../todos/todos

//事件處理函式
  bindViewTap: function() {
    wx.navigateTo({
      url: '../todos/todos'
    })
  },複製程式碼

然後呢,我們簡單的先寫一個介面,其實什麼都沒有,就是一個view。把 todos.wxml 改成下面的模樣:

<!--todos.wxml-->
<view class="container todo-list">

</view>複製程式碼

然後把導航欄標題設定一下,叫 Awesome Todos吧,把 todos.json 改成下面的樣子:

{
    "navigationBarTitleText": "Awesome Todos"
}複製程式碼

樣式呢,也非常簡單粗暴的來一個吧:

.todo-list {
  display: block;
  padding: 40rpx;
  width: 100vw;
  margin: 0 auto;
}複製程式碼

快速搭建一個Web API

好的,下面我們要寫關鍵的 todos.js 了。在寫之前,我們需要一個伺服器提供資料,這裡介紹一個可以非常快速便捷的搭建一個模擬Web API的利器:json-server。 使用 npm i -g json-server 安裝,然後隨便挑一個目錄建一個todos-data.json 檔案:

{
  "todos": [
    {
      "id": 1,
      "desc": "have breakfast",
      "completed": false
    },
    {
      "id": 2,
      "desc": "have lunch",
      "completed": false
    },
    {
      "id": 3,
      "desc": "take a break",
      "completed": false
    },
    {
      "id": 4,
      "desc": "having fun",
      "completed": false
    },
    {
      "id": 5,
      "desc": "新的伺服器版本不錯",
      "completed": true
    }
  ]
}複製程式碼

這個時候,你的Web API就差一步之遙了,現在在命令列視窗敲入 json-server ./todos-data.json就大功告成了。

現學現賣微信小程式開發(二)
json-server服務啟動了

你可以開啟瀏覽器輸入 http://localhost:3000/todos 看看是否返回的是我們的資料。這個Web API是完全RESTful的,也就是說

  • 查詢所有待辦事項:以GET方法訪問http://localhost:3000/todos
    • 查詢單個待辦事項:以GET方法訪問http://localhost:3000/todos/id,比如id是1,那麼訪問http://localhost:3000/todos/1
    • 更新某個待辦事項:以PUT方法訪問http://localhost:3000/todos/id
    • 刪除某個待辦事項:以DELETE方法訪問http://localhost:3000/todos/id
    • 增加一個待辦事項:以POST方法訪問http://localhost:3000/todos

回到正題,開始Todo的開發

我們一開始的 todos.js 是這個樣子的:

const URL = 'http://localhost:3000/todos'

let pageParams = {
  data: { todos: [], desc: '' }
}

pageParams.onLoad = function () {
  const that = this
  wx.request({
    url: URL,
    data: JSON.stringify({}),
    header: { 'content-type': 'application/json' },
    method: 'GET',
    success: res => {
      console.log(res.data)
      that.setData({
        todos: res.data
      })
    },
    fail: () => console.error('something is wrong'),
    complete: () => console.log('todos loaded')
  })
}

Page(pageParams)複製程式碼

這段程式碼非常簡單,建立一個pageParams物件並給本地資料初始化。然後定義onLoad生命週期函式,利用 wx.request 建立一個 HTTP GET 請求取得返回的資料,然後用setData去更新本地資料。注意一點 const that = this 是一個常用的避免 this 的context出現切換時出現問題的小技巧。再有就是我們無法直接寫入data,只能使用setData方法來進行更新。

那麼我們看看是否成功吧,點選左側的除錯,然後點首頁的頭像進入我們的todo頁面,當然現在介面上啥也沒有,但Console中還是有料滴。

現學現賣微信小程式開發(二)
Console中可以看到我們的Web API返回的結果

既然API調通了,我們就來讓結果顯示到頁面上吧,首先改造頁面如下。

<!--todos.wxml-->
<view class="container todo-list">
  <block wx:for="{{todos}}" wx:for-item="todo" wx:key="todo.id">
    <view class="todo-item">
      <text class="desc">{{todo.desc}}</text>
    </view>
  </block>
</view>複製程式碼

這段程式碼中,view 是個檢視容器,感覺可以把它想象成HTML中的div。微信小程式中除了 view 之外,目前還提供了 scroll-viewswiper 兩種容器,顧名思義 scroll-view 是用於可滾動的場景,而 swiper 是用於可以手指滑動切換內容的場景。

在元件上使用 wx:for 繫結一個陣列,即可使用陣列中各項的資料重複渲染該元件。wx:for-item 意思是設定陣列當前元素的變數名。wx:key 設定列表中專案的唯一的識別符號。注意提供wx:key可以提升重新渲染時的效能,所以儘量提供。

block 是一個挺怪的設計,它不是一個視覺化的元素,感覺純粹為提供資料繫結而準備,在資料繫結時,以 block 來組織比較複雜的元件組合。所以block這一段的意思就是對於陣列todos中的每一個todo,重複渲染下面這段

<view class="todo-item">
  <text class="desc">{{todo.desc}}</text>
</view>複製程式碼

當然我們也需要擴充一下css,哦,不對,是wxss。

.todo-list {
  display: block;
  padding: 40rpx;
  width: 100vw;
  margin: 0 auto;
}
.todo-item {
  display: flex;
  flex-direction: row;
  flex-basis: 1;
  justify-content: space-around;
  align-items: stretch;
  width: 80 vw;
  padding: 20rpx;
  border-bottom: 1rpx solid #ededed;
}
.desc {
  vertical-align: middle;
  flex-grow: 1;
}複製程式碼

現在看一下效果,列表成功顯示了。

現學現賣微信小程式開發(二)
todos列表

事件的處理

現在我們給Todos新增兩個功能吧:Toggle(切換完成狀態)和Remove(刪除該事項)。這樣的話,需要給todo的描述之前加一個完成狀態的核取方塊以及一個在todo的描述之後的刪除按鈕。我們沒有使用微信提供的 checkbox ,而是用 icon 元件和 wx:if 來做處理,目的也是多展示一些特性。

<!--todos.wxml-->
<view class="container todo-list">
  <block wx:for="{{todos}}" wx:for-item="todo" wx:key="todo.id">
    <view class="todo-item">
      <icon bindtap="toggleTodo" data-todo="{{todo}}" class="icon" type="success" wx:if="{{todo.completed}}"></icon>
      <icon bindtap="toggleTodo" data-todo="{{todo}}" class="icon" type="success_circle" wx:if="{{!todo.completed}}"></icon>
      <text bindtap="toggleTodo" data-todo="{{todo}}" class="desc">{{todo.desc}}</text>
      <icon bindtap="removeTodo" data-todo="{{todo}}" class="remove" type="clear"></icon>
    </view>
  </block>
</view>複製程式碼

上面的模版中,我們看到多了一些新面孔:

  • wx:if:這個比較好理解,就是條件渲染,在todo已完成的狀態下顯示一個icon,未完成狀態下顯示另一個icon。
  • bindtap:這個是什麼呢?它是微信小程式提供的繫結事件的機制,其繫結表示式為:bind+事件="事件處理函式"。我們上面的例子中的 bindtap="toggleTodo" 就是對於tap(觸碰後馬上離開)事件處理是一個在Page中定義的叫 toggleTodo 的函式。
  • data-todo:很多時候我們需要在事件傳遞時攜帶一些資料過去,比如Toggle這個功能,我們需要在事件處理函式中知道是哪個todo要切換完成狀態。data-todo就是定義要傳輸的資料用的,data表明是要在 事件中傳輸的資料,而後面的todo表明這個資料在dataset中的key,通過這個key我們可以在Page中使用 event.target.dataset.todo得到這個資料。這也意味著我們可以通過多個key傳遞多個資料。

值的注意的一點是除了 bindXXX 這種繫結事件的形式外,還有一種形式是 catchXXX,它們的區別是 bind 事件繫結不會阻止冒泡事件向上冒泡,catch 事件繫結可以阻止冒泡事件向上冒泡。什麼是冒泡呢?事件會繼續向上(父元件或父節點)傳遞就是冒泡,反之就是非冒泡。

在微信小程式中,冒泡的事件如下表列出的,其他如沒有特殊的宣告都是非冒泡事件

事件 觸發條件
touchstart 手指觸控動作開始
touchmove 手指觸控後移動
touchcancel 手指觸控動作被打斷,如來電提醒,彈窗
touchend 手指觸控動作結束
tap 手指觸控後馬上離開
longtap 手指觸控後,超過350ms再離開

對於這些冒泡事件來說,如果我們要阻止其冒泡的行為,可以使用 catchXXX 來繫結事件。

const URL = 'http://localhost:3000/todos'

let pageParams = {
  data: { todos: [], desc: '' }
}

pageParams.onLoad = function () {
  const that = this
  wx.request({
    url: URL,
    data: JSON.stringify({}),
    header: { 'content-type': 'application/json' },
    method: 'GET',
    success: res => {
      console.log(res.data)
      that.setData({
        todos: res.data
      })
    },
    fail: () => console.error('something is wrong'),
    complete: () => console.log('get req completed')
  })
}

pageParams.toggleTodo = function (event) {
  const that = this
  const selectedTodo = event.target.dataset.todo
  const url = `${URL}/${selectedTodo.id}`
  const updatedTodo = Object.assign({}, selectedTodo, {completed: !selectedTodo.completed})

  wx.request({
    url: url,
    data: JSON.stringify(updatedTodo),
    header: { 'content-type': 'application/json' },
    method: 'PUT',
    success: res => {
      console.log(res.data)
      that.setData({
        todos: that.data.todos.map(todo => {
          if(todo.id === updatedTodo.id){
            return updatedTodo
          }
          return todo
        })
      })
    },
    fail: () => console.error('something is wrong'),
    complete: () => console.log('toggle req completed')
  })
}

pageParams.removeTodo = function (event) {
  const that = this
  const selectedTodo = event.target.dataset.todo
  const url = `${URL}/${selectedTodo.id}`;

  wx.request({
    url: url,
    data: JSON.stringify(selectedTodo),
    header: { 'content-type': 'application/json' },
    method: 'DELETE',
    success: res => {
      console.log(res.data)
      that.setData({
        todos: that.data.todos.filter(todo => todo.id !== selectedTodo.id)
      })
    },
    fail: () => console.error('something is wrong'),
    complete: () => console.log('delete req completed')
  })
}

Page(pageParams)複製程式碼

接下來的事情就變的很簡單,我們在 todos.js 中增加兩個處理函式用於處理toggle和remove,事件處理函式有一個引數就是event。處理邏輯還是先提交HTTP請求處理伺服器端資料,處理成功後再處理本地記憶體資料。

當然 wxss 再更新一下:

.todo-list {
  display: block;
  padding: 40rpx;
  width: 100vw;
  margin: 0 auto;
}
.todo-item {
  display: flex;
  flex-direction: row;
  flex-basis: 1;
  justify-content: space-around;
  align-items: stretch;
  width: 80 vw;
  padding: 20rpx;
    border-bottom: 1rpx solid #ededed;
}
.icon {
  vertical-align: middle;
}
.remove {
  float: right;
  align-self: flex-end;
}
.desc {
  vertical-align: middle;
  flex-grow: 1;
}複製程式碼

現在的效果是這樣滴

現學現賣微信小程式開發(二)
可以工作的todo,當然還沒有新增todo的功能

新增Todo、過濾器、優化程式碼結構、引入Rx等話題我們後面繼續,這次就先到這裡。

相關文章