Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 10052 сообщений
Cообщения с меткой

javascript - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

Дайджест свежих материалов из мира фронтенда за последнюю неделю №225 (22 — 28 августа 2016)

Воскресенье, 28 Августа 2016 г. 22:06 (ссылка)

Предлагаем вашему вниманию подборку с ссылками на полезные ресурсы и интересные материалы из области фронтенда





















Веб-разработка
CSS
Javascript
Браузеры
Новости и занимательное


Веб-разработка



CSS





JavaScript





Браузеры





Новости и занимательное





Просим прощения за возможные опечатки или неработающие/дублирующиеся ссылки. Если вы заметили проблему — напишите пожалуйста в личку, мы стараемся оперативно их исправлять.





Дайджест за прошлую неделю.

Материал подготовили dersmoll и alekskorovin.
Original source: habrahabr.ru.

https://habrahabr.ru/post/308698/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Как я изобретал велосипед, или мой первый MEAN-проект

Воскресенье, 28 Августа 2016 г. 17:26 (ссылка)



Сегодня, в период стремительного развития веб-технологий, опытному фронтэнд-разработчику нужно всегда оставаться в тренде, каждый день углубляя свои познания. А что делать, если Вы только начинаете свой путь в мире веб? Вы уже переболели вёрсткой и на этом не хотите останавливаться. Вас тянет в загадочный мир JavaScript! Если это про Вас, надеюсь данная статья придётся к стати.



Имея за плечами полуторагодовой опыт работы в качестве фронтэнд-разработчика, я, утомившись монотонной вёрсткой очередного рядового проекта, задался целью углубить познания в сфере веб-программирования. У меня возникло желание создать своё первое single page application. Выбор стека технологий был очевиден, так как я всегда был не равнодушен к Node.js, методология MEAN стала тем, что доктор прописал.



Сегодня в интернете существует бесчисленное количество разных туториалов, в которых создают множество приложений helloworld, todo, management agency и т.д. Но просто бездумно следовать шагам туториала — не мой выбор. Я же решил создать некое подобие мессенджера: приложение с возможностью регистрации новых пользователей, созданием диалогов между ними, общения с chat-ботом для тестовых пользователей. И так, тщательно продумав план действий, я приступил к работе.



Далее мой рассказ опишет основные моменты создания данного приложения, а для большей наглядности демо я оставлю тут.



*Также хочу отметить, что цель данной статьи, быть может, помочь обучающимся не наступить на грабли, на которые в своё время наступил я, и дать возможность более опытным разработчикам просмотреть код и высказать своё мнение в комментариях.



Составим план действий:




  1. Подготовительные работы

  2. Создание системы авторизации

  3. Чат на Angular2 и Socket.io



Подготовительные работы



Подготовка рабочего места — это неотъемлемый процесс любой разработки, а качественное выполнение данной задачи — залог успеха в дальнейшем. Первым делом, нужно установить Express и настроить единую систему конфигурирования нашего проекта. Если с первым и так всё понятно, то на втором я остановлюсь по подробнее.



И так, воспользуемся замечательным модулем nconf. Давайте создадим папку с названием config, а в её индексный файл запишем:



const nconf = require('nconf');
const path = require('path');

nconf.argv()
.env()
.file({ file: path.join(__dirname, './config.json') });

module.exports = nconf;


Далее в этой папке создадим файл с названием config.json и внесём в него первую настройку — порт, который слушает наше приложение:



{
"port": 2016
}


Чтоб внедрить данную настройку в приложение, нужно всего ничего, написать одну/две строки кода:



const config = require('./config');

let port = process.env.PORT || config.get('port');
app.set('port', port);


Но стоит отметить, это будет работать в случае, если порт будет задан таким образом:



const server = http.createServer(app);
server.listen(app.get('port'));


Следующая наша задача — настроить единую систему логгирования в нашем приложении. Как писал автор статьи "О логгировании в Node.js":



Писать в логи надо и много, и мало. Настолько мало, чтобы понять в каком состоянии приложение сейчас, и настолько много, чтобы, если приложение рухнуло, понять почему.

Для этой задачи воспользуемся модулем winston:



const winston = require('winston');
const env = process.env.NODE_ENV;

function getLogger(module) {
let path = module.filename.split('\').slice(-2).join('/');
return new winston.Logger({
transports: [
new winston.transports.Console({
level: env == 'development' ? 'debug' : 'error',
showLevel: true,
colorize: true,
label: path
})
]
});
}

module.exports = getLogger;


Конечно, настройка может быть и более гибкой, но на данном этапе нам этого будет достаточно. Чтоб воспользоваться нашим новоиспечённым логгером, нужно всего-ничего подключить данный модуль в ваш рабочий файл и вызвать его в нужном месте:



const log = require('./libs/log')(module); 
log.info('Have a nice day =)');


Следующей нашей задачей станет настройка правильной обработки ошибок при обычных и ajax запросах. Для этого мы внесём некие изменения в код, который заранее был сгенерирован Express (в примере указан только development error handler):



// development error handler
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
if(res.req.headers['x-requested-with'] == 'XMLHttpRequest'){
res.json(err);
} else{
// will print stacktrace
res.render('error', {
message: err.message,
error: err
});
}
});
}


Мы практически закончили с подготовительными работами, осталась одна маленькая, но отнюдь не маловажная деталь: настроить работу с базой данных. Первым делом настроим подключение к MongoDB с помощью модуля mongoose:



const mongoose = require('mongoose');
const config = require('../config');

mongoose.connect(config.get('mongoose:uri'), config.get('mongoose:options'));

module.exports = mongoose;


В mongoose.connect мы передаём два аргумента: uri и options, которые я заранее прописал в конфиге (подробнее о них можно прочесть в документации к модулю).



Процесс создания моделей пользователей и диалогов я описывать не буду, так как схожий процесс отлично описал автор веб-ресурса learn.javascript.ru в своём скринкасте по Node.js в видеоуроке "Создаём модель для пользователя / Основы Mongoose", лишь упомяну, что каждый пользователь будет иметь такие свойства, как username, hashedPassword, salt, dialogs и created. Свойство dialogs, в свою очередь, будет возвращать объект: ключ — id собеседника, значение — id диалога.



Если кому-то всё-таки интересно взглянуть на код данных моделей:



users.js
const mongoose  = require('../libs/mongoose');
const Schema = mongoose.Schema;
const crypto = require('crypto');

let userSchema = new Schema({
username: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
dialogs: {
type: Schema.Types.Mixed,
default: {defaulteDialog: 1}
},
created: {
type: Date,
default: Date.now
}
});

userSchema.methods.encryptPassword = function(password){
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};
userSchema.methods.checkPassword = function(password){
return this.encryptPassword(password) === this.hashedPassword;
}
userSchema.virtual('password')
.set(function(password){
this._plainPassword = password;
this.salt = Math.random() + '';
this.hashedPassword = this.encryptPassword(password);
})
.get(function(){
return this._plainPassword;
});

module.exports = mongoose.model('User', userSchema);
dialogs.js
const mongoose  = require('../libs/mongoose');
const Schema = mongoose.Schema;

let dialogSchema = new Schema({
data: {
type: [],
required: true
}
})

module.exports = mongoose.model('Dialog', dialogSchema);


Осталось всего-ничего — прикрутить сессии к костяку нашего приложения. Для этого создадим файл session.js и подключим в него такие модули, как express-session, connect-mongo, и созданный нами модуль из файла mongoose.js:



const mongoose = require('./mongoose');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);

module.exports = session({
secret: 'My secret key!',
resave: false,
saveUninitialized: true,
cookie:{
maxAge: null,
httpOnly: true,
path: '/'
},
store: new MongoStore({mongooseConnection: mongoose.connection})
})


Выносить данную настройку в отдельный файл важно, но не обязательно. Это предоставит возможность в дальнейшем без особого труда помирить сессии и веб-соккеты между собой. Теперь подключим данный модуль в app.js:



const session = require('./libs/session');
app.use(session);


При чём, app.use(session) обязательно нужно указать после app.use(cookieParser()), чтобы cookie уже успели быть прочитанными. Всё! Теперь мы имеем возможность сохранять сессии в нашу базу данных.



И на этом подготовительные работы — окончены. Пора приступать к самому интересному!



Создание системы авторизации



Создание системы авторизации будет делиться на два основных этапа: фронтэнд и бэкэнд. Так как, затеивая данное приложение, я собирался всё время учить что-то новое, а с Angular1.x я уже имел опыт работы, фронтэнд часть решил организовывать на Angular2. Тот факт, что, когда я создавал приложение, уже была выпущена четвёртая (а сейчас пятая) предрелизная версия данного фреймворка, вселил во мне уверенность, что оф-релиз уже не за горами. И так, собравшись с мыслями, я сел за написание авторизации.



Для ребят, которые ещё не сталкивались с разработкой на Angular2, прошу не удивляться, если в коде ниже вы встретите не известный вам ранее синтаксис javascript. Всё дело в том, что весь Angular2 построен на typescript. И нет, это вовсе не означает, что работать с данным фреймворком используя обычный javascript нельзя! Вот к примеру отличная статья, в ходе которой автор рассматривает разработку на Angular2 с использованием ES6.



Но typescript — это javascript, который масштабируется. Являясь компилируемым надмножеством javascript, этот язык добавляет в него все фичи из ES6 & ES7, настоящее ООП с блэк-джеком и классами, строгую типизацию и ещё много крутейших штук. И пугаться здесь нечего: ведь всё, что валидно в javascript, будет работать и в typescript!



Первым делом создадим файл user-authenticate.service.ts, в нём будет находиться сервис авторизации:



import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';

@Injectable()
export class UserAuthenticateService{
private authenticated = false;
constructor(private http: Http) {}
}


Далее внутри нашего класса создадим несколько методов: login, logout, singup, isLoggedIn. Все эти методы однотипны: каждый выполняет свою задачу по отправке запроса типа post на соответствующий адрес. Не сложно догадаться, какую логическую нагрузку несёт каждый из них. Рассмотрим код метода login:



login(username, password) {
let self = this;
let headers = new Headers();
headers.append('Content-Type', 'application/json');

return this.http
.post( 'authentication/login', JSON.stringify({ username, password }), { headers })
.map(function(res){
let answer = res.json();
self.authenticated = answer.authenticated;
return answer;
});
}


Чтоб вызвать данный метод из компонента Angular2, нужно внедрить данный сервис в соответствующий компонент:



import { UserAuthenticateService } from '../services/user-authenticate.service';

@Component({ ... })

export class SingInComponent{
constructor(private userAuthenticateService: UserAuthenticateService, private router: Router){ ... }
onSubmit() {
let self = this;
let username = this.form.name.value;
let password = this.form.password.value;

this.userAuthenticateService
.login(username, password)
.subscribe(function(result) {
self.onSubmitResult(result);
});
}
}


Стоит отметить: для получения доступа к одному и тому же экземпляру сервиса из разных компонентов, его нужно внедрять в общий родительский компонент.



И на этом мы оканчиваем фронтэнд этап создания системы авторизации.



Приступая к бэкэнд разработке, рекомендую вам ознакомиться с интересным модулем async (документация к модулю). Он станет мощным инструментом в вашем арсенале для работы с асинхронными функциями javascript.



Давайте создадим файл authentication.js в уже существующей директории routes. Теперь укажем данный middleware в app.js:



const authentication = require('./routes/authentication');
app.use('/authentication', authentication);


Далее просто создадим обработчик для запроса пост на адрес authentication/login. Чтоб не писать длинную простыню из различных if...else воспользуемся методом waterfall из вышеупомянутого модуля async. Данный метод позволяет выполнять коллекцию асинхронных задач по-порядку, передавая результаты предидущей задачи в аргументы следующей, а на выходе выполнить какой-нибудь полезный колбек. Давайте сейчас и напишем данный колбек:



const express = require('express');
const router = express.Router();
const User = require('../models/users');
const Response = require('../models/response');

const async = require('async');
const log = require('../libs/log')(module);

router.post('/login', function (req, res, next) {
async.waterfall([ ... ], function(err, results){
let authResponse = new Response(req.session.authenticated, {}, err);
res.json(authResponse);
})
}


Для собственного удобства я заранее подготовил конструктор Response:



const Response = function (authenticated, data, authError) {
this.authenticated = authenticated;
this.data = data;
this.authError = authError;
}

module.exports = Response;


Нам осталось только записать функции в нужном нам порядке в массив, переданный первым аргументом в async.waterfall. Давайте создадим эти самые функции:



function findUser(callback){
User.findOne({username: req.body.username}, function (err, user) {
if(err) return next(err);
(user) ? callback(null, user) : callback('username');
}
}

function checkPassword(user, callback){
(user.checkPassword(req.body.password)) ? callback(null, user) : callback('password');
}

function saveInSession (user, callback){
req.session.authenticated = true;
req.session.userId = user.id;
callback(null);
}


Вкратце опишу, что здесь происходит: мы ищем пользователя в базе данных, если такового здесь нет, вызываем колбек с ошибкой 'username', в случае удачного поиска передаём пользователя в колбек; вызываем метод checkPassword, опять же, если пароль верный, передаём пользователя в колбек, в противном случае вызываем колбек с ошибкой 'password'; далее сохраняем сессию в базу данных и вызываем завершающий колбек.



Вот и всё! Теперь пользователи нашего приложения имеют возможность авторизации.



Чат на Angular2 и Socket.io



Мы подошли к написанию функции, несущей в себе основную смысловую нагрузку нашего приложения. В данном разделе мы организуем алгоритм подключения к диалогам (chat-rooms) и функцию отправки/получения сообщений. Для этого мы воспользуемся библиотекой Socket.io, которая позволяет очень просто реализовать обмен данными между браузером и сервером в реальном времени.



Создадим файл sockets.js и подключим данный модуль в bin/www (входной файл Express):



const io = require('../sockets/sockets')(server);


Так как Socket.io работает с протоколом web-sockets, нам необходимо придумать способ передать ей сессию текущего пользователя. Для этого в уже созданный нами файл sockets.js запишем:



const session = require('../libs/session');

module.exports = (function(server) {
const io = require('socket.io').listen(server);

io.use(function(socket, next) {
session(socket.handshake, {}, next);
});

return io;
});


Socket.io построена таким образом, что браузер и сервер всё время обмениваются различными событиями: браузер генерирует события, на которые реагирует сервер, и на оборот, сервер генерирует события, на которые реагирует браузер. Давайте напишем обработчики событий на стороне клиента:



import { Component } from '@angular/core';
import { Router } from '@angular/router';

declare let io: any;

@Component({ ... })

export class ChatFieldComponent {
socket: any;
constructor(private router: Router, private userDataService: UserDataService){
this.socket = io.connect();

this.socket.on('connect', () => this.joinDialog());
this.socket.on('joined to dialog', (data) => this.getDialog(data));
this.socket.on('message', (data) => this.getMessage(data));
}
}


В коде выше мы создали три обработчика событий: connect, joined to dialog, message. Каждый из них вызывает соответствующую ему функцию. Так, событие connect вызывает функцию joinDialog(), которая в свою очередь генерирует серверное событие join dialog, с которым передаёт id собеседника.



joinDialog(){
this.socket.emit('join dialog', this.userDataService.currentOpponent._id);
}


Далее всё просто: событие joined to dialog получает массив с сообщениями пользователей, событие message добавляет новые сообщения в выше упомянутый массив.



getDialog(data) => this.dialog = data;
getMessage(data) => this.dialog.push(data);


Чтоб в дальнейшем уже не возвращаться к фронтэнду, давайте создадим функцию, которая будет отправлять сообщения пользователя:



sendMessage($event){
$event.preventDefault();
if (this.messageInputQuery !== ''){
this.socket.emit('message', this.messageInputQuery);
}
this.messageInputQuery = '';
}


Данная функция генерирует событие message, с которым и передаёт текст отправленного сообщения.



Дело осталось за малым — написать обработчики событий на стороне сервера!



io.on('connection', function(socket){
let currentDialog, currentOpponent;

socket.on('join dialog', function (data) { ... });
socket.on('message', function(data){ ... });
})


В переменные currentDialog и currentOpponent мы будем сохранять идентификаторы текущего диалога и собеседника.



Приступим к написанию алгоритма подключения к диалогу. Для этого воспользуемся библиотекой async, а именно вышеупомянутым методом watterfall. Очерёдность наших действий:



Покинуть предидущий диалог:
function leaveRooms(callback){
// Проходим циклом по всем комнатам и покидаем их
for(let room in socket.rooms){
socket.leave(room)
}
// Переходим к выполнению следующей задачи
callback(null);
}
Получить из базы данных пользователя и его собеседника:
function findCurrentUsers(callback) {
// Параллельно выполняем коллекцию асинхронных задач:
// - поиск текущего пользователя
// - поиск текущего собеседника
async.parallel([findCurrentUser, findCurrentOpponent], function(err, results){
if (err) callback(err);
// Передаём пользователей в колбэк, переходим к выполнению следующей задачи
callback(null, results[0], results[1]);
})
}
Подключиться к существующему/создать новый диалог:
function getDialogId(user, opponent, callback){
// Проверяем существование диалога между вышеупомянутыми пользователями
if (user.dialogs[currentOpponent]) {
let dialogId = user.dialogs[currentOpponent];
// Передаём в колбек Id диалога, переходим к выполнению следующей задачи
callback(null, dialogId);
} else{
// Последовательно выполняем коллекцию задач:
// - создание диалога
// - сохранение ссылки на него пользователям
async.waterfall([createDialog, saveDialogIdToUser], function(err, dialogId){
if (err) callback(err);
// Передаём в колбек Id диалога, переходим к выполнению следующей задачи
callback(null, dialogId);
})
}
}
Получить историю сообщений:
function getDialogData(dialogId, callback){
// Выполняем поиск диалога в базе данных
Dialog.findById(dialogId, function(err, dialog){
if (err) callback('Error in connecting to dialog');
// Передаём в колбек диалог, переходим к выполнению глобального колбэка
callback(null, dialog);
})
}
Вызов вышеупомянутых функций, глобальный колбек:
// Последовательно выполняем коллекцию задач
async.waterfall([
leaveRooms,
findCurrentUsers,
getDialogId,
getDialogData
],
// Глобальный колбэк
function(err, dialog){
if (err) log.error(err);

currentDialog = dialog;
// Подключаемся к данной комнате
socket.join(currentDialog.id);
// Генерируем событие joined to dialog, с которым передаём историю сообщений пользователей
io.sockets.connected[socket.id].emit('joined to dialog', currentDialog.data);
}
)


На этом алгорим подключения к диалогу закончен, осталось всего ничего написать обработчик для события message:



socket.on('message', function(data){
let message = data;
let currentUser = socket.handshake.session.userId;

let newMessage = new Message(message, currentUser);

currentDialog.data.push(newMessage);
currentDialog.markModified('data');

currentDialog.save(function(err){
if (err) log.error('Error in saveing dialog =(');
io.to(currentDialog.id).emit('message', newMessage);
})
})


В данном примере кода мы сохранили в переменные текст сообщения и идентификатор пользователя, затем с помощью заранее созданного конструктора Message создали объект нового сообщения, добавили его в массив и, сохранив обновлённый диалог в базу данных, сгенерировали событие message в данной комнате, с которым и передали сообщение.



Вот и всё наше приложение готово!



Вывод



Хех, вы всё-таки дочитали?! Не смотря на объёмы статьи, я не успел обозреть все детали создания приложения, так как мои возможности ограничены данным форматом. Но выполняя данную работу я не только значительно углубил свои познания в сфере веб-программирования, но и получил море удовольствия от выполненной работы. Ребят, никогда не бойтесь браться за что-то новое, сложное, ведь, если тщательно подойти к делу, постепенно разбираясь с всплывающими вопросами, даже с нулевым опытом на старте, можно создать что-то действительно хорошее!


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/307896/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Почему я все ещё использую function в JavaScript?

Среда, 24 Августа 2016 г. 17:57 (ссылка)

Предлагаю читателям «Хабрахабра» вольный перевод статьи «Constant confusion: why I still use JavaScript function statements» от Билла Суро (Bill Sourour).



В далеких 90-х, когда я только изучал JavaScript, мы пытались писать «Hello World» с помощью оператора function. Примерно так:



function helloWorld() {
return ‘Hello World!’;
}


В настоящее же время крутые ребята пишут функцию “Hello World” вот так:



const helloWorld = () => 'Hello World!';


Здесь используется стрелочная функция, добавленная в JavaScript в стандарте ES2015. Она выглядит чертовски прекрасно. Всё умещается в одну строку. Так кратко. Так замечательно.



Неудивительно, что стрелочные функции стали одной из самых популярных особенностей нового стандарта.



Когда я впервые их увидел, я выглядел примерно как Фрай из Футурамы



image

Даже если учесть, что Babel бесплатен



Так вот, спустя 20 лет изучения JavaScript и после начала использования ES2015 в некоторых проектах, как думаете, каким же образом я напишу Hello World сегодня? Вот так:



function helloWord() {
return ‘Hello World!’;
}


После того как я показал вам новый способ, вы едва ли станете даже смотреть на код “старой школы”, представленный выше.



Целых 3 строчки на такую маленькую простую функцию?! Зачем здесь столько лишних символов?



Я знаю о чем вы думаете:



image

Ни у кого нет времени на это!



Не стоит считать, что мне не нравятся стрелочные функции. Но в случае, когда мне надо объявить функцию верхнего уровня в моем коде, я буду использовать старомодный оператор function.



Надеюсь из этой цитаты Мартина “Дяди Боба” станет понятно, зачем же я это делаю.



Отношение времени, потраченного на чтение кода, по отношению ко времени, потраченному на его написание, составляет 10 к 1. Мы постоянно читаем наш старый код во время работы над новым кодом.



Из-за того, что это отношение слишком высоко, нам хочется, чтобы чтение нашего кода в последующем было легким и непринужденным, несмотря на то что для этого придется потратить больше усилий во время его написания.



Роберт Мартин: Чистый код: создание, анализ и рефакторинг


Оператор function имеет 2 явных преимущества по сравнению со стрелочными функциями.



Преимущество №1: Ясность намерения



Когда мы просматриваем тысячи строк кода в день, будет полезным понимать намерения программиста так быстро и легко, как это только возможно.



Взгляните на это:



const maxNumberOfItemsInCart = ...;


Вы уже просмотрели почти всю строку, но вам все еще непонятно, что будет на месте многоточия: функция или какое-то значение. Это может быть как:



const maxNumberOfItemsInCart = 100;


… так и:



const maxNumberOfItemsInCart = (statusPoints) => statusPoints * 10;


Если же вы используете оператор function, то никакой двусмысленности уже не будет.



Взгляните на:



const maxNumberOfItemsInCart = 100;


… против:



function maxNumberOfItemsInCart(statusPoints) {
return statusPoints * 10;
}


Намерения программиста ясны с самого начала строки.



Возможно вы используете редактор кода с подсветкой синтаксиса. Может вы быстро читаете. Скорее вы просто думаете, что не получите большого преимущества от добавления пары строчек.



Я вас понимаю. Краткая запись все еще выглядит довольно привлекательно.



На самом деле, если бы это являлось моей единственной причиной, то я бы смог убедить себя, что читабельностью можно пожертвовать.



Но все же это не единственная моя причина.



Преимущество №2: Порядок объявления == Порядок выполнения



В идеальном случае, я хотел бы объявлять мой код примерно в том порядке, в котором я хочу, чтобы он выполнялся.



Но при использовании стрелочных функций это будет являться препятствием для меня: любое значение, объявленное через const, недоступно до того момента, пока до него не дойдет исполнение кода.



image

Приготовьтесь к куче тарабарщины, которая должна доказать (надеюсь), что я разбираюсь в том, о чем говорю



Единственная вещь, которую вы должны уяснить в том, что написано ниже, это то, что вы не можете использовать const до того, как вы объявите её.



Данный код вызовет ошибку:



sayHelloTo(‘Bill’);
const sayHelloTo = (name) => `Hello ${name}`;


Все потому что в тот момент, когда движок JavaScript считает код, то он сделает привязку для функции «sayHelloTo», но не проинициализирует её.



Все объявления в JavaScript привязываются на ранней стадии, но инициализируются они в разное время. Другими словами, JavaScript привязывает объявление константы «sayHelloTo» — считывает ее, перемещает наверх и выделяет место в памяти под ее хранение — но при этом не устанавливает ей какого-либо значения до того момента, пока не дойдет до нее в процессе выполнения.



Время между привязкой «sayHelloTo» и ее инициализацией называется «временная мертвая зона» (temporal dead zone — TDZ).



Если вы используете ES2015 прямо в браузере, не переводя код в ES5 с помощью Babel, представленный ниже пример также выдаст ошибку:



if(thing) { 
console.log(thing);
}
const thing = 'awesome thing';


Если же заменить здесь const на var, то мы не получим ошибки. Дело в том, что переменные инициализируются со значением undefined сразу во время привязки, в отличие от констант. Но что-то я отвлекся…



Оператор function, в отличие от const, не страдает от проблемы TDZ. Данный код будет валидным:



sayHelloTo(‘Bill’);
function sayHelloTo(name) {
return `Hello ${name}`;
}


Это потому, что оператор function позволяет инициализировать функцию сразу же после привязки — до того, как будет выполнен какой-либо код.



Таким образом становится неважно, где размещен код функции, она становится доступна с самого начала исполнения.



Использование const заставляет нас писать код, который выглядит беспорядочно. Мы должны сначала начинать работать с низкоуровневыми функциями и затем подниматься все выше. Мой мозг не работает в этом направлении. Я хочу видеть общую картину до того, как стану углубляться в детали.



Большая часть кода написана людьми. Так что стоит учитывать то, что понимание большинства людей напрямую связано с порядком выполнения.



По факту, разве не будет прекрасно, если мы предоставим краткую сводку небольшой части нашего API в начале нашего кода? С оператором function мы легко можем это сделать.



Посмотрите на этот (отчасти выдуманный) модуль для корзины товаров…



export {
createCart,
addItemToCart,
removeItemFromCart,
cartSubTotal,
cartTotal,
saveCart,
clearCart,
}
function createCart(customerId) {...}
function isValidCustomer(customerId) {...}
function addItemToCart(item, cart) {...}
function isValidCart(cart) {...}
function isValidItem(item) {...}
...


Со стрелочными функциями он будет выглядеть как-то так:



const _isValidCustomer = (customerId) => ...
const _isValidCart = (cart) => ...
const _isValidItem = (item) => ...
const createCart = (customerId) => ...
const addItemToCart = (item, cart) => ...
...

export {
createCart,
addItemToCart,
removeItemFromCart,
cartSubTotal,
cartTotal,
saveCart,
clearCart,
}


А теперь представьте, что это большой модуль с огромным количеством небольших внутренних функций. Что вы предпочтете в таком случае?



Наверняка найдутся люди, которые станут утверждать, что использовать что-то до его объявления — это неестественно и может привести к неизвестным последствиям. Утверждающие это могут быть первоклассными специалистами и разбираться в своей области, но как ни крути — это всего-лишь ваше мнение, а не подтвержденный факт.



Я же считаю, что код является средством общения. Действительно хороший код может рассказать первоклассную историю. Я позволяю компиляторам, транспайлерам и минимизаторам оптимизировать код для машины. Но сам я хочу оптимизировать мой код для простого человека, который просто хочет понять что же у меня написано.



Так что же насчёт стрелочный функций?



Да. Они все еще прекрасны.



Я обычно использую стрелочные функции для передачи небольшой функции в качестве значения для функции уровнем выше. Я использую их с promise, с map, с filter, с reduce. Именно здесь они будут являться прекрасным выбором.



Вот некоторые примеры:



const goodSingers = singers.filter((singer) => singer.name !== 'Justin Bieber');
function tonyMontana() {
return getTheMoney().then((money) => power)
.then((power) => women);
}


На этом я пожалуй и закончу. Спасибо за чтение!
Original source: habrahabr.ru.

https://habrahabr.ru/post/308446/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Ember: Декларативная шаблонизация c компонируемыми хелперами

Вторник, 24 Августа 2016 г. 02:25 (ссылка)

Ранее, я упоминала, что помощники (Helper) Ember'а были введены в версии 1.13. На мой взгляд, помощники одни из самых полезных, но не часто обсуждаемых, функций Ember.



В моей лекции Idiomatic Ember на EmberConf 2016, я в деталях рассказала о хелперах, показывая, как они могут быть использованы для подключения и существенного расширения функционала Handlebars.

Они могут использоваться для декларативной шаблонизации — стиля шаблонизации использующего компонируемые действия (composing actions), и предоставляя шаблонам больше ответственности в отношении презентационный логики.



Вместе с коллегой Marten Schilstra, из DockYard, мы создали аддон ember-composable-helpers. Это пакет декларативных помощников, и с его помощью можно уменьшить количество шаблонного кода в Вашем приложении. Вы можете установить его так:



ember install ember-composable-helpers


Один из моих любимых помощников в аддоне — pipe (и его closure версия, pipe-action). Он позволяет декларативно составлять действия в шаблоне, вместо создания множества вариантов в компоненте:



{{perform-calculation
add=(action "add")
subtract=(action "subtract")
multiply=(action "multiply")
square=(action "square")
}}


{{! perform-calculation/template.hbs }}




Помощник pipe был вдохновлён оператором pipe Elixir'а (|>), который позволяет написать следующее:



A(B(C(D(E), "F"), "G"), "H")


Как цепочка преобразователей:



E
|> D()
|> C("F")
|> B("G")
|> A("H")


Используя pipe оператор, можно естественным образом выразить как E передаётся функции D, затем полученное значение передаётся C в качестве первого аргумента, и так далее. Я думаю, мы можем согласиться, что pipe версия намного легче для чтения!



Если бы Вы только знали мощь хелперов



Вы можете подумать, что помощник это примитивная KeyWord конструкция, использующая Ember и HTMLBars для расширения выражений, предоставленных Handlebars.

На самом базовом уровне, Handlebars отвечает за компиляцию hbs шаблонов в HTML:



{{myText}}



Hello world!



Ember и HTMLBars строится поверх этого, добавляя полезные выражения вроде action, mut, get, и hash. На самом деле, все знакомые хелперы, которые вы используете (от each до component) являются частью HTMLBars!



Ember Helpers работают на более высоком уровне, чем HTMLBars helpers, и могут быть использованы для создания новых выражений в Ember, позволяя эффективно расширить шаблонизацию с собственным поведением.



Всё зависит от обстоятельств



Более опытные или консервативные разработчики могли бы посчитать это опасным: при всей своей полезности для конечных пользователей, он также может открыть возможности для злоупотреблений.



Для примера, в Elixir, макросы могут быть использованы для расширения языка, но не рекомендуется для реального использования. Это было закреплено в отличной книге Chris McCord Метапрограммирование на Elixir – "Правило 1: Не используйте макросы".



К счастью, помощники не настолько мощные как макросы Elixir, и играют важную роль в модели программирования Ember. В отличии от макросов, которые позволяют достучаться до AST, использование Ember помощника для расширения презентационной логики приемлемо пока мы не злоупотребляем этим, и это та особенность, где опыт вступает в игру. Так что используйте хелперы с умом.



Уберите прочь Вашу логику с моего газона



Некоторые люди могут испытывать дискомфорт при использовании аддонов, содержащих помощники, из-за неправильного представления о том, что они привносят слишком много логики в шаблоны, и предпочитают держать их свободными от неё.



В качестве лучшей практики, мы должны воздержаться от сложной логики в шаблонах, а также использования вложенных конструкций вроде этой:



{{#unless (or (and (gte value 0) (lt value 0.0001))
(and (lt value 0) (not allowNegativeResults)))}}
...
{{/unless}}


Вместо этого используйте вычисляемые свойства (computed property).



В то же время, сохранять свои шаблоны на 100% свободными от логики очень тяжело – Вы, вероятнее всего уже используете логические помощники вроде if/else или unless. Можно легко упустить из виду тот факт, что количество логики в шаблоне строго не определено.



Назад в будущее



ember-composable-helpers, на самом деле, не значительно увеличит количество логики в шаблонах, если правильно использовать, он инкапсулирует логику представления внутрь этих помощников, и во многих случаях может помочь Вам ликвидировать избыточный код в компонентах или контроллерах.



Например, Вы могли бы написать что-то подобное в вашем приложении:



import Ember from 'ember';

const {
Component,
computed: { filterBy, setDiff },
set
} = Ember;

export default Component.extend({
activeEmployees: filterBy('employees', 'isActive'),
inactiveEmployees: setDiff('employees', 'activeEmployees')
});


Довольно распространённая практика, иметь "компонент-посредник" для использования в других компонентах. С помощью ember-composable-helpers можно писать такие конструкции непосредственно в шаблоне, где всё абсолютно понятно:



Active Employees


{{#each (filter-by "isActive" engineers) as |employee|}}
{{employee.name}} is active!
{{/each}}

Inactive Employees


{{#each (reject-by "isActive" engineers) as |employee|}}
{{employee.name}} is inactive!
{{/each}}


Вы можете думать о компонируемых помощниках как своего рода вычисляемых макро свойствах, которые можно использовать и создавать непосредственно в шаблоне. И поскольку Вы можете создавать суб-выражения в Ember, они могут стать мощной конструкцией для уменьшения шаблонного кода в приложении.



С учетом выше сказанного, помните, не слишком увлекайтесь глубокой вложенностью!



Лучшее решение



Как и любые другие инструменты программирования, важно проявлять здравый смысл и использовать их с умом.



Если Вы можете что-то сделать, не значит, что вы должны.

Хорошо написанный уровень представления(view layer) означает, что шаблоны должны быть декларативными (всё совершенно очевидно), насколько это возможно, а не то, что мы должны избегать логики вообще.

Тем не менее, мы не выступаем за перемещение всей своей логики в шаблон — опять же, количество логики в шаблоне строго не регламетировано.



Если Вы хотите увидеть, как аддон используется, Katherin Siracusa написала отличную статью о том, как она использует ember-composable-helpers в AlphaSights:



Данный паттерн позволяет выполнять операции над данными и в далнейшем более вспомогательные, краткосрочные действия, постоянно возникающие в нашем приложении. Используя компонируемые хелперы, мы можем сделать это довольно просто, без особого дублирования и, не беспокоясь о нежелательных побочных эффектах.

Вы также можете принять участие в обсуждении на нашем Slack канале #e-composable-helpers.



Как всегда, спасибо за внимание!


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/308326/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Стоит ли Typescript усилий?

Вторник, 23 Августа 2016 г. 22:54 (ссылка)

image

Typescript — это скриптовый язык, компилируемый в JavaScript. Разработка Microsoft, которая, на сегодняшний день, успела завоевать и фанатов и недоброжелателей. Главный вопрос для начинающих, и не только: «Зачем он мне нужен?».



Поскольку статей, описывающих достоинства и недостатки, описание языка и примеры работы — валом, я хотел бы описать практические выводы при работе в достаточно крупном приложении.



Начнем с основных плюсов:




  1. Строгая типизация

  2. Читабельность кода

  3. Более легкий переход из мира строгой типизации, нежели на JavaScript напрямую, в котором правит бал Динамика

  4. Обратная совместимость с JavaScript

  5. Широкая поддержка IDE





Строгая типизация



Позволяет более полно описывать свойства и методы обьектов и классов, из-за чего пропадает необходимость, которая, лично меня, дико раздражала, делать проверку всех, входящих в метод или функцию, аргументов:



function checkAllMyArgsAgain(check, me, please) {
if(check && me && please) {
if(check instanceof CheckObject){
console.log('Урааааа!');
} else {
console.log('И снова исключение...')
}
if(me){ } // И так далее.......
}
}




В любом случае, TypeScript сильно упрощает этот процесс:

function checkAllMyArgsAgain(check: CheckObject, me: MeObject, please: string): string {
return 'Какая проверка аргументов? Вы о чем? ';
}




И мы плавно перейдем ко второму пункту, которому первый сильно способствует:



Читабельность кода



Из примера выше, очевидно, что читать код на TypeScript будет проще, ввиду отсутствия нагромождений кода, характерные Javascript.



Можно представить, во что превратится код, написанный на JavaScript, спустя какое-то время…



function checkMe(check, me) {
if(check && me) {
if(check){ ... }
if(me){ ... }
}
}

function andCheckMe(check, me) {
if(check && me) {
if(check){ ... }
if(me){ ... }
}
}

function andCheckMeToo(check, me) {
if(check && me) {
if(check){ ... }
if(me){ ... }
}
}




Против:

function checkMe(check: CheckObject, me: MeObject) {
console.log('Ну круто же!');
}
function andCheckMe(check: CheckObject, me: MeObject) {
console.log('Просто песня');
}
function andCheckMeToo(check: CheckObject, me: MeObject) {
console.log('Писать легко и с удовольствием');
}




В некоторых случаях, в JS, можно уменьшить ущерб путем абстракций, но в целом, TS сильно впереди по данному вопросу.



Более легкий переход в мир JS из мира статики



Многие проявляют интерес к JS, но их отпугивает некая хаотичность и непредсказуемость языка. Здесь Вам сильно поможет TS, который позволяет писать JS, более привычным и понятным путем.



Обратная совместимость с JS



И это, таки, приятный бонус, потому как не придется переписывать весь существующий код, чтобы поиграться с TS и написать лишь часть приложения на нем + есть возможность перенести все любимые библиотеки с JS на TS, если будет такая необходимость.



Широкая поддержка IDE



TypeScript, на данный момент, имеет поддержку в любой популярной IDE, включая IDEA, WebStorm, Sublime, Atom, и так далее. Соответственно, менять любимую среду разработки не придется.



Теперь опишем основные минусы:




  1. Строгая типизация

  2. Компилятор

  3. Debug

  4. А вдруг помрет?





Строгая типизация



Этот пункт работает как во благо, так и во вред, потому что необходимо описывать все типы для всех обьектов, классов, переменных, и иже с ними, что было не свойственно JavaScript ранее.



Но куда большее зло — миграция существующих популярных JS решений на TS. Видите ли, для каждой портированной либы необходимо описать .d.ts файл, в котором и хранятся все возвращаемые типы и описание всех методов. Уверен, что портирование таких монстров, как jQuery, потребовало немало приседаний.



Компилятор



Он существенно уменьшает вероятность «тупой» ошибки, типа пропущенных запятых и неправильно написанных имен переменных, но, также, убивает всю прелесть JS, когда большую часть решений можно было написать на коленках и проверить в консоли браузера.



Не стоит забывать, что время от времени, придется обновлять и сам компилятор, поскольку язык развивается и, рано или поздно, появится необходимость обновить существующую версию компилятора, а, возможно, и самого кода.



Debug



Естественно, любому разработчику необходимо понимать, как, быстро и эффективно, проверять код на ошибки, ловить и исправлять баги. И, первым делом, надо разобраться, как это процесс наладить в TypeScript. Существует множество гайдов для различных IDE: WebStorm и Visual Studio о том, как все это дело настроить в редакторах кода и эффективно работать, но с JS этого делать не надо было, потому как, можно было запустить код, наставить точек останова внутри редактора или браузера и спокойно дебажить. С TS придется немного попотеть.



А вдруг помрет?



Это интересный и серьйозный вопрос, потому как стандарты JS идут в ногу со временем и интегрируют в сам язык многое, что было полезно в TS: классы, стрелочные функции, и так далее. Рано или поздно, все полезные функции TS будет так или иначе перенесены в стандарт языка и TS повесит бутсы на гвоздь. А если ребята из Microsoft решат, что им это не нужно и полностью оставят TS на волю open-source? Понятно, что являясь открытым продуктом, TS не лишиться поддержки совсем — всегда найдутся энтузиасты, но поддержка такого гиганта, как Microsoft, никогда не помешает.



Опять же сам Microsoft, как основного разработчика, многие относят к минусу, потому как репутация у гиганта весьма спорная: Skype, Nokia, Windows Phone, Windows Vista, и так далее.



Надеюсь, что помог кому-то с решением о данном вопросе, либо же заставил задуматься о целесообразности перехода.

Всем спасибо, все свободны!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/308374/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Что нового в Marionette.js 3.0?

Вторник, 23 Августа 2016 г. 15:25 (ссылка)



Прошло более 2-х лет с того времени как 3-я версия начала разрабатываться и вот наконец-то сегодня она была зарелизена! Итак, кому интересно, кто ждал и работает с Marionette.js — добро пожаловать в подкат.





Ребята с кор команды очень хорошо поработали и добавили парочку хороших изменений.

Давайте начнем.



View



Marionette.View заметно преобразовалось. Теперь это не просто неспользуемый класс, как мы помним из документации версии 2



Note: The Marionette.View class is not intended to be used directly

а полноценная View.



Более того, она теперь содержит в себе и Marionette.View, и Marionette.ItemView и Marionette.LayoutView. Да, как вы поняли теперь у нас больше нет ни Marionette.ItemView ни Marionette.LayoutView, они были удалены. Для того чтобы использовать 3-ю версию в коде нужно лишь заменить ваши Marionette.ItemView и Marionette.LayoutView на Marionette.View и все.



Давайте рассмотрим небольшой примерчик:



import Mn from 'backbone.marionette';

const MyView = Mn.View.extend({
template: _.template('

Превый заголовок для 3-й версии

'),

onRender() {
console.log('Моя вью была отрендерена')
}
});


Живой пример



И пример как использовать View в качестве LayoutView



import Mn from 'backbone.marionette';

const MyView = Mn.View.extend({
regions: {
region1:'#region1',
region2: '#region2'
},
onRender() {
this.showChildView('region1', childView1);
this.showChildView('region2', childView2);
}
});


Живой пример



С этим разобрались. Идем дальше.



CompositeView



Marionette.CompositeView стала deprecated что вызывает сразу множество вопросов. Как мне теперь создать табличку или деревовидное меню как это было рекомендовано в документации?

Все очень просто, для этого нужно использовать Marionette.View и Marionette.CollectionView. Ребята приготовили хорошие сравнительные примеры как для таблиц так и для деревовидных меню.



Кстати, документация для новой версии заметно улучшилась. Над ней хорошо поработал Scott Walton, он же автор Marionette Guides.



CollectionView



Marionette.CollectionView осталось в основном без изменений.

Методы getChildView и getEmptyView были удалены. Вместо этого можно делать так



Mn.CollectionView({
childView() {
// мой код
},
emptyView() {
// мой код
}
});


Так же, Backbone.BabySitter удален из зависимостей и полностью интегрирован в ядро фреймворка.

Теперь давайте просто освежим память небольшим примерчиком.



import Mn from 'backbone.marionette';

const data = [
{
item: 'Превая запись'
},
{
item: 'Вторая запись'
},
{
item: 'Третья запись'
}
];
const collection = new Backbone.Collection(data);
const childViewTemplate = _.template('');

const ChildView = Mn.View.extend({
template: childViewTemplate
});

const collectionView = new Mn.CollectionView({
el: 'body',
childView: ChildView,
collection: collection
});

collectionView.render();


Живой пример



View.Events



Жизненный цикл View претерпел изменений. События before:show и show были удалены.

Теперь он выглядит так:



before:render -> render -> before:attach -> attach -> dome:refresh

before:detach -> detach -> before:destroy -> destroy


Пример



import Mn from 'backbone.marionette';

const MyView = Mn.View.extend({
template: false,
onBeforeRender() {
console.log(1)
},
onRender() {
console.log(2)
},
onbeforeDestroy() {
console.log(3)
},
onDestroy() {
console.log(4);
}
});

const myView = new MyView();
myView.render();
myView.destroy();


Живой пример



ChildView Events



API немного изменилось и теперь вместо childEvents нужно использовать childViewEvents.



import Mn from 'backbone.marionette';

const MyView = Mn.View.extend({
childViewEvents: {
'some:event': 'eventHandler'
}
eventHandler() {
console.log('Событие дочернего елемента');
}
});


Живой пример



Templates



templateHelpers был переименован в templateContext.



import Mn from 'backbone.marionette';

const MyView = Mn.View.extend({
template: template,
templateContext() {
return {
version: '3.0'
}
}
});


Живой пример



Backbone.Radio



На смену Backbone.Wreqr пришел Backbone.Radio — мощная библиотека для общения между модулями в приложении.

Backbone.Radio плотно интегрирован в Marionette.Object что дает возможность слушать все события приложения в одном месте.



Живой пример



Изменения в API



 - `bindEntityEvents` ->  `bindEvents`
- `unbindEntityEvents ` -> `unbindEvents`
- `normalizeUIString`, `normalizeUIKeys`, `normalizeUIValues` -> `normalizeMethods`
- `proxyGetOption` -> `getOption`
- `proxyBindEntityEvents` -> `bindEvents`
- `proxyUnbindEntityEvents` -> `unbindEvents`


Что было удалено?




  • Marionette.Controller

  • Marionette.Module

  • Marionette.RegionManager



Переезд на новую версию должен быть не очень болезненным.

Вот коммиты одного из лидеров кор команды Paul Falgout в один из своих проектов:



templateHelpers -> templateContext
Marionette.ItemView -> Marionette.View
Marionette.LayoutView -> Marionette.View
childEvents -> childViewEvents
render:collection / onRenderCollection -> render:children / onRenderChildren
before:show / show / onBeforeShow / onShow -> attach etc


Чтобы облегчить жизнь разработчикам, была создана баблиотека marionette-v3-compat.

Так же есть примеры третьей версии с разными сборщиками проектов.



Marionette.js github репозиторий





Какую библиотеку Вы используете для фронтенд приложений?
























































































Проголосовал 121 человек. Воздержалось 30 человек.





Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.


Original source: habrahabr.ru.

https://habrahabr.ru/post/308138/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Использование С++ в AWS Lambda

Вторник, 23 Августа 2016 г. 13:34 (ссылка)

В этой статье я планирую описать процесс создания и деплоя в AWS лямбда-функции, которая будет вызывать нативный код из С++ аддона. Как вы сможете увидеть, этот процесс не сильно отличается от создания обычной AWS Lambda функции на Node.js — вам лишь нужно настроить своё окружение в соответствии с требованиями AWS.



Что такое AWS Lambda?





Цитируя документацию:



AWS Lambda это вычислительный сервис, в который вы можете загружить свой код, который будет запущен на инфраструктуре AWS по вашему поручению. После загрузки кода и создания того, что мы называем лямбда-функцией, сервис AWS Lambda берёт на себя ответственность за контроль и управление вычислительными мощностями, необходимыми для выполнения данного кода. Вы можете использовать AWS Lambda следующими способами:




  • Как событийно-ориентированный вычислительный сервис, когда AWS Lambda запускает ваш код при возникновении некоторых событий, таких как изменение данных в Amazon S3 или таблице Amazon DynamoDB.

  • Как вычислительный сервис, который будет запускать ваш код в ответ на HTTP-запрос к Amazon API Gateway или запросам от AWS SDK.





AWS Lambda — очень крутая платформа, но поддерживает всего несколько языков: Java, Node.js и Python. Что же делать, если мы хотим выполнить некоторый код на С++? Ну, вы определённо можете слинковать код на С++ с Java-кодом, да и Python умеет это делать. Но мы посмотрим, как это сделать на Node.js. В мире Node.js интеграция с кодом на С++ традиционно происходит через аддоны. Аддон на С++ к Node.js представляет собой скомпилированный (нативный) модуль Node.js, который может быть вызван из JavaScript или любого другого Node.js-модуля.





Аддоны к Node.js это большая тема — если вы их раньше не писали, возможно, стоит почитать что-то вроде этой серии постов или более узкоспециализировано об интеграции С++ и Node.js в веб-проектах. Есть и хорошая книга на эту тему.



Аддоны в AWS Lambda





Чем же использование аддонов в AWS Lambda отличается от классического сценария их использования? Самая большая проблема состоит в том, что AWS Lambda не собирается вызывать node-gyp или любой другой инструмент сборки перед запуском вашей функции — вы должны собрать полностью функциональный бинарный пакет. Это означает, как минимум, то, что вы должны собрать ваш аддон на Linux перед деплоем в AWS Lambda. А если у вас есть какие-нибудь зависимости, то собирать нужно не просто на Linux, а на Amazon Linux. Есть и другие нюансы, о которых я расскажу дальше.



Эта статья не о построении сложных смешанных приложений на Node.js + C++ в инфраструктуре Amazon, она лишь описывает базовые техники сборки и деплоя таких программ. По остальным темам можно обратиться к документации Amazon — там есть куча примеров.



Я собираюсь написать С++ аддон, который будет содержать функцию, принимающую три числа и возвращающий их среднее значение. Да, я знаю, это вот как-раз то, что можно написать только на С++. Мы выставим данную функцию в качестве доступной для использования через AWS Lambda и протестируем её работу через AWS CLI.



Настройка рабочего окружения



Есть причина, по которой Java с её слоганом "напиши однажды, запускай везде" стала популярной — и эта причина в сложности распространения скомпилированного бинарного кода между разными платформами. Java не решила все эти проблемы идеально («напиши однажды, отлаживай везде»), но с тех пор мы прошли длинный путь. Чаще всего мы блаженно забываем о платформенно-специфичных проблемах, когда пишем код на Node.js — ведь Javascript это платформенно-независимый язык. И даже в случаях, когда Node.js приложения зависят от нативных аддонов, это легко решается на разных платформах благодаря npm и node-gyp.



Многие из этих удобств, однако, теряются при использовании Amazon Lambda — нам необходимо полностью собрать нашу Node.js-программу (и её зависимости). Если мы используем нативный аддон, это означает, что собирать всё необходимое нам придётся на той же архитектуре и платформе, где работает AWS Lambda (64-битный Linux), а кроме того нужно будет использовать ту же самую версию рантайма Node.js, который используется в AWS Lambda.



Требование №1: Linux




Мы, конечно же, можем разрабатывать / тестировать / отлаживать лямбда-функции с аддонами на OS X или Windows, но когда мы дойдём до этапа деплоя в AWS Lambda — нам понадобится zip-файл со всем содержимым модуля Node.js — включая все его зависимости. Нативный код, входящий в состав этого zip-файла, должен запускаться в инфраструктуре AWS Lambda. А это значит, что собирать его нам нужно будет только под Linux. Обратите внимание, что в этом примере я не использую никаких дополнительных библиотек — мой код на С++ полностью независимый. Как я объясню детальнее дальше — если вам нужны зависимости от внешних библиотек, понадобиться пойти немного глубже.



Я буду делать все мои эксперименты в этой статье на Linux Mint.



Требование 2: 64-bit




Это, возможно, следовало назвать требованием №1… По тем же самым причинам, о которых рассказано выше — вам нужно создать для деплоя zip-файл с бинарниками под архитектуру x64. Так что ваш старенький запылившийся 32-битный Linux на виртуалке не подойдёт.



Требование 3: Node.js версии 4.3




На момент написания данной статьи AWS Lambda поддерживает Node.js 0.10 и 4.3. Вам абсолютно точно лучше выбрать 4.3. В будущем актуальная версия может измениться — следите за этим. Я люблю использовать nvm для установки и удобного переключения между версиями Node.js. Если у вас ещё нет этого инструмента — пойдите и установите его прямо сейчас:



curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.profile




А теперь установите Node.js 4.3 и node-gyp



nvm install 4.3
npm install -g node-gyp




Требование 4: инструменты для сборки C++ кода (с поддержкой С++11)




Когда вы разрабатываете аддон для Node.js v4+, вы должны использовать компилятор с поддержкой С++11. Последние версии Visual Studio (Windows) и XCode (Mac OS X) подойдут для разработки и тестирования, но, поскольку нам нужно будет собрать всё под Linux, нам понадобиться g++ 4.7 (или более свежий). Вот как установить g++ 4.9 на Mint/Ubuntu:



sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install g++-4.9
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 20




Создаём аддон (локально)





Нам понадобиться создать два Node.js-проекта. Один будет нашим С++ аддоном, который вообще не будет содержать в себе ничего относящегося к AWS Lambda — просто классический нативный аддон. Второй же проект будет лямбда-функций в терминах AWS Lambda — то есть модулем Node.js, который будет импортировать нативный аддон и брать на себя вызов его функционала. Если вы хотите попробовать на своей машине — весь код здесь, а конкретно этот пример — в папке lambda-cpp.



Давайте начнём с аддона.



mkdir lambda-cpp
mkdir lambda-cpp/addon
cd lambda-cpp/addon




Для создания аддона нам понадобятся три файла — код на С++, package.json чтобы сказать Node.js как обращаться с этим аддоном и binding.gyp для процесса сборки. Давайте начнём с самого простого — binding.gyp



{
"targets": [
{
"target_name": "average",
"sources": [ "average.cpp" ]
}
]
}




Это, вероятно, простейший вариант файла binding.gyp, который только возможно создать — мы задаём имя цели и исходники для компиляции. При необходимости здесь можно наворотить сложнейшие вещи, отражающие опции компилятора, пути к внешним каталогам, библиотекам и т.д. Просто помните, что всё, на что вы ссылаетесь должно быть статически слинковано в бинарник и собрано под архитектуру x64.



Теперь давайте создадим package.json, который должен определять точку входа данного аддона:



{
"name": "average",
"version": "1.0.0",
"main": "./build/Release/average",
"gypfile": true,
"author": "Scott Frees <scott.frees@gmail.com> (http://scottfrees.com/)",
"license": "ISC"
}




Ключевой момент здесь — это свойство «main», которое объясняет Node.js, что вот этот конкретный бинарник является точкой входа данного модуля и именно он должен быть загружен каждый раз, когда кто-то делает require('average').



Теперь исходный код. Давайте откроем average.cpp и создадим простой аддон с функцией, которая возвращает среднее значение всех переданных ей параметров (не будем ограничиваться лишь тремя!).



#include 

using namespace v8;

void Average(const FunctionCallbackInfo& args) {
Isolate * isolate = args.GetIsolate();
double sum = 0;
int count = 0;

for (int i = 0; i < args.Length(); i++){
if ( args[i]->IsNumber()) {
sum += args[i]->NumberValue();
count++;
}
}

Local retval = Number::New(isolate, sum / count);
args.GetReturnValue().Set(retval);
}


void init(Local exports) {
NODE_SET_METHOD(exports, "average", Average);
}

NODE_MODULE(average, init)




Если вы не знакомы с использованием V8, пожалуйста, почитайте об этом какие-нибудь статьи или книгу — данный пост не об этом. Вкратце, макрос NODE_MODULE в конце файла указывает, какая функция должна быть вызвана, когда данный модуль будет загружен. Функция init добавляет новую функцию в список экспортируемых данным модулем — ассоциируя С++ функцию Average с вызываемой из Javascript функцией average.



Мы можем собрать всё это с помощью node-gyp configure build. Если всё прошло хорошо, то вы увидите gyp info ok в конце вывода. В качестве простого теста давайте создадим файл test.js и сделаем из него несколько вызовов:



// test.js
const addon = require('./build/Release/average');

console.log(addon.average(1, 2, 3, 4));
console.log(addon.average(1, "hello", "world", 42));




Запустите этот код с помощью команды node test.js и вы увидите в консоли ответы 2.5 и 21.5. Обратите внимание, что строки "hello" и "world" не повлияли на результаты рассчётов, поскольку аддон проверил входные параметры и использовал в рассчётах лишь числа.



Теперь нужно удалить test.js — он не будет частью нашего аддона, который мы собираемся задеплоить в AWS Lambda.



Создаём лямбда-функцию





А теперь давайте создадим, собственно, лямбда-функцию для AWS Lambda. Как вы (возможно) уже знаете, для AWS Lambda нам необходимо создать обработчик, который будет вызываться каждый раз, когда произойдёт некоторое событие. Этот обработчик получит описание данного события (которое может быть, например, операцией изменения данных в S3 или DynamoDB) в виде JS-объекта. Для этого теста мы используем простое событие, описываемое следующим JSON:



{
op1: 4,
op2: 15,
op3: 2
}




Мы можем сделать это прямо в папке аддона, но я предпочитаю создать отдельный модуль Node.js и подтянуть локальный аддон как npm-зависимость. Давайте создадим новую папку где-то рядом с lambda-cpp/addon, пусть она будет называться lambda-cpp/lambda.



cd ..
mkdir lambda
cd lambda




Теперь создадим файл index.js и напишем в нём следующий код:



exports.averageHandler = function(event, context, callback) {
const addon = require('average');
var result = addon.average(event.op1, event.op2, event.op3)
callback(null, result);
}




Заметьте, что мы сослались на внешнюю зависимость "average". Давайте создадим файл package.json, в котором опишем ссылку на локальный аддон:



{
"name": "lambda-demo",
"version": "1.0.0",
"main": "index.js",
"author": "Scott Frees <scott.frees@gmail.com> (http://scottfrees.com/)",
"license": "ISC",
"dependencies": {
"average": "file:../addon"
}
}




Когда вы выполните команду npm install, npm вытянет ваш локальный аддон и скопирует его в подпапку node_modules, а также вызовет node-gyp для его сборки. Структура ваших папок и файлов после этого будет выглядеть вот так:




/lambda-cpp
-- /addon
-- average.cpp
-- binding.gyp
-- package.json
-- /lambda
-- index.js
-- package.json
-- node_modules/
-- average/ (contains the binary addon)




Локальное тестирование





Теперь у нас есть файл index.js, экспортирующий обработчик вызовов AWS Lambda и мы можем попробовать загрузить его туда. Но давайте вначале протестируем его локально. Есть отличный модуль, который называется lambda-local — он может помочь нам с тестированием.



npm install -g lambda-local




После его установки мы можем вызвать нашу лямбда-функцию по имени обработчика "averageHandler" и передать ему наше тестовое событие. Давайте создадим файл sample.js и напишем в него:



module.exports = {
op1: 4,
op2: 15,
op3: 2
};




Теперь мы можем выполнить нашу лямбду командой:



lambda-local -l index.js -h averageHandler -e sample.js
Logs
------
START RequestId: 33711c24-01b6-fb59-803d-b96070ccdda5
END


Message
------
7




Как и ожидалось, результат равен 7 (среднее значение чисел 4, 15 и 2).



Деплой с помощью AWS CLI





Есть два способа деплоя кода в AWS Lambda — через веб-интерфейс и через утилиты командной строки (CLI). Я планирую использовать CLI, поскольку данный подход кажется мне более универсальным. Однако, всё описанное далее при желании можно сделать и через веб-интерфейс.



Если у вас ещё нет AWS-аккаунта — сейчас самое время его создать. Дальше нужно создать Администратора. Полная инструкция есть в документации Амазона. Не забудьте добавить созданному Администратору роль AWSLambdaBasicExecutionRole.



Теперь, когда у вас есть пользователь с правами Администратора, нужно получить ключ для конфигурации AWS CLI. Вы можете сделать это через IAM-консоль. Как скачать свой ключ в виде csv-файла рассказывается вот в этой инструкции.



Как только у вас будет ключ, можно устанавливать CLI. Есть несколько способов сделать это, и для установки нам в любом случае понадобится Python. Наиболее простой способ, на мой взгляд, это воспользоваться установщиком:



curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws




Дальше нужно сконфигурировать CLI. Запустите команду aws configure и введите ваш ключ и секретный код. Вы также можете выбрать регион по-умолчанию и формат вывода. Вы, скорее всего, захотите присоединить профиль к данной конфигурации (поскольку он понадобится дальее) с помощью аргумента --profile.



aws configure --profile lambdaProfile
AWS Access Key ID [None]: XXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXX
Default region name [None]: us-west-2
Default output format [None]:




Вы можете проверить, что всё настроено верно, запустив команду просмотра всех лямбда-функций:



aws lambda list-functions
{
"Functions": []
}




Поскольку мы только начали работу — никаких функций пока нет. Но по крайней мере мы не увидели никаких сообщений об ошибках — это хорошо.



Упаковка лямбда-функции и аддона





Наиболее важный (и часто обсуждаемый в интернете) шаг во всём этом процессе — это убедиться в том, что весь ваш модуль будет упакован в zip-файл корректно. Вот наиболее важные вещи, которые нужно проверить:




  1. Файл index.js должен быть в корневой папке zip-файла. Вы не должны упаковывать саму папку /lambda-addon/lambda — только её содержимое. Другими словами — если вы распакуете созданный zip файл в текущую папку — файл index.js должен оказаться в этой же папке, а не в подпапке.

  2. Папка node_modules и всё её содерижмое должно быть упаковано в zip-файл.

  3. Вы должны собрать аддон и упаковать его в zip-файл на правильной платформе (см. выше требования — Linux, x64 и т.д.)





В папке, где находится index.js, упакуйте все файлы, которые должны быть задеплоены. Я создам zip-файл в родительской папке.



zip -r ../average.zip node_modules/ average.cpp index.js binding.gyp package.json 




*Обратите внимание на ключ "-r" — нам нужно упаковать всё содержимое папки node_modules. Проверьте полученный файл командой less, должно получиться что-то такое:



less ../average.zip

Archive: ../average.zip
Length Method Size Cmpr Date Time CRC-32 Name
-------- ------ ------- ---- ---------- ----- -------- ----
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/
1 Stored 1 0% 2016-08-17 17:39 6abf4a82 node_modules/average/output.txt
478 Defl:N 285 40% 2016-08-17 19:02 e1d45ac4 node_modules/average/package.json
102 Defl:N 70 31% 2016-08-17 15:03 1f1fa0b3 node_modules/average/binding.gyp
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/
115 Defl:N 110 4% 2016-08-17 19:02 c79d3594 node_modules/average/build/binding.Makefile
3243 Defl:N 990 70% 2016-08-17 19:02 d3905d6b node_modules/average/build/average.target.mk
3805 Defl:N 1294 66% 2016-08-17 19:02 654f090c node_modules/average/build/config.gypi
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/.deps/
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/.deps/Release/
125 Defl:N 67 46% 2016-08-17 19:02 daf7c95b node_modules/average/build/Release/.deps/Release/average.node.d
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/.deps/Release/obj.target/
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/.deps/Release/obj.target/average/
1213 Defl:N 386 68% 2016-08-17 19:02 b5e711d9 node_modules/average/build/Release/.deps/Release/obj.target/average/average.o.d
208 Defl:N 118 43% 2016-08-17 19:02 c8a1d92a node_modules/average/build/Release/.deps/Release/obj.target/average.node.d
13416 Defl:N 3279 76% 2016-08-17 19:02 d18dc3d5 node_modules/average/build/Release/average.node
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/obj.target/
0 Stored 0 0% 2016-08-17 19:02 00000000 node_modules/average/build/Release/obj.target/average/
5080 Defl:N 1587 69% 2016-08-17 19:02 6aae9857 node_modules/average/build/Release/obj.target/average/average.o
13416 Defl:N 3279 76% 2016-08-17 19:02 d18dc3d5 node_modules/average/build/Release/obj.target/average.node
12824 Defl:N 4759 63% 2016-08-17 19:02 f8435fef node_modules/average/build/Makefile
554 Defl:N 331 40% 2016-08-17 15:38 18255a6e node_modules/average/average.cpp
237 Defl:N 141 41% 2016-08-17 19:02 7942bb01 index.js
224 Defl:N 159 29% 2016-08-17 18:53 d3d59efb package.json
-------- ------- --- -------
55041 16856 69% 26 files
(type 'q' to exit less)




Если вы не видите содержимого папки node_modules внутри zip-файла или если файлы имеют дополнительный уровень вложенности в иерархии папок — ещё раз перечитайте всё, что написано выше!



Загрузка в AWS Lambda





Теперь мы можем создать лямбда-функцию с помощью команды «lambda create-function».



aws lambda create-function \
--region us-west-2 \
--function-name average \
--zip-file fileb://../average.zip \
--handler index.averageHandler \
--runtime nodejs4.3 \
--role arn:aws:iam::729041145942:role/lambda_execute




Большинство параметров говорят сами за себя — но если вы не знакомы с AWS Lambda, то параметр "role" может для вас выглядеть несколько загадочно. Как говорилось выше, для работы с AWS Lambda вам необходимо было создать роль, имеющую разрешение AWSLambdaBasicExecutionRole. Вы можете получить строку, начинающуюся с "arn:" для этой роли через веб-интерфейс IAM (кликнув на этой роли).



Если всё пройдёт хорошо, вы должны получить JSON с ответом, содержащим некоторую дополнительную информацию о только что задеплоеной лямбда-функции.



Тестирование с помощью AWS CLI





Теперь, когда мы задеплоили нашу лямбда-функцию, давайте протестируем её с помощью того же интерфейса командной строки. Вызовем нашу функцию, передав ей описание того же самого события, что и в прошлый раз.



aws lambda invoke \
--invocation-type RequestResponse \
--function-name average \
--region us-west-2 \
--log-type Tail \
--payload '{"op1":4, "op2":15, "op3":2}' \
--profile lambdaProfile \
output.txt




Вы получите ответ вот в такой форме:



{
"LogResult": "U1RBUlQgUmVxdWVzdElkOiAxM2UxNTk4ZC02NGMxLTExZTYtODQ0Ny0wZDZjMjJjMTRhZWYgVmVyc2lvbjogJExBVEVTVApFTkQgUmVxdWVzdElkOiAxM2UxNTk4ZC02NGMxLTExZTYtODQ0Ny0wZDZjMjJjMTRhZWYKUkVQT1JUIFJlcXVlc3RJZDogMTNlMTU5OGQtNjRjMS0xMWU2LTg0NDctMGQ2YzIyYzE0YWVmCUR1cmF0aW9uOiAwLjUxIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMzUgTUIJCg==",
"StatusCode": 200
}




Не очень пока что понятно, но это легко исправить. Параметр "LogResult" закодировано в base64, так что мы можем раскодировать его:



echo U1RBUlQgUmVxdWVzdElkOiAxM2UxNTk4ZC02NGMxLTExZTYtODQ0Ny0wZDZjMjJjMTRhZWYgVmVyc2lvbjogJExBVEVTVApFTkQgUmVxdWVzdElkOiAxM2UxNTk4ZC02NGMxLTExZTYtODQ0Ny0wZDZjMjJjMTRhZWYKUkVQT1JUIFJlcXVlc3RJZDogMTNlMTU5OGQtNjRjMS0xMWU2LTg0NDctMGQ2YzIyYzE0YWVmCUR1cmF0aW9uOiAwLjUxIG1zCUJpbGxlZCBEdXJhdGlvbjogMTAwIG1zIAlNZW1vcnkgU2l6ZTogMTI4IE1CCU1heCBNZW1vcnkgVXNlZDogMzUgTUIJCg== |  base64 --decode 

START RequestId: 13e1598d-64c1-11e6-8447-0d6c22c14aef Version: $LATEST
END RequestId: 13e1598d-64c1-11e6-8447-0d6c22c14aef
REPORT RequestId: 13e1598d-64c1-11e6-8447-0d6c22c14aef Duration: 0.51 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 35 MB




Стало немного более читабельно, но всё же дало нам не очень много понимания о произошедшим. Это потому, что наша лямбда-функция не написала ничего в лог-файл. Если вы хотите увидеть результат — то можете протестировать функцию через веб-интерфейс, где легче увидеть входные и выходные параметры. А пока вы можете изменить свой файл index.js, перепаковать zip-файл, передеплоить его и вызвать свою функцию снова:



exports.averageHandler = function(event, context, callback) {
const addon = require('./build/Release/average');
console.log(event);
var result = addon.average(event.op1, event.op2, event.op3)
console.log(result);
callback(null, result);
}




После декодирования ответа вы увидите что-то вроде этого:



START RequestId: 1081efc9-64c3-11e6-ac21-43355c8afb1e Version: $LATEST
2016-08-17T21:39:24.013Z 1081efc9-64c3-11e6-ac21-43355c8afb1e { op1: 4, op2: 15, op3: 2 }
2016-08-17T21:39:24.013Z 1081efc9-64c3-11e6-ac21-43355c8afb1e 7
END RequestId: 1081efc9-64c3-11e6-ac21-43355c8afb1e
REPORT RequestId: 1081efc9-64c3-11e6-ac21-43355c8afb1e Duration: 1.75 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 17 MB




Дальнейшие планы





Итак, на данный момент у нас имеется на 100% рабочая AWS Lambda-функция, которая вызывает С++ код из аддона. Мы, конечно, пока не сделали чего-то реально полезного. Поскольку наша лямбда-функция делает некоторые рассчёты, следующим логичным шагом будет привязать её к Gateway API, чтобы входные параметры можно было брать из HTTP-запросов. О том, как это сделать, вы можете почитать в Getting Started — секцию о вызове лямбда-функций.



Я надеюсь, вы теперь убедились, что деплой С++ кода в AWS Lambda возможен и даже не слишком сложен — достаточно придерживаться описанных в начале статьи требования по сборке, и всё будет хорошо. Остальные шаги достаточно тривиальны и полностью аналогичны деплою любой лямбда-функции в AWS. Как я уже говорил, если ваш аддон требует каких-то зависимостей, их придётся статически слинковать в его бинарник.



Весь код из данной статьи доступен здесь.
Original source: habrahabr.ru.

https://habrahabr.ru/post/308330/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Книга «Веб-разработка с применением Node и Express. Полноценное использование стека JavaScript»

Вторник, 23 Августа 2016 г. 12:10 (ссылка)

image JavaScript — самый популярный язык написания клиентских сценариев. Это основополагающая технология для создания всевозможных анимаций и переходов. Без JavaScript практически невозможно обойтись, если требуется добиться современной функциональности на стороне клиента. Единственная проблема с JavaScript — он не прощает неуклюжего программирования. Экосистема Node помогает значительно повысить качество приложений — предоставляет фреймворки, библиотеки и утилиты, ускоряющие разработку и поощряющие написание хорошего кода.



Эта книга предназначена для программистов, желающих создавать веб-приложения (обычные сайты, воплощающие REST-интерфейсы программирования приложений или что-то среднее между ними) с использованием JavaScript, Node и Express. Для чтения книги вам не потребуется опыт работы с Node, однако необходим хотя бы небольшой опыт работы с JavaScript.



Для кого предназначена эта книга



Безусловно, эта книга предназначена для программистов, желающих создавать веб-приложения (обычные сайты, воплощающие REST интерфейсы программирования приложений или что-то среднее между ними) с использованием JavaScript, Node и Express. Один из замечательных аспектов разработки для платформы Node — привлечение совершенно нового круга программистов. Доступность и гибкость JavaScript привлекли программистов-самоучек со всего мира. Никогда еще в истории вычислительной техники программирование не было столь доступным. Количество и качество онлайн-ресурсов для изучения программирования (и получения помощи в случае проблем) потрясает и вдохновляет. Так что приглашаю вас стать одним из этих новых (возможно, выучившихся самостоятельно) программистов.



Кроме того, конечно, есть программисты вроде меня, уже давно работающие в этой сфере. Подобно многим программистам моего времени, я начал с ассемблера и языка BASIC, а затем имел дело с Pascal, C++, Perl, Java, PHP, Ruby, C, C# и JavaScript. В университете я столкнулся и с языками программирования более узкого применения, такими как ML, LISP и PROLOG. Многие из этих языков близки и дороги моему сердцу, но ни один из них не кажется мне столь многообещающим, как JavaScript. Так что я пишу эту книгу и для таких программистов, как я сам, с богатым опытом и, возможно, более философским взглядом на определенные технологии.



Опыт работы с Node не требуется, однако необходим хотя бы небольшой опыт работы с JavaScript. Если вы новичок в программировании, рекомендую вам Codecademy. Если же вы опытный программист, рекомендую книгу Дугласа Крокфорда «java script: сильные стороны». Примеры, приведенные в этой книге, могут быть использованы с любой операционной системой, на которой работает Node, включая Windows, OS X и Linux. Примеры предназначены для работающих с командной строкой (терминалом), так что вам нужно будет хотя бы некоторое знание командной строки вашей системы.



Как устроена эта книга



Главы 1 и 2 познакомят вас с Node и Express, а также с инструментами, которые вы будете использовать во время чтения этой книги. В главах 3 и 4 вы начнете применять Express и строить каркас учебного сайта, используемого в качестве примера во всей дальнейшей книге.



В главе 5 обсуждаются тестирование и контроль качества, а глава 6 охватывает некоторые из наиболее важных структурных компонентов Node, а также их расширение и использование в Express. Глава 7 описывает шаблонизацию (с применением семантической системы веб-шаблонов Handlebars), закладывая основы практического построения сайтов с помощью Express. Главы 8 и 9 охватывают куки-файлы, сеансы и обработчики форм, очерчивая круг тем, знание которых понадобится вам для построения сайтов с базовой функциональностью с помощью Express.



В главе 10 исследуется программное обеспечение промежуточного уровня — центральная концепция Connect (одного из основных компонентов Express).



Глава 11 объясняет, как использовать программное обеспечение промежуточного уровня для отправки сообщений электронной почты с сервера, и обсуждает шаблоны сообщений и относящиеся к электронной почте вопросы безопасности.



Глава 12 предлагает предварительный обзор вопросов, связанных с вводом в эксплуатацию. Хотя на этом этапе книги у вас еще нет всей информации, необходимой для создания готового к эксплуатации сайта, обдумывание ввода в эксплуатацию сейчас избавит вас от множества проблем в будущем.



Глава 13 рассказывает о хранении данных с упором наMongoDB (одну из основных документоориентированных баз данных).



Глава 14 углубляется в подробности маршрутизации в Express (в то, как URL сопоставляются с контентом), а глава 15 отклоняется на обсуждение написания API с помощью Express. Глава 16 охватывает подробности обслуживания статического контента с упором на максимизацию производительности. Глава 17 описывает популярную парадигму «модель — представление — контроллер» (model — view — controll er, MVC) и ее соответствие Express.



В главе 18 обсуждается безопасность: как встроить в ваше приложение аутентификацию и авторизацию (с упором на использование стороннего провайдера аутентификации), а также организацию доступа к вашему сайту по протоколу HTTPS.



Глава 19 объясняет, как осуществить интеграцию со сторонними сервисами. В качестве примеров приводятся социальная сеть Twitter, картографический сервис Googl e Maps и сервис службы погоды Weather Underground.



Главы 20 и 21 готовят вас к важному моменту: запуску вашего сайта. Они охватывают отладку, так что вы сможете избавиться от каких-либо недостатков перед запуском, и процесс запуска в эксплуатацию. Глава 22 рассказывает о следующем важном этапе — сопровождении.



Завершает книгу глава 23, в которой указываются дополнительные источники информации на тот случай, если вы захотите продолжить изучение Node и Express, а также места, где сможете получить помощь и консультацию.



Учебный сайт



Начиная с главы 3, на протяжении всей книги будет использоваться единый пример — сайт турфирмы Meadowlark Travel. Поскольку я только что возвратился из поездки в Лиссабон, у меня на уме были путешествия, и сайт, выбранный мной для примера, предназначен для вымышленной туристической фирмы из моего родного штата Орегон (western meadowlark — западный луговой трупиал — это птица-символ штата Орегон). Meadowlark Travel связывает путешественников с местными экскурсоводами-любителями и сотрудничает с фирмами, выдающими напрокат велосипеды и мотороллеры и предлагающими туры по данной местности. В дополнение поддерживается база данных местных достопримечательностей, включающая историческую информацию и сервисы, учитывающие местоположение пользователя.



Как и любой учебный пример, сайт Meadowlark Travel вымышлен, но это пример, охватывающий множество проблем, с которыми сталкиваются реальные сайты: интеграция сторонних компонентов, геолокация, электронная коммерция, безопасность.



Поскольку в центре внимания этой книги инфраструктура серверной части, учебный сайт не будет завершенным — он просто служит вымышленным примером реального сайта, для того чтобы придать примерам полноту и обеспечить требуемый контекст. Вероятно, вы работаете над собственным сайтом и сможете использовать пример сайта Meadowlark Travel в качестве шаблона для него.



Об авторе



Итан Браун — старший разработчик программного обеспечения в Pop Art, маркетинговом агентстве, расположенном в Портленде. Он отвечает за архитектуру и реализацию сайтов и веб-сервисов для клиентов, начиная с малого бизнеса вплоть до международных корпораций. У него более чем 20-летний опыт программирования, и он считает, что стек JavaScript — это веб-платформа будущего.



Более подробно с книгой можно ознакомиться на сайте издательства

Оглавление

Отрывок



Для Хаброжителей скидка 25% по купону — Web

По факту оплаты бумажной книги отправляем на e-mail электронные версии книги.
Original source: habrahabr.ru.

https://habrahabr.ru/post/308316/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<javascript - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda