Skip to content

前端框架配置约定

CntChen edited this page Apr 15, 2019 · 4 revisions

背景

页可视化搭建框架 pipeline 实现了编辑器和页面前端框架的分离, 支持任意前端框架页面的可视化搭建.

使用 pipeline 实现页面可视化搭建时, 并不需要切换原有业务的前端框架, 只需要对页面前端框架的组件写法, 文件目录和构建脚本做一些约束和改造, 就可以复用该前端框架现有的技术组件和业务组件, 实现可视化搭建.

这篇教程主要介绍 pipeline 对前端框架的约定, 并以 react 为例, 介绍前端框架改造后的代码结构和构建脚本的约定.

工程约定

页面目录约定

总体目录, 后续会详细介绍.

.
├── README.md
├── scripts // 项目构建脚本
   ├── build-server.js // pipeline 打包
   ├── build.js // 常规的项目 prod 打包
   ├── server-check-schema.js // pipeline 配置数据校验
   ├── server-copy-config.js // pipeline 配置文件拷贝
   ├── server-generate-library-info.js // 生成 pipeline 组件库信息
   ├── server-template-archive.js // 生成 pipeline 页面模板压缩包
   ├── utils-schema-validator.js // 页面和组件的 JSON Schema 校验器
   └── start.js // 本地开发构建
├── config
   ├── index.js // 页面全局替换字段配置
   └── webpack.xxx.js // webpack 配置
├── pipeline-template.zip // 生成的 pipeline 压缩文件
├── server // pipeline 后台渲染工作目录
   ├── config // 组件配置信息
   ├── dist // pipeline 打包后的页面
   ├── node.js // 页面后台渲染脚本
   └── preview-inserted-script.js // 预览模式的嵌入脚本
└── src
  └── config // 页面和组件的配置信息
      ├── base-config-schema.json // 页面全局配置约束文件
      ├── base-config.json // 页面全局配置
      └── components.json // 组件列表描述文件

构建命令

$ npm run start # 本地开发构建
$ npm run build # prod 模式构建, 构建出可发布页面
$ npm run server # pipeline 后台构建
$ npm run render # 后台构建后本地验证渲染结果
$ npm run template # 打包页面资源用于上传到 pipeline 后台服务器

pipeline 已经提供了几大主流前端框架的模板, 可以参考熟悉的框架模板来看这个教程:

组件约定

pipeline 页面可视化编辑其实是对页面各组件的可视化编辑. 为了实现组件的可视化编辑, 需要提供配置表单给用户填入配置数据, 并将配置数据传给组件.

组件文件和目录约定

约定组件目录为 src/components, 其中每个子目录为一个页面组件.

组件路径示例:

./src/components
├── pipeline-gap-demo // 间隔区组件
├── pipeline-header-demo // 头部组件
├── pipeline-info-demo // 信息组件
└── pipeline-weather-demo // 天气组件

头部组件文件示例:

./pipeline-header-demo
├── config // 组件配置字段声明目录
   ├── data.json // 组件默认配置字段
   └── schema.js // 组件配置字段的 Schema 声明
├── index.js // 组件源码
├── style.less
└── package.json // 组件描述文件

页面组件的目录下有几个特定文件, 其中 config/data.json, config/schema.jsconfig/package.json 为 pipeline 约定了名称和格式的文件.

组件实例可以查看这里: pipeline-header-demo.

config/data.json

是组件的默认配置数据, 其内容是组件添加到页面时填入的默认配置数据. 示例:

{ 
  "title": "页面可视化搭建框架",
  "src": "https://avatars3.githubusercontent.com/u/38666040",
  "link": "https://github.com/page-pipepline"
}

config/schema.js

为组件配置数据的 JSON Schema, 用于编辑时自动生成组件配置表单. 示例:

module.exports = {
  title: '头部区',
  type: 'object',
  properties: {
    title: {
      title: '头部标题',
      type: 'string',
      default: '头部标题',
    },
    src: {
      title: '头部图片',
      description: '试试输入新的图片URL: https://vuejs.org/images/logo.png',
      type: 'string',
      default: '头部图片'
    },
    link: {
      title: '头部图片点击跳转',
      type: 'string',
      format: 'url',
      default: '头部图片点击跳转',
    }
  }
};

其具体写法可以参考: JSON Schema Demo.

config/package.json

组件描述文件, 包含组件英文名称, 组件中文名称等. 示例:

{
  "pipeline": {
    "name": "pipeline-header-demo",
    "title": "头部区示例",
  }
}

组件渲染入口

组件目录中的 index.js/index.vue 为组件源码, 各个前端框架有不同的组件语法, 但都需遵循 pipeline 的组件属性(Props)约定, 见下一节.

组件属性约定

组件属性 props.config

组件的配置数据通过组件属性传入组件中, 配置数据名称约定为 props.config, 数据类型为 object. props.config 对象的字段与 config/schema.js 的声明一致, 其默认数据为 config/data.json.

在组件中使用 props.config 作为组件渲染和组件逻辑的常量.

下图展示了头部组件(pipeline-header-demo)的 props.config 用法. 其中 props.config.titleprops.config.src 用于组件模板, 并渲染到 View 上; props.config.link 作为点击交互的跳转链接, 用于动态逻辑中.

组件属性的使用实例可以查看这里: pipeline-header-demo.

组件id和组件name

除了传入 config 到组件中, 还需要通过 props 传入另外2个组件信息: data-component-iddata-component-name, 用于可视化编辑器唯一标识组件, 实现编辑组件器切换和预览页面滚动.

给组件传入3个属性的方式如下图所示:

并且需将 data-component-iddata-component-name 添加为组件根 HTML 元素的自定义属性.

添加方式如下图所示:

添加到组件根 HTML 元素的渲染结果:

组件列表声明文件

组件列表声明文件声明了页面使用的组件列表, 包含组件名称, 组件id, 组件配置数据等.

pipeline 使用组件列表声明文件和动态组件生成页面, 所以只要修改组件列表声明文件, 就能得到新的页面:

文件内容

组件列表声明文件约定路径为 src/config/components.json. 组件列表声明文件示例如下图所示.

其中 id 表示组件id, name 表示组件名称, data 表示组件属性(Props), data 会注入到组件的 Props.config 中.

pipeline 使用不嵌套的组件排列方式, 所以组件声明文件其实是一个数组对象.

组件列表声明文件实例可以查看这里: compenents.json

使用组件列表声明文件

在页面入口引用组件声明文件, 并且根据组件声明文件进行页面组件的渲染.

如 react 的渲染示例代码:

import mycomponents from './config/components'; // 引入组件声明文件

// 组件声明优先从全局对象上取
const pipelineComponents = (typeof window !== 'undefined') ?
    window.INIT_DATA || mycomponents : mycomponents;

// 页面中的组件类
const components: {
  'pipeline-gap-demo': PipelineGapDemo,
  'pipeline-info-demo': PipelineInfoDemo,
  'pipeline-header-demo': PipelineHeaderDemo,
  'pipeline-weather-demo': PipelineWeatherDemo,
},

// 根据组件声明实例化组件
const Components = pipelineComponents.map(oneComponent => {
  const OneComponent = components[oneComponent.name];
  return OneComponent && (
    <OneComponent
    key={oneComponent.id}
    data-component-id={oneComponent.id}
    data-component-name={oneComponent.name}
    config={oneComponent.data}/>);
});

// 渲染页面组件
return (
  <div className="App">
    {Components}
  </div>
);

其中的关键逻辑是: 组件列表声明优先从全局对象中取, pipeline 做后台渲染时, 会将可视化编辑后的组件列表声明插入到页面的全局对象中. 这点和服务端渲染(SSR)将请求后台获取的数据插入到页面全局对象的做法很相似. 注入的全局对象名称为 window.INIT_DATA.

页面全局配置

页面全局配置是指超出各个组件配置范围, 应用在整个页面内容和样式的配置. 页面全局配置的使用方式是通过 html-webpack-plugin 将配置字符串插入到页面 html 模板.

相关文件

添加页面全局默认配置 src/config/base-config.json 和配置约束src/config/base-config-schema.json.

  • src/config/base-config.json 的配置数据会在本地调试和生产打包时替换到页面 html 模板中. 在 pipeline 后台打包时,会将配置数据占位符插入页面, 后台渲染时将配置数据替换掉占位符, 实现配置数据修改.

  • src/config/base-config-schema.json 是配置数据的 JSON Schema, 用于在页面上自动生成页面全局配置的配置表单.

这2个文件的写法可以参考: pipeline-template-react-config.

页面模板修改

给页面 html 模板添加待替换的配置字符

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <style type="text/css">
      html, body {
        background-color: <%= htmlWebpackPlugin.options.backgroundColor %>
      }
    </style>
  </head>
  <body>
    <%= htmlWebpackPlugin.options.reactInstancePlaceholder %>
  </body>
</html>

其中的 htmlWebpackPlugin.options.backgroundColor, htmlWebpackPlugin.options.reactInstancePlaceholder 为待替换的配置字符. <% ... %>html-webpack-plugin 的占位符规则.

可参考实例: html-template.

实现配置字符的替换

config/index.js 中引入页面全局默认配置文件 src/config/base-config.json, 并转换为开发模式和 pipeline 后台模式需要的替换对象.

参考实例: config/index.js.

// 引入页面全局默认配置文件
const baseConfig = require('../src/config/base-config.json');

// 开发模式和生产打包时将配置数据各个字段替换为对应的默认数据
const HWPPageBaseConfig = Object.keys(baseConfig).reduce((accumulator, key) => {
  accumulator[key] = baseConfig[key];
  return accumulator;
}, {});

// pipeline 后台打包时将配置数据各个字段替换为配置数据占位符
const HWPPageBaseConfigForServer = Object.keys(baseConfig).reduce((accumulator, key) => {
  accumulator[key] = `<!--baseConfig-${key}-->`;
  return accumulator;
}, {});

// 各个构建模式的页面全局配置替换对象
module.exports = {
  build: {
    HWPPageBaseConfig
  },
  dev: {
    HWPPageBaseConfig
  },
  server: {
    HWPPageBaseConfigForServer,
  },
};
  • 开发模式和生产模式的打包, 需要得到目标页面, 此时的页面全局配置数据直接替换到页面模板中.
  • pipeline 后台打包模式, 页面全局配置占位符是转换为另外一种格式的占位符(格式: <!--baseConfig-${key}-->), 然后在页面可视化编辑时根据用户修改的数据, 对 <!--baseConfig-${key}--> 做替换, 替换为用户配置的数据.

htmlWebpackPlugin 构建脚本

然后给 htmlWebpackPlugin 添加模板内容替换的参数. 需要给开发模式, 生产模式和 pipeline 构建模式都添加对应的 htmlWebpackPlugin 参数.

config/webpack.config.dev.js 中的添加示例如下:

  htmlWebpackPlugins.push(
    new HtmlWebpackPlugin({
      inject: true,
      chunks: [item],
      template: paths.appHtml,
      filename: item + '.html',
      // 添加模板内容替换的参数
      ...config.dev.HWPPageBaseConfig
    }));

可参考实例: webpack.config.dev.js.

添加后台打包脚本

页面模板已有了本地开发(dev)和生产构建(prod)的打包方式, 需要添加 pipeline 后台需要的打包模式 -- server.

相关目录

pipeline 后台使用的目录为 server. 需要先新建 server 目录.

server 目录中的文件如下:

./
├── config // 打包生成的组件配置文件和页面配置文件
├── dist // pipeline 后台打包的结果目录
├── node.js // pipeline 后台渲染脚本
└── preview-inserted-script.js // 编辑器插入到预览页面的脚本, 用于编辑器获取页面状态

server 打包模式中, 会将页面打包到 server/dist, 将组件配置和页面配置相关的文件打包到 server/config. 这两个目录是构建生成的, 不需要手动添加.

node.js 是后台渲染脚本, 会将用户编辑的配置内容插入到页面, 并且重新打包页面.

preview-inserted-script.js 在可视化编辑模式下插入到页面中, 监听页面收到的 poseMessage事件, 实现页面自动滚动到当前编辑的组件处.

添加通用构建脚本

在构建目录 scripts 中添加4个构建脚本, 4个脚本名称和用处如下:

server-check-schema.js // 校验各组件配置字段和其 Schema 是否匹配, 页面全局配置和其 Schema 是否匹配
server-copy-config.js // 将 src/config 下的组件声明文件和页面全局配置等 copy 到后台打包目录
server-generate-library-info.js // 收集和生成组件库中组件的描述信息、默认配置数据和配置约束
utils-schema-validator.js // JSON Schema 校验函数, server-check-schema.js 中使用

这几个脚本是遵循 pipeline 约定的文件, 直接从已支持框架的页面源码中复制就可以了. 参考示例: scripts.

添加 server 的 webpack 打包配置

复制 prod 构建 webpack 配置 config/webpack.config.prod.jsconfig/webpack.server.config.prod.js, 作为 server 构建的配置文件, 再做少量修改.

忽略 src/config/components.json

src/config/components.json 为组件列表声明文件, 后台打包时组件列表声明文件是直接插入到页面中的, 所以不需要在构建时候引入.

module.exports = {
...
externals: {
  './config/components': {
    commonjs2: '../config/components.json',
    commonjs: '../config/components.json',
  },
},
...

src/config/components.json 会通过 window 全局变量(window.INIT_DATA)注入, 在页面渲染时优先从全局变量上取.

// 组件声明优先从全局对象上取
const pipelineComponents = (typeof window !== 'undefined') ?
    window.INIT_DATA || mycomponents : mycomponents;

修改输出目录

修改输出目录为 pipeline 后台需目录 server/dist

// config/paths.js: appBuildServer: resolveApp('server/dist'),
output: {
  ...
  path: paths.appBuildServer,
  ...

添加 htmlwebpackplugin 页面全局字段替换配置

页面全局配置在 pipeline 后台渲染中会替换为用户编辑的字段. 源码页面中的 <%= htmlWebpackPlugin.options.xxx %> 占位符, 替换为 <!--baseConfig-xxx-->; pipeline 后台渲染再将 <!--baseConfig-xxx--> 替换为用户编辑的页面全局字段.

htmlWebpackPlugins.push(
  new HtmlWebpackPlugin({
    ...
    ...config.server.HWPPageBaseConfigForServer,
    ...
  }))

添加 npm 命令

在 package.json 中添加 server 打包脚本:

"server": "rm -rf server/dist && rm -rf server/config && node scripts/build-server.js"

添加 pipeline 后台渲染脚本

可视化编辑后的组件列表和组件配置存储到后台的 server/config/components.json 文件中; 页面全局配置存储到 server/config/base-config.json 中.

pipeline 的后台渲染原理非常简单:

  • components.json 插入到 index.html 中作为全局变量.
  • base-config.json 中的字段值插入到 index.html.

后台渲染脚本为 server/node.js, 其会读取 components.jsonbase-config.json, 并将需要的数据插入到 index.html 中.

具体实例可以参考: node.js

本地测试后台渲染是否可用指令:

$ npm run render

添加压缩脚本

server 打包脚本生成的资源文件, 要作为页面模板上传到 pipeline 后台, 需要对文件夹进行压缩打包. 同时 pipeline 对目录结构有要求, 通过压缩脚本可以规范化压缩操作.

压缩脚本在 scripts/server-template-archive.js中, 使用 archive node 压缩库进行资源的压缩.

压缩脚本:

$ npm run archive

压缩结果约定为 pipeline-template.zip, 所在目录为工程根目录.

server-template-archive.js 文件的压缩功能可以直接复制其他框架中的相同文件. 参考示例: server-template-archive.

其中差异点在于添加要打包的目录和文件.

压缩打包是给 pipeline 后台使用, 可以忽略的目录有: node_modules(依赖包), dist/build(prod模式生产构建输出目录), pipeline-template.zip(压缩包本身)等.

需要打包的目录参考:

// 需要打包的目录
const directoryList = [
  'scripts',
  'config',
  'server',
  'src'
];
// 需要打包文件
const fileList = [
  '.babelrc',
  '.editorconfig',
  '.eslintignore',
  '.eslintrc.js',
  '.postcssrc.js',
  'package.json',
  'README.md',
];

EOF