国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

帶你玩轉小程序開發實踐|含直播回顧視頻

fxp / 1447人閱讀

摘要:小程序的視圖層目前使用作為渲染載體,而邏輯層是由獨立的作為運行環境。比如小程序的,通信一次就像是寫情書所以,嚴格來說,小程序是微信定制的混合開發模式。出棧入棧解決小程序接口不支持的問題小程序的所有接口,都是通過傳統的回調函數形式來調用的。

作者:張利濤,視頻課程《微信小程序教學》、《基于Koa2搭建Node.js實戰項目教學》主編,滬江前端架構師

本文原創,轉載請注明作者及出處

小程序和 H5 區別

小程序的運行過程

解決小程序接口不支持 Promise 的問題

小程序組件化開發及通信

小程序和 H5 區別
我們不一樣,不一樣,不一樣。
運行環境 runtime

首先從官方文檔可以看到,小程序的運行環境并不是瀏覽器環境:

小程序框架提供了自己的視圖層描述語言 WXML 和 WXSS,以及基于 JavaScript 的邏輯層框架,并在視圖層與邏輯層間提供了數據傳輸和事件系統,可以讓開發者可以方便的聚焦于數據與邏輯上。

小程序的視圖層目前使用 WebView 作為渲染載體,而邏輯層是由獨立的 JavascriptCore 作為運行環境。在架構上,WebView 和 JavascriptCore 都是獨立的模塊,并不具備數據直接共享的通道。當前,視圖層和邏輯層的數據傳輸,實際上通過兩邊提供的 evaluateJavascript 所實現。即用戶傳輸的數據,需要將其轉換為字符串形式傳遞,同時把轉換后的數據內容拼接成一份 JS 腳本,再通過執行 JS 腳本的形式傳遞到兩邊獨立環境。

而 evaluateJavascript 的執行會受很多方面的影響,數據到達視圖層并不是實時的。同一進程內的 WebView 實際上會共享一個 JS VM,如果 WebView 內 JS 線程正在執行渲染或其他邏輯,會影響 evaluateJavascript 腳本的實際執行時間,另外多個 WebView 也會搶占 JS VM 的執行權限;另外還有 JS 本身的編譯執行耗時,都是影響數據傳輸速度的因素。

而所謂的運行環境,對于任何語言的運行,它們都需要有一個環境——runtime。瀏覽器和 Node.js 都能運行 JavaScript,但它們都只是指定場景下的 runtime,所有各有不同。而小程序的運行環境,是微信定制化的 runtime。

大家可以做一個小實驗,分別在瀏覽器環境和小程序環境打開各自的控制臺,運行下面的代碼來進行一個 20 億次的循環:

var k
for (var i = 0; i < 2000000000; i++) {
  k = i
}

瀏覽器控制臺下運行時,當前頁面是完全不能動,因為 JS 和視圖共用一個線程,相互阻塞。

小程序控制臺下運行時,當前視圖可以動,如果綁定有事件,也會一樣觸發,只不過事件的回調需要在 『循環結束』 之后。

視圖層和邏輯層如果共用一個線程,優點是通信速度快(離的近就是好),缺點是相互阻塞。比如瀏覽器。

視圖層和邏輯層如果分處兩個環境,優點是相互不阻塞,缺點是通信成本高(異地戀)。比如小程序的 setData,通信一次就像是寫情書!

所以,嚴格來說,小程序是微信定制的混合開發模式。

在 JavaScript 的基礎上,小程序做了一些修改,以方便開發小程序。

增加 App 和 Page 方法,進行程序和頁面的注冊。【增加了 Component】

增加 getApp 和 getCurrentPages 方法,分別用來獲取 App 實例和當前頁面棧。

提供豐富的 API,如微信用戶數據,掃一掃,支付等微信特有能力。【調用原生組件:Cordova、ReactNative、Weex 等】

每個頁面有獨立的作用域,并提供模塊化能力。

由于框架并非運行在瀏覽器中,所以 JavaScript 在 web 中一些能力都無法使用,如 document,window 等。【小程序的 JsCore 環境】

開發者寫的所有代碼最終將會打包成一份 JavaScript,并在小程序啟動的時候運行,直到小程序銷毀。類似 ServiceWorker,所以邏輯層也稱之為 App Service。

與傳統的 HTML 相比,WXML 更像是一種模板式的標簽語言

從實踐體驗上看,我們可以從小程序視圖上看到 Java FreeMarker 框架、Velocity、smarty 之類的影子。

小程序視圖支持如下

數據綁定 {{}}
列表渲染 wx:for
條件判斷 wx:if
模板 tempalte
事件 bindtap
引用 import include
可在視圖中應用的腳本語言  wxs
...

Java FreeMarker 也同樣支持上述功能。

數據綁定 ${}
列表渲染 list指令
條件判斷 if指令
模板 FTL
事件 原生事件
引用 import include 指令
內建函數 比如『時間格式化』
可在視圖中應用的腳本語言 宏 marco
...
 小程序的運行過程

我們在微信上打開一個小程序
微信客戶端在打開小程序之前,會把整個小程序的代碼包下載到本地。

微信 App 從微信服務器下載小程序的文件包
為了流暢的用戶體驗和性能問題,小程序的文件包不能超過 2M。另外要注意,小程序目錄下的所有文件上傳時候都會打到一個包里面,所以盡量少用圖片和第三方的庫,特別是圖片。

解析 app.json 配置信息初始化導航欄,窗口樣式,包含的頁面列表

加載運行 app.js
初始化小程序,創建 app 實例

根據 app.json,加載運行第一個頁面初始化第一個 Page

路由切換
以棧的形式維護了當前的所有頁面。最多 5 個頁面。出棧入棧

 解決小程序接口不支持 Promise 的問題

小程序的所有接口,都是通過傳統的回調函數形式來調用的。回調函數真正的問題在于他剝奪了我們使用 return 和 throw 這些關鍵字的能力。而 Promise 很好地解決了這一切。

那么,如何通過 Promise 的方式來調用小程序接口呢?

查看一下小程序的官方文檔,我們會發現,幾乎所有的接口都是同一種書寫形式:

wx.request({
  url: "test.php", //僅為示例,并非真實的接口地址
  data: {
    x: "",
    y: ""
  },
  header: {
    "content-type": "application/json" // 默認值
  },
  success: function(res) {
    console.log(res.data)
  },
  fail: function(res) {
    console.log(res)
  }
})

所以,我們可以通過簡單的 Promise 寫法,把小程序接口裝飾一下。代碼如下:

wx.request2 = (option = {}) => {
  // 返回一個 Promise 實例對象,這樣就可以使用 then 和 throw
  return new Promise((resolve, reject) => {
    option.success = res => {
      // 重寫 API 的 success 回調函數
      resolve(res)
    }
    option.fail = res => {
      // 重寫 API 的 fail 回調函數
      reject(res)
    }
    wx.request(option) // 裝飾后,進行正常的接口請求
  })
}

上述代碼簡單的展現了如何把一個請求接口包裝成 Promise 形式。但在實戰項目中,可能有多個接口需要我們去包裝處理,每一個都多帶帶包裝是不現實的。這時候,我們就需要用一些技巧來處理了。

其實思路很簡單:我們把需要 Promise 化的『接口名字』存放在一個『數組』中,然后對這個數組進行循環處理。

這里我們利用了 ECMAScript5 的特性 Object.defineProperty 來重寫接口的取值過程。

let wxKeys = [
  // 存儲需要Promise化的接口名字
  "showModal",
  "request"
]
// 擴展 Promise 的 finally 功能
Promise.prototype.finally = function(callback) {
  let P = this.constructor
  return this.then(
    value => P.resolve(callback()).then(() => value),
    reason =>
      P.resolve(callback()).then(() => {
        throw reason
      })
  )
}
wxKeys.forEach(key => {
  const wxKeyFn = wx[key] // 將wx的原生函數臨時保存下來
  if (wxKeyFn && typeof wxKeyFn === "function") {
    // 如果這個值存在并且是函數的話,進行重寫
    Object.defineProperty(wx, key, {
      get() {
        // 一旦目標對象訪問該屬性,就會調用這個方法,并返回結果
        // 調用 wx.request({}) 時候,就相當于在調用此函數
        return (option = {}) => {
          // 函數運行后,返回 Promise 實例對象
          return new Promise((resolve, reject) => {
            option.success = res => {
              resolve(res)
            }
            option.fail = res => {
              reject(res)
            }
            wxKeyFn(option)
          })
        }
      }
    })
  }
})

注: Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,并返回這個對象。

用法也很簡單,我們把上述代碼保存在一個 js 文件中,比如 utils/toPromise.js,然后在 app.js 中引入就可以了:

import "./util/toPromise"

App({
  onLoad() {
    wx
      .request({
        url: "http://www.weather.com.cn/data/sk/101010100.html"
      })
      .then(res => {
        console.log("come from Promised api, then:", res)
      })
      .catch(err => {
        console.log("come from Promised api, catch:", err)
      })
      .finally(res => {
        console.log("come from Promised api, finally:")
      })
  }
})
小程序組件化開發

小程序從 1.6.3 版本開始,支持簡潔的組件化編程

官方支持組件化之前的做法
// 組件內部實現
export default class TranslatePop {
    constructor(owner, deviceInfo = {}) {
        this.owner = owner;
        this.defaultOption = {}
    }
    init() {
        this.applyData({...})
    }
    applyData(data) {
        let optData = Object.assign(this.defaultOption, data);
        this.owner && this.owner.setData({
            translatePopData: optData
        })
    }
}
// index.js 中調用
translatePop = new TranslatePop(this);
translatePop.init();

實現方式比較簡單,就是在調用一個組件時候,把當前環境的上下文 content 傳遞給組件,在組件內部實現 setData 調用。

應用官方支持的方式來實現

官方組件示例:

Component({
  properties: {
    // 這里定義了innerText屬性,屬性值可以在組件使用時指定
    innerText: {
      type: String,
      value: "default value"
    }
  },
  data: {
    // 這里是一些組件內部數據
    someData: {}
  },
  methods: {
    // 這里是一個自定義方法
    customMethod: function() {}
  }
})
結合 Redux 實現組件通信

在 React 項目中 Redux 是如何工作的

單一數據源

整個應用的 state 被儲存在一棵 object tree 中,并且這個 object tree 只存在于唯一一個 store 中。

State 是只讀的

惟一改變 state 的方法就是觸發 action,action 是一個用于描述已發生事件的普通對象

使用純函數來執行修改

為了描述 action 如何改變 state tree ,你需要編寫 reducers。

Props 傳遞 —— Render 渲染

如果你有看過 Redux 的源碼就會發現,上述的過程可以簡化描述如下:

訂閱:監聽狀態————保存對應的回調

發布:狀態變化————執行回調函數

同步視圖:回調函數同步數據到視圖

第三步:同步視圖,在 React 中,State 發生變化后會觸發 Render 來更新視圖。

而小程序中,如果我們通過 setData 改變 data,同樣可以更新視圖。

所以,我們實現小程序組件通信的思路如下:

觀察者模式/發布訂閱模式

裝飾者模式/Object.defineProperty (Vuejs 的設計路線)

在小程序中實現組件通信

先預覽下我們的最終項目結構:

├── components/
│     ├── count/
│        ├── count.js
│        ├── count.json
│        ├── count.wxml
│        ├── count.wxss 
│     ├── footer/ 
│        ├── footer.js
│        ├── footer.json
│        ├── footer.wxml
│        ├── footer.wxss
├── pages/
│     ├── index/
│        ├── ...
│     ├── log/ 
│        ├── ...
├── reducers/
│     ├── counter.js
│     ├── index.js
│     ├── redux.min.js
├── utils/
│     ├── connect.js
│     ├── shallowEqual.js
│     ├── toPromise.js
├── app.js
├── app.json
├── app.wxss
1. 實現『發布訂閱』功能

首先,我們從 cdn 或官方網站獲取 redux.min.js,放在結構里面

創建 reducers 目錄下的文件:

// /reducers/index.js
import { createStore, combineReducers } from "./redux.min.js"
import counter from "./counter"

export default createStore(combineReducers({
  counter: counter
}))

// /reducers/counter.js
const INITIAL_STATE = {
  count: 0,
  rest: 0
}
const Counter = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case "COUNTER_ADD_1": {
      let { count } = state
      return Object.assign({}, state, { count: count + 1 })
    }
    case "COUNTER_CLEAR": {
      let { rest } = state
      return Object.assign({}, state, { count: 0, rest: rest+1 })
    }
    default: {
      return state
    }
  }
}
export default Counter

我們定義了一個需要傳遞的場景值 count,用來代表例子中的『點擊次數』,rest 代表『重置次數』。

然后在 app.js 中引入,并植入到小程序全局中:

//app.js
import Store from "./reducers/index"
App({
  Store,
})
2. 利用 『裝飾者模式』,對小程序的生命周期進行包裝,狀態發生變化時候,如果狀態值不一樣,就同步 setData
// 引用了 react-redux 中的工具函數,用來判斷兩個狀態是否相等
import shallowEqual from "./shallowEqual"
// 獲取我們在 app.js 中植入的全局變量 Store
let __Store = getApp().Store
// 函數變量,用來過濾出我們想要的 state,方便對比賦值
let mapStateToData
// 用來補全配置項中的生命周期函數
let baseObj = {
  __observer: null,
  onLoad() { },
  onUnload() { },
  onShow() { },
  onHide() { }
}
let config = {
  __Store,
  __dispatch: __Store.dispatch,
  __destroy: null,
  __observer() {
    // 對象中的 super,指向其原型 prototype
    if (super.__observer) {
      super.__observer()
      return
    }
    const state = __Store.getState()
    const newData = mapStateToData(state)
    const oldData = mapStateToData(this.data || {})
    if (shallowEqual(oldData, newData)) {// 狀態值沒有發生變化就返回
      return
    }
    this.setData(newData)
  },
  onLoad() {
    super.onLoad()
    this.__destroy = this.__Store.subscribe(this.__observer)
    this.__observer()
  },
  onUnload() {
    super.onUnload()
    this.__destroy && this.__destroy() & delete this.__destroy
  },
  onShow() {
    super.onShow()
    if (!this.__destroy) {
      this.__destroy = this.__Store.subscribe(this.__observer)
      this.__observer()
    }
  },
  onHide() {
    super.onHide()
    this.__destroy && this.__destroy() & delete this.__destroy
  }
}
export default (mapState = () => { }) => {
  mapStateToData = mapState
  return (options = {}) => {
    // 補全生命周期
    let opts = Object.assign({}, baseObj, options)
    // 把業務代碼中的 opts 配置對象,指定為 config 的原型,方便『裝飾者調用』
    Object.setPrototypeOf(config, opts)
    return config
  }
}

調用方法:

// pages/index/index.js
import connect from "../../utils/connect"
const mapStateToProps = (state) => {
  return {
    counter: state.counter
  }
}
Page(connect(mapStateToProps)({
  data: {
    innerText: "Hello 點我加1哦"
  },
  bindBtn() {
    this.__dispatch({
      type: "COUNTER_ADD_1"
    })
  }
}))

最終效果展示:

項目源碼地址:
https://github.com/ikcamp/xcx-redux

直播視頻地址:
https://www.cctalk.com/v/15137361643293

iKcamp官網:https://www.ikcamp.com

iKcamp新課程推出啦~~~~~開始免費連載啦~每周2更共11堂iKcamp課|基于Koa2搭建Node.js實戰項目教學(含視頻)| 課程大綱介紹

滬江iKcamp出品微信小程序教學共5章16小節匯總(含視頻)

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92306.html

相關文章

  • 老司機【分享】帶你玩轉阿里云服務器

    摘要:阿里云是國內云服務器市場的龍頭,性價比高,速度快又安全,是站長建站首選的云服務器之一。作為一個老司機,福利吧也和大家分享一下我的阿里云推廣經驗,教大家如何免費推廣云大使。阿里云是國內云服務器市場的龍頭,性價比高,速度快又安全,是站長建站首選的云服務器之一。福利吧使用的也是阿里云服務器,是折騰了很多次網站搬家后,才選擇了阿里云。身邊好幾個站長最后都殊途同歸,用了阿里云,可見阿里云服務器性能確實...

    banana_pi 評論0 收藏0
  • 【深入淺出容器云】五分鐘帶你玩轉Docker容器服務

    摘要:深入淺出容器云系列文章是由時速云出品,本文是第二篇,歡迎大家不吝賜教。容器服務是一種高度可擴展的高性能容器管理服務,服務于應用的完整生命周期。存儲卷容器服務支持有狀態和無狀態服務。當容器重新部署時也會隨著容器在不同主機之間遷移。 導語:隨著以Docker為代表的容器技術在國內的迅速發展,容器云也逐漸被廣大開發者所熟知,但容器云(CaaS)相比傳統的云主機(IaaS)在實際應用中還存在著...

    AlexTuan 評論0 收藏0
  • 帶你玩轉 JavaScript ES6 (七) - 異步

    摘要:說明處理方法被異步執行了。意味著操作成功完成。狀態的對象可能觸發狀態并傳遞一個值給相應的狀態處理方法,也可能觸發失敗狀態并傳遞失敗信息。注生命周期相關內容引用自四使用和異步加載圖片這是官方給出的示例,部分的代碼如下 帶你玩轉 JavaScript ES6 (七) - 異步 本文同步帶你玩轉 JavaScript ES6 (七) - 異步,轉載請注明出處。 本章我們將學習 ES6 中的 ...

    he_xd 評論0 收藏0
  • 帶你玩轉css3的3D!

    摘要:透視即是以現實的視角來看屏幕上的事物,從而展現的效果。旋轉則不再是平面上的旋轉,而是三維坐標系的旋轉,就包括軸,軸,軸旋轉。必須與屬性一同使用,而且只影響轉換元素。可自由轉載引用,但需署名作者且注明文章出處。 showImg(https://segmentfault.com/img/bVzJoZ); 話不多說,先上demo 酷炫css3走馬燈/正方體動畫: https://bupt-...

    Panda 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<