-
Functions that work with
assets
- Change file structure
getKeyContent
- Remove unnecessary code
- Data filling
- Maintainable, extensible
When you write code initially, it is like a draft. You do it fast and your only goal is to make it working and to see how things fits to each other. After a while you do a revision of your code, and edit it, making it clearer, shorter, more reusable, split it into logical modules.
It is good to move out from components everything without this
to some modules (utils). Another hint for you is a length of file: a module with more than 100 lines is an applicant to been split.
If you look at our project file structure, you'll notice, that there are main code and assets
-- additional files that we read and use from components, e.g. keyboard data files and audios.
It is good to move functionality related to external files into separate module. Maybe we decide to change our file structure in the future, and it will be easy to rewrite paths to files in one place, instead of seeking for them in all components.
Functions that work with assets
getAudioFileName
and getKeyLabels
If you remember, on top of Keyboard.js
and Key.js
we placed functions, that aren't part of components. Create in the project root directory a new file utils.js
and move them there. "Move" means cut (copy and delete ctrl+x
) from components, and paste to utils.js
).
utils.js
export const getAudioFileName = (keyContent, shiftKey) => {
...
}
export const getKeyLabels = keyContent => {
...
}
Write export
before each function, so we can import them from other files.
On top of Keyboard.js
add line:
import { getAudioFileName } from '../utils.js'
On top of Key.js
add line:
import { getKeyLabels } from '../utils.js'
Code of 2 components became clearer.
Open the app. It should work as before.
loadKeyboardData
Look at Keyboard
method getKeyboardData
:
Keyboard.js
async getKeyboardData(lang) {
const { default: keyboardData } = await import(
`../keyboardData/${lang}.js`
)
this.keyboardData[lang] = keyboardData
}
There is an operation with an asset file -- keyboard data. Let's move it to utils.
Add to utils.js
a new function:
utils.js
export const loadKeyboardData = async lang => {
const { default: keyboardData } = await import(`../keyboardData/${lang}.js`)
return keyboardData
}
In Keyboard.js
import it, and use inside getKeyboardData
method:
Keyboard.js
import { getAudioFileName, /* add: */loadKeyboardData } from '../utils.js'
...
methods: {
...
async getKeyboardData(lang) {
this.keyboardData[lang] = await loadKeyboardData(lang)
},
...
}
Now we get keyboard data in loadKeyboardData
, so the name getKeyboardData
isn't intuitive as before. Let's rename it to setKeyboardData
. In Keyboard.js
find/replace old name with the new one (ctrl+h
, Replace All). There were only 3 matches.
Open the app, it should work as before.
playKeyAudio
In Keyboard.js
method playKey
look at function playKeyAudio
.
Keyboard.js
const playKeyAudio = (lang, code, shiftKey) => {
const keyContent = this.getKeyContent(lang, code)
const fileName = getAudioFileName(keyContent, shiftKey)
const audio = new Audio(`../keyboardData/${lang}/${fileName}.mp3`)
return audio.play()
}
It also has a link to an external file, so let's move it to utils.js
.
In utils.js
there is no this
, because this
is a component itself, so we should rewrite playKeyAudio
without this
.
utils.js
export const playKeyAudio = (lang, keyContent, shiftKey) => {
const fileName = getAudioFileName(keyContent, shiftKey)
const audio = new Audio(`../keyboardData/${lang}/${fileName}.mp3`)
return audio.play()
}
In top of Keyboard.js
import this new function, and remove getAudioFileName
import, because we use it now only in utils.
Keyboard.js
import { playKeyAudio, loadKeyboardData } from '../utils.js'
...
methods: {
...
playKey(keyContent) {
const { code } = keyContent
const { shiftKey, currentLang } = this
playKeyAudio(currentLang, keyContent, shiftKey).catch(() => {
// fallback
if (this.currentLang !== 'en') {
const keyContent = this.getKeyContent('en', code)
playKeyAudio('en', keyContent, shiftKey)
}
})
},
}
Open your app. It should work as before.
Conclusion
By moving all functions working with external files (assets) we achieved:
- clarity of components -- they became shorter and clearer
- flexibility of the whole project.
E.g. now we can change file structure of the project easily, because every link to assets are placed in one module. We can add new assets into game rapidly, develop advanced methods to work with them etc. It is easy to do, because all logic related to assets are placed in one place.
Change file structure
In the folder keyboardData
create a folder sounds
and move there en
, ru
, ar
folders.
In utils
for path in playKeyAudio
add /sounds/
:
utils.js
const audio = new Audio(`../keyboardData/sounds/${lang}/${fileName}.mp3`)
Open the app. It should work as before.
Despite the fact that we have changed file structure of our project, we are not afraid that we forget to change some path somewhere in components. Because every functions that use files are placed compactly in one module.
getKeyContent
getKeyContent
looks like something working outside components, even thought it doesn't work with assets. It does universal calculation for any keyboardData
and key code
. Let's move it to utils
, and extend its functionality:
utils.js
export const getKeyContent = ({ keyboardData, code = '', value = '' }) => {
return keyboardData.flat().find(elem => {
const { main, shifted } = elem
return elem.code === code || value === main || value === shifted
})
}
We replaced argument lang
with keyboardData
to remove this
from the function. Now, before every call of getKeyContent
, we should rewrite code using lang
and this
to calculate keyboardData
.
We replaced set of arguments (lang, code)
with the object { keyboardData, code, ... }
-- to not care about their ordering anymore.
We added a new argument value
- to find keyContent
not only by its code, but also by its value.
And we specified default values for code
and value
: { code = '', value = '' }
to prevent errors when only one of them is specified.
Let's rewrite Keyboard
using new version of getKeyContent
.
Keyboard.js first lines
import { playKeyAudio, loadKeyboardData, /* add: */ getKeyContent } from '../utils.js'
...
Find (ctrl+f
) all getKeyContent
matches in Keyboard.js
, and replace it with the rewritten imported function, with new arguments:
Keyboard.js mounted
/* replace:
const keyContent = this.getKeyContent(this.currentLang, code)
with next 2 lines */
const keyboardData = this.keyboardData[this.currentLang]
const keyContent = getKeyContent({ keyboardData, code })
Keyboard.js methods playKey
/* replace:
const keyContent = this.getKeyContent('en', code)
with next 2 lines */
const keyboardData = this.keyboardData['en']
const keyContent = getKeyContent({ keyboardData, code })
Now we have the universal advanced function to work with keyboardData
, that we can improve easily in the future. We will use it chapter 15 to add new feature to the app.
Open the app. It should work as before.
Remove unnecessary code
In index.html
remove all commented code inside <body>
tag. We don't need it anymore.
index.html
<body>
<div id="app">{{ message }}</div>
<script src="./index.js" type="module"></script>
</body>
In App.js
remove from the template first line App-{{currentLang}}
.
In Keyboard.js
remove from the template first 2 lines:
<div>activeKey: {{activeKey}}</div>
<div>shiftKey: {{shiftKey}}</div>
Data filling
The joyful moment has come to see the entire keyboard.
Download the project repo archive and unzip it. Remove folder keyboardData
from your project. Copy folder keyboardData
from the downloaded project and put it to your project root folder. In other words -- replace your old incomplete data with the new complete data.
Result
3 language keyboards with 6 rows and 80 keys should work just like before our 3 test rows with 15 keys worked.
Maintainable, extensible
Notice, that adding new data didn't affect on our main code base. We didn't do any extra steps to make the app working. It just works itself, because we separated main code from data.
It's been said, that our app is:
- maintainable
- extensible
If someone tomorrow will send to us a new keyboard data: Japanese, Hebrew, Georgian, Armenian, Hindi, Thai... We can upgrade our app in 1 minute. In other words: we can maintain and extend the app easily.
Top comments (0)