ejs模板渲染的使用及在动态生成代码中的应用

1,192 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

ejs的基本使用

首先安装ejs:

npm install ejs

第一种用法

const ejs = require('ejs')

const html = '<div><%= user.name %></div>'
const options = {}
const data = {
  user: {
    name: 'pcj'
  }
}
const data2 = {
  user: {
    name: 'pcj2'
  }
}
// 这一步是编译,返回compile function, 用于解析html中的ejs模板,主要消耗性能就是这一步
const template = ejs.compile(html, options)
const template1 = template(data)
const template2 = template(data2)

console.log(template1, template2)  // <div>pcj</div> <div>pcj2</div>

第二种用法

const templateRender = ejs.render(html, data, options)
console.log(templateRender) // <div>pcj</div>

这种就是把上面要执行两步的统一了,这种用法适用于渲染一次,如果需要渲染多次,使用第一种用法

第三种用法

传入一个文件路径作为参数。注意,这里文件不一定是html后缀的文件,可以是任何文件,它都会被ejs解析成字符串。

// template.html
<div><%= user.name %></div>

// index.js
// promise的用法
const renderFile = ejs.renderFile(
  path.resolve(__dirname, './template.html'),
  data,
  options
)
renderFile.then(file => {
  console.log(file) // '<div>pcj</div>\n' 后面有个换行符
})

// 回调函数的用法
ejs.renderFile(
  path.resolve(__dirname, './template.html'),
  data,
  options,
  (err, file) => {
    console.log(file) // '<div>pcj</div>\n' 后面有个换行符
  }
)

ejs标签的含义

1. <% js脚本 %>

脚本标签,用于流程控制,无输出

<% if (user) { %> // 因为是无输出,所以这一行不会在最后的结果中显示
   <div><%= user.name -%></div>
<% } %>

显示:

<div>pcj</div>

2. <%-

输出非转义的数据到模板。

// index.html
<div><%- user.nickname -%></div>

const data = {
  user: {
    name: 'pcj',
    nickname: '<h2>sam</h2>',
    copyright: '2020'
  }
}
ejs.renderFile(
  path.resolve(__dirname, './index.html'),
  data,
  {},
  (err, file) => {
    console.log(file) // <div><h2>sam</h2></div>
  }
)

可以看到,输出的结果中不会转义<h2>sam</h2>,原样输出。如果是<%= user.nickname -%>,那么结果是<div>&lt;h2&gt;sam&lt;/h2&gt;</div>

3. -%>

删除紧随其后的换行符。因为%>后面都会带有一个空行,为了不显示空行可以用-%>

<%- user.nickname %>

image.png

<%- user.nickname -%>

image.png

可以看到空行没有了。

ejs常用的辅助功能

包含

<%- include('header', { header: 'header' }); -%>
<h1>
  Title
</h1>
<p>
  My page
</p>
<%- include('footer', { footer: 'footer' }); -%>

自定义分隔符

const ejs = require('ejs')
const users = ['geddy', 'neil', 'alex']

// 单个模板文件
ejs.render('<?= users.join(" | "); ?>', {users: users},
    {delimiter: '?'});
// => 'geddy | neil | alex'

// 全局
ejs.delimiter = '$';
ejs.render('<$= users.join(" | "); $>', {users: users});
// => 'geddy | neil | alex'

自定义文件加载器

它可以在加载文件之前统一做一些操作,类似于钩子函数。

const ejs = require('ejs')
let myFileLoader = function (filePath) {
  return 'myFileLoader: ' + fs.readFileSync(filePath);
};

ejs.fileLoader = myFileLoad;

重新定义了fileLoader逻辑后,那么所有读取的文件的时候都会先执行这个myFileLoader

这里我们可以定义自己的逻辑,但是需要注意的时候在使用include引用文件的时候,也会执行这个方法。

ejs如何动态生成代码

我们在使用vue-cli创建vue项目时,public/index.html有如下代码:

<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>

这个就是ejs语法,它里面的值是用户定义的,所以一开始不会写死。

我们在开发脚手架项目时,也遇到一个问题:当用脚手架创建一个项目时,项目的名称和版本号需要动态的根据用户传入的值填入。

那如何完成呢?

首先修改package.json,把需要动态填入的值用ejs语法占位

{
    "name": "<%= className %>",
    "version": "<%= version %>",
}

最后根据用户的值进行渲染:

function ejsRender(options) {
  const dir = process.cwd()
  // 项目信息
  const projectInfo = this.projectInfo
  return new Promise((resolve, reject) => {
    // 利用glob库把根目录下的所有文件筛选出来
    glob(
      '**',
      {
        cwd: dir,
        // 忽略那些文件,比如node_modules
        ignore: options.ignore,
        // 不包含文件夹本身,我们只需要文件夹里面的内容
        nodir: true
      },
      function (err, files) {
        if (err) {
          reject(err)
        }
        Promise.all(
          files.map(file => {
            const filePath = path.join(dir, file)
            return new Promise((resolve1, reject1) => {
              // 使用projectInfo的值填充ejs语法对应的值
              ejs.renderFile(
                filePath,
                projectInfo,
                {},
                function (err, result) {
                  if (err) {
                    reject1(err)
                  } else {
                    // 现在拿到的是编译后的字符串,但是并没有帮我们写入到文件中
                    fse.writeFileSync(filePath, result)
                    resolve1(result)
                  }
                }
              )
            })
          })
        )
          .then(() => {
            resolve()
          })
          .catch(err => {
            reject(err)
          })
      }
    )
  })
}

至此,就完成了动态生成代码。