领导让我一个后端用webpack打包出一个freemarker模板!
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
开篇
今天领导给我分配了一个任务,让我把一个前端项目的访问入口改到后端访问,由后端来渲染返回。比如,原来https://www.example.com/article/xxxxx
这个链接是直接访问的前端想,领导想改成访问后,由后端返回页面内容。
领导:你把这个xx项目的首页面给我改成后端渲染的。
我:前后端分离不好好的吗?改成后端渲染干啥?
领导:为了适配客户的个性化操作、个性化配置。
我:前端项目打包为了防止发布时cdn缓存,打包文件时,资源文件做了版本控制, 模板文件需要放在前端项目里。
领导:对啊,所以让你来做啊。
我:我是后端。
领导:xy项目的前端不是你做的吗?
我:那不是缺人,我临时顶替的嘛。。。
领导:嗯,现在也缺人。
我:。。。
分析
首先,通过后端渲染的话,需要一个模板文件。我们公司用的是freemarker
,于是暂定对这个项目改造的话,也用freemarker
作为模板。
由于前端项目打包,js
、css
、image
等资源都会带上一个版本号,好像是文件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
插件的一个类或者说一个插件,这个插件有如下作用:
- 为html文件中引入的js、css等资源动态添加每次编译后的hash
- 生成一个html文件入口
于是想着,我是不是可以自己写个插件呢?在打包的时候,给我生成一个freemarker模板文件。
查了一些资料,了解了webpack
一些术语,比如plugin
、compiler
、compilation
等,各位前端大大肯定比我明白的多,这里就不再介绍了,然后找了个示例开始实现这个功能。
功能实现
原始模板内容如下:
<!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-start
、ftl-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'
})
然后就看如何实现的吧:
这段代码核心点就在于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