简化的目的:

  • 展现和逻辑分离。让页面模板文件仅仅和展现相关,从而方便做为 GUI Designer / 设计师工具的文件格式。
  • 简化语法变种,方便 transpile 到不同的运行时平台,例如微信小程序。
  • 修复了 jsx 语法不方便表达模板之间的 slot 组合关系的问题

简化后的页面模板使用体验大致如下所述

展现和逻辑分离

和 .vue 一个单文件定义组件不同,我们把组件拆分为 .tm 和 .ts 两个文件,例如

SomeComponent.tm

<html><body><div :title="myTitle" /></body>
</html>

SomeComponent.ts

import * as Biz from '@triones/biz-kernel';
export class SomeComponent extends Biz.MarkupView {public readonly myProp1: string;public readonly myProp2?: string;public get myTitle() {return '..some title...' + this.myProp1;}
}

当调用 SomeComponent 组件的时候,会创建对应的 SomeComponent.ts 这个类的实例。然后渲染页面的时候,会用 SomeComponent.tm 来渲染。通过 :title 这样的属性来绑定“展现”和“逻辑”,渲染的时候 get myTitle() 这个 getter 属性会被求值。

调用组件可以传参,例如

<SomeComponent myProp1="hello" />

这个时候,从 public readonly myProp1: string 上就可以取到传入的 myProp1 属性。没有传的 myProp2 属性就是 undefined。

对于事件处理,需要做如下声明

SomeComponent.tm

<html><body><button @onClick="sayHello">hello</button></body>
</html>

SomeComponent.ts

import * as Biz from '@triones/biz-kernel';
export class SomeComponent extends Biz.MarkupView {public sayHello() {// ...}
}

和 vue 类似的是都是用 : 和 @ 来表达属性绑定和事件处理。和 vue 不同的是,在绑定的地方只能写属性名字引用对应的属性,不能写复杂的表达式。和微信的 wxml 语法类似,bind:tap 这样的事件绑定的地方,也是只能写一个 event handler 的方法名,不能写表达式。

习惯了在页面模板里写大量逻辑代码的同学会非常不习惯,觉得这是开历史的倒车。但是把逻辑从页面模板里分离之后,这个纯展示的页面模板是非常适合做为 GUI Designer 的持久化格式的,或者从 sketch / figma 这样的设计师工具导出。

零散小组件

为了避免写很多组件,要建很多文件。我们可以把相关的小组件,合并写在一个大文件里。例如这样

<template #default><Header /><Body /><Footer />
</template
<template #Header>...
</template>
<template #Body>...
</template>
<template #Footer>...
</template>

这么写就省得建立 4 个组件了,合并写到一个组件里。如果没有写 <template #default>,那默认就是包裹在 <template #default> 里的。从语法上来说,也非常类似微信小程序的 wxml 的写法。只是微信小程序的 wxml 没有 <template #default>,而是直接写在了文件的顶层。

也就是这么写

<div>hello</div>

和这么写

<template #default><div>hello</div>
</template>

是等价的

给父组件传复杂参数

在 react / vue 中一个非常常见的做法是用 children 来表达数据属性。例如

<LineChart width={500} height={300} data={data}><XAxis dataKey="name"/><YAxis/><CartesianGrid stroke="#eee" strokeDasharray="5 5"/>
</LineChart>

如果是 react 组件,往往会用 React.Children 来遍历这些子节点,从中提取数据做为参数来使用。也就是给一个组件传递 children,有的时候传的是组件,有的时候传的是渲染组件的函数,有的时候传递过去被当成 array/object 使用。这也是 jsx 语法从一开始没有设计好的地方。

这个问题的实质是类 html 语法,限定了属性是简单的字符串。如果我们这么写:

<LineChart XAxis="{ dataKey: "name" }" YAxis CartesianGrid="{ stroke: "#eee", strokeDasharray="5 5" } />

书写体验会非常难受,这一行 LineChart 组件的调用也会非常的长。把这些属性折行之后写成 children,就可以更结构化地进行书写。

为了改进类 html 语法的书写体验,我们在新语法里,增加了 # 的概念。

<LineChart :wdith="500" :height="300" :data="data"><attr #XAxis dataKey="name"/><attr #YAxis>true</attr><attr #CartesianGrid stroke="#eee" strokeDasharray="5 5"/>
</LineChart>

用 #xxx 的方式来表达,我是给组件传递了 xxx 属性。这样做为数据使用的 children,和做为组件使用的 children 就被区分开了。

给父组件传递 slot

#xxx 除了可以用来传递 attr 之外,也可以用来传递 slot。slot 分为三种:

  • UI fragment:例如一个 card 组件有一个 title 区域是可以自定义的。那么可以定义一个名为 title 的 slot,允许使用者传入自己的实现。
  • 会被多次实例化的 template:例如一个 ListView 的每个 ListItem 是可用户自定义的。但是和 card 组件的 title 不同,这里需要用户传入一个“函数”,因为每一行的数据是不一样的。
  • 虚拟节点:微信小程序有一个概念叫虚拟节点。其实是上面所说的 template 的弱化形式。用户传入的是一个“组件名称”,而不是一个“函数”。然后根据传入的组件名字,会多次实例化该组件,达到用户自定义的目的。

我们先来看 UI fragment 形式的表达方式:

定义组件

<div class="container"><header><slot :render="header" /></header><main><slot :render="children" /></main><footer><slot :render="footer" /></footer>
</div>

通过 <slot> 来表达,这个地方可以用户自定义。

使用组件

<BaseLayout><fragment #header><h1>Here might be a page title</h1></fragment><fragment #children><p>A paragraph for the main content.</p><p>And another one.</p></fragment><fragment #footer><p>Here's some contact info</p></fragment>
</BaseLayout>

这里用 <fragment> 来表达,传入的是一个具体的实例,而不是可以被多次调用的参数。如果是 React,那么类型就是 ReactNode。

第二种就是传入可被多次实例化的 template

定义组件

<div class="container"><header><slot :render="header" title="abc" /></header><footer><slot :render="footer" title="xyz" /></footer>
</div>

<slot> 渲染的额时候,额外提供了参数

使用组件

<BaseLayout><template #header><h1>Here might be a page title: {{ #header.title }}</h1></template><template #footer><p>Here's some contact info: {{ #footer.title }}</p></template>
</BaseLayout>

传入 template 和 fragment 不同,template 会捕获参数,从而可以在 template 内容里引用。template 比 fragment 更常用,所以有一种简写形式:

使用组件

<BaseLayout><h1 #header>Here might be a page title: {{ #header.title }}</h1><p #footer>Here's some contact info: {{ #footer.title }}</p>
</BaseLayout>

综上 #xxx 是一种传递各种复杂属性的写法。相比 jsx 的语法,折行书写更舒服。

引用组件

vue 没有在页面模板里写 import 的语法。最初 vue 是假设所有组件全局可引用的。而我们希望组件必须 import 了才可以使用,这样就方便引入第三方的组件。例如引用微信小程序原生语法书写的组件:

<import from="@vant/weapp/tag/index" as="VantTag"/>
<template #default><view><VantTag /></view>
</template>

这里引用的 @vant/weapp 是外部第三方组件,通过 import 就可以直接被使用。import 后的组件可以当作“虚拟节点”传递出去使用。这个是微信小程序特有的概念:

<import from="@app/MyComps/MyHeader" />
<import from="@app/MyComps/MyFooter" />
<template #default><BaseLayout :header="<MyHeader/>" :footer="<MyFooter/>" />
</template>

BaseLayout 要声明自己接受这么两个抽象节点,所以要和普通的 slot 区分开来

<div class="container"><header><slot :component="header" title="abc" /></header><footer><slot :component="footer" title="xyz" /></footer>
</div>

区别就是 <slot :component ..> 代替了 <slot :render ...>。

和 jsx 一样的习惯,如果是小写开头的组件,则是平台 native 的组件。例如 <div> <view> 这些都是不用 import 可以直接使用的。

引用 children

如果组件只有一个 slot 要传,那没有必要给这个 slot 命名了。缺省的 slot 名字就叫 children

定义组件

<div class="container"><main><slot :render="children" /></main>
</div>

使用组件

<BaseLayout><p>A paragraph for the main content.</p><p>And another one.</p>
</BaseLayout>

这个实际上等价于

使用组件

<BaseLayout><fragment #children><p>A paragraph for the main content.</p><p>And another one.</p></fragment>
</BaseLayout>

也就是普通写法下的子组件,其实是一个 fragment 类型的 slot。

用 slot 来实现分支和循环

有了 slot,就相当于有了回调函数。那么就可以把分支和循环变成内置组件的功能,而不是一种固定语法。比如内置一个叫 <dynamic> 的组件

<dynamic :visible="vip"><p>you are vip</p>
</dynamic>

分支

<dynamic :slot="userType"><fragment #vip><p>you are vip</p></fragment><fragment #normal><p>you are normal</p></fragment>
</dynamic>

循环

<template #default><ul><dynamic :expand="items" key="id"><MyItem #element></dynamic></ul>
</template>
<template #MyItem><span>{{ #MyItem.firstName }}</span><span>{{ #MyItem.lastName }}
</template>

核心区别

  • 展示和逻辑分离,分成两个文件来写。不允许在页面模板里写表达式
  • 改进 html 传递复杂属性写法不方便的问题,引入写在 tag body 中的 #xxx 属性
  • 把 slot 做为第一等的公民,映射到各种运行时平台的三种 slot 类型
  • 以 slot 为基础,用内置组件代替分支/循环的语法

语法很大程度上是口味问题。不是说这么写一定就比 jsx,vue,wxml 要高级。只是多了一种选择。如果你需要选择一种面向设计师的序列化格式,或者需要支持代码生成,可以考虑考虑上述的类 vue 语法。

vue 模板_简化版的 vue 页面模板语法相关推荐

  1. php vue模板,探索 PHP 与 Vue 通用直出模板方案

    测试方案,欢迎提出新的思路,共同探讨! 什么是"页面直出" 我们通常说的"页面直出",其实就是服务端渲染(SSR, Server-Side Render). 最 ...

  2. php vue模板,探索PHP与Vue通用直出模板方案

    测试方案,欢迎提出新的思路,共同探讨! 什么是"页面直出" 我们通常说的"页面直出",其实就是服务端渲染(SSR, Server-Side Render). 最 ...

  3. vuex构建vue项目_如何使用Vue.js,Vuex,Vuetify和Firebase构建单页应用程序

    vuex构建vue项目 如何使用Vuetify和Vue路由器安装Vue并构建SPA (How to install Vue and build an SPA using Vuetify and Vue ...

  4. vscode生成vue模板快捷键_VSCode写vue项目一键生成.vue模版,修改定义其他模板的方法...

    1. 安装一个插件,识别vue文件 2.新建代码片段 文件–>首选项–>用户代码片段–>点击新建代码片段–取名vue.json 确定 3.粘贴入自己写的.vue模板 { " ...

  5. cli vue 卸载_记录使用@vue/cli搭建Vue3项目完整流程

    最近发现vue两大UI框架Element UI和Ant Design Vue都已经支持Vue3了,如果再不学习Vue3就落伍了,此文章记录下使用@vue/cli搭建Vue3项目完整流程. 1 安装vu ...

  6. vs code vue插件_干货分享 | Vue框架常见问题浅谈

    友情提示:全文7800多文字,预计阅读时间10分钟 Vue是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手, ...

  7. js定义全局变量 vue页面_详解Vue.js 定义全局变量的几种实现方式

    详解Vue.js 定义全局变量的几种实现方式 发布于 2020-8-11| 复制链接 本篇文章主要介绍了VUE 全局变量的几种实现方式,小妖觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小妖 ...

  8. Vue教程_基础(一)

    目录 章节 地址 Vue教程_tips https://blog.csdn.net/weixin_46349544/article/details/124082287 Vue教程_基础(一) http ...

  9. 4.Vue 模板语法

    Hello,我是 Alex 007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫 Alex 的人太多了,再加上每天007的生活,Alex 007就诞生了. Vue模板语法 这篇文章我们来 ...

最新文章

  1. 实战 | 某小公司项目环境部署演变之路
  2. PHP 将二叉查找树转换为双向链表,要求不能创建新节点,只能调节节点指针
  3. Leetcode 912.排序算法(快排)
  4. mysql当前时间减一分钟_MySQL数据库事务的机制【总结】
  5. oracle服务器修改机器名,Oracle 11g R2 RAC环境下修改主机名
  6. 业务中台建设与应用_容易网业务中台建设,助力企业数字化转型
  7. 9个提高代码运行效率的小技巧你知道几个?
  8. HttpHandler和HttpModule 心得介绍
  9. 已解决E: dpkg was interrupted, you must manually run ‘sudo dpkg --configure -a‘ to correct the problem.
  10. Uva 12563 - Jin Ge Jin Qu(01背包)
  11. JavaScript高级程序设计(4)
  12. java贪吃蛇食物_JAVA贪吃蛇课程怎么处理食物的随机性
  13. 移动端H5页面中加载的图片,在chrome和安卓手机中显示正常,在iphone和safari浏览器中个别图片显示问号的问题处理
  14. 【新知实验室 腾讯云TRTC实时音视频体验】
  15. 网络安全:ARP和IP协议
  16. 2015杭州云栖大会
  17. 【数据库技术课程设计】 电信学院考研信息管理系统 +【Visual FoxPro】
  18. WPF MVVM架构 程序退出右下角托盘图标简单解决方案
  19. 139考研数学高分系列线性代数-杨超
  20. 文献解读 | m6A——程序性细胞死亡与癌症的“双刃剑”

热门文章

  1. LeetCode简单题之数组拆分 I
  2. Linux下Flash-LED的处理
  3. RGB-D对红外热像仪和毫米波雷达标定
  4. 深度人脸识别:CVPR2020论文要点
  5. Linux crontab 命令基本说明
  6. Python:urllib2模块Handler处理器 和 自定义Opener
  7. [JAVA EE]ajax 方式提交数据
  8. adb.exe: device offline
  9. HarmonyOS 修改App 的name
  10. java.lang.IllegalArgumentException: columnNames.length = 3, columnValues.length = 4