點擊上方「程式設計師黑叔」,選擇「置頂或者星標」
用Vue、Vuex 做俄羅斯方塊本項目靈感來源於 React 版的俄羅斯方塊,由於對其實現原理較感興趣,而且相比於 React 更喜歡 Vue, 於是把 React 版的重構為了 Vue 版的,大致思路是把組件當成一個個函數,保證一個輸入(props)能得到一個確定的輸出(view),然後對不同方法也是做同樣處理,對於 Redux 使用 Vuex 精簡化
效果預覽效果預覽正常速度的錄製,體驗流暢。
響應式響應式不僅指屏幕的自適應,而是在PC使用鍵盤、在手機使用手指的響應式操作:
手機數據持久化視頻
玩單機遊戲最怕什麼?斷電。通過訂閱 store.subscribe,將state儲存在localStorage,精確記錄所有狀態。網頁關了刷新了、程序崩潰了、手機沒電了,重新打開連接,都可以繼續。
Vuex 狀態預覽(Vue DevTools extension)preview視頻
Vuex 設計管理了所有應存的狀態,這是上面持久化的保證。
遊戲框架使用的是 Vue + Vuex
1、Web Audio Api遊戲裡有很多不同的音效,而實際上只引用了一個音效文件:/build/music.mp3。藉助Web Audio Api能夠以毫秒級精確、高頻率的播放音效,這是<audio>標籤所做不到的。在遊戲進行中按住方向鍵移動方塊,便可以聽到高頻率的音效。
網頁音效進階WAA 是一套全新的相對獨立的接口系統,對音頻文件擁有更高的處理權限以及更專業的內置音頻效果,是W3C的推薦接口,能專業處理「音速、音量、環境、音色可視化、高頻、音向」等需求,下圖介紹了WAA的使用流程。
流程其中Source代表一個音頻源,Destination代表最終的輸出,多個Source合成出了Destination。原始碼:/src/unit/music.js 實現了ajax加載mp3,並轉為WAA,控制播放的過程。
WAA 在各個瀏覽器的最新2個版本下的支持情況(CanIUse)
瀏覽器兼容可以看到IE陣營與大部分安卓機不能使用,其他ok。
Web Audio Api 學習資料:
Getting Started with Web Audio API3、遊戲在體驗上的優化按下方向鍵水平移動和豎直移動的觸發頻率是不同的,遊戲可以定義觸發頻率,代替原生的事件頻率,原始碼:/src/unit/event.js ;左右移動可以 delay 掉落的速度,但在撞牆移動的時候 delay 的稍小;在速度為6級時 通過delay 會保證在一行內水平完整移動一次;對按鈕同時註冊touchstart和mousedown事件,以供響應式遊戲。當touchstart發生時,不會觸發mousedown,而當mousedown發生時,由於滑鼠移開事件元素可以不觸發mouseup,將同時監聽mouseout 模擬 mouseup。原始碼:/src/components/keyboard/index.js;監聽了 visibilitychange 事件,當頁面被隱藏\切換的時候,遊戲將不會進行,切換回來將繼續,這個focus狀態也被寫進了 Vuex 中。所以當用手機玩來電話時,遊戲進度將保存;PC開著遊戲幹別的也不會聽到gameover,這有點像 ios 應用的切換。在任意時刻刷新網頁,(比如消除方塊時、遊戲結束時)也能還原當前狀態;遊戲中唯一用到的圖片是,其他都是CSS;遊戲兼容 Chrome、Firefox、IE9+、Edge等;可以在遊戲未開始時制定初始的棋盤(十個級別)和速度(六個級別);一次消除1行得100分、2行得300分、3行得700分、4行得1500分;方塊掉落速度會隨著消除的行數增加(每20行增加一個級別);4、開發中的經驗梳理,以及如何把 React 項目重構為 Vue 版本Vue 版本和 React 版本核心代碼基本相同,但在編寫組件的時候遇到了幾個問題,比如:
如何把 React 組件改寫成 Vue 的,我的思路是把組件當成函數,保證一個輸入(props)能得到一個確定的輸出(view),然後對不同方法也是做同樣處理, React 的 setState 會觸發 render 方法,所以可以在 methods 自定義 render 方法再在 state 變化後手動觸發 render 方法
生命周期,簡單來說, React 的 componentWillMount 對應 Vue 的 beforeMount, React 的 componentDidMount 對應 Vue 的 mounted,React 的用來優化性能的 shouldComponentUpdate 在 Vue 裡並不需要,不需要手動優化這也是我喜歡 Vue 的一點
Vue 沒有 React 的componentWillReceiveProps 的生命周期,我的解決方法是使用 watch 配合 deep:true 來監聽 props 的變化,如:
watch: { $props: { deep: true, handler(nextProps) { //xxx } } }在必要時候使用 jsx 和 'render' 函數,是的, vue 支持 jsx,在這個項目中matrix 組件 的功能邏輯較複雜,使用 template 模版來渲染組件已經不合適了, React 每次 setState 會觸發 'render' 方法,所以我們可以在 methods自定義 'render' 方法再在 state 變化後手動觸發 'render' 方法,但是這個方法對有複雜邏輯的組件來說會變得很繁瑣,我的解決方法是通過 Vue 的 jsx 轉換插件babel-plugin-transform-vue-jsx來使用 jsx 語法對頁面進行渲染,當 props 或 state 變化了自動觸發 'render' 方法,另外要注意的是 vue 的 jsx 和 React 的 jsx 書寫上有一點的差異, 當 'render' 方法存在時,template 語法會失效. 'render' 函數一個比較實用的用處是在開發類似 React-log 之類的不需要渲染 html 只需要執行一些方法的組件時 template 會顯得很多餘,因為這時候並不需要渲染 dom 了,如果用了 'render' 函數,簡單的在 'render' 函數裡 return false 就行,如: react-log5、架構差異Redux 的數據流向是通過 mapStateToProps 把 store 的狀態轉化為 props 然後通過connect 函數注入到 根組件,根組件再把這些 props 傳入不同組件,當 store 的狀態變化,根組件會重新 render, 更新子組件上的 props,子組件再 根據新 props重新 render引用知乎一個答友的回答https://www.zhihu.com/question/47686258來說就是:
「單例store的數據在react中可以通過view組件的屬性(props)不斷由父模塊「單向」傳遞給子模塊,形成一個樹狀分流結構。如果我們把redux比作整個應用的「心肺」 (redux的flux功能像心臟,reducer功能像肺部毛細血管),那麼這個過程可以比作心臟(store)將氧分子(數據)通過動脈毛細血管(props)送到各個器官組織(view組件)末端的view組件,又可以通過flux機制,將攜帶交互意圖信息的action反饋給store。這個過程有點像將攜帶代謝產物的「紅細胞」(action)通過靜脈毛細血管又泵回心臟(store)action流回到store以後,action以參數的形式又被分流到各個具體的reducer組件中,這些reducer同樣構成一個樹狀的hierarchy。這個過程像靜脈血中的紅細胞(action)被運輸到肺部毛細血管(reducer組件)接收到action後,各個child reducer以返回值的形式,將最新的state返回給parent reducer,最終確保整個單例store的所有數據是最新的。這個過程可以比作肺部毛細血管的血液充氧後,又被重新泵回了心臟」
而 vuex 的思路則不同,任何組件都隨時可以通過 this.$store.state.xxx 獲取 store 上的數據,更自由,從 store 實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態:
computed: { keyboard () { return this.store.state.keyboard } }調用 store.commit 提交 payload修改數據 或者 store.dispatch 提交 mutation 間接修改 store 上的數據, commit 和 dispatch 的區別在於 commit 用於同步修改狀態, dispatch 用於異步修改狀態,異步完成需要再調用 commit,一般簡單的需求只需要 commit 一個 payload 就行,只要 store 上的數據變了,組件都會自動重新渲染
6、開發瀏覽自動打開 http://localhost:8080多語言 在 i18n.json 配置多語言環境,使用"lan"參數匹配語言如:https://Binaryify.github.io/vue-tetris/?lan=en和http://binaryify.github.io/vue-tetris/?lan=zh7、幸福時刻8、一起嗨起來戳左下"閱讀原文"即可玩一玩!