摘要:前言省略準(zhǔn)備了解微信小程序是什么微信小程序官方文檔了解應(yīng)用狀態(tài)管理方案也是架構(gòu)的具體實(shí)現(xiàn)了解打包工具了解代碼轉(zhuǎn)譯工具原理大致是借助語(yǔ)法分析工具之類的將代碼解析成抽象語(yǔ)法樹再重寫成最終的代碼測(cè)試工具等等請(qǐng)根據(jù)需要選擇微信小程序目前版本的實(shí)現(xiàn)需
前言: 省略...
準(zhǔn)備了解微信小程序是什么? 微信小程序官方文檔
了解應(yīng)用狀態(tài)管理方案: Redux, 也是Flux架構(gòu)的具體實(shí)現(xiàn)
了解Javascript打包工具: webpack
了解ES6/7代碼轉(zhuǎn)譯(transcompile)工具: Babel, 原理大致是借助語(yǔ)法分析工具(Esprima之類的), 將代碼解析成抽象語(yǔ)法樹, 再"重寫"成最終的代碼.
Javascript測(cè)試工具: jest, mocha等等, 請(qǐng)根據(jù)需要選擇.
TL;DR;微信小程序目前版本的API實(shí)現(xiàn)需要兼顧方方面面, 所以仍然使用callback寫法, 眾所周知的Callback-Hell是傳統(tǒng)js語(yǔ)法上的歷史問(wèn)題, 但畢竟稱手的工具是開發(fā)效率的源泉. 因此筆者對(duì)當(dāng)前版本的微信小程序API做了簡(jiǎn)單的封裝 weapp.
同時(shí), 微信小程序框架本身專注于交互和UI的實(shí)現(xiàn), 并未提供內(nèi)置的狀態(tài)管理, 如果眾多的異步操作都直接在App或者Page中一一實(shí)現(xiàn), 相信寫起來(lái)會(huì)是一場(chǎng)噩夢(mèng), 而且不易于測(cè)試, 筆者又因此針對(duì)微信小程序?qū)崿F(xiàn)了一個(gè)基于Redux方案的狀態(tài)管理模塊, 用以方便的在小程序中實(shí)現(xiàn)應(yīng)用狀態(tài)管理 redux-weapp.
特別地, 微信小程序構(gòu)建(編譯)時(shí)不支持從App scope之外require文件, npm在此就不好用了. 所以, 我們需要實(shí)時(shí)build依賴到應(yīng)用本地, 在微信小程序中引用本地的modules, 對(duì)于這種構(gòu)建場(chǎng)景, 筆者認(rèn)為webpack算是最方便的方案. 大家都說(shuō)COPY到本地是最最最方便的方式~~
安裝工具和依賴模塊 下載微信小程序開發(fā)者工具開發(fā)者工具是用nwjs模擬的環(huán)境, 實(shí)際在微信中是JavascriptCore環(huán)境, 不過(guò)不用擔(dān)心, 只是兩個(gè)不同的vm, 本質(zhì)是一樣的.
nwjs可能存在一些小bug, 寫代碼的時(shí)候注意一下就好.
下載 微信小程序開發(fā)者工具
用npm命令開始一個(gè)微信小程序項(xiàng)目mkdir myapp cd myapp npm init開始安裝必要的依賴模塊
由于除了小程序運(yùn)行時(shí)需要的模塊, 還有構(gòu)建所需要的模塊, 看起來(lái)會(huì)比較多, 不過(guò)不用擔(dān)心, 大多數(shù)都是聲明性的, 不需要你直接調(diào)用.
為了方便經(jīng)驗(yàn)少些的同學(xué)理解, 我將這些依賴分步安裝.
代碼轉(zhuǎn)譯工具, Babel
npm install --save-dev babel-cli babel-core babel-loader babel-plugin-add-module-exports babel-polyfill babel-preset-es2015 babel-preset-stage-0
有了上面這些模塊, 就可以在構(gòu)建時(shí)將ES6/7的代碼轉(zhuǎn)譯為ES5的代碼了(其實(shí)解釋器都只認(rèn)ES5).
安裝打包工具, webpack
npm install webpack --save-dev
在此, 我們只需要對(duì)代碼進(jìn)行打包, 不需要dev server和hot module replace功能, 因此只需要安裝webpack module本身, 無(wú)需安裝其他擴(kuò)展和插件.
安裝Redux
npm install redux redux-thunk --save-dev
由于在實(shí)際應(yīng)用中, 我們經(jīng)常會(huì)需要異步調(diào)用API服務(wù)器的接口, 所以需要redux-thunk這個(gè)模塊來(lái)處理 異步action.
安裝開發(fā)小程序的輔助模塊
npm install xixilive/weapp xixilive/redux-weapp --save-dev
其中, weapp模塊是對(duì)微信小程序API的wrapper, 提供了更易于使用的API, redux-weapp是基于Redux對(duì)微信小程序進(jìn)行狀態(tài)管理.
建立項(xiàng)目目錄結(jié)構(gòu)如下myapp |- es6 # 源代碼 |- myapp.js # 在app.js文件中require此文件 |- lib # 存放編譯之后的js文件 |- pages # 小程序頁(yè)面定義 |- projects |- projects.js |- projects.json |- projects.wxml |- projects.wxss ... |- app.js # 小程序入口文件 |- app.json |- app.wxss |- webpack.config.js # webpack配置文件編寫構(gòu)建腳本
首先得寫webpack.config.js, 這個(gè)是必須的, 由于這個(gè)構(gòu)建是為了本地化微信小程序的依賴, 因此只處理js文件, 若需要打包其他諸如css, image等資源, 請(qǐng)讀者自行研究. 實(shí)際上, 微信小程序包有1MB的上限.
// webpack.config.js var path = require("path"), webpack = require("webpack") var jsLoader = { test: /.js$/, // 你也可以用.es6做文件擴(kuò)展名, 然后在這里定義相應(yīng)的pattern loader: "babel", query: { // 代碼轉(zhuǎn)譯預(yù)設(shè), 并不包含ES新特性的polyfill, polyfill需要在具體代碼中顯示require presets: ["es2015", "stage-0"] }, // 指定轉(zhuǎn)譯es6目錄下的代碼 include: path.join(__dirname, "es6"), // 指定不轉(zhuǎn)譯node_modules下的代碼 exclude: path.join(__dirname, "node_modules") } module.exports = { // sourcemap 選項(xiàng), 建議開發(fā)時(shí)包含sourcemap, production版本時(shí)去掉(節(jié)能減排) devtool: null, // 指定es6目錄為context目錄, 這樣在下面的entry, output部分就可以少些幾個(gè)`../`了 context: path.join(__dirname, "es6"), // 定義要打包的文件 // 比如: `{entry: {out: ["./x", "./y","./z"]}}` 的意思是: 將x,y,z等這些文件打包成一個(gè)文件,取名為: out // 具體請(qǐng)參看webpack文檔 entry: { myapp: "./myapp" }, output: { // 將打包后的文件輸出到lib目錄 path: path.join(__dirname, "lib"), // 將打包后的文件命名為 myapp, `[name]`可以理解為模板變量 filename: "[name].js", // module規(guī)范為 `umd`, 兼容commonjs和amd, 具體請(qǐng)參看webpack文檔 libraryTarget: "umd" }, module: { loaders: [jsLoader] }, resolve: { extensions: ["", ".js"], // 將es6目錄指定為加載目錄, 這樣在require/import時(shí)就會(huì)自動(dòng)在這個(gè)目錄下resolve文件(可以省去不少../) modulesDirectories: ["es6", "node_modules"] }, plugins: [ new webpack.NoErrorsPlugin(), // 通常會(huì)需要區(qū)分dev和production, 建議定義這個(gè)變量 // 編譯后會(huì)在global中定義`process.env`這個(gè)Object new webpack.DefinePlugin({ "process.env": { "NODE_ENV": JSON.stringify("development") } }) ] }定義npm命令
test 筆者比較喜歡jest, 所以在此就用jest做范例了.
// package.json "scripts": { "pretest": "eslint es6", //推薦進(jìn)行靜態(tài)檢查 "test": "jest", ... }, ..., // jest允許在package.json中定義配置 "jest": { "automock": false, "bail": true, "transform": { ".js": "/node_modules/babel-jest" //用babel轉(zhuǎn)譯 }, "testPathDirs": [ " /__tests__/" ], "testRegex": ".test.js$", "unmockedModulePathPatterns": [ "/node_modules/" ], "testPathIgnorePatterns": [ "/node_modules/" ] }
build 這里就是構(gòu)建的命令了, 成敗在此一舉 :)
// package.json "scripts": { ..., // 帶上watch選項(xiàng), 實(shí)時(shí)編譯修改, 由于小程序開發(fā)工具也監(jiān)視應(yīng)用文件的修改, 所以es6目錄下的js文件修改, 將導(dǎo)致小程序開發(fā)工具自動(dòng)重新加載 "build": "webpack --watch --progress --colors --config webpack.config.js" },寫應(yīng)用代碼
總算進(jìn)入正題了(工欲善其事,...), 借助上述的 weapp 和 redux-weapp, 希望你會(huì)感到很舒服~~.
在這個(gè)范例(myapp)中, 我們目標(biāo)是去查詢 github/octokit 的開源項(xiàng)目, 并顯示在小程序中.
建議不了解Redux的讀者先去快速了解一下(2 hours) Getting started with Redux - from egghead
myapp模塊定義store: /es6/store.js
這里只是簡(jiǎn)單的范例, 實(shí)際中會(huì)有比較復(fù)雜的store shape, 需要引入更多的middleware來(lái)處理動(dòng)作和狀態(tài)的變化.
// /es6/store.js import {createStore, applyMiddleware, bindActionCreators} from "redux" import thunk from "redux-thunk" import reducers from "./reducers" export default function(initState = {}){ return createStore( reducers, initState, applyMiddleware(thunk) ) }
定義reducers: /es6/reducers.js
Reducer就是處理因Store dispatch actions時(shí)發(fā)生的狀態(tài)變化的function, 參數(shù)總是為(state, action)
// /es6/reducers.js import { combineReducers } from "redux" // 處理projects邏輯 const projects = (state = [], action) => { switch (action.type) { case "PROJECTS_LOADED": return state.concat[action.payload] //other cases } return state } // 將多個(gè)reducer合并起來(lái) // 這里就可以看出store的結(jié)構(gòu)了, 是不是很 predictable ? export default combineReducers({ projects })
定義actions: /es6/actions.js
Action通常是個(gè)Plain Object, 總是被Store dispatch, 描述了"發(fā)生了什么, 結(jié)果是什么"的邏輯
// /es6/actions.js import {weapp} from "weapp" // 更好的方法是定義一個(gè)api module, 來(lái)處理網(wǎng)絡(luò)請(qǐng)求 const http = weapp.Http("https://api.github.com") // 這是一個(gè)異步action, redux-thunk會(huì)處理返回值為Function的action(可以編入繞口令大全了~~) export const loadProjects = (org) => { return (dispatch) => { http.get(`/orgs/${org}/repos`).then(response => { // 讓store去廣播"PROJECTS_LOADED"這件事情發(fā)生了 dispatch({ type: "PROJECTS_LOADED", payload: response }) }) } }
myapp模塊入口: /es6/myapp.js
// /es6/myapp.js import {bindActionCreators} from "redux" import {weapp} from "weapp" import connect from "redux-weapp" import store from "./store" import actions from "./actions" export { weapp, connect, bindActionCreators, store, actions }小程序模塊
入口文件: app.js 和 app.json
// /app.js App({ // 方便起見, 這里不做任何life-cycle處理 })
app.json
{ "pages": [ "pages/projects/projects" ], "window": { "navigationBarTitleText": "Orchid" }, "networkTimeout": { "request": 10000, "downloadFile": 10000 }, "debug": true }
頁(yè)面邏輯: projects.js
如上定義, 小程序的啟動(dòng)頁(yè)面是projects
// /pages/projects/projects.js // 引入編譯過(guò)的modules import { weapp, connect, bindActionCreators, store, actions } from "../../lib/app" // 標(biāo)準(zhǔn)Page定義Object const config = { data: { projects: [] //for init-render }, onReady(){ // 哪里來(lái)的 loadProjects? 往下看 this.loadProjects("octokit") }, onStateChange(nextState){ this.setData({projects: nextState}) } } // connect store with page const page = connect.Page( store, // required // 這個(gè)頁(yè)面只關(guān)注projects變化 (state) => ({projects: state.projects}), // 將Action定義與Store.dispatch binding在一起, 這樣就是一個(gè)可以發(fā)起對(duì)github API的請(qǐng)求了 (dispatch) => { return { loadProjects: bindActionCreators(actions.loadProjects, dispatch) } } ) // 啟動(dòng)被connect過(guò)的頁(yè)面 Page(page(config))
頁(yè)面UI: projects.wxml
后記{{project.name}}
范例代碼未實(shí)際運(yùn)行, 僅用以表示開發(fā)步驟, 我會(huì)盡快把這個(gè)范例實(shí)現(xiàn)完整, 放到github上.
最后, 謝謝您耐心閱讀至此!
參考https://github.com/nwjs/nw.js...
http://redux.js.org
https://webpack.github.io
https://facebook.github.io/je...
https://babeljs.io
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81085.html
摘要:于是,我重新思考了下,最佳的微信小程序開發(fā)實(shí)踐應(yīng)該是無(wú)痛的,且舒服的,無(wú)痛的是指在小程序的飛速發(fā)展變更中,我們不用重復(fù)的浪費(fèi)學(xué)習(xí)第三方框架和原生框架。以上,便可以在微信小程序中使用了。 weapp-starter 微信小程序開發(fā)最佳實(shí)踐 項(xiàng)目地址 為什么會(huì)有這個(gè) repo 在小程序之初便開發(fā)應(yīng)用了,現(xiàn)在小程序的開發(fā)也越來(lái)越成熟了,完善了很多的API、組件、架構(gòu)等,社區(qū)也由原來(lái)的零星點(diǎn)點(diǎn)...
摘要:微信小程序應(yīng)用號(hào)開發(fā)資源匯總文檔工具教程代碼插件組件文檔從搭建一個(gè)微信小程序開始小程序開發(fā)文檔小程序設(shè)計(jì)指南工具小程序開發(fā)者工具官方支持微信小程序?qū)崟r(shí)預(yù)覽的支持的微信小程序組件化開發(fā)框架轉(zhuǎn)在線工具小程序云端增強(qiáng)社區(qū)微信小程序 微信(小程序or應(yīng)用號(hào))開發(fā)資源匯總-文檔-工具-教程-代碼-插件-組件 文檔 從搭建一個(gè)微信小程序開始 小程序開發(fā)文檔 小程序設(shè)計(jì)指南 工具 小程序開發(fā)者...
閱讀 2449·2021-10-08 10:17
閱讀 1824·2021-09-06 15:02
閱讀 2539·2019-08-29 17:30
閱讀 2663·2019-08-29 13:24
閱讀 1522·2019-08-29 11:12
閱讀 3337·2019-08-28 17:52
閱讀 666·2019-08-26 11:30
閱讀 3577·2019-08-26 11:01