细说在线版H5页面生成器

上次说得不够仔细,今天继续,不要停

Github: 传送门
演示地址:传送门

目录说明

  1. 阅读前准备
  2. 概要
  3. 后台代码简读
  4. 前端代码简读
  5. 待完善功能
  6. 总结

阅读前准备

  1. 了解node
  2. 了解express框架
  3. 了解mongodb以及node 连接框架 mongoose
  4. 了解nunjucks模板

概要

设计思路和主要实现步骤都写在这里:从零打造在线版H5页面生成器

设计思路和主要实现步骤都是正经而且值得借鉴的(有点不谦虚哈),但是由于赶时间以及玩的心态作祟,具体的代码细节上就显得有点混乱尴尬了,主要体现在这几个方面:

  1. 项目结构随性
  2. 未使用打包工具对代码进行压缩合并以及其它预编译处理,违背现在前端工程化的潮流,webpack.config.js放那唬人,就当是方便场景代码生成后阅读吧
  3. 可读性低,难以维护的模板代码
  4. 只兼容高版本的浏览器,建议在chrome上运行。由于自己很傲娇的使用formData来封装了图片上传插件,所以可以肯定的是,ie10以下的浏览器是肯定无法上传图片的
  5. 很多功能都没有完善,因为懒

“自我批评这么诚恳,咋不见你改?”
拜托,我忙啊,给我点动力呗?我看有没有的
点赞!
点赞!
点赞!
保持楼上队形
打赏!
打赏!
打赏!

如若不然,就请给颗星吧

安装使用

  1. 安装mongodb
  2. npm install
  3. npm start
  4. 访问: http://localhost:3000/home

目录结构说明

|-- output                       --------静态资源目录(express static)
    |--client                    --------前端界面对应静态资源
        |--css                   --------前端css目录
        |--images                --------前端图片目录
        |--js                    --------前端js目录
    |--projects                  --------保存后h5场景代码输出目录
        |--projectId             --------根据项目id创建的目录
            |--css               --------输出代码的css样式
            |--images            --------输出代码的图片目录
            |--js                --------输出代码的js目录
            |--view.html         --------html
    |--temp                      --------预览时候h5场景代码输出的临时目录
    |--upload                    --------图片上传地址
|-- server                       --------服务器端代码
    |--db                        --------数据库相关
      |--dao                     --------数据库操作
      |--models                  --------mongoose模型
      |--config.js               --------数据库链接配置文件
      |--connect.js              --------数据库连接
    |--factory                   --------h5代码构建工厂
        |--static                --------h5场景代码静态资源(原装复制)
        |--template              --------h5场景代码模板
        |--build.js              --------核心渲染处理
        |--build_bac.js          --------测试js,直接运行渲染
        |--config.js             --------mock数据
    |--middleware                --------express中间件
    |--routers                   --------express路由
    |--utils                     --------公共代码
    |--views                     --------nunjucks模板
|-- server.js                    --------服务入口文件
|-- webpack.config.js            --------未使用

后台代码简读

一、核心渲染(build)

从零打造在线版H5页面生成器中已经有详细介绍,关键在于实例模板化,以及数据结构的定义。

这里尚有需要特别说明的一点:各个击破
我们定义好模板,定义好数据结构的时候,前端还没开始开发,整个流程是无法串起来的,所以我们要构建最小的可以运行测试的单元,也就是build_bac.js(刚开始它是build.js),这是一个可以使用node独立运行的js文件,然后创建一个config.js数据实例,把渲染功能跑通,最后再将其封装成提供外部调用的模块build.js(当然在最后跟前端串起来跑的时候会有很多新的改动)。

关键字:单元测试、各个击破。

二、数据库相关

没有什么太复杂的东西,使用mongoose,一个数据模型,实现增删改查。

之所以一个数据模型就够,主要是因为使用了非关系型数据库,一对多,多对多的关系中并不需要为数据冗余的问题而分表

三、express路由

为了减少配置工作量,在server/routers/index.js中,自动扫描路由文件,有点杀鸡用牛刀的意思,压根就没几个路由文件

    // 控制器路由表
    var actionList = [];
    files = utils.getAllFiles(process.rootPath + '/server/routers');
    for (var key in files) {
        if(files[key].indexOf('index.js') < 0) {
            actionList.push(files[key].replace(process.rootPath + '/server/routers/', ''));
        }
    }

    module.exports = (app) => {
    //循环配置控制器路由表
    Array.from(actionList, (page) => {
        require('./' + page)(app);
        return page;
    });
};

nunjucks模板(views)

  1. _inc目录维护一些html片段,或共享,或减少单个模板文件的代码,增强可读性
  2. home.html项目列表,增删改查
  3. show-h5.html预览页面
  4. edit.html编辑页面(新增或修改,核心页面)

edit.html的模板代码异常复杂,后文会详诉

前端js代码简读

如果用一句话来概括这个工具,不过是将大量场景实例提升为模板,并将满足既定数据结构模型的数据实例与模板结合渲染生成h5场景代码的过程。这句话并未突出构造数据实例这一过程(似乎在从零打造在线版H5页面生成器中也被一笔带过了),然而,实现的过程中,构造数据实例的过程,即用户在前端进行可视化编辑操作过程,不光是重中之中,而且也是难中之难。此次再不能避而不谈了

一、拖拽、文件上传插件编写

拖拽插件:div拖拽缩放jquery插件编写——带8个控制点

文件上传插件:使用formData新特性,低版本浏览器不兼容,只为方便并get新技能,详见ZUpload.js

第一反应是通过网络渠道寻找合适的插件,而不是费劲的重复造轮子,只是并未找到比较合适而且可以随意控制的。重复造轮子有时候也是指的拥有的

二、入口(edit.js)

读代码的第一要务就是找到代码的入口,edit.js是也~
在“从零打造”中我们已经说过,要将负责的任务分解,所以入口edit.js的主要任务是协调调度各个模块

    //定义全局变量
    this.pageController = false; //页面控制器
    this.rTab = new RTab(); //右侧面板控制器
    //对话框 因为用到rTab的方法所以放在后面
    this.dueDialog();
    /**
     - 处理page
     */
    this.duePage();
    /**
     -  处理背景图片上传,必须在pageController之后
     */
    this.dueBgUpload();
    /**
     - 处理文本 必须在创建page之后
     */
    this.dueText();
    /**
     - 处理图片 必须在创建page之后
     */
    this.dueImage();
    /**
     - 处理预览
     */
    this.duePreview();
    /**
     - 处理头部按钮事件
     */
    this.dueHeadButton();
    /**
     - 处理头部按钮切换事件
     */
    this.changeTab();

三、右侧属性编辑面板控制(rTab.js)

在上例入口代码可以看到,rTab对象在初始化的时候是最先建立的,因为页面控制器,以及图片文本编辑都需要依赖于它来构建,rTab复制哪些事情呢?

  1. 维护用户操作过程中产生的数据,并且在用户点击保存和预览的时候,可以随时构造符合后台要求的数据实例,用于构建输出结果
     //page数据
     pageData = window.pageData || {};
     //pageItem数据
     pageItemData = window.pageItemData || {};

     /**
     +  生成后台需要的数据结构
     */
    buildData: function() {
        var self = this;
        var pages = [];
        var pageItems = self.sortPageItems();
        $('.page-wrap').each(function(index) {
            var key = $(this).attr('id');
            var pageObj = pageData[key];
            var spb = pageObj.bgImage.split('/');
            var burl = '../images/' + spb[spb.length -1];
            var page = {
                burl: burl,
                bgColor: pageObj.bgColor,
                items: pageItems[key] || []
            };
            pages.push(page);
        });

        return {
            pages: pages
        };
    }
  1. 面板切换:编辑屏、编辑文本、编辑图片对应了不同的编辑面板,需要适时切换,最重要的还是要切换的时候要把当前编辑的数据保存,也就是保存到上一步的pageDatapageItemData

四、屏/页控制器(page.js)

场景页面的核心要素为:屏/页->屏内项目->项目动画,所以首先我们要构建的是屏/页控制对象,鉴于屏以及舞台上的所有操作的数据变化都需要rTap来监控,所以需要传入rTap来构造屏控制器对象

  1. 屏的增删改:每一屏应该有一个一一对应编辑舞台,也就是中间区域,用于编辑展示自身的背景,以及屏内元素。另外,请时刻记住还有一个rTab需要传进来构造
  /**
   *  创建page
   */
  createPage: function() {
      //索引递增
      this.pageIndex++;

      //增加page
      var pageId = 'page' + this.pageIndex;
      var pageHtml = ...;//此处省略几万字
      var page = $(pageHtml);
      $('#pageContainer').append(page);
      //增加对应的舞台(同步创建舞台)
      this.createStage(pageId);
      //设置当前页
      this.switchPage(pageId);
  },
  1. 创建拖拽对象,拖拽插件之前已经有单独的文章介绍,这里主要有两点需要注意,在item点击,以及舞台空白区域点击的时候插件以及有事件监听,插件外部我们也需要监听这两个事件来保存数据,所以需要使用回调的方法切入事件
  //创建拖拽对象
  var self = this;
  this.zresize = new ZResize({
      stage: '#mainContent',
      onTriggerItem: function(el) { //item获取焦点时候切入item数据保存处理
          self.onItemTrigger(el);
      },
      onHideItem: function() { //点击舞台空白区域切入切换到page属性面板的处理
          self.onHideItem();
      },
      onDrag: function(org, options) {
          self.onDrag(org, options);
      }
  });
  1. 判断是否为编辑状态/非新增:编辑状态需要把原先保存的数据置入,这个通过在模板中置入数据,但是需要将冰冷的数据增加可拖拽的能力,于是便有了如下处理
    //如果有pageItemData数据, 编辑状态
    if(window.pageItemData) {
        for(var key in pageItemData) {
            self.zresize.addResizeCapa($('#' + key));
        }
    }

这里不得不说,由于编辑功能的存在,导致edit.html极其复杂的模板写法,在模板中需要遍历pages,并且遍历pages中的item,并且定义page和items的属性。同时还在此初始化了pageData,和pageItemData这两个伴随着应用的整个生命周期的内存对象。

<script type="text/javascript">
        {% if data %}
          window.isEdit = true; //编辑状态标志
          window.settingData = {//项目配置信息
              ...
          };
          window.pageData = {//pageData
             ...
          };
          window.pageItemData = {//pageItemData
            ...
          }
        {% endif %}
</script>

edit.js中的弹窗处理,也需要通过模板注入的对象进行判断

  var self = this;
  if (!window.isEdit) { //新建则弹出窗口
      $('#pageInfoDialog').show();
  } else { //编辑的时候注入
      $('#author').val(settingData.author);
      $('#projectName').val(settingData.name);
      $('#projectDiscript').val(settingData.discript);
      self.rTab.setPreview($('#uploadCover'), settingData.cover);
  }

五、文本和图片控制器(textItem.js&imageItem.js)

这两个对象跟page和rTab都是紧密结合的,所以需要传入page和rTab来构造,这两个item对象主要是控制面板的输入和舞台item在拖拽操作,以及这面板和舞台的级联关系

说白了,就是一些事件监听

待完善功能

  1. 图片库:实现图片可重用
  2. page之间的入场动画和出场动画定义(现在写死了一种)
  3. page的复制
  4. 默认主题库
  5. 背景音乐
  6. ..........

感兴趣的同学们可以fork代码,改完之后pull request啊,服务全人类的事业进行到底

总结

如果说还需要总结的话,那么就是请多多关注

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容