“First, you deny him, then you hate him, and then you cannot live without him.”
from the report of Artem Kurbatov "BEM: master class"
The BEM methodology exists for quite a long time and has been adopted by Google, EPAM Systems, BBC, Alfa-Bank. However, it is still of concern to the typical developer and mid-level project manager.
For some brave souls, studying BEM did not go beyond limiting the possibilities of CSS to produce more predictable results. And although BEM has long gone beyond the layout, still the question “Do you know BEM?” Can be heard: “Of course, this is about underscores in classes.”
If your understanding of BEM is close to this, I will answer you with the words of the employer when hiring a new graduate: “Forget about what you heard about BEM before.” The BEM methodology is as interesting as the majority knows nothing about it. To understand the beauty of BEM, you need to be aware of all the technologies, libraries, frameworks and tools that BEM provides. Examine them, remain an alien, a child who is surprised at what adults have come to terms with.
What is BEM for me?
Surely you have heard the stories, as daring geeks, having closed in the garage, invented another startup. The first successes allowed them to attract investment and enter the market with a stunning product. How did they manage to do this, how did they manage to withstand the peak loads and not get bogged down in eternal refactoring? How to not to as usual?
BEM is a life hack for startups. Compared to many other frameworks and technologies, starting to work with BEM requires some effort, but it pays off with architecture that provides sustainable extensibility. All technologies, frameworks and libraries of BEM initially laid down the principles of a declarative approach. And this is what makes them so incomprehensible at first glance and loved in the end.
After reading this article, you will not comprehend Zen BEM, but you will definitely be able to respond to adults that they learned how to create full-fledged dynamic BEM projects using not only CSS. And if tomorrow your application will suffer the fate of +10 K users per day, be calm - you will have time to celebrate this event.
Today we will talk about the new template repository for dynamic projects bem-express , which not only allows you to deploy a BEM project in one click, but also does automatic reassembly of the project and restarting the browser. On its basis, we will develop a dynamic application and describe the interaction process of various BEM technologies. We will deliberately not consider the issues of typesetting and client-side JavaScript, in order to avoid the fate of “underscore in classes”.
What will we develop?
Search application Social Services Search Robot (abbr. SSSR), which on demand will show the latest tweets and videos from YouTube.
We will use:
First you need to install:
Important! Users of the Windows operating system must additionally install Git Bash .
To make the article brighter, we will paint a little:
In BEM, there is no separation of technology into major and minor. There is a set, and the choice of application is determined individually:
Learn more about the BEMJSON input format .
Let's look at them in more detail.
Defines a list of BEM entities for the page.
Such a list in BEM is called a declaration . The task of the declaration is to determine what and in what order to connect to the assembly.
Declarations are described in files with the .bemdecl.js
extension.
Example
exports.blocks = [ { name: 'page' }, { name: 'header' }, { name: 'body' }, { name: 'footer' } ];
When the number of blocks goes over the line “easy to remember”, the problem arises of enumerating them in the necessary order. Therefore, they usually declare a particular block, which should be considered as the central "entry point".
All other BEM entities fall into an assembly according to dependencies .
Defines dependencies between BEM entities that are spaced apart in the file structure of the project and not reflected in the declaration.
Dependencies are described as a JavaScript object in files with the .deps.js
extension.
Example
/* page header */ ({ block: 'page', shouldDeps: [ { block: 'header' } ] })
The name of the technology comes from the English word dependence and indicates the desire to connect to the assembly some kind of BEM entity. Students of BEM sometimes forget about the declarativeness of technologies and wonder: why for the block described in the template, its styles and scripts are not collected.
Remember: when you describe a template ( BEMHTML or BEMTREE ) with some kind of block inside (a child node), you simply add a new HTML element. In order for the styles and scripts of this block to fall into an assembly, it is necessary to describe the dependency on it.
For example, in order to add blocks header
, body
and footer
to an assembly, it is necessary to describe dependence on them:
/* page header, body, footer */ ({ block: 'page', shouldDeps: [ 'header', 'body', 'footer' ] })
The following diagram shows the dependency build logic:
index(DECL) # page | └──> page(DEPS) # page header, body, footer | ├──> header(DEPS) | | | └──> ... | ├──> body(DEPS) | | | └──> ... | └──> footer(DEPS) | └──> ...
It is part of the bem-xjst template engine and converts data to BEMJSON.
Templates are described in BEMJSON format in files with the .bemtree.js
extension.
Template input and output:
It is part of the bem-xjst template engine and converts the BEMJSON description of the page into HTML.
Templates are described in files with the .bemhtml.js
extension.
Template input and output:
Client - side JavaScript framework for web development within the framework of the BEM methodology.
JavaScript code is described in files with a .js
extension.
Allows:
Programmers have a tradition: start programming in a new language or framework with the Hello, World application. The application typically displays the words “Hello, World” in the output stream, thereby demonstrating that it starts and performs I / O operations.
Let's create it and then expand it to the desired SSSR.
We need a local copy of the bem-express template repository . It can be done with git.
Note. For OS X or Linux users, all commands are executed in the terminal. Windows users will need Git Bash. Make sure Git Bash is running as administrator.
When solving problems of developing dynamic applications within BEM, a template repository bem-express was created . It contains the necessary minimum of configuration files and solves a whole class of tasks, such as building a project, setting up linters, connecting libraries, etc.
By default, the main BEM libraries are connected to it:
To create the Hello, World application:
Clone bem-express:
git clone https://github.com/bem/bem-express.git sssr-project
Note. This example uses bem-express
version 2.00.
Go to the project directory:
cd sssr-project
Delete the versioning history of the source repository:
rm -rf .git
Initialize your own Git repository:
git init
Install dependencies:
npm install
Note. Do not use root root
when installing npm dependencies.
Build the project and start the server:
npm run dev
Note. For the assembly is responsible ENB .
When you start the application in the terminal, a message appears stating that the server is running on port 3000:
Server is listening on 3000
.
Note. If port3000
used by another program, it can be reassigned. For example, at8000
:
Method 1. Change the value when you start the application.PORT=8000 npm run dev
Method 2: Change the default value in theserver/config.js
.defaultPort: 8000,
On the computer started:
*.blocks/
and rebuilds the structure of the project;Open a browser and enter the address localhost: 3000 .
A page should open with the following content:
Index page content footer content
Note. If, when the application is launched in Windows, a notification from the Firewall is displayed:
- Disable the Public Network option.
- Install the Private Network option.
- Allow access.
Open the server/index.js
and make the following changes (see comments) to the code beginning with the line app.get('/', function(req, res)
:
/** * GET- * @function * @param {object} req - . * @param {object} res - . */ app.get('/', function(req, res) { var hello = 'Hello'; // `hello` var world = 'World'; // `world` render(req, res, { view: 'page-index', title: 'Main page', meta: { description: 'Page description', og: { url: 'https://site.com', siteName: 'Site name' } }, hello: hello, // `hello` `this.data.hello` world: world // `world` `this.data.world` }) });
Open the common.blocks/page-index/page-index.bemtree.js
and replace its contents with the following:
block('page-index').content()(function() { // `this` var data = this.data; // : `data.hello: 'Hello'`, `data.world: 'World'` return data.hello + ', ' + data.world; });
After saving, the server will automatically restart and the page content will change to:
Hello, World footer content
Hello, World app is ready.
Did not work out?
If you are having difficulty creating an application, look for a solution on the forum . If there is no ready answer, ask a question.
Actually, we have reached the development stage of the SSSR application. Let me remind you that on request the application will display the latest tweets and videos from YouTube.
Immediately we will run into the near future - the application will look like this:
The user sends a request to the server.
The application applies for data to the Twitter Search API and the YouTube Data API in accordance with the received request.
The application passes the received data to a BEMTREE template that converts the data to BEMJSON.
The application sends a BEMJSON BEMHTML template engine that converts BEMJSON to HTML.
The application returns the result (HTML page) to the user.
The basic implementation of Node remains as simple as possible. Instead of embedding all the possible components directly in Node, the developers provide additional functionality in the form of separate modules (packages).
The Node module system is modeled on the CommonJS system, the mechanism for creating interacting modules. A central place in the system is occupied by a contract that must be carried out by developers in order for their modules to interact normally with others.
All packages installed using the npm package manager are located in the node_modules
directory.
The modules are connected using the require
command. If the package is installed using npm, you do not need to specify the path. It is enough to specify the name:
var express = require('express');
When you connect your own local module, you must specify the path to it:
var someModule = require('./somefolder/somemodule');
An important feature of any module is that it must be designed to interact with Node. To do this, the module must be exported using module.exports
:
module.exports = { // some module };
The application will require the following modules:
You can install them with one command:
npm install express passport passport-youtube-v3 twitter googleapis moment --save
Before starting to write code, we will slightly change the structure of the Hello, World application taken as the basis.
Changes for:
static
directory
Create an images
subdirectory.
images
subdirectory.common.blocks
directory
Edit the root/root.bemtree.js
.
Change:
favicon: '/favicon.ico',
On:
favicon: '/images/favicon.ico',
server
directory
Edit the index.js
file.
Change:
.use(favicon(path.join(staticFolder, 'favicon.ico')))
On:
.use(favicon(path.join(staticFolder, '/images/favicon.ico')))
server
directory
Create subdirectories:
controllers
- controllers;helpers
- helpers;middleware
- intermediate modules.Create empty JS
files for future modules:
app.js
- the module for mounting intermediate modules (makes them available in the application);auth.js
- authentication module on YouTube;routes.js
is a web request routing module.Add the following code to the app.js
file.
Add the following code to the routes.js
file.
Change the config
file extension:
config.js
-> config.json
Edit the config.json
file.
Change:
module.exports = { staticFolder: 'static', defaultPort: 3000, cacheTTL: 30000, sessionSecret: 'REPLACE_ME_WITH_RANDOM_STRING' };
On:
{ "staticFolder": "static", "defaultPort": 3000, "cacheTTL": 30000, "sessionSecret": "REPLACE_ME_WITH_RANDOM_STRING" }
Change the entire current content of the index.js
file to the next .
Note. In index.js
, only the functionality responsible for launching the application and listening to requests on the port remains.
Directory controllers
Create an empty JS
file:
index.js
is a request processing and HTML rendering controller.index.js
file.helpers
directory
Create empty JS
files:
index.js
- entry point for helpers;twitter.js
- helper module for working with Twitter Search API;youtube.js
is a helper module for working with the YouTube Data API.Directory middleware
Create an empty JS
file:
auth.js
is a YouTube authentication verification module.Twitter and Google services store a variety of user data — tweets, Youtube videos, Mail messages, photos, and so on. To provide convenient access to this data from other applications or third-party services, they use the open authorization protocol OAuth 2.0 .
According to the protocol, the developer registers the application on the OAuth server and requests access to certain data. An authorized user permits or denies it.
Twitter offers applications the ability to issue authenticated requests on behalf of the application itself.
Where to begin?
Consumer Key:Consumer Secret
using the Base64 method.Use received tokens and keys in the Twitter Search API requests.
Note. Postman will help you get an OAuth token using a POST request in exchange for code derived from the Base64 method.
To encode a string using the Base64 method:
Create a string like: Consumer Key:Consumer Secret
.
Example
xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg
Note. You can get the Consumer Key and Consumer Secret keys by going to the Keys and Access Tokens tab of your application .
echo -n "xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg" | base64
echo -n "xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg" | base64
.Copy the resulting code.
Example
eHZ6MWV2RlM0d0VFUFRHRUZQSEdFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==
Note. If you are having difficulty, use the online resource base64encode.org .
To get a token in exchange for a code:
Run Postman.
Note. By default, a tab opens in which you need to form a POST request to the Twitter OAuth server.
https://api.twitter.com/oauth2/token
.Enter the Authorization
header with a value (Value field) Basic < Consumer Key:Consumer Secret>
in the Key field.
Example
Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEdFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==
Note. Basic indicates the basic authentication method.
Enter a second Content-Type
header with the value application/x-www-form-urlencoded;charset=UTF-8
.
Example
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
x-www-form-urlencoded
option.grant_type
with the value client_credentials
.Click the Send button.
The OAuth server will return the token in JSON format:
{ "token_type": "bearer", "access_token": "AAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAA" }
Important! Save received tokens and keys (Consumer Key and Consumer Secret). They are required for the application configuration file .
Google offers applications the ability to issue authenticated requests on behalf of the application itself.
Note. The module passport-youtube-v3 is responsible for receiving and updating the OAuth token using a POST request in exchange for an authorization code.
Where to begin?
http://localhost:3000
) in your application account.Important! Save the received keys (Client ID and Client Secret). They are required for the application configuration file .
After all the keys and tokens are received, they must be added to the application configuration file:
Add the services
field to the server/config.json
file.
"services": { "twitter": { "consumer_key": "", "consumer_secret": "", "bearer_token": "" }, "youtube": { "client_id": "", "client_secret": "", "redirect_url": "http://localhost:3000" } }
Hide the server/config.json
from the Git version control system in order not to accidentally add private keys to the file repository.
# .gitignore server/config.json
The Twitter Search API allows you to find the latest or most popular tweets posted on Twitter.com in the last 7 days.
For a successful API call, you need to make the following changes:
Directory controllers
index.js
file to the next .helpers
directory
Add the following content to the index.js
file:
module.exports = { twitter: require('./twitter') };
twitter.js
file.YouTube Data API allows you to find videos published on Youtube.com. By default, the following resources are included in the search result set: video, channels, playlists.
For a successful API call, you need to make the following changes:
server
directory
Add the following code to the auth.js
file.
Edit the routes.js
file.
Change:
var router = require('express').Router(), controllers = require('./controllers'); router .get('/ping/', function(req, res) { res.send('ok'); }) .get('/', controllers.getContent); module.exports = router;
On:
var router = require('express').Router(), controllers = require('./controllers'), passportYouTube = require('./auth'), middleware = require('./middleware/auth'), isAuthenticated = middleware.isAuthenticated; router .get('/auth/youtube', passportYouTube.authenticate('youtube')) .get('/auth/youtube/callback', passportYouTube.authenticate('youtube', { failureRedirect: '/error', failureFlash: true }), (req, res) => { res.redirect('/'); }) .get('/', isAuthenticated, controllers.getContent); module.exports = router;
Directory controllers
index.js
file to the next .helpers
directory
Add the following content to the index.js
file (see comment):
module.exports = { twitter: require('./twitter'), youtube: require('./youtube') // `youtube.js` };
youtube.js
file.Directory middleware
Add the following content to the auth.js
file:
module.exports = { isAuthenticated: function(req, res, next) { if (req.isAuthenticated()) return next(); return res.redirect('/auth/youtube'); } };
We deliberately did not consider the issues of layout and client-side JavaScript. This would lead to a larger volume, and, therefore, to a lesser practical value of this article.
The layout process is reduced to the following steps:
common.blocks
directory.common.blocks
directory.static/images
directory.npm run dev
.The Social Services Search Robot application is ready.
Did not work out?
If you are having difficulty creating an application, look for a solution on the forum . If there is no ready answer, ask a question.
Source: https://habr.com/ru/post/337166/
All Articles