Skip to content

chvin/react-tetris

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
March 2, 2019 18:57
February 6, 2017 16:08
src
March 2, 2019 18:56
February 6, 2017 16:08
December 20, 2016 23:48
December 23, 2016 10:14
December 8, 2018 00:54
May 12, 2019 05:22
April 2, 2018 19:47

English introduction

Please view README-EN.md


用React�Redux�Immutable�俄罗斯方�


俄罗斯方�是一直�类程�语言热衷实现的�典游�,JavaScript的实现版本也有很多,用React �好俄罗斯方�则�了我一个目标。

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


效果预览

效果预览

正常速度的录制,体验�畅。

�应�

�应�

�仅指�幕的自适应,而是在PC使用键盘�在手机使用手指的�应��作:

手机

数��久化

数��久化

玩�机游�最怕什么?断电。通过订阅 store.subscribe,将state储存在localStorage,精确记录所有状�。网页关了刷新了�程�崩溃了�手机没电了,�新打开连接,都�以继续。

Redux 状�预览(Redux DevTools extension)

Redux状�预览

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 中的Object与Array等使用的是引用赋值,新的对象简�的引用了原始对象,改�新也将影�旧的:

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);

是�是很简�?

关于 “===�:

我们知�对于Object与Array的===比较,是对引用地�的比较而�是“值比较�,如:

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

对于上é?¢å?ªèƒ½é‡‡ç”¨ deepCopyã€?deepCompareæ?¥é??历比较,ä¸?仅麻烦且好性能。

我们感��一下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

目标:将state -> Immutable化。 关键的库:gajus/redux-immutable 将原æ?¥ Reduxæ??供的combineReducers改由上é?¢çš„库æ??供:

// rootReducers.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>标签所��到的。在游�进行中按�方�键移动方�,便�以�到高频率的音效。

网页音效进阶

WAA 是一套全新的相对独立的接å?£ç³»ç»Ÿï¼Œå¯¹éŸ³é¢‘文件拥有更高的处ç?†æ?ƒé™?以å?Šæ›´ä¸“业的内置音频效果,是W3C的推è??接å?£ï¼Œèƒ½ä¸“业处ç?†â€œéŸ³é€Ÿã€?音é‡?ã€?环境ã€?音色å?¯è§†åŒ–ã€?高频ã€?音å?‘â€?等需求,下图介ç»?了WAA的使用æµ?程。

�程

其中Source代表一个音频�,Destination代表最终的输出,多个Source��出了Destination。 �代�:/src/unit/music.js 实现了ajax加载mp3,并转为WAA,控制播放的过程。

WAA 在�个�览器的最新2个版本下的支�情况(CanIUse)

�览器兼容

�以看到IE阵�与大部分安�机�能使用,其他ok。

Web Audio Api 学习资料:


4�游�在体验上的优化

  • 技术:
    • 按下方å?‘键水平移动和竖直移动的触å?‘频率是ä¸?å?Œçš„,游æˆ?å?¯ä»¥å®šä¹‰è§¦å?‘频率,代替原生的事件频率,æº?代ç ?:/src/unit/event.js ï¼›
    • å·¦å?³ç§»åЍå?¯ä»¥ delay 掉è?½çš„速度,但在撞墙移动的时候 delay çš„ç¨?å°?;在速度为6级时 通过delay 会ä¿?è¯?在一行内水平完整移动一次;
    • 对按钮å?Œæ—¶æ³¨å†Œtouchstartå’Œmousedown事件,以供å“?应å¼?游æˆ?。当touchstartå?‘生时,ä¸?会触å?‘mousedown,而当mousedownå?‘生时,由于鼠标移开事件元素å?¯ä»¥ä¸?触å?‘mouseup,将å?Œæ—¶ç›‘å?¬mouseout 模拟 mouseup。æº?代ç ?:/src/components/keyboard/index.jsï¼›
    • 监å?¬äº† visibilitychange 事件,当页é?¢è¢«éš?è—?\切æ?¢çš„æ—¶å€™ï¼Œæ¸¸æˆ?å°†ä¸?会进行,切æ?¢å›žæ?¥å°†ç»§ç»­ï¼Œè¿™ä¸ªfocus状æ€?也被写进了Redux中。所以当用手机玩æ?¥ç”µè¯?时,游æˆ?进度将ä¿?存;PCå¼€ç?€æ¸¸æˆ?干别的也ä¸?会å?¬åˆ°gameover,这有点åƒ? ios 应用的切æ?¢ã€‚
    • 在任æ„?时刻刷新网页,(比如消除方å?—æ—¶ã€?游æˆ?结æ?Ÿæ—¶ï¼‰ä¹Ÿèƒ½è¿˜åŽŸå½“å‰?状æ€?ï¼›
    • 游æˆ?中唯一用到的图片是image,其他都是CSSï¼›
    • 游æˆ?兼容 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就好;而跳出框架å?ˆæœ‰äº§å“?的很多特点å?¯ä»¥çŽ©ï¼Œä¸ºäº†è¾¾åˆ°ä½ çš„éœ€æ±‚ï¼Œè¿™äº›éƒ½å°†è‡ªç„¶çš„æŽ¨è¿›æŠ€æœ¯çš„å?‘展。
  • 一个项目从零开始,功能一点一滴慢慢累积,就会盖æˆ?高楼,ä¸?è¦?ç•?难,有想法就敲起æ?¥å?§ã€‚ ^_^

7�控制�程

控制�程


8�开�

安装

npm install

�行

npm start

�览自动打开 http://127.0.0.1:8080/

多语言

在 i18n.json �置多语言环境,使用"lan"�数匹�语言如:https://chvin.github.io/react-tetris/?lan=en

打包编译

npm run build

在build文件夹下生�结果。

About

Use React, Redux, Immutable to code Tetris. 🎮

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published