Make Tetris by Vue, Vuex, Immutable


This project is inspired by Tetris of React version. Since I am interested in its implementation principle and prefer Vue to React, Refactoring the React version into the Vue version, the general idea is to treat components as functions to ensure that an input (props) can get a certain output (view), and then do the same for different methods. For Redux, Vuex is used to simplify

IO/Vue-Tetris /


Results the preview

Normal speed recording, smooth experience.

responsive

It refers not only to screen adaptation, but also to responsive manipulation using the keyboard on the PC and the finger on the phone:

Data persistence

video

What are you most afraid of playing PC games? Without electricity. State is stored in localStorage by subscribing to Store. subscribe, keeping an accurate record of all states. The web page has been closed and refreshed, the application has crashed, the phone is out of battery, re-open the connection, you can continue.

Vuex Status Preview (Vue DevTools extension)

video

The Vuex design manages all the required state, which is the guarantee of persistence above.


The game framework uses Vue + Vuex, which adds Immutable to ensure performance and data reliability

1. What is Immutable?

Immutable is data that, once created, cannot be changed. Any modification or addition or deletion of an Immutable object returns a new Immutable object.

First:

Let’s look at the following code:

function keyLog(touchFn) { let data = { key: 'value' }; f(data); console.log(data.key); // Guess what will be printed? }Copy the code

Without looking at F, you don’t know what it does to data, and you can’t confirm what will be printed. But if data is Immutable, you can be sure to print value:

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

Object and Array in JavaScript use reference assignment. The new Object simply refers to the original Object. Changing the new Object also affects the old:

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

Although this can save memory, but when the application is complex, resulting in uncontrollable state, is a great hidden danger, saving memory advantages become more than worth the loss.

Immutable is not the same, corresponding:

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

About “= = =” :

=== === === === === =

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

Only deepCopy and deepCompare can be used for traversal comparison, which is not only troublesome but also good performance.

Let’s feel the Immutable way!

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); // trueCopy the code

Immutable mutable

2. Web Audio Api

There are a lot of different sound effects in the game, and only one sound file is actually referenced: /build/music.mp3. The Web Audio Api makes it possible to play sound with millisecond accuracy at high frequencies that the < Audio > tag does not. Hold down the D-pad to move a block while playing and you can hear a high-frequency sound effect.

WAA is a new set of relatively independent interface system, audio files have higher processing permissions and more professional built-in audio effects, is the W3C recommended interface, professional processing of “sound speed, volume, environment, timbre visualization, high frequency, tone orientation” and other requirements, the following figure describes the use of WAA process.

Where Source represents an audio Source, Destination represents the final output, and Destination is synthesized from multiple sources. Source code: / SRC /unit/music.js Ajax load MP3, and to WAA, control the playback process.

WAA support in the latest 2 versions of each browser (CanIUse)

You can see IE camp can’t be used with most Android, other OK.

The Web Audio Api

  • | MDN Web API interface
  • Getting Started with Web Audio API

3. Optimization of game experience


4. Review the experience during development and how to refactor React project into Vue version

The Vue version and React version have the same core code, but there are some problems when writing components. For example:

  1. Stores on the React version use immutable data. Stores on vuex use immutable data instead of listening for changes. Therefore, stores use ordinary data and pass data where they are needed FromJS conversions, provided by imMUTABLE, are performed via immutable toJS where ordinary data is required In the actual refactoring process, I avoided the core game implementation logic as much as possible. In fact, I completed the refactoring without understanding the game implementation logic. It was just a matter of keeping the input and output of the method consistent

  2. To rewrite the React component to a Vue, I thought of the component as a function, making sure that an input (props) gets a definite output (view), and then doing the same for different methods. React setState triggers the render method, so you can manually trigger the render method after the methods custom render method changes state

  3. In simple terms, React componentWillMount corresponds to Vue’s beforeMount React ComponentShouldComponentUpdate is shouldComponentUpdate is not needed in Vue. This is also what I like about Vue

  4. Vue didn’t React componentWillReceiveProps lifecycle, my solution is to use a watch with deep: true to monitor the change of the props, such as:

  watch: {
    $props: {
      deep: true,
      handler(nextProps) {
        //xxx
      }
    }
  }Copy the code
  1. Use JSX and render functions when necessary. Yes, Vue supports JSX in this projectMatrix componentThe functional logic is more complex to usetemplateTemplates for rendering components are no longer appropriate, React setState triggers the render method every time, so we can use methods to customize the render method and then manually trigger the render method after the state changes. However, this method can become cumbersome for components with complex logic JSX conversion plug-in for Vuebabel-plugin-transform-vue-jsxThe render method is automatically triggered when props or state changes. Note that there is a slight difference between JSX and React JSX. The template syntax is invalid when the render method is present. One useful use of the render function is that template is redundant when developing components such as react-log that don’t need to render HTML and only need to execute some methods Function, simply return false, as in:react-log

5. Architectural differences

Redux uses mapStateToProps to convert the state of the Store to props, which is then injected into the root component via connect. The root component passes these props to different components. When the state of the store changes, the root component reprocesses the props Render, update the props on the subcomponents, child components according to the new props to render reference zhihu a a friend answer www.zhihu.com/question/47… To say:

The singleton store data in React can be passed “unidirectional” ** * from the parent module to the child modules through the properties (props) of the View component, forming a tree-like branching structure. If we compare Redux to the “heart and lungs” of the entire application (Flux function of Redux is like heart and reducer function is like lung capillaries), This process can be compared to the heart (Store) sending oxygen molecules (data) to the VIEW component at the end of each organ tissue (VIEW component) through arterial capillaries (props), and the action carrying interactive intention information can be fed back to the store through the flux mechanism. This process is similar to that “red blood cells” (action) carrying metabolites are pumped back to the heart (store) through venous capillaries. After the action flows back to the store, it is divided into various reducer components in the form of parameters. These reducer also constitute a tree hierarchy. This process is like the red blood cells (actions) in venous blood being transported to the lung capillaries (reducer components). After receiving the actions, each child reducer returns the latest state to the parent reducer in the form of return values. Finally, ensure that all data for the entire singleton store is up to date. This process can be compared to the capillaries in the lungs where the blood is oxygenated and then pumped back into the heart back to step 1

This.$store.state. XXX allows any component to retrieve data from a store at any time. The simplest way to retrieve state from a store instance is to return a state in a calculated property:

computed: {
    keyboard () {
      return this.store.state.keyboard
    }
  }Copy the code

Call store.mit to submit payload to change data, or store. Dispatch to submit mutation to change data on store. Dispatch is used to change the state asynchronously, and commit is required after asynchronous completion. Generally, simple requirements only need to commit a payload. As long as the data on the store changes, the component will automatically re-render

6, development,

The installation

npm install
Copy the code

run

npm run dev
Copy the code

Browse to automatically open localhost:8080

multilingual

In i18n. Json configuration multi-language environment, using “LAN” parameter matching languages such as: https://Binaryify.github.io/vue-tetris/? lan=en

Package compiled

npm run build
Copy the code

Generate the results under the dist folder.