共计 6128 个字符,预计需要花费 16 分钟才能阅读完成。
和大多数人一样,我出生寒门学子,没钱没势,所有的一切都是从零开始,只能空手套白狼,本文章主要是为了分享下我个人的创业经历以及一个产品从无到有的过程,后面也会聊到我做 H5 工具的相关技术方案,希望能帮助读者在未来的工作或者是创业路上有所帮助。
2010 年第一次创业
务实的人都不甘于平庸,我亦如此,也许越是有挑战的东西,我越是感兴趣,创业的想法就像一颗毒瘤一直在我脑中去不掉,大学第一天上学,我把所有的生活费在阿里巴巴上进了一堆跳舞毯,开始了人生的第一次创业,我记得那是 2010 年上大一的事情了,虽然没有赚回本钱,但是我意外的认识了生产跳舞毯的老板。在学校里卖不完跳舞毯,于是我开了个淘宝店铺在网上卖,虽然网上也没卖完,却阴差阳错的成为了跳舞毯公司的网店兼职设计师(精通美图秀秀),再后面就自学了 PS,成为了一个 UI 设计师,我大学学的是 java,因为学习了 PS,也就顺理成章的成了前端工程师。也为接下来的创业奠定了技术的基础。
2012 年第一个工作室
大学 2 年期间,我的前端技术逐渐成熟了,可以做一些官网站了,于是我打算在乐山本地找一家网络公司做兼职,很快我成功的拿到了他们的网站订单,创意的设计风格加上物美价廉,很快成为了这家公司稳定的合作方,于是我在学校里面创立了自己的工作室(馒头工作室)。我的头像也是 M 这个 LOGO 也一直沿用至今。
2014 年人生第一家公司
14 年,我们都被大学老师赶出去找工作,刚刚出来的我找了一家公司,但是很快就按耐不住想要创业的想法,于是聚集了几个同学,我们一起创办了一家公司,受到了“云来场景应用”的启发,当时我们就在想,要是能批量生产 H5 就好了,我们提出了“即见即所得”的概念,很快在 14 年发布了第一款产品 ” 酷 APP”,我这里还保留了几张珍贵的截图:
但是当时缺乏商业经验以及面临公司生存的问题,没有拿到融资的我们很快就坚持不下去了,然后项目以失败告终!团队也各奔东西。
小知识:什么是 H5?微信扫描以下二维码即可查看 H5
H5DS v1.x 版本
虽然第一款自主研发的产品失败了,但是我依然觉得这个产品是个好东西,于是贼心不死的我在 15 年团队解散后发布了另外一个编辑器产品(H5DS)也就是现在的 1.x 版本。界面是参考 PhotoShop 来做的,是不是感觉很像 PS?
H5DS v2.x 版本
有了 1.x 版本,那就有 2.x 是吧?期间我去了一家创业公司,在那家公司我用下班的业余时间,在 H5DS 的基础上又重构了一个版本,2.x 的编辑器。界面清爽了很多,而且功能也更多了。
H5DS v3.x 版本
16 年的时候,大多数创业公司都输给了资本,我所在的创业公司也无法避免,随着上家创业公司的倒闭,我没有马上找工作,回到家里,我继续捣鼓我的编辑器,于是很快发布了 3.x 版本。界面从白变黑,整个代码也重写了。可以说 3.x 版本完全是重构的。
H5DS v4.x 版本
到现在 3 年了,因为兼职的原因,所以进度很慢,后来在一次技术分享会上面,我认识了我的技术合伙人,基于 3.x 版本,我们一起发布了 4.0 版本,3.0 版本是 jquery 的版本,那么 4.x 就是 react 的版本。
H5DS v5.x 版本
4.x 版本的生命周期是很短的,在多次讨论后,我们在 4.x 之上,我们很快就发布了 5.x 版本。5.x 版本在界面上没有做任何改动,但是代码完全重构了,5.x 也就是现在的版本。从此选择了拥抱 react 生态,海量 react 组件库在 H5DS 中都可以使用。
产品版本总结
任何产品都非一来就完美的,如果是那样,我也不用重构那么多版本了,慢慢改进打磨迭代升级,产品就会越来越完善,越来越强大。可以看到产品的进步,也可以看到我们想法的转变,任何一次升级都是为了更好的迎合市场的需求,如果希望自己的产品能走的更远,那就不要停下来!创业也是一样,并非一开始就什么都会,什么都有,也是慢慢积累起来的经验和知识,然后学以致用。感觉有点像玩游戏打怪升级,一开始在新手村混,然后到普通玩家区,再到高玩(高端玩家)区,一开始就到高玩区,肯定死翘翘了!
H5DS 产品概述
H5DS(HTML5 design software)h5ds.com 是一款真正意义上的 HTML5 页面制作工具,非常难得是这个项目我做了 5 年,版本也换了好几个,依旧初心不改!未来还有很多个五年,我也希望自己的工具会越做越好,下面就来聊聊这个花了我无数心血做的产品。
准确的产品定位
类似的竞品也比较多。比如易企秀,maka,兔展,人人秀,wps 秀堂,凡科 H5 等。我们的产品究竟有何不同?
普通用户而言:H5 是手机上用于营销的滑动页面。
专业技术人员:H5 是 HTML5 的简称,是一门技术方案,滑动页面只是 H5 的一小块应用领域。
很明显,其他竞品的定位是做营销滑动页面,而我们的定位是 HTML5 的编辑器。可以做滑动页面,网站,3D 虚拟现实,数据报表,PC 网站,小程序,在线 PS,在线动画制作等应用。
从一个技术到产品再到销售
任何成熟稳定的产品都不是一朝一夕就能完成的,从 1.x 到 5.x,我从一个技术慢慢也有了产品的思维,这个项目让我学到了太多的东西,我觉得是任何公司都给不了一个技术的东西,做了这个项目,让我有了和可客户坐在咖啡厅唇枪舌战的经历,同时我也有了产品经理的经验,我规划的功能也曾遭受用户的质疑,甚至有用户专门发了一个 word 文档给我,里面罗列了 40 多个编辑器改进意见,做一个产品是一件非常有意义的事情,我也兼职了 UI 的工作,玩起了 PS,如何设计交互让用户感觉更友好,让我掉了不少头发!这个项目让我体验了设计师,技术,产品,销售,至少哪天不写代码了,还可以去跑下业务!
创业九十九死一生
一路走来,我感觉自己创业以来什么都缺,缺人才,缺资金,缺资源,的确,创业是非常艰难的,如果说九死一生是其他行业的创业,那 IT 行业真的是 99 死一生。14 年在风口的创业公司有多少能活到现在?14 年我们创业的时候也是冲着融资去的,但是现在不是了,我觉得首先要活下去,然后再想如何发展壮大,如何赚钱。我所知道的很多 IT 公司都需要靠融资才可以活下去,一旦资金链断裂了,公司立马倒闭,H5DS 在发布 4.x 版本的时候就已经能做到自负盈亏了,所以目前我们不需要融资,不用考虑生存的问题,虽然节奏慢了点,但是不至于倒闭做不下去了,先解决温饱,才可以创造价值。我希望我们的产品能走的很远,只要活着,就有希望!虽然看上去比较悲观,但是创业真的非常残酷!我非常庆幸自己没有放弃,一直坚持做自己的产品,算下来,今年是 5 年了,正好是 v5 版本。产品也比较接近我的预期。
未来会走的更远
非常庆幸,我们在第一个 5 年活下来了,未来我们会站在巨人的肩膀上,走的更远!至于未来五年的规划,虽然我已经做好了,但是市场永远是变化不可测的,我们也会不断的更新产品,推出新鲜的功能,只希望能在这条道路上走的更远!
技术干货分享
聊了太多和技术不挂边的东西,差点忘记自己是一个技术了,接下来会聊一些技术相关的东西,也算是分享一些干货给给位技术同僚!就编辑器技术部分,这里给大家做个分享。
技术选型
技术选型我们采用 react + mobx + koa2 + mysql 整体都是 JS 技术栈,至于为什么要这样选型,因为我们人少,技术保持统一,方便维护!另外一方面原因是因为我们是一个创业团队,技术资源非常宝贵,必须尽可能的节约开发成本,也是为什么我们会选择纯 JS 的技术栈,这样我们每个人都可以做前端也可以做后端。
mobx 最最核心的概念只有 2 个。@observable 和 @observer,它们分别对应的是被观察者和观察者。这是大家常见的观察者模式,这里使用了 ES7 中的 装饰器。参数发生变化时自动触发 render 更新视图,这个和 vue 是一样的原理。
之所以没有使用 vue 是因为我们也需要 react 的 state 配合起来做性能优化以及灵活的数据处理。我们可以结合防抖函数去做性能优化,控制或者选择性的去更新视图。下面举个例子:
import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import debounce from 'lodash/debounce';
@inject('h5ds')
@observer
class Demo extends Component {constructor(props) {super(props);
this.state = {count: props.h5ds.count // 默认是 1}
}
// 防抖函数控制性能
updateOtherRender = debounce(() => {const { count} = this.state;
// 如果大于 10 才会去更新其他地方的视图
if(count > 10) {this.props.h5ds.count = this.state.count}
}, 500)
changeValue = e => {this.setState({count: e.target.value}, this.updateOtherRender)
}
render() {return <input type="number" value={this.state.count} onChange={this.changeValue}/>
}
}
我们在很多地方都有用到上面这种写法,react 提倡的最小模块化,我们也希望模块之间的影响会最小,如果一个参数在多个模块中被使用,在快速输入的时候务必会变的很慢。
整体架构
我们把项目整体划分成了多个子项目 api,editor(编辑器),web(前端管理),admin(后端管理),app-preview(H5 预览),plugins(H5 插件部分)。既要拆分,又要做到很小的耦合性是非常困难的,所以我们做了最大可能性的解耦,架构图如下。
如果想降低耦合度,web,app-preview,plugins 均可独立打包,独立部署!如果考虑到部署的便利性,可以打包后合并代码进行部署。
- 插件说明
我们把 H5 里面的元素统一定义为插件,比如图片,文字,音频,形状等元素,这些插件都有一些共同的属性,如宽高,位置,动画效果,交互事件等,所以我们把非共性抽离出来做成插件,而且插件可以独立开发和维护,完整的插件包括了渲染引擎和数据编辑器,渲染引擎在预览页面和可视化编辑的时候使用,编辑器部分在编辑器中选中对应的图层后使用。
- 预览页面说明
预览是独立的页面,因为我们要确保这个页面可以移植,区别于编辑器页面,渲染原理: 编辑器输出是一个 json 数据,这个数据包含了 H5 页面的所有配置信息,比如每个插件的大小位置,页面的颜色高度背景等。在独立的预览页面里面只需要解析这个 json 数据,然后根据 json 数据渲染页面即可,这里就需要 h5ds 的页面渲染引擎进行解析渲染。
- 编辑器内核说明
我们把 editor 做成了一个 React 组件包。可以像下面这样使用:
a. 引入必须的资源包。
<link rel="stylesheet" href="//at.alicdn.com/t/font_1160472_ybl2xl0ao8.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_157397_ze6q8vjbeme.css">
<link href="//cdn.bootcss.com/Swiper/4.5.0/css/swiper.min.css" rel="stylesheet">
<link href="//cdn.bootcss.com/antd/3.23.0-beta.0/antd.min.css" rel="stylesheet">
<script src="//cdn.h5ds.com/lib/plugins/swiper.min.js"></script>
<script src="//cdn.h5ds.com/lib/plugins/jquery.min.js"></script>
<script src="//cdn.h5ds.com/lib/plugins/h5ds-vendor-0.0.1.min.js"></script>
<script src="//cdn.bootcss.com/pubsub-js/1.7.0/pubsub.min.js"></script>
<script src="//cdn.bootcss.com/moment.js/2.24.0/moment.min.js"></script>
<script src="//cdn.bootcss.com/antd/3.23.0-beta.0/antd.min.js"></script>
b. 安装使用 H5DS
npm install h5ds –save
import 'h5ds/editor/style.css';
import React, {Component} from 'react';
import H5dsEditor from 'h5ds/editor';
class Editor extends Component {constructor(props) {super(props);
this.state = {data: null};
}
/**
* 保存 APP
*/
saveApp = async data => {console.log('saveApp ->', data);
};
/**
* 发布 app
*/
publishApp = async data => {console.log('publishApp ->', data);
};
componentDidMount() {
// 模拟异步加载数,设置 defaultData 会默认加载一个初始化数据
setTimeout(() => {this.setState({ data: 'defaultData'});
}, 100);
}
/**
* 使用编辑器部分
*/
render() {const { data} = this.state;
return (
<H5dsEditor
plugins={['demo']} // 第三方插件包
data={data}
debugger={false} // debugger=true 用于调试插件
options={{
publishApp: this.publishApp,
saveApp: this.saveApp, // 保存应用
appId: 'test_app_id' // 当前 appId
}}
/>
);
}
}
export default Editor;
从插件加载到插件挂载
最初我们的插件都是打成 umd 包,然后使用 requirejs 加载,但是 requirejs 会判断是否浏览器已经实现了 require 和 define 方法。如果浏览器已经自带 require 和 define 方法,或者之前已经有一个 requirejs 脚本执行,那么这个 requirejs 就会立刻停止执行,可能和有些包冲突起来!然后我们决定取消 AMD 的加载模式,采用挂载的方式,把插件模块挂载到一个全局变量下面!同时也兼容 UMD 模式!去掉了 requirejs 采用挂载的模式代码更少,更快,更灵活!
加入我们
QQ 群:549856478
官方网站:www.h5ds.com