likes
comments
collection
share

领导让我一个后端用webpack打包出一个freemarker模板!

作者站长头像
站长
· 阅读数 50

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

开篇

今天领导给我分配了一个任务,让我把一个前端项目的访问入口改到后端访问,由后端来渲染返回。比如,原来https://www.example.com/article/xxxxx这个链接是直接访问的前端想,领导想改成访问后,由后端返回页面内容。

领导:你把这个xx项目的首页面给我改成后端渲染的。

我:前后端分离不好好的吗?改成后端渲染干啥?

领导:为了适配客户的个性化操作、个性化配置。

我:前端项目打包为了防止发布时cdn缓存,打包文件时,资源文件做了版本控制, 模板文件需要放在前端项目里。

领导:对啊,所以让你来做啊。

我:我是后端。

领导:xy项目的前端不是你做的吗?

我:那不是缺人,我临时顶替的嘛。。。

领导:嗯,现在也缺人。

我:。。。

分析

首先,通过后端渲染的话,需要一个模板文件。我们公司用的是freemarker,于是暂定对这个项目改造的话,也用freemarker作为模板。

由于前端项目打包,jscssimage等资源都会带上一个版本号,好像是文件hash值,这个我作为后端不是很了解,只知道每次打包时,这个值都会变。例如:app.67c6f41.js

既然资源文件会变的话,那么生成freemarker模板的逻辑只能放在前端了。于是,了解了下前端项目的打包过程,一般是用webpack进行打包的,然后想到另外一个部门好像也是走的后端渲染,于是借来项目看看。

打开他们的项目查看打包过程,用的是webpack+gulp进行打包的。好吧,一看我就头大了,一个webpack就够我头大一会了,又来个gulp,还是算了。

于是,先分析现在这个项目的打包逻辑。

插件机制

在看项目里的webpack.config.js时,看到这样一串代码:

new HtmlWebpackPlugin({
    template: './src/index.html',
    filename: 'index.html',
    minify,
    libs: libs,
    alwaysWriteToDisk: true,
    chunks: ['app'],
})

HtmlWebpackPlugin是属于html-webapck-plugin插件的一个类或者说一个插件,这个插件有如下作用:

  1. 为html文件中引入的js、css等资源动态添加每次编译后的hash
  2. 生成一个html文件入口

于是想着,我是不是可以自己写个插件呢?在打包的时候,给我生成一个freemarker模板文件。

查了一些资料,了解了webpack一些术语,比如plugincompilercompilation等,各位前端大大肯定比我明白的多,这里就不再介绍了,然后找了个示例开始实现这个功能。

功能实现

原始模板内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="cache-control" content="no-cache">
    <META http-equiv="pragma" content="no-cache">
    <title>Test</title>
<body>
    <!-- 其他业务逻辑 -->
    <!-- 下面两行是后来加上去的-->
    <!-- ftl-start -->
    <!-- ftl-end -->
</body>
</html>

现在是想要把下面这块内容想办法插入到上面的文件里,我在原始模板加了两行注释即ftl-startftl-end,把这两行替换掉就好了,创建一个文件index.ftl内容如下:

<script>
    var user = {
        id:${user.id},
        name: <#if user.name?exists>"${user.title?js_string}"<#else>''</#if>,
        headImg:<#if user.headImg?exists>"${user.headImg?js_string}"<#else>''</#if>,
        createTime:${user.createTime?long!1670459566116},
        type:<#if user.type?exists>${user.type}<#else>0</#if>,
        status:<#if user.status?exists>${user.status}<#else>0</#if>
    };
</script>

为了不影响原来的业务逻辑,把原有HtmlWebpackPlugin进行复制了一份,把内容输出到index.ftlh

new HtmlWebpackPlugin({
    template: './src/index.html',
    filename: 'index.ftlh',
    minify,
    libs: libs,
    alwaysWriteToDisk: true,
    chunks: ['app'],
})

接下来就是往index.ftlh里面塞其他内容,定义一个Plugin如下,含义为,将index.ftl的内容注入到index.ftlh

new FtlWebpackPlugin({
    target: 'index.ftlh',
    inject: './src/view/index.ftl'
})

然后就看如何实现的吧:

领导让我一个后端用webpack打包出一个freemarker模板!

这段代码核心点就在于htmlWebpackPluginBeforeHtmlProcessing这个钩子,也就是在html处理前执行,拿到两个文件内容,进行替换。

看看最终效果:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="cache-control" content="no-cache">
    <META http-equiv="pragma" content="no-cache">
    <title>Test</title>
<body>
    <!-- 其他业务逻辑 -->
    <script>
        var user = {
            id:${user.id},
            name: <#if user.name?exists>"${user.title?js_string}"<#else>''</#if>,
            headImg:<#if user.headImg?exists>"${user.headImg?js_string}"<#else>''</#if>,
            createTime:${user.createTime?long!1670459566116},
            type:<#if user.type?exists>${user.type}<#else>0</#if>,
            status:<#if user.status?exists>${user.status}<#else>0</#if>
        };
    </script>
</body>
</html>

后记

到这里功能就完成了,功能实现后看起来很简单,但作为一个后端,在完成的过程中,经历了很多坎坷,包括webpack的版本,一些过时的文档带来的干扰等等,不过还好,最终还是实现类。

转载自:https://juejin.cn/post/7174579555196207117
评论
请登录