Отправка электронной почты в формате HTML

от автора

Введение

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

Основные нюансы при формировании таких писем:

  • Все стили должны встраиваться (inline) в виде атрибута style для конкретного HTML-элемента.
  • Все изображения должны встраиваться, либо как отдельные вложения в в письме, либо в виде base64-кодированных данных (второе банально удобнее).
  • Письмо должно поддерживать DKIM (настройка мэйлера).

Ранее я использовал для формирования HTML-писем проект Premailer, созданный на Ruby. Пришлось даже заняться поддержкой проекта (сейчас времени на это нет, мэйнтейнеры приветствуются).

Сейчас же хотелось избежать внедрения Ruby, в то время, как Node проник везде.

Juice

К счастью, современная экосистема Node предоставляет богатые возможности по формированию электронных писем. Мы выбрали цепочку по формированию электронной почты в виде pug-шаблонов, преобразованию оных с помощью juice и подстановки конкретных данных на бэкэнде (у нас это Perl).

Установка

npm install gulp-cli -g npm install gulp --save-dev npm install gulp-rename --save-dev npm install gulp-pug --save-dev npm install premailer-gulp-juice --save-dev npm install gulp-postcss --save-dev npm install autoprefixer --save-dev npm install gulp-less --save-dev

gulpfile.babel.js:

'use strict';  import gulp from 'gulp'; import mail from './builder/tasks/mail'; gulp.task('mail', mail);

builder/tasks/mail.js:

'use strict';  import gulp from 'gulp'; import stylesheets from './mail/stylesheets'; import templates from './mail/templates'; import clean from './mail/clean';  const mail = gulp.series(clean, stylesheets, templates);  export default mail;

builder/tasks/mail/stylesheets.js

'use strict';  import gulp from 'gulp'; import config from 'config'; import rename from 'gulp-rename'; import postcss from 'gulp-postcss'; import autoprefixer from 'autoprefixer'; import less from 'gulp-less';  const stylesheetsPath = config.get('srcPath') + '/mail/stylesheets';  const stylesheetsGlob = stylesheetsPath + '/**/*.less';  const mailStylesheets = () => {   return gulp.src(stylesheetsGlob)     .pipe(less())     .pipe(postcss([       autoprefixer({browsers: ['last 2 versions']}),     ]))     .pipe(gulp.dest(stylesheetsPath)); };  export default mailStylesheets;

builder/tasks/mail/templates.js:

'use strict';  import gulp from 'gulp'; import config from 'config'; import pug from 'gulp-pug'; import rename from 'gulp-rename'; import juice from 'premailer-gulp-juice';  const templatesPath = config.get('srcPath') + '/mail'; const mailPath = config.get('mailPath');  const templatesGlob = templatesPath + '/**/*.pug';  const mailTemplates = () => {   return gulp.src(templatesGlob)     .pipe(rename(path => {       path.extname = '.html';     }))     .pipe(pug({       client: false     }))     .pipe(juice({       webResources: {         relativeTo: templatesPath,         images: 100,         strict: true       }     }))     .pipe(gulp.dest(mailPath)); };  export default mailTemplates;

'use strict';  import del from 'del'; import gutil from 'gulp-util';  const clean = done => {   return del([     'mail/*.html',     'src/mail/stylesheets/*.css'   ]).then(() => {     gutil.log(gutil.colors.green('Delete src/mail/stylesheets/*.css and mail/*.html'));     done();   }); };  export default clean;

Типичный шаблон выглядит так (generic.pug):

include base.pug  +base     tr(height='74')         td.b-mail__table-row--heading(align='left', valign='top') Привет,     tr         td(align='left', valign='top')             | <%== $html %>

Где base.pug:

mixin base(icon, alreadyEncoded)     doctype html     head         meta(charset="utf8")         link(rel="stylesheet", href="/stylesheets/mail.css")     body         table(width='100%', border='0', cellspacing='0', cellpadding='0')             tbody                 tr                     td.b-mail(align='center', valign='top', bgcolor='#ffffff')                         br                         br                         table(width='750', border='0', cellspacing='0', cellpadding='0')                             tbody.b-mail__table                                 tr.b-mail__table-row(height='89')                                 tr.b-mail__table-row                                     td(align='left', valign='top', width='70')                                         img(src='/images/logo.jpg')                                     td(align='left', valign='top')                                         table(width='480', border='0', cellspacing='0', cellpadding='0')                                             tbody                                                 if block                                                     block                                     td(align='right', valign='top')                                         if alreadyEncoded                                             img.fixed(src!=icon, data-inline-ignore)                                         else if icon                                             img.fixed(src!=icon)                         br                         br                 tr                     td(align='center', valign='top')

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

gulp mail

ViewAction

Многие почтовые клиенты, такие, как GMail/Inbox, поддерживают специальные действия в режиме просмотра сообщений. Внедрить их проще простого, добавив в содержимое сообщения следующие тэги:

div(itemscope, itemtype="http://schema.org/EmailMessage")   div(itemprop="action", itemscope, itemtype="http://schema.org/ViewAction")     link(itemprop="url", href="https://github.com/imlucas/gulp-juice/pull/9")     meta(itemprop="name", content="View Pull Request")   meta(itemprop="description", content="View this Pull Request on GitHub")

ссылка на оригинал статьи https://habrahabr.ru/post/317810/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *