其他
星创编辑器在投放业务中的落地|得物技术
目录
一、落地页技术架构
1. 名词解释
2. 技术架构图
3. 基础框架搭建
二、模板JSON设计
1. 新增模板类型
2. 抽象模板配置
3. 模板渲染
三、后台权限设计
四、B端编辑器的选型思考
1. 画布配置预览
1.1 动态远程组件
1.2 Iframe
2. 画布通信
3. B端组件配置
五、收益
六、总结 & 规划
搭建一个落地页需要涉及到多方合作,需要不断地进行沟通协调。繁杂的流程需要耗费很多的时间,因此我们推动产品重新搭建了一个专门服务于软广投放流程的编辑器——星创,完成广告搭建在投放业务各系统中的闭环。
一
落地页技术架构
名词解释
技术架构图
下面我将从基础框架、模版类型配置、模版渲染3个方面阐述落地页编辑器的技术选型思路。
基础框架搭建
二
模板JSON设计
{
// 模板对应的类型
templateType: string,
// 模板变化的配置
defaultConfig: {
},
// 模板可变配置
variableConfig:{}
// 模板不变的配置
globalConfig: {
},
}
新增模板类型
是为了渲染层对于不同模板类型走不同的渲染模式。比如静态化的广告落地页,不涉及请求接口的。统一走SSG或者ISR的渲染模式。如果是依赖接口请求的落地页,走SSR或者是react 18的流式渲染RSC。
不同的渲染模式,打包出来的产物不同,性能优化的策略侧重点也是不一样的。 之前投放地落地页链接都是xx-plus/xxx/xx,没法一眼看出当前落地页属于哪个类型,由于我们需要做精细化的落地页数据归因,所以我们存储了落地页的模板类型,所以最后投放链接都是这样。
https://cdn-xx.xx.com/xx-plus/{{模板类型}}/{{模板ID}}
批量修改场景:过去投放落地页会有在节假日进行批量换图、批量修改配置的需求。过去在哪吒中中需要投入大量的维护成本,我们将落地页的类型维度单独抽象出来,通过支持对类型的批量修改来进一步提效。
抽象模板配置
模板渲染
三
后台权限设计
四
B端编辑器的选型思考
画布配置预览
动态远程组件
先有一个组件。 将组件打包成UMD格式,可供浏览器使用(后面会介绍UMD),或者使用System.js去打包。 将其上传到CDN,然后还需要一个物料管理平台,主要管理组件的版本、支持注册、可回滚。 编辑器沙箱能力的考虑,防止污染全局。 SSR根据组件类型进行动态渲染。
const DynamicComponent = ({name, children, ...props}) => {
const Component = useMemo(() => {
return React.lazy(async () => fetchComponent(name))
}, [name])
return (
<Suspense
fallback={
<div style={{alignItems: 'center', justifyContent: 'center', flex: 1}}>
<span style={{fontSize: 50}}>Loading...</span>
</div>
}>
<Component {...props}>{children}</Component>
</Suspense>
)
}
export default React.memo(DynamicComponent)
动态性(当组件需要更新时,可以直接JS内容,就可以实现动态更新)。 不确定性(对于主应用而言,其不知道用户会拖拽多少个组件以及每个组件长什么样,它只需要将用户拖拽的JSON数组进行循环遍历,并渲染,然后将配置的属性传递过去就可以了,具体到每个组件具体是长什么样其不关心)。
我们一个落地页的组件类型的是确定的,在编辑器配置落地页,只有选择模板类型,选择完模板类型,自动会加载模板对应的组件配置。不存在用户拖拽、选择。星创落地页产品的核心逻辑就是轻量。
Iframe
///预览路由
export default HocPreview(({ componentList }) => <Container componentList={componentList} />)
// 正式路由
export default HocApp(({ componentList }: { componentList: any }) => {
return <Container componentList={componentList} />
})
天然的沙盒化因为iframe的天然隔离性,画布渲染器中的所有逻辑、样式不会影响编辑器本身。 利于多人编辑单人编辑时使用iframe进行通信,而多人编辑时可将iframe通信切换成WebSocket通信,设计时有异曲同工之妙。 编辑器页面打开快,体验好,不需要加载很多物料,预览的加载收敛到SSR去处理。
可扩性特别差,新增落地页类型,都需要开发、测试。不支持定制化。 没法做到组件级别的回滚。
画布通信
代理状态,基于Proxy。 响应式更新,对状态的操作都会通过代理进行,Valtio内部会跟踪并自动渲染。 细粒度渲染,Valtio的代理可以实现细粒度的依赖跟踪,这样只有组件实际使用的状态变化时才会重新渲染,避免了不必要的渲染,性能方面很不错。 简洁的API,Valtio尽可能地让开发者操作状态就像操作普通对象一样自然。 订阅/通知模式。 状态适用于组件之外,除了组件外,也支持用在某些逻辑上。
简洁性:Zustand通过一个干净直观的API简化了状态管理,减少了代码的复杂性。不需要应用外侧包裹一层Provider。 性能:Zustand高度优化,为你的应用提供卓越的性能。 可扩展性:随着你的项目增长,Zustand仍然易于使用并且扩展性好。 不变性:Zustand鼓励不变性,使跟踪状态变化和调试问题变得更容易。 灵活性:它不限于特定的框架。你可以在React、React Native或任何其他JavaScript环境中使用Zustand。
** 高阶组件 基于iframe 进行封装 */
export const HocPreview = (Template: (componentList: any) => JSX.Element) => {
return ({ data }: { data: any }) => {
const [componentList, setComponentList] = useState(data?.componentList ?? [])
useEffect(() => {
// 在iframe中且没有hash值的时候,添加hover样式并通知父级
if (window.self !== window.top && !window.location.hash) {
const compElemList = document.querySelectorAll('.editor-box>div>*')
compElemList.forEach((item, index) => {
item.addEventListener('mouseenter', () => {
window.parent.postMessage(index, '*')
compElemList.forEach((citem) => {
citem.classList.remove('actived')
})
item.classList.add('actived')
})
})
}
window.addEventListener('message', ({ data }: { data: PostComponentMsg[] }) => {
setComponentList([...data])
})
// 去除预览页面的滚动条
document.querySelector('html')?.classList.add('no-scroll-bar')
}, [])
const handleClick = (e: SyntheticEvent) => {
if (window.self !== window.top) {
e.stopPropagation()
}
}
return (
<div className="editor-box" onClickCapture={handleClick}>
{/* <Mask /> */}
<Template componentList={componentList} />
</div>
)
}
}
B端组件配置
五
收益
提升投放业务人员的体验,提供软广服务的多样性。 积累数据效果,迭代站外落地页的效果。 潜在钱效收益:过往流程过长会发生影响如期上线的问题,大促节点如期上线抢占高下单率时机对钱效也有正向影响。 规避人工搭建跳转错误造成损失问题。
六
总结 & 规划
丰富外投落地页的通用能力,提高拉新和还原指标。 落地星创落地页数据洞察,从投放到站内承接,我们需要拿到更多的信息,能够得到更多的投放策略。 建立一套高效率的机制能快速实验和数据回收,帮助业务达成目标。
往期回顾
文 / Fly
关注得物技术,每周一、三、五更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。
“
扫码添加小助手微信
如有任何疑问,或想要了解更多技术资讯,请添加小助手微信: