用 React+Redux+Immutable 做俄罗斯方块

俄罗斯方块是一向各种顺序语言热中完成的典范游戏,JavsScript的完成版本也有许多,用React 做好俄罗斯方块则成了我一个目的。

https://chvin.github.io/react-tetris 玩一玩!

开源地点:https://github.com/chvin/react-tetris

效果预览

《用 React+Redux+Immutable 做俄罗斯方块》

一般速率的录制,体验流通。

响应式

《用 React+Redux+Immutable 做俄罗斯方块》

不仅指屏幕的自适应,而是在PC运用键盘、在手机运用手指的响应式操纵

《用 React+Redux+Immutable 做俄罗斯方块》

数据耐久化

《用 React+Redux+Immutable 做俄罗斯方块》

玩单机游戏最怕什么?断电。经由历程定阅 store.subscribe,将state贮存在localStorage,准确纪录一切状况。网页关了革新了、顺序崩溃了、手机没电了,从新翻开衔接,都可以继承。

Redux 状况预览

《用 React+Redux+Immutable 做俄罗斯方块》

Redux设想管理了一切应存的状况,这是上面耐久化的保证。

游戏框架运用的是 React + Redux,个中再加入了 Immutable,用它的实例来做来Redux的state。(有关React和Redux的引见可以看:React入门实例Redux中文文档

1、什么是 Immutable?

Immutable 是一旦建立,就不能再被变动的数据。对 Immutable 对象的任何修正或增添删除操纵都邑返回一个新的 Immutable 对象。

初识

让我们看下面一段代码:

function keyLog(touchFn) {
  let data = { key: 'value' };
  f(data);
  console.log(data.key); // 猜猜会打印什么?
}

不检察f,不晓得它对 data 做了什么,没法确认会打印什么。但假如 data 是 Immutable,你可以肯定打印的是 value

function keyLog(touchFn) {
  let data = Immutable.Map({ key: 'value' });
  f(data);
  console.log(data.get('key'));  // value
}

JavaScript 中的ObjectArray等运用的是援用赋值,新的对象简朴的援用了原始对象,改变新也将影响旧的:

foo = {a: 1};  bar = foo;  bar.a = 2;
foo.a // 2

虽然如许做可以勤俭内存,但当运用庞杂后,造成了状况不可控,是很大的隐患,勤俭的内存长处变得得不偿失。

Immutable则不一样,响应的:

foo = Immutable.Map({ a: 1 });  bar = foo.set('a', 2);
foo.get('a') // 1

简约

Redux中,它的最优做法是每一个reducer都返回一个新的对象(数组),所以我们经常会看到如许的代码:

// reducer
...
return [
   ...oldArr.slice(0, 3),
   newValue,
   ...oldArr.slice(4)
];

为了返回新的对象(数组),不能不有上面新鲜的模样,而在运用更深的数据构造时会变的更辣手。
让我们看看Immutable的做法:

// reducer
...
return oldArr.set(4, newValue);

是否是很简约?

关于 “===”

我们晓得关于ObjectArray===比较,是对援用地点的比较而不是“值比较”,如:

{a:1, b:2, c:3} === {a:1, b:2, c:3}; // false
[1, 2, [3, 4]] === [1, 2, [3, 4]]; // false

关于上面只能采纳 deepCopydeepCompare`来遍历比较,不仅贫苦且好机能。

我们感觉来一下Immutable的做法!

map1 = Immutable.Map({a:1, b:2, c:3});
map2 = Immutable.Map({a:1, b:2, c:3});
Immutable.is(map1, map2); // true

// List1 = Immutable.List([1, 2, Immutable.List[3, 4]]);
List1 = Immutable.fromJS([1, 2, [3, 4]]);
List2 = Immutable.fromJS([1, 2, [3, 4]]);
Immutable.is(List1, List2); // true

好像有阵清风吹过。

React 做机能优化时有一个大招,就是运用 shouldComponentUpdate(),但它默许返回 true,即始终会实行 render() 要领,背面做 Virtual DOM 比较。

在运用原生属性时,为了得出shouldComponentUpdate准确的true or false,不能不必deepCopy、deepCompare来算出答案,斲丧的机能很不划算。而在有了Immutable以后,运用上面的要领对深层构造的比较就变的轻而易举。

关于「俄罗斯方块」,试想棋盘是一个二维数组,可以挪动的方块则是外形(也是二维数组)+坐标。棋盘与方块的叠加则组成了末了的效果Matrix。游戏中上面的属性都由Immutable构建,经由历程它的比较要领,可以轻松写好shouldComponentUpdate。源代码:/src/components/matrix/index.js#L35

Immutable进修材料:

2、如安在Redux中运用Immutable

将本来 Redux供应的combineReducers改由上面的库供应:

// rootReduers.js
// import { combineReducers } from 'redux'; // 旧的要领
import { combineReducers } from 'redux-immutable'; // 新的要领

import prop1 from './prop1';
import prop2 from './prop2';
import prop3 from './prop3';

const rootReducer = combineReducers({
  prop1, prop2, prop3,
});


// store.js
// 建立store的要领和通例一样
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);
export default store;

经由历程新的combineReducers将把store对象转化成Immutable,在container中运用时也会略有差别(但这正是我们想要的):

const mapStateToProps = (state) => ({
  prop1: state.get('prop1'),
  prop2: state.get('prop2'),
  prop3: state.get('prop3'),
  next: state.get('next'),
});
export default connect(mapStateToProps)(App);

3、Web Audio Api

游戏里有许多差别的音效,而实际上只援用了一个音效文件:/build/music.mp3。借助Web Audio Api可以以毫秒级准确、高频次的播放音效,这是<audio>标签所做不到的。在游戏举行中按住方向键挪动方块,便可以听到高频次的音效。

《用 React+Redux+Immutable 做俄罗斯方块》

WAA 是一套全新的相对自力的接口体系,对音频文件具有更高的处置惩罚权限以及更专业的内置音频效果,是W3C的引荐接口,能专业处置惩罚“音速、音量、环境、音色可视化、高频、音向”等需求,下图引见了WAA的运用流程。

《用 React+Redux+Immutable 做俄罗斯方块》

个中Source代表一个音频源,Destination代表终究的输出,多个Source合成出了Destination。
源代码:/src/unit/music.js 完成了ajax加载mp3,并转为WAA,掌握播放的历程。

WAA 在各个浏览器的最新2个版本下的支撑状况(CanIUse

《用 React+Redux+Immutable 做俄罗斯方块》

可以看到IE阵营与大部分安卓机不能运用,其他ok。

Web Audio Api 进修材料:

4、游戏在体验上的优化

手艺:

  • 按下方向键程度挪动和竖直挪动的触发频次是差别的,游戏可以定义触发频次,替代原生的事宜频次,源代码:/src/unit/event.js

  • 摆布挪动可以 delay 掉落的速率,但在撞墙挪动的时候 delay 的稍小;在速率为6级时 经由历程delay 会保证在一行内程度完全挪动一次;

  • 对按钮同时注册touchstartmousedown事宜,以供响应式游戏。当touchstart发作时,不会触发mousedown,而当mousedown发作时,由于鼠标移开事宜元素可以不触发mouseup,将同时监听mouseout 模仿 mouseup`。源代码:/src/components/keyboard/index.js

  • 监听了 visibilitychange 事宜,当页面被隐蔽切换的时候,游戏将不会举行,切换返来将继承,这个focus状况也被写进了Redux中。所以当用手机玩来电话时,游戏进度将保留;PC开着游戏干别的也不会听到gameover,这有点像 ios 运用的切换。

  • 恣意时候革新网页,(比方消弭方块时、游戏结束时)也能复原当前状况;

  • 游戏中唯一用到的图片,其他都是CSS;
    《用 React+Redux+Immutable 做俄罗斯方块》

  • 游戏兼容 Chrome、Firefox、IE9+、Edge等;

弄法:

  • 可以在游戏未开始时制订初始的棋盘(十个级别)和速率(六个级别);

  • 一次消弭1行得100分、2行得300分、3行得700分、4行得1500分;

  • 方块掉落速率会跟着消弭的行数增添(每20行增添一个级别);

5、开辟中的履历梳理

  • 为一切的component都编写了shouldComponentUpdate,在手机上的机能相对有明显的提拔。中大型运用在碰到机能上的题目的时候,写好shouldComponentUpdate 一定会帮你一把。

  • 无状况组件`(Stateless Functional Components)是没有生命周期的。而由于上条要素,一切组件都须要生命周期 shouldComponentUpdate,所以未运用无状况组件。

  • 在 webpack.config.js 中的 devServer属性写入host: ‘0.0.0.0’`,可以在开辟时用ip接见,不范围在localhost;

  • redux中的store并不是只能经由历程connect将要领传递给container,可以跳出组件,在别的文件拿出来做流程掌握(dispatch),源代码:/src/control/states.js

  • 用 react+redux 做耐久化异常的轻易,只要将redux状况贮存,在每一个reduers做初始化的时候读取就好。

  • 经由历程设置 .eslintrc.js 与 webpack.config.js ,项目中集成了 ESLint` 磨练。运用 ESLint 可以使编码按范例编写,有效地掌握代码质量。不符范例的代码在开辟时(或build时)都能经由历程IDE与掌握台发明毛病。 参考:Airbnb: React运用范例

6、总结

  • 作为一个 React 的练手运用,在完成的历程当中发明小小的“方块”照样有许多的细节可以优化和打磨,这时候就是磨练一位前端工程师的仔细和功力的时候。

  • 优化的方向既有 React 的自身,比方哪些状况由 Redux存,哪些状况给组件的state就好;而跳出框架又有产物的许多特性可以玩,为了到达你的需求,这些都将天然的推动手艺的生长。

  • 一个项目从零开始,功用一点一滴逐步积累,就会盖成高楼,不要畏难,有主意就敲起来吧。 ^_^

    原文作者:Chvin
    原文地址: https://segmentfault.com/a/1190000007878885
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞