剛搭建了一個博客,咋都得寫上幾篇文章,否則不就白搭了。vue
一開始打算用 hexo + gitalk + github 搭建一個,可是人太懶,總想一次完事,之後更新就寫一篇 .md 文章就能夠了,不想弄其餘流程了,而後就用了 vue + github-issuse-api + gitalk + github 搭建了一個一勞永逸的辦法,除非 github 掛了。react
官方介紹
是小程序【最先(wepy你已經很成熟了)】 的框架之一,是一款讓小程序支持 【組件化開發(如今小程序已經支持npm構建,好像沒啥優點)】 的框架,經過預編譯的手段讓開發者能夠選擇本身 【喜歡的開發風格(類 vue 語法,數據劫持,自動 setData)】 去開發小程序。框架的細節優化,Promise,【Async Functions(原生不支持)】的引入都是爲了能讓開發小程序項目變得更加簡單,高效git
- 優勢
- vue 語法,更好的開發體驗
- 更好的組件化
- 更多的組件庫
- 支持更多 ECMAscript 語法
- 缺點
- 官方維護不太積極,遇到問題解決太慢
- 引入了額外的代碼,增長了代碼體積
複製代碼
- 研究源碼版本 2.0.0-alpha.8
- wepy 核心代碼是 cli 和 core
- wepy-cli 是文件轉換打包的處理
- core 是對數據綁定生命週期注入的處理
複製代碼
// core/weapp/apis/index.js
import { app, page, component } from '../native/index';
export function initGlobalAPI (wepy) {
wepy.app = app; // return App({})
wepy.page = page; // return Page({})
wepy.component = component; // return Component({})
return wepy;
}
複製代碼
寫過 wepy2.0 的就知道,經過 wepy.page(), wepy.component(), 注入頁面和組件,以及在app.wpy中的 wepy.app() 方法 ,經過這三個方法,分別會返回 App() Page() Component(),寫過原生到小夥伴是否是很熟悉這三個 函數,分別是實現組冊小程序,註冊頁面和組件,這樣就和原生關聯起來了github
拿 app 爲例, 首先這個函數作了什麼?app 函數裏運行 patchAppLifecycle 函數 並傳入小程序運行環境 appConfig 中的對象【至關於 APP({}) 函數中的對象】,而後將 全部生命週期綁定上去,每次原生環境運行,觸發原生生命週期,每一個生命週期都會觸發一個回調函數,在執行 .wey 文件中存在的生命週期函數邏輯,這樣,小程序的生命週期和你頁面的生命週期進行了關聯web
// core/weapp/native/app.js
export function app (option, rel) {
let appConfig = {};
patchAppLifecycle(appConfig, option, rel); // 註冊和初始化生命週期
return App(appConfig); // 這裏最終運行於小程序中 appConfig 就是小程序js中的邏輯
}
// patchAppLifecycle 函數
// core/weapp/init/lifecycle
export function patchAppLifecycle (appConfig, options, rel = {}) {
appConfig.onLaunch = function (...args) {
let vm = new WepyApp();
// 生命週期回調,觸發頁面生命週期函數
return callUserMethod(vm, vm.$options, 'onLaunch', args);
};
// 至關於 App({onLaunch: function() {}})
// 獲取生命週期列表
let lifecycle = getLifecycycle(WEAPP_APP_LIFECYCLE, rel, 'app');
// 循環綁定生命週期
lifecycle.forEach(k => {
appConfig[k] = function (...args) {
return callUserMethod(app, app.$options, k, args);
};
});
};
複製代碼
若是你瞭解過 vue 的數據綁定那這個實現原理也和 vue 同樣 (虛!!我懷疑是抄的,連函數名都同樣),也是實現了一個觀察者模式,只是,vue 作的更新處理是 patch 函數 diff 虛擬 dom 實現更新 web,而,wepy作調是調用 setData 更新數據,話很少說,上代碼。npm
// core/weapp/init/lifecycle
export function patchLifecycle (output, options, rel, isComponent) {
// output 小程序的運行環境
const initLifecycle = function (...args) {
let vm = new initClass();
// 將小程序 this 綁定到 vm.$wx 上去
vm.$wx = this;
// 初始化數據,進行數據綁定,進行綁定對數據只是 data 中的數據
initData(vm, output.data, isComponent);
// 進行依賴收集,作 setData
initRender(vm, Object.keys(vm._data).concat(Object.keys(vm._props)).concat(Object.keys(vm._computedWatchers || {})));
return callUserMethod(vm, vm.$options, 'created', args);
};
// 綁定到小程序到生命週期中去,作點事
// 至關於 Page({created: initLifecycle(){}})
output.created = initLifecycle;
};
複製代碼
來看看 initData 作了啥, 對 data 進行了處理,代理到 _data 屬性上去,進行觀察小程序
export function initData (vm, data) {
vm._data = _data;
Object.keys(_data).forEach(key => {proxy(vm, '_data', key)});
observe({
vm: vm,
key: '',
value: _data,
parent: '',
root: true
});
}
複製代碼
observe 函數作了啥api
// core/weapp/observer/index.js
export function observe ({vm, key, value, parent, root}) {
// 判斷是否爲觀察者 __ob__ 屬性定義
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
}
// 觀察數據
ob = new Observer({vm: vm, key: key, value: value, parent});
return ob
}
class Observer {
// 若是是數組,則作特殊處理,由於數組 push, pop 等不能劫持,因此須要特殊處理
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(key, value);
} else {
// 不然劫持數據
this.walk(key, value);
}
}
walk (key, obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive({ vm: this.vm, obj: obj, key: keys[i], value: obj[keys[i]], parent: obj });
}
}
defineReactive () {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const val = getter ? getter.call(obj) : value
// 依賴收集
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(val)) {
dependArray(val)
}
}
}
return val
},
set: function reactiveSetter (newVal) {
const val = getter ? getter.call(obj) : value
// 更新數據
value = newVal
// 通知數據更新
dep.notify();
}
})
}
複製代碼
若是是數組須要作特殊處理,數據,push,pop等方式改變等數據不能被劫持,若是不是,就作數據劫持 getter setter,數據劫持的初始化已經作完了,如今在來看如進行等依賴收集,回到 initRender() 上了,進行依賴收集,就得觸發一次數據到 getter, initRender 就是幹這個的,watch 觀察每一個頁面實例,觸發一次 data getter 進行依賴收集,每次 setter 到時候就會觸發這個函數 setData數組
// core/weapp/init/render.js
export function initRender (vm, keys) {
return new Watcher(vm, function () {
// 會觸發數據到 getter 進行依賴收集
Object.keys(keys).forEach(key => clone(vm[key]))
// 調用 setData 進行數據更新,vm.$wx 在頁面 created 鉤子到時候進行綁定,寫在了前面,是小程序中的 this
vm.$wx.setData(dirty, renderFlushCallbacks)
}
}, function () {
}, null, true);
};
複製代碼
能夠看到最後編譯出來就是執行最開始導出的三個函數,對應到不一樣的小程序頁中去hexo
// app.js
// vendor.js 爲編譯後的核心代碼,至關於,導出的 wepy
var _core = _interopRequireDefault(require('vendor.js')(1));
// 調用 app 函數,return App({}) 完成對小程序的註冊
_core["default"].app({}, {a: 1})
複製代碼
// page.js
// vendor.js 爲編譯後的核心代碼,至關於,導出的 wepy
var _core = _interopRequireDefault(require('../vendor.js')(1));
// 調用 page 函數,return Page({}) 完成對小程序頁面的註冊
_core["default"].page({}, {});
複製代碼