携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
回顾
上一篇分享了前端拖拽配置和schema的实现。当页面元数据schema存储后,该如何操作才能将schema解析渲染到目标环境呢?请看下文介绍。
代码仓库地址
这是文章示例的低代码平台源码仓库
github:github.com/iamwhj/mobi…
目标环境
我们这个项目的目标环境是移动端,也就是需要做移动端适配,然后生成H5页面。
获取元数据
常规的方式是直接根据活动ID请求接口去获取,当然这个做法是可行的,但是如果发布生成活动落地页后,高并发访问量场景,服务器能否扛得住,静态资源都可上CDN,但是获取活动元数据这个接口是实打实的需要通过我们自己的服务器,所以请往下看,我们选择另一种方式。
服务端构建插入,我们在生成活动html文件的时候,直接将元数据插入到html文件里
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><!-- ACTIVITY_TITLE --></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- ACTIVITY_DATA -->
</body>
</html>
预先设置好插入锚点,ACTIVITY_TITLE、ACTIVITY_DATA就是插入页面title和元数据的锚点。
// 获取html模板
const htmlTemplateDao = new HtmlTemplateDao();
const htmlRecord: any = await htmlTemplateDao.find();
let htmlTemplate = htmlRecord[0].html;
const ACTIVITY_TITLE = activityData.detail.title;
const ACTIVITY_DATA = `<script> var activity = ${activity.page}; var activityId = ${activityId} </script>`;
htmlTemplate = htmlTemplate
.replace('<!-- ACTIVITY_TITLE -->', ACTIVITY_TITLE)
.replace('<!-- ACTIVITY_DATA -->', ACTIVITY_DATA);
return htmlTemplate
服务端处理,将页面title和元数据填充。
解析引擎实现
-
移动端适配
// 将 px 统一转化成 rem components.value.forEach((comp) => { for (const key in comp.style) { // 将px转换成rem(移动端适配) if (typeof comp.style[key] === 'number') comp.style[key] = comp.style[key] / VUE_APP_UNIT_WIDTH + 'rem'; } comp.ripeParams = { ...comp.style, ...comp.detail, }; }); -
动态组件渲染
<template v-for="comp in components" :key="comp.id"> <div :style="{ 'text-align': comp.style.align, 'font-size': 0 }"> <div style="display: inline-block"> <component :is="comp.name" v-bind="comp.ripeParams" :clickChock="clickChock(comp.click)" ></component> </div> </div> </template> -
点击事件处理
// 统一处理点击事件 const clickChock = (click) => { // 不是移动端 if (!isMobileEnv() || !click) return () => false; if (click.type === 'link' && click.url) { // link return () => (window.location.href = click.url); } else if (click.type === 'dialog') { // dialog return () => { // 弹窗出现 clickEventDialog.value = true; click.dialogTitle && (clickDialogInfo.value.title = click.dialogTitle); click.dialogContent && (clickDialogInfo.value.content = click.dialogContent); }; } };预览功能
将预览服务打包成静态资源,利用iframe进行跨域请求,接口直接返回字符替换完成后html
export const generateHtml = async (activity: any, publish?: boolean) => { const activityData = JSON.parse(activity.page); const activityId = activity.id; // 获取html模板 const htmlTemplateDao = new HtmlTemplateDao(); const htmlRecord: any = await htmlTemplateDao.find(); let htmlTemplate = htmlRecord[0].html; const ACTIVITY_TITLE = activityData.detail.title; const ACTIVITY_DATA = `<script> var activity = ${activity.page}; var activityId = ${activityId} </script>`; htmlTemplate = htmlTemplate .replace('<!-- ACTIVITY_TITLE -->', ACTIVITY_TITLE) .replace('<!-- ACTIVITY_DATA -->', ACTIVITY_DATA); if (publish) { const { HTML_FOLDER } = process.env; const folderPath = resolve(__dirname, '../../', HTML_FOLDER as string); if (!existsSync(folderPath)) mkdirSync(folderPath); // 活动发布 writeFileSync(resolve(folderPath, `${activityId}.html`), htmlTemplate, 'utf-8') } return htmlTemplate }这样就能实现预览,只有发布的时候才正在生成活动的html文件。
总结
获取元数据,统一做移动端适配后,遍历渲染出组件。
将预览服务打包成静态资源,使用iframe跨域请求使用这些静态资源。