你與小程序開發的距離有多遠?

你與小程序開發的距離有多遠

2017年1月9日凌晨。小程序正式發佈。css

對焦10年前iPhone的發佈時間,產品之神張小龍顯然是想讓這一天具備十分重要的歷史意義。小程序發佈以後,它終於揭開了最終面目,咱們不得不認可,這一天,一定是一個新時代的開端。html

做爲一個第一批小程序的開發者,從小程序內測之初經過開發工具破解版開始嘗試小程序,見證了小程序官方文檔的每一次更新,踩太小程序的大多數坑,也見證了好幾個小程序社區的逐步發展,到最終把本身的小程序過審上線,這個過程對我而言,收穫良多。前端

爲了對這段時間的收穫作一個總結,也但願能將本身的經驗分享給你們,因此有了這篇文章,讓想要開發小程序的小夥伴們對比一下本身掌握的知識,距離開發一個成熟的小程序,到底還差多少。也讓想要學習小程序的朋友,有一個大的方向。vue

1、小程序開發工具爲新手朋友提供了什麼樣的便利?

2016這一全年來,前端開發發生了很重要的變化,好比ES6的全面普及,react的持續火熱,vue的爆發式增加,全部人都知道學習這些東西很重要,可是爲何不少不少的新手朋友們每每只能瞭解皮毛而難以真正掌握呢?react

那就是由於構建工具的門檻過高。 es6

不少人在學習別人的代碼的時候,會很驚訝的發現,爲何別人的代碼,和瀏覽器能識別的樣子,差距那麼大?其實全都是構建工具的功勞。不管是ES6,仍是react,組件化,模塊化,這些東西通通都會經過構建工具,最終變成瀏覽器能識別的樣子,也就是咱們最初學習時所知道的那個簡單樣子,一個頁面,包括css,html,js,image等web

對於大多數團隊來講,只要公司中有大神能搭建一個成熟的開發環境,對於這些知識的學習實際上是容易不少的,這也是爲何,不少人在進入某個團隊的時候成長速度會變得更快的緣由所在。可是對於一個剛入門的新手來講,想要搭建一個能用的構建工具是很是困難的。json

而對於這個門檻,小程序的開發工具,則完美的幫你們解決了這個問題。咱們不須要額外搭建任何開發環境,只須要下載《微信web開發工具》,就能夠看着小程序的文檔開始寫demo了。redux

因此若是你想要低成本的學習ES6,體驗組件化的開發模式,小程序應該算是一個不錯的選擇。小程序

2、 須要什麼樣的基礎知識

熟悉文檔

因爲小程序是中文文檔,因此我相信對大多數人來講,學習成本很是低。咱們須要實現什麼效果,須要用到什麼組件和api,這些基礎的東西就在文檔裏,咱們只須要熟悉他們便可,若是你連文檔都不熟悉,真的談不上開發小程序了,可是小程序文檔確實足夠簡單以至於對大多數人來講,過一遍就知道應該怎麼玩了。

須要對html,css的知識有足夠的熟悉程度

小程序的組件,仍然是基於html與css的知識來完成,咱們只須要對html與css足夠熟悉,就可以很輕鬆的完成小程序的佈局。諸如文檔流,包含塊,BFC,定位系統,怪異盒模型,彈性盒模型等等知識,都可以用獲得。

須要對JavaScript足夠熟悉

和網頁應用同樣,在小程序裏,全部的功能都由js完成。而因爲三方插件的匱乏,對於js的封裝能力和對數據的處理能力就會要求高一點。而且須要你對模塊化有必定的認知。

須要有控制數據就能改變UI的思惟

沒有接觸過類MVC模式框架的朋友,每每對於這個思惟轉變有必定程度上的難以接受。因此常常看到有人在學習angular和react時處處詢問如何把jQuery引入進來,其實在99%的場景下,咱們都再也不須要獲取到DOM節點,只須要操做數據和方法就能完成全部的事情,咱們須要有這樣一個心理準備。

2、開發小程序,咱們面臨着什麼樣的挑戰

若是你只是想要寫一個demo,看着官方文檔把一些組件,api體驗一下,那是沒有什麼挑戰的。可是若是咱們想要開發一個成熟健全的小程序,那麼面臨的挑戰就不少。

好比:
如何保存登陸狀態,UI狀態等?
爲了給用戶節約流量,應該如何規劃緩存機制?
不支持webview,咱們如何展現html文章?
如何應對三方插件匱乏的情況?
沒有提供明確的組件機制,咱們應該如何處理組件?
不支持promise,咱們如何處理異步?
小程序體積限制爲1024k,咱們應該如何優化代碼與處理靜態資源?
具體功能應該如何實現等等... ...

固然,並非每個小程序都要考慮全部的問題,這裏我從我本身開發的小程序的角度,跟你們分享一下,我遇到了哪些問題,如何解決,用了什麼樣的方案。

如何保存各類輕量緩存數據以及狀態值

假如在一個簡單的學習demo中,咱們想要改變一個矩形的寬高,並把結果保存起來,只須要設定一個全局變量便可。這樣咱們就能夠在任何地方知道這個矩形改變以後的寬高了,可是咱們知道,爲了防止變量污染,以及在多人開發中出現命名衝突等問題,咱們的原則上是不能有全局變量的。那麼若是不用全局變量,咱們應該怎麼作?

若是學習過react的同窗,應該會對redux有所耳聞。redux就可以解決這個問題。可是redux對於小程序而言,因爲功能過於強大,反而不太適合。所以,嘗試本身造一個簡單的輪子來解決這個問題。

小程序中,咱們可使用es6的模塊化規範,來建立模塊,引入模塊等,若是對於模塊化的開發思惟還不太熟悉,建議先學習一下。

建立一個叫state.js的文件,該文件就是一個獨立的模塊,專門用來處理輕量的數據緩存

首先建立一個state對象,準備將數據以json的格式存起來

let states = {} // 存儲變量複製代碼

分別提供一個獲取數據的方法,一個獲取states所有數據的方法

function get (name) {
    if(states[name]) { return states[name] }
    return ''
}

function getStates () {
    return states
}複製代碼

再提供一個保存數據的方法,咱們知道若是隻保存一個key-value的數據很簡單,可是咱們想要實現react中,setState一樣功能的話,則須要作一些特殊的處理。

function set (options, target) {
    let keys = Object.keys(options)
    let o = target ? target : states
    keys.map( item => {
        if(typeof o[item] == 'undefined') {
            o[item] = options[item]
        } 
        else {
            if(utils.type(o[item]) == 'object') {
                set(options[item], o[item])
            } else {
                o[item] = options[item]
            }
        }
        return item
    })
}複製代碼

對外提供訪問的接口,這個模塊就算完成了。

module.exports = {
    get: get,
    getStates: getStates,
    set: set
}複製代碼

在別的模塊中,使用方式以下

// 先引用
import state from './utils/state'

// 保存一個值
state.set('single', { c: 1, d: 2 })
// 查看一下single中的數據
state.get('single')
// 只修改single中的c值
state.set({
  single: { c: 20 }
})
// 修改完成以後再查看一下結果,這也是set方法的厲害之處,這對於咱們保存提供了極大的便利複製代碼

雖然這個模塊代碼簡單,可是state可以存儲足夠多的數據,它提供了全局變量給咱們帶來的便利,也避免了全局變量的弊端,甚至還給組件之間的交互提供了可能。若是你沒有意識到這個模塊的重要性,那麼你還得多寫幾個demo細細的體會一下。

上面的state模塊還須要什麼重要的拓展嗎?

咱們試想一下,若是咱們想要修改一個皮膚設置項,或者咱們要切換白天/黑夜模式的外觀,咱們應該怎麼作?爲何個人按鈕一改變,全局的皮膚就能當即作出響應?

這個時候,咱們須要掌握js設計模式中,一個極爲重要的模式, 訂閱-通知模式,或者叫作觀察者模式,監聽者模式均可以。在一個app中,若是咱們想要作好緩存和用戶體驗,就會大量用到它。

在修改皮膚這個例子中,咱們改變了按鈕的狀態值,這個狀態改變值觸發了一個事件,這個事件完成了對皮膚的修改。固然在此以前,咱們還得將這個事件,與這個狀態值綁定起來。

綁定事件 -> 點擊按鈕狀態值改變 -> 觸發事件 -> 皮膚修改完成

所以,該模式的原理也大概以下,咱們首先須要將對應的事件保存起來與變量值的key,這個過程就叫作綁定,相似事件綁定。在狀態值改變時,咱們就發送一個通知,告訴咱們的模塊,狀態值改變了,應該執行事件了,因而事件執行。

聲明一個存儲數據的數組

let events = [] // 存儲事件複製代碼

每個綁定,都會以對象的形式,保存在數組中

events = [{
    name: 'changeToNight',
    handler: changeFn,
    page: targetPage
}]複製代碼

添加一個綁定事件

function bind (name, notification, targetPage) {
    if (name && notification) {
        if (!targetPage) {
            console.error('bind error: 沒有綁定頁面對象')
            return;
        } 
        events.push({
            name: name,
            handler: notification,
            page: targetPage
        })
    } else {
        console.error('bind error: no name or handler')
    }
}複製代碼

添加一個通知事件

function dispatch (name, value) {
    if(!value) {
        value = get(name)
    }
    else {
        set({
            [name]: value
        })
    }
    if(!events.length) { return }

    events.map( (item, i) => {
        if(item.name == name) {
            item.handler(value)
        }
    })
}複製代碼

簡單的實現了一下,若是你們想要將該模式運用的更加自如,還須要專門花精力去研究他。我這裏只是簡單的暫時了一種實現方式。

如何解決html文章的展現

因爲小程序中不支持html標籤,它有本身的一套標籤組件,所以咱們在使用時,並不能直接把html文章顯示出來。爲了可以解決這個問題,咱們須要將html標籤轉換爲小程序支持的標籤。

有一個叫作html2json.js的組件。它遍歷html的標籤結構,並根據便利結果將標籤內容以json的數據形式保存起來,咱們就能夠經過該json數據生成對應的小程序標籤。

固然若是是咱們本身作的話還比較麻煩,好在有大神在第一時間提供了一個叫作wxParse的三方插件。你們能夠去搜索使用一下。

因爲該插件功能太齊全,不少個人小程序用不上,因此就根據上面我說的思路本身實現了一個輕量級的,恰好夠本身用。

如何考慮緩存機制

上面的state組件,可以在必定程度上緩存一些輕量的數據。可是該組件的生命週期短暫,在小程序退出以後數據就會消失,並且對於數據量比較大的狀況,也不適合用state組件來緩存。

每個小程序有10M的本地緩存空間。並且小程序也提供了緩存和清除緩存的api,所以這不是咱們的難點,咱們的難點在於,如何分清哪些數據應該緩存,每一種數據應該緩存多久?如何更新?服務器數據若是更新了應該如何同步本地緩存?

緩存策略作得好很差,在很大程度上會決定你小程序的總體質量。固然這裏我就不詳細展開解讀了,涉及到不少東西,一時半會兒感受說不清楚了,你們只要考慮清楚了上面的幾個問題,相信都可以結合本身的實際狀況,弄出一個合理的方案。

如何處理組件以及組件的數據傳遞,組件的交互等

小程序並無提供明確的組件機制。可是咱們知道,在小程序裏,一個頁面能夠由xx.wxml, xx.wxjs, xx.wxss, xx.json組成,這是一個基本結構。並且小程序提供了js的模塊引入,wxml的模板引入,這就爲咱們自定義組件創造了可能。

+ component
   + rect
      - rect.wxml
      - rect.wxss
      - rect.js   

+ pages
   + index
      - index.wxml
      - index.wxss
      - index.js複製代碼

咱們建立了一個rect組件,並但願咱們本身可以自定義矩形的顏色,點擊以後,還會再修改一次顏色

一切從簡,rect組件中代碼

// rect.wxml
<template name="rect">
  <view class="single-rect" bindtap="changeColor" style="background-color: {{rectColor}}"></view>  
</template>

// rect.wxss
.single-rect {
  width: 100px;
  height: 100px;
  background-color: red;
}

// rect.js
module.exports = {
  changeColor: function (_this) {
    _this.setData({
      rectColor: 'orange'
    })
  }
}複製代碼

index頁面中引入該組件

// index.wxml
<import src="../../component/rect/rect.wxml" />
<template is="rect" data="{{rectColor}}"></template>

// index.wxss
@import "../../component/rect/rect.wxss";

// index.js
import rect from '../../component/rect/rect'

Page({
    data: {},
    onLoad: function () {
        console.log(rect)
    },
    changeColor: function () {
        rect.changeColor(this)
    }
})複製代碼

這個例子演示了組件的建立與使用,其中包含了數據傳遞。固然看上去有點麻煩,所以咱們能夠經過構建工具來簡化這個過程。這裏就很少說了,在不增長額外構建的狀況下,這樣使用是徹底沒有問題的。至於不一樣組件之間的交互,就得經過上面的state組件來完成。

如何使用Promise

Promise的重要性不言而喻,這裏就再也不介紹promise了,若是不明白的同窗能夠去其餘文章裏學習一下。可是小程序通過幾回改變,已經決定取消對Promise的支持,在實際應用中,promise幾乎無處不在使用。所以,咱們須要本身引入一個polyfill來支持promise。

小程序對於接口的返回結果進行了一層統一的封裝,可是咱們並不想使用這樣的結果,這個結果會致使代碼量的增長,所以咱們能夠經過promise的過濾設置,只返回咱們想要的東西便可。

若是可以準確理解resolve與reject,那麼咱們就可以隨意的定製過濾規則了。

function wxPromise (cb) {
    return function (result = {}) {
        return new Promise ((resolve, reject) => {
            result.success = _res => {
                if(_res.statusCode) {
                    /(2|3)\d+/.test(_res.statusCode) ? resolve(_res.data) : reject(_res.data)
                } else {
                    resolve(_res)
                }
            }
            result.fail = (...args) => {
                reject(...args)
            }
            cb(result)
        })
    }
}複製代碼

固然還有許多具體功能的具體實現方案,好比如何實現加載更多下拉刷新,如何實現圖片懶加載,如何實現k線圖的繪製,若是實現本地數據同步刷新等等等,因爲時間關係,就不一一介紹了,之後有時間再跟你們分享吧。

本文主要目的是但願給你們提供一個學習方向,所以不少知識點都是一筆帶過,並未詳細講解,須要你們去其餘地方加深學習

ps,姍姍來遲,由我獨立開發的《老虎淘股》小程序終於過審發佈了。[撒花],歡迎你們搜索體驗,多提意見~ ~

相關文章
相關標籤/搜索