积木系统上线半年,取得了些成绩,也暴露出不少问题,加上 2.0 版本也准备开动,因此正是时候来个总结反思下。
项目开始之前
系统要解决的问题
产品运营在产品侧来说,是个大事,产品的冲量、用户的活跃等等一大堆指标都靠它了,有人说再好的产品不运营也是个渣渣。于此同时,产品运营对技术岗的同学来说,是无休无止的赶时间点(节假日、网络热点)以及不是很能体现技术含量的重复性的简单页面。
这个矛盾,让产品和技术双方都很沮丧,产品觉得技术不够重视,技术则觉得不应该在重复性的简单的工作上投入过多时间。
好吧,这其实就是积木系统想要解决的问题以及终极目标,让产品同学可以快速发布页面,同时技术同学沉淀组件(积木)来避免重复性工作,如下图:
系统的核心功能
分析了各方的痛点以及诉求之后,系统的核心功能其实和容易理出来:
- 简单易用的、可视化的可编辑页面
- 通用的、简便地组件接入机制
当然除此之外还要有:
- 发布系统,既然是批量的生成页面,也就没必要每次都有测试(首次发布要严格测试)来发布
- 编译系统,类似 grunt ,fis 做的事,发布之前要编译,如果资源合并、CDN 路径替换等等
- 业务管理系统,是的,这个系统不能只为腾讯课堂服务
- 权限系统,不多说
- 响应式,一次配置,多端运行
……
开始技术规划之前,我们有必要也必须要分析现有的解决方案,没必要重复轮子。
现有的一些解决方案
传统的后台管理系统(CMS)
不管是产品还是开发,对 CMS 应该都不陌生。
技术开发好前端页面以及后台录入系统,产品在录入系统录入和修改数据让后发布。
这个方案离我们的期望很远:
- 不能可视化的编辑页面
- 页面的路径都不容易变动,而我们的活动则是无穷多个
- 前端代码倒是可以组件化,但也也就停留在代码层面,而不是系统层面
毫不留情的 pass 。
mmrp
这是一个优秀的运营系统,地址 http://mmrp.oa.com/ 。
官方团队这样描述它:
MMRP 全称是 The MultiMedia Release Platform,数字多媒体内容发布系统。
它是一个全新理念的运营需求处理系统,通过 B/S 在线绑定数据及前端代码,录入模块库并通过按需求组合组件,生成网页发布到 CDN 服务器群,旨在推动过渡到工业化时代,避免重复劳动,节省人力资源成本输出价值最大化,同时减少版本风险,缩短研发周期,统一视觉表现。
是的,这是积木系统的前辈,运营系统的先行者。但我们在做深入分析时,也发现了一些缺陷:
- 交互复杂。产品可以在页面拖拽组件,还可以给组件绑定事件(比如 click),多个组件的之间的联动等等等等。这些操作我作为一个开发者在使用的时候都有些云里雾里,不要说产品了。产品在这里的诉求是简单配置,快速发布,绑定 click 事件什么的真的有些夸张了。
- 难以维护和移植。组件和系统是耦合的,这一点很致命。没新增一个组件,系统都要做相应调整,这对于多业务的系统来说是不可介绍的,假如我们的 tapd 也是每新建一个项目就得改下系统……太可怕了!
上面的缺陷,丝毫不影响 mmrp 的光辉,虽然它已经停止维护了,但还是要向它致敬!
积木系统的设计
现有的系统并不能满足刚需,所以,积木系统蓄势待发。
经过团队(imweb)几轮的讨论,架构如下:
可视化和组件化摆到了核心位置,也对应了积木系统的两大核心:系统本身和组件体系。
系统:
- 可视化编辑
- 发布
- 接入各种组件
组件:
- 开发过程和系统无关
- 逻辑和系统无关
- 遵照系统约定
系统不求花哨,但求实用。更多的细节有时间在单独来篇文章,这里就不赘述。
取得的成绩
接入的业务
- 腾讯课堂
- QQ 电影票
发布的活动
总数将近 50 个,其中响应式 30 个。响应式如果走开发流程的话,工作量翻倍。
换算成工作量 (20 + 30 * 2) * 3 = 240 天。这是保守估计,一个活动算了 3d 工作量,同时这也仅仅是算了前端开发,没有算上后台、cgi 、视觉等等。
存在的问题以及 2.0 版特性
问题同样不少,比如接入其他业务还是不够方便、组件与系统联调也不是非常简单,为了解决遇到的问题,让系统更容易接入、开发和移植,积木系统 2.0 已经在规划中!
2.0 的新特性包括但不限于以下几点:
- 系统和业务分离,业务逻辑以插件的形式接入系统,方便业务接入。
- 组件开发套件,无需搭建系统来 debug 组件。
- 更多的表单类型支持,这里的表单是编辑页产品配置表单,包括单选、多选等等常用表单,颜色选择器优化以及能展现复杂数据结构的组件 型表单。
- 更易定制的、易于业务优化的编译、打包系统。
1. iconfont 简介
什么是 iconfont?正如字面意思,就是图标字体,下面我给大家慢慢道来
web 页面包含什么元素?
- 文字
- 链接
- 流媒体
- 视频
- 音频
- 图片
- 背景图(大)
- 插图(中,例如照片集,课程封面等)
- 图标(小)
在所有包含 ui 的程序架构中,以上不同的 ui 元素在各种环境中都会遭遇到不同的问题,同时也都有与之对应的解决方案
iconfont 就是一个解决【图标】问题的解决方案之一
1.1 图标问题
首先来看一下图标会有什么难题:
- 图标的大小会变,在两种情况下:
- 每次 ui 改版,那些厌倦一成不变喜欢尝鲜的设计师们总是会淘气地改变图标大小,图标一般是点缀其他事物,例如文字,有时候设计会把某个图标从一段描述文字改成标题的点缀,这时候图标就需要变大
- 自适应页面,整个页面的大小都在变,难道图标还能独善其身吗?
- 图标会经常换,这点还是那些设计师的问题,他们说不好看要换,作为开发我们还能说什么?
- 图标自身也会变,“来把那个图标改成红色吧,鲜艳点!”,“那个谁,把那个图标的底色改成蓝色吧~”,“…”
背景图和插图一般不会有这些问题,他们偏向于主体内容而不是点缀,一般不会改变。背景图一般是平铺,插图一般也是定宽,所以大小也不怎么需要改变。
1.2 图标解决方案
图标的解决方案有:
- 使用 png 图片,这是最传统的方案,兼容性最好,使用方面基本不会有技术问题。但是它不够方便,主要有以下两个问题:
- 只要图标稍有改动都必须换新的图片,即使只是换个颜色或者透明度
- 图标大小改变问题,不管是改图标还是需要自适应。使用大图片会造成两个问题:缩放效率和大图流量浪费加载慢
- 使用 svg 图片可以很好的解决图片大小的问题,但是兼容性是这个解决方案的致命问题,svg 不兼容 IE6~8,svg 不兼容 Android2.3
- css,对于一些简单的图标,比如箭头,叉叉等,可以使用 css 来绘制而成,这也是现在移动端经常使用的方式,毕竟移动端对 css3 兼容性较好,但是利用 css 绘制的图片的问题是不能支持自适应,自适应的 css 会出现小数点,图标本身就是小的,所以当位置相差 1px 看起来都会很明显
1.3 iconfont 解决方案
iconfont 是图标的另一种解决方案,它是把一些简单的图标制作成字体,然后让图标变成和字体一样使用
iconfont 有以下优点:
- 字体是矢量的,所以可以随意改变大小
- 因为它是字体,所以所有字体的 css 都可以使用,比如 font-size,color,background,opacity 等
- iconfont 的制作也很简单,现在有很多线上制作 iconfont 的站点,只要上传 svg 的图标设计稿,就能线上生成 iconfont 字体文件,而且连使用代码都直接生成
- iconfont 没有兼容性问题,IE6,Android2.3 都能够兼容
2. 使用 iconfont
虽然使用 iconfont 没有图片那么简单,但也没有想象中那么难,下面来看看怎么使用 iconfont
首先,你需要制作 iconfont,现在有许多 iconfont 的站点,比如 fontello
我们随便选择一个图标:
然后我们可以自定义字码:
最后我们就可以下载字体文件了:
下载完,解压出来,我们可以看到有 demo,有字体文件,也有使用代码:
可以看到使用代码里面已经把各种兼容性考虑到了:
使用代码也给到,注意使用类名也是可以在站点中自定义的:
另外注意的是,字体文件也是可以内链的,在 fontello-embedded.css 文件里面:
基本上,利用提供的代码,基本我们就可以完全兼容的使用 iconfont,下面介绍移动端使用 iconfont,在移动端只需要如下代码:
在移动端,只需要 truetype 类型
效果如下:
使用 iconfont,我们可以应用许多字体样式,现在我们改变一下样式:
效果如下:
可以发现,改变一个图标的颜色,背景色,大小都是非常方便的一件事
另一方面,当需要改一个图标的时候,我们可以在制作 iconfont 的时候,替换掉一个图标即可,使用的类名和字码都是可以定制的,这样就可以在不用修改业务使用代码的情况下,只需要替换掉内链的字体代码就可以完成替换图标的工作
以上代码经过实机测试,兼容 IOS4,Android2.3
在移动端,iconfont 也可以使用外链形式,这里就不再赘述
3. 小结
在解决图标的问题上,不管是兼容性,灵活性,扩展性,iconfont 都是一个很好的解决方案
3.1 iconfont 优势
- 灵活性,改变图标的颜色,背景色,大小都非常简单
- 兼容性,兼容所有流行的浏览器,不仅 h5 可以使用 iconfont,app 也可以使用 iconfont,关于这方面可以查看其它线上分享
- 扩展性,替换图标很方便,新增图标也非常简单,也不需要考虑图标合并的问题,图片方案需要 css sprite
- 高效性,iconfont 有矢量特性,没有图片缩放的消耗高
- 在使用上字体文件和普通的静态资源一样,既可以外链也可以内链,并且字体文件也可以使用 gzip 压缩
- 在移动端上,可以只使用 truetype 类型,非常灵活小巧
- 现在很多项目已经在使用 iconfont,先不说国外,比如国内,阿里巴巴各个平台(不仅 pc,h5,还有 app)已经全面使用 iconfont,并且阿里巴巴还搭建了一个线上 iconfont 站点,这是一个很完善的站点,上面有阿里几个主要业务的图标资源库,也可以让用户自己制作图标,完善用户自身的图标库,让用户之间可以共享形成生态,同时站点的使用说明也非常完整,从图标设计,iconfont 制作和 iconfont 使用(里面包含了各个平台的使用方法)都有很完善的说明
3.2 iconfont 缺点
- 制作 iconfont 需要 svg 设计稿,对于开发来说没有图片来的方便
- iconfont 有些特有的问题,详情可参考 @font-face and performance,不过许多问题在移动端是不存在的
3.3 结语
总的来说,iconfont 是可以应用的,特别是在移动端,如果不兼容 Android2.3,使用 svg 图片也是可以接受的,实际上制作 iconfont 也是需要 svg 资源的,所以两者其实很类似
另外,阿里巴巴主要业务都已经广泛应用 iconfont,并且还有成熟的线上站点支持,最起码在可行性方面是可以不用过多考虑的,虽然在使用 iconfont 的过程中可能会遇到许多问题,但是鉴于 iconfont 应用广泛的前提下,线上的资源也会非常丰富,应该不需要过多的担心
最后想说,我们业务是可以考虑使用的!如果要应用 iconfont,我们还需要设计们的支持!
一、Polymer
Polymer 是 Google 在 2013 年的 Google I/O 大会上提出了一个新的 UI 框架。Polymer 的实现使用了 WebComponent 标准,并且 Polymer 可保证针对包含各种平台的 Web Component 规范本地实现的浏览器、库和组件的使用效果完全相同。
1.1 Polymer 框架:
Polymer 框架可以分为三个层次:
- 基础层 (platform.js):是基本构建块。大多数情况下,基础层都是本地浏览器的 API。
- 核心层 (polymer.js):实现基础层的辅助器。
- 元素层:建立在核心层之上的 UI 组件或非 UI 组件。
1.2 基础层
基础层包括以下技术:
- DOM Mutation Oberservers 和 Object.observe():用于观察 DOM 元素的变更,是纯 JavaScript 对象。
- 指针事件:处理鼠标和触摸操作,支持所有的平台。
- 阴影 DOM:封装元素内的结构和样式,适合自定义元素。
- 自定义元素:可以自定义 HTML5 的元素。自定义元素的名字必须包含一个破折号,这是一种简单的命名空间标识,以区别于标准元素。
- HTML 导入:包自定义元素。这些包可能包含 HTML、CSS 和 JavaScript。
- 模型驱动的视图 (MDV):把数据直接绑定到 HTML。
- Web 动画:一套统一的 Web 动画 API。
阴影 DOM、自定义元素和 HTML 元素 Web Components,是网络组件模型。Web Components 是 Polymer 框架的最重要的基础。
platform.js 目前浏览器还没有提供,它仅有 31KB 大小。
1.3 核心层和元素层,即组件 UI 和组件逻辑
1 2 3 |
<polymer-panels on-select="panelSelectHandler" selected="{{selectedPanelIndex}}"> </polymer-panels> |
其架构是面向组件的,它由 HTML5 元素组成,元素甚至可以用户界面,比如动画是元素,它没有 UI,而是代替点。同时响应式设计内建了许多 Widget,这意味着它们能自适应多种给定的平台,如手机、平板、桌面等。
二、Polymer 的一个例子
1. 先看下面 polymer 的一个例子代码
1 2 3 4 5 6 |
<script src="../components/platform/platform.js"></script> <!-- 下面用到的几个组件 --> <link rel="import" href="../components/core-header-panel/core-header-panel.html"> <link rel="import" href="../components/core-toolbar/core-toolbar.html"> <link rel="import" href="../components/paper-tabs/paper-tabs.html"> |
2. Polymer 使用 HTML imports 技术来加载组件。
HTML imports 提供了依赖管理, 确保自定义元素及其所有的依赖项都在使用之前被加载进来。
3. 要增加一个工具条 (toolbar), 可以在 body 标签内添加下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
<core-header-panel> <core-toolbar> <!-- 添加一些选项卡,以paper-开头的是Material design风格的标签,具有很炫酷的效果 --> <paper-tabs valueattr=<span class="string">"name"</span> selected=<span class="string">"all"</span> <span class="keyword">self</span>-end> <paper-tab name=<span class="string">"all"</span>>所有</paper-tab> <paper-tab name=<span class="string">"favorites"</span>>收藏</paper-tab> </paper-tabs> </core-toolbar> <!-- 主要的页面内容将会放在这里 --> </core-header-panel> <core-header-panel> |
元素是一个简单的容器, 例如包含一个 header 和一些内容。默认情况下, header 保持在屏幕的顶部, 但也可以设置为随内容滚动。core-toolbar 元素作为容器, 可以存放 选项卡 (tab) 的, 菜单按钮以及其他控件。
给迪例子较为简单,目前由于以下兼容性 Polymer 用的还不是很多,但是通过 Polymer 组件化的思想,也可以给我们一些组件未来化的方向。
三、Polymer 的 vm 特性
3.1、数据的双向绑定
Polymer 支持双向的数据绑定。数据绑定通过扩展 HTML 和 DOM API 来支持应用的 UI (DOM) 及其底层数据 (数据模型) 之前的有效分离。更新数据模型会反映在 DOM 上,而 DOM 上的用户输入会立即赋值到数据模型上。
对于 Polymer elements 来说,数据模型始终就是 element 本身。比如想想这个简单的 element:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<polymer-element name=<span class="string">"name-tag"</span>> <template> 这是一个 <b>{{owner}}</b> 的 name-tag element。 </template> <script> Polymer(<span class="string">'name-tag'</span>, { <span class="comment">// initialize the element's model</span> ready: <span class="keyword">function</span>() { <span class="keyword">this</span>.owner = <span class="string">'Rafael'</span>; } }); </script> </polymer-element> |
这里 owner 属性里的 name-tag 就相当于 mvvm 中 vuejs 的 derectives,angular 中的 controller,如果你更新了 owner 属性
1 2 |
document.querySelector('name-tag').owner = 'shabi'; |
你就会看到: 这是一个 shabi 的 name-tag element。
3.2 template 惰性元素
这点实现原理就比较简单,使用了 template 包含一段 html 片段,那这段 html 片段开始是隐藏的,将会在渲染完成后再插入到页面中,个人分析,这样做的一个主要原因就是防止 mvvm 中 html 未初始化时的模板代码到正式生成 html 页面过程中闪的过程,使用 angular 或 avalon 的话一般会遇到这样的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<polymer-element name=<span class="string">"greeting-tag"</span>> <!-- 最外面的 template 定义了 element 的 shadow DOM --> <template> <ul> <template repeat=<span class="string">"{{s in salutations}}"</span>> <li>{{s.what}}: <input type=<span class="string">"text"</span> value=<span class="string">"{{s.who}}"</span>></li> </template> </ul> </template> <script> Polymer(<span class="string">'greeting-tag'</span>, { ready: <span class="keyword">function</span>() { <span class="comment">// 植入 element 的数据模型 (数组 salutations)</span> <span class="keyword">this</span>.salutations = [ {what: <span class="string">'Hello'</span>, who: <span class="string">'World'</span>}, {what: <span class="string">'GoodBye'</span>, who: <span class="string">'DOM APIs'</span>}, {what: <span class="string">'Hello'</span>, who: <span class="string">'Declarative'</span>}, {what: <span class="string">'GoodBye'</span>, who: <span class="string">'Imperative'</span>} ]; } }); </script> </polymer-element> |
例如这里 template 里面的内容开始是隐藏的,将会在 mv 扫面节点完成后插入到 dom 树里,避免页面闪的发生。其中模板里面的变量使用的类 mustache 语法,和 Avalon 极其类似。
3.3 数据绑定与事件处理
这部分下次来讲,这次主要讲 polymer,这部分也可以参考我之前 qvm 的看下 mvvm 中事件绑定和代理的实现。qvm gitHub 地址
四、前端组件化的发展方向
当然 polymer 只是前端组件化未来的一种方向,这种思想也即将带来一系列前端新的技术方向出现,例如可能
####1. Web Component 规范化定义
基于标准化的组件定义方式,我们便可以像 W3C 等标准组织一样来定义组件标准,成为 html 规范的一部分,不用依赖其它组件,成为未来 web 开发基础规范来实现,支持 vm 监听功能等。例如 polymer 这种方案的发展就借鉴了 webcomponent 和 mvvm 的融合思想。
2. react 生态
react 的组件化生态思想和 webComponent 也及其相似,当然 react 做了更多的事情,除了 web Component,react 想做的其实可以称为 platform component。
3. angular 2.0 和 EmberJS 等现有成熟方案的改进
angular2.0 已明确提出将支持 Node 绑定、模板集成、元素自定义和支持网络组件的无缝集成;ember 的发展情况依然,今后也不排除 jQuery 等框架的进化重生,以全新的面目出现。
4. 开发者的其它小众解决方案
在企业中,针对企业特殊性的业务,企业前端开发者也可以根据 webComponent 的思想自己实现更加灵活可用的组件拼装解决方案。相比之下,这个方向的探索研究甚至会更加受欢迎,因为只有开发者才关注关注自己的组件怎么管理。例如目前 qiqi 项目所用的方案就比较符合这个方向。
写在前面
Github: https://github.com/AlloyTeam/AlloyGameEngine
HTML 5 新增了 progress 标签,那么再去使用 AlloyRenderingEngine 去模拟进度条是否多余?
不多余。有四大好处:
- 样式更加灵活 (想怎么绘制就怎么绘制)
- 跨平台跨浏览器样式更加统一(使用便签,各个浏览器默认样式是五花八门)
- 效果更加酷炫,(比如燃烧的进度条= =!)
- 像素能够统一管理
统一像素管理的好处:
- 更容易的全尺寸缩小和放大(最常见的:全屏游戏)
- 缩小和放大不用操心内部元素错位(只是交给浏览器去进行插值运算)
- 更好的滤镜控制(如游戏中死亡画面,全屏黑白化)
- 更好的移植性和跨平台性(opengl<->canvas2d<->webgl<->opengl 等等各种 mapping)
Vue.js 是一个基于 MVVM
模型的 web 库。通过双向数据绑定连接 View 和 Model 层。实际的 DOM 操作被封装成 Directives
和 Filters
。
基本定义
每个 Vue 对象的实例是一个 ViewModel。创建方式:
1 2 3 4 5 |
<span class="keyword">var</span> vue = <span class="keyword">new</span> Vue({ el: view, data: model }); |
其中 vue.$el
用于管理 View 层上的 DOM 。而 vue.$data
用于管理 Model 层的数据,可以通过 vue.$data.property
访问 Model 层数据,也可以直接 vue.property
访问。
Hello World 入门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div id=<span class="string">"example"</span>> <h1>{{ title }}</h1> <ul> <li v-repeat=<span class="string">'todo:todoList'</span>>{{ todo | uppercase }}</li> </ul> </div> <span class="comment">// 对应 js</span> <span class="keyword">var</span> demo = <span class="keyword">new</span> Vue({ el: <span class="string">'#example'</span>, data: { title: <span class="string">'todo list'</span>, todoList: [<span class="string">'do work'</span>, <span class="string">'read book'</span>, <span class="string">'shopping'</span>] } }); |
从上面的例子可以看出:
- 模版替换使用的是
{{ variable }}
- Directives 格式是
v-xxx
,如上 v-repeat 。 - Filtrs 格式是
{{ variable | filter }}
,如上 uppercase
事件
在 DOM 节点上通过 v-on
绑定处理函数(可以是表达式)。函数的第一个参数是 DOM Event 对象,该对象附带 targetVM
指向 DOM 对应的 ViewModel。
1 2 3 4 5 6 7 8 9 10 11 |
<div id=<span class="string">"example"</span> v-on=<span class="string">"click : clickHandler"</span>></div> <span class="keyword">var</span> vue = <span class="keyword">new</span> Vue({ el: <span class="string">'#example'</span>, data: {}, methods: { clickHandler: <span class="keyword">function</span>(e){ console.log(e.targetVM); <span class="comment">// 指向vue对象,可以理解为this。</span> } } }); |
自定义指令
内置的指令不够用怎么办?想自定义数据变化对 DOM 的影响怎么破?
Vue.js 允许自定义全局指令,格式:
1 2 3 4 5 6 7 8 9 10 11 12 |
Vue.directive(id, { bind: <span class="keyword">function</span>(){ <span class="comment">// 仅在初始化绑定元素时调用</span> }, update: <span class="keyword">function</span>(newVal, oldVal){ <span class="comment">// 初始化时调用一次,以后每次数据变化,就被调用</span> }, unbind: <span class="keyword">function</span>(){ <span class="comment">// 仅在指令解绑时被调用</span> } }); |
同时,在指令函数中,提供了一些 this
上下文的公开属性(这里列举了几个常用的):
el
: 访问绑定的 DOM 元素,提供 View 层访问。vm
: 访问指令对应的上下文,ViewModel 对象,this.vm.$el = this.el
expression
: 指令绑定的表达式,不包括参数和 filterargs
: 参数
举个栗子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<div id=<span class="string">"example"</span> v-demo=<span class="string">"args: message"</span> v-on=<span class="string">"click: onClick"</span>></div> Vue.directive(<span class="string">'demo'</span>, { acceptStatement: <span class="keyword">true</span>, bind: <span class="keyword">function</span>(){ <span class="keyword">this</span>.el.style.cssText = <span class="string">'color: red; background: #666;'</span>; }, update: <span class="keyword">function</span>(newVal, oldVal){ <span class="keyword">this</span>.el.innerHTML = <span class="string">'name = '</span> + <span class="keyword">this</span>.name + <span class="string">'<br/>'</span> + <span class="string">'arg = '</span> + <span class="keyword">this</span>.arg + <span class="string">'<br/>'</span> + <span class="string">'expression = '</span> + <span class="keyword">this</span>.expression + <span class="string">'<br/>'</span>; console.log(<span class="keyword">this</span>.vm.<span class="variable">$data</span>); console.log(<span class="keyword">this</span>.el === <span class="keyword">this</span>.vm.<span class="variable">$el</span>); } }); <span class="keyword">var</span> demo = <span class="keyword">new</span> Vue({ el: <span class="string">'#example'</span>, data: { message: <span class="string">'hello world!'</span> }, methods: { onClick: <span class="keyword">function</span>(){ <span class="comment">// custom directive update will be called.</span> <span class="keyword">this</span>.<span class="variable">$data</span>.message = <span class="string">'hahaha!'</span>; } } }); |
自定义过滤器
Vue.js 允许使用全局函数 Vue.filter()
定义过滤器,将 Model 数据输出到 View 层之前进行数据转化。
1 2 |
Vue.filter(id, <span class="keyword">function</span>(){}); |
双向过滤器允许 View 层数据(input 元素)变回写到 Model 层之前,进行转化,定义方式如下:
1 2 3 4 5 |
Vue.filter(id, { read: <span class="keyword">function</span>(val){}, write: <span class="keyword">function</span>(newVal, oldVal){} }); |
举个栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<div id=<span class="string">"example"</span>> <p>{{ message }}</p> <input type=<span class="string">'text'</span> v-model=<span class="string">"message | twoWays"</span>></div> </div> Vue.filter(<span class="string">'twoWays'</span>, { read: <span class="keyword">function</span>(val){ <span class="keyword">return</span> <span class="string">'read '</span> + val; }, write: <span class="keyword">function</span>(newVal, oldVal){ console.log(newVal, oldVal); <span class="keyword">return</span> ov + <span class="string">' write'</span>; } }); <span class="keyword">var</span> demo = <span class="keyword">new</span> Vue({ el: <span class="string">'#example'</span>, data: { message: <span class="string">'hello'</span> } }); |
总结
Vue.js 提供的核心是 MVVM 中的 VM,确保视图和数据的一致性。同时,借鉴了 Angular 中的 Directive 和 Filter 的概念,但是却简化了 API。
写在前面
Github: https://github.com/AlloyTeam/AlloyGameEngine
在 dom 元素里,自带了 input 标签,设置其 type 为 text,它就是一个文本框。
那么在 Canvas 中模拟 input 文本框是不是闲的没事找事?绝对不是!
因为在游戏当中可以统一化像素管理,具体统一化像素管理有什么好处,以后新开文章详细讨论。
SodaRender
SodaRender 是一款具有类似 Angular 模板写法的模板引擎, 使用 SodaRender 模板引擎会带来更直观的优点: