大家平时会有发邮件的时候没有一个模板而发愁吗?在发邮件的时候,很多时候我们还是需要一些邮件定制一些邮件模板,这样对于用户体验来说更棒。下面我来介绍一下技术细节,有说的不好的地方,请拍砖!
首先在我们来设计一下模板文件的结构目录:
emailplates -- Template1 |-- emailplate.json |-- html.hbs |-- style.styl -- Template2 |-- emailplate.json |-- html.hbs |-- style.styl> emalilplates为邮件模板根目录的文件夹 > Template1、Template2为邮件模板的文件夹
接下来非常重要的是邮件模板文件夹下相关文件结构设计介绍:
-
html.
邮件模板html语法支持格式( jade, handlebars, eco, ejs) template engine
-
style.stylus
用stylus是因为考虑到他能支持css, sass, less的基本语法,总之就是因为它很强大.
-
emailplate.json
我们在邮件模板主题中也许会需要动态的在邮件主题中改变一些内容,这个时候就需要传递该json的参数
下面是具体技术实现过程,我就用我最擅长的nodejs中的coffeescript语法实现给大家看吧.
首先引进一些必须的lib:
_ = require 'underscore'
glob = require 'glob'
async = require 'async'
fs = require 'fs'
cons = require 'consolidate'
stylus = require 'stylus'
juice = require 'juice'
接下来我们构造一个emailplates的类,并且分别加入一些基本的方法:
module.exports = class Emailplate
settings:
views: './emailplates'
constructor: (settings = {}) ->
_.extend [@settings](/user/settings), settings
get: (name) ->
[@settings](/user/settings)[name]
set: (setting, val) ->
if arguments.length is 1
_.extend [@settings](/user/settings), setting
else
[@settings](/user/settings)[setting] = val
this
settings
中的参数views
是用来设置emailplates
的存放路径,当然,我们在调用的时候可以随时改掉这个路径
获取所有主题信息的方法
themes: (fn) ->
glob "#{[@settings](/user/settings).views}/**/emailplate.json", (err, files) ->
parallel = []
_.each files, (file) ->
parallel.push (cb) ->
fs.readFile file, 'utf-8', (err, content) ->
info = JSON.parse content
cb null, info
async.parallel parallel, fn
获取单个主题信息的方法
theme: (name, fn) ->
fs.readFile "#{[@settings](/user/settings).views}/#{name}/emailplate.json", 'utf-8', (err, content) ->
return fn err if err
info = JSON.parse content
fn null, info
多个获取主题信息的方法我简单介绍下: 我用了glob这个模块遍历所有主题下的emailplate.json而得到我们所需的信息, emailplate.json中是一些信息介绍和要传人emailplates view中的变量如:
{ "name": "模板名称", "template": { "engine": "handlebars(引擎名称)", "extension": "hbs(后缀名)" }, "locals": { "description": "This is description(邮件模板描述信息)", "title": "This is title(邮件标题)", "content" : "This is content(邮件内容)" } }> 另外要主要的是,为了避免在循环的时候拿不到值需要用each 或者 do...while等等方式去解决,而我们在读文件的时候需要转下编码,我默然转为utf-8格式,然后用async这个插件进行同步并行的方式读取。
最后,我们还需要一个render的方法把html和css编译后显示出来。
render: (theme, options, fn) ->
if _.isFunction options
fn = options
options = {}
themeDir = "#{[@settings](/user/settings).views}/#{theme}"
fs.readFile "#{themeDir}/emailplate.json", 'utf-8', (err, content) ->
info = JSON.parse content
options = _.defaults options, info.locals
async.parallel
html: (cb) ->
cons[info.template.engine] "#{themeDir}/html.#{info.template.extension}", options, cb
css: (cb) ->
fs.readFile "#{themeDir}/style.styl", 'utf-8', (err, content) ->
stylus.render content, cb
,
(err, results) ->
html = juice results.html, results.css
fn null, html
在这里,大家可以看到,我用了async同步并行的方式去读html和css,然后等待都读完后在进行编译操作,把得到的结果进行回调处理。另外需提醒的是,theme为主题名称, 而options为要传入的变量.
最后大家进行调用的时候可以用以下代码,css和html文件的代码我就不贴了,大家随便打点东西即可: 初始化代码:
var Emailplate = require('emailplate')
, emailplate = new Emailplate();
#get和set 邮件模板路径代码
emailplate.set('views', __dirname + '/emailplates');
emailplate.get('views');
#获取所有邮件模板信息
emailplate.themes(function(err, infos) {
});
#获取单个邮件模板信息
emailplate.theme('themeName', function(err, info) {
});
# render 邮件模板显示出来
emailplate.render('themeName', {title: "emailplate test"}, function(err, html) {
#这里返回的结果是编译后的html代码
});