4

关于如何在 Weex 上使用 Vanilla 代码写页面, 在前面有一篇文章已经介绍过了:
https://hashnode.com/post/run...
大致上, 可以参考这个 Demo, 其实就是一些 DOM 操作,
https://github.com/alibaba/we...
所以我明确的一个事实是 Weex 确实有一套 DOM API,
而且这套 API 应该说是所有基于 Weex 的框架的基础, 像 Weex, Rax, 甚至如果有 Angular 版本.

在 Weex 仓库里可以看到目前有 4 个框架, 其中 Vanilla 对应没有框架,
https://github.com/apache/inc...
但实际上这个代码已经过时了, 比如后来的 weex 全局变量没有的完成,
所以为了方便我在本地开发, 我自己基于 Vue fork 了一个版本, 用来试验,
https://gist.github.com/jiyin...
试验项目可以从 GitHub 上看, 用单个文件生成的 Todolist:
https://github.com/mvc-works/...
最终的界面:

在这个 Todolist 当中我实现了基本的增删改功能, 但明显确实一下功能:

  • 清除已读的任务

  • 排序

没有做是因为太难实现了, 虽然也不是那么难, 但作为试验项目主要的目的吧,
我主要还是未来试验 Weex 的 DOM API 能不能写出像样的页面,
经过简单的封装, DOM 部分的写法在 CoffeeScript 里挺清晰的,


# DOM tree

mainTree = ->
  div style: styleBody,
    div style: styleContainer,
      div style: styleHeader,
        input
          ref: 'input'
          style: styleInput
          attr: {value: '', placeholder: 'Some task...'}
          event: {input: onDraft}
        text
          style: styleButton
          attr: {value: 'Add task'}
          event: {click: onAdd}
      text
        ref: 'raw'
        attr: {value: getRaw()}
        style: styleContent
      div
        ref: 'content'
        style: styleContent

# mounting document

doc.documentElement.appendChild (helicalExpandTree mainTree())

熟悉 DOM 操作的应该能脑补出我的代码来:

helicalExpandTree = (tree) ->
  # console.log '\n\nExpanding:', JSON.stringify(tree)
  [name, props, children] = tree
  element = doc.createElement name,
    style: props.style
    attr: props.attr
  if props.event
    for key, value of props.event
      element.addEvent key, value
  if children?
    # console.log 'CHILDREN:', JSON.stringify(children)
    for child in children
      childElement = helicalExpandTree child
      # console.log '\n\nChild to append:', JSON.stringify(childElement)
      element.appendChild childElement
  if props.ref?
    domRefs[props.ref] = element
  element

然后是事件处理, 这就有点问题了, 实际上有两个地方需要处理, Model 和 View,
熟悉 jQuery 的同学应该就能猜到了, 需要通过 event.target 做 DOM 操作,
两边都处理, 保证界面上没有出现状态不一致的情况, 当然写起来很啰嗦, 维护性差,

onRemove = (taskId) -> (event) ->
  store.tasks = store.tasks.filter (task) -> task.id isnt taskId
  # DOM operations
  taskElement = event.target.parentNode
  taskElement.parentNode.removeChild taskElement
  modifyRaw()

如果页面当中还有 Tab 的话, Tab 页之间的状态管理会让页面变得非常复杂,
当然为了方便使用, 我模仿了 React 的 ref 写法用来指定 DOM 的特定引用,
这样在小的页面当中也没有使用 jQuery 选择 DOM 节点的必要了.
所以看上去整个页面还是可以跑通的, 虽然效果实在很奇怪.


onAdd = (event) ->
  newTask = id: getId(), done: 'false', text: store.draft
  store.draft = ''
  store.tasks.unshift newTask
  # DOM operations
  console.log 'DOM operations!!!'
  domRefs.input.setAttr 'value', ''
  newElement = helicalExpandTree (taskTree newTask)
  console.log '\nnewElement:', newElement
  domRefs.content.appendChild newElement
  modifyRaw()

其实可以从这个试验反思一下 Vue React 这些东西在移动端是否合适,
可以用这些框架主要是为了桌面平台的单页面 App 建造起来的,
附带了各种状态管理, 组件化, DSL, 编程风格在里边, 以及各种开发工具,
最主要的是声明式的写法确实提高了开发速度和组件的复用.
不过手机上状态管理的没有桌面端复杂倒是, 未必需要 React 那么强的功能.

手写 DOM 操作的方法基本上都是效率低下, 可维护性极差, 之类的,
除非是为了写几乎静态的页面, 这种方案完全可以被扔进历史的废纸篓...
不过也有一点好处, 就是没有太多抽象, 页面启动非常快, 提交很小.
虽然 Weex 一直运行在一个 js runtime 中, 但初始化组件多多少少有一点开销.
也许说是微乎其微... 但是从理论上而言, 启动过程需要执行的代码总是有区别的.

当然总体上说这样写几乎没什么用, 只是证明 Native DOM API 能正常用:
http://weex.apache.org/refere...
没有明确的观点, 只是换着思路想想写 Weex 遇到的一些问题...


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者


引用和评论

0 条评论