7
template

yutent edited this page 2024-06-17 12:02:07 +08:00

模板

模板语法基于原生的字符串模板, 并实现了一套指令, 提供类似vue的API.

基础用法示例


import { html, Component } from 'wkit'

class Foo extends Component {

  render() {
    return html`
      <h1>Hello Wkit!</h1>
    `
  }
}

渲染变量

class Foo extends Component {
  static props = {
    name: 'Wkit'
  }

  ...

  render() {
    return html`
      <h1>Hello ${this.name}!</h1>
    `
  }

}

列表渲染

类似于React的语法, 框架会自动拼接数组, 无需自己调用join()方法(纯文本渲染时, 等同于普通变量输出, 有额外格式要求时请手动join())。

class Foo extends Component {
  static props = {
    name: 'Wkit',
    list: ['foo', 'bar']
  }

  ...

  render() {
    return html`
      <h1>Hello ${this.name}!</h1>
      <pre>
        纯文本渲染:
        ${this.list}
      </pre>
      
      带dom结构的渲染: 
      <ul>
      ${
        this.list.map((it, i) => html`<li>${i + 1}: ${it}</li>`)
      }
      </ul>
    `
  }

}

条件渲染

限制于原生的字符串模板的语法限制, 不支持复杂的流程语句, 所以只能用三元表达式等简单的表达式来实现。

v1.12.0 起, 可使用when()方法代替三元表达式; which()方法代替switch流程语句。


class Foo extends Component {
  static props = {
    name: 'Wkit',
    avatar: ''
  }

  ...

  render() {
    return html`
      <h1>Hello ${this.name || 'Wkit'}!</h1>
      
      ${
        this.avatar ? html`<img src=${this.avatar}>` : html`<wc-avatar name=${this.name}></wc-avatar>`
      }
    `
  }

}

节点属性绑定

这个, 在上一个例子中, 已经有用到了, 模板中的节点属性, 可以加引号, 也可以不加, 效果上完全一致, 区别只是不加引号有语法高亮。

另外, 还引入了一个特殊的绑定语法, 跟vue一样, 在属性名前加冒号:, 带冒号的属性, 赋值时是直接赋值的, 而不是调用setAttribute(), 即意味着, 你可以通过这种方式, 传递复杂的数据类型。

另外, 由于字符串模板的原理与vue/react等框架不一样, 要实现双向绑定, 需要借助live()方法(详见下文)。


class Foo extends Component {
  static props = {
    name: 'Wkit',
    info: {age: 18, height: 177}
  }

  ...

  render() {
    return html`
      <h1>Hello ${this.name || 'Wkit'}!</h1>
      
      <wc-user-card
        name=${this.name}
        :info=${this.info}  
      >
      </wc-user-card>
    `
  }

}

事件绑定

框架内置了强大又友好的事件绑定机制, 语法和vue类似, @xx=yy, 即可向所在节点添加xx事件的监听。

事件回调支持使用匿名函数, 框架内部会修正该函数的this指向; 利用这个特性, 可以实现列表渲染中动态传递参数给回调函数。

绑定在组件内的事件, 在组件移除出文档树时,均会自动释放, 无需手动unbind。(v1.6.0起支持)


class Foo extends Component {
  static props = {
    count: 0
  }

  ...
  
  handleClick(ev) {
    this.count++
  }

  render() {
    return html`
      <h1>total click ${this.count}</h1>
      
      <button @click=${this.handleClick}>click +1</button>
      
      <!-- 也可以使用匿名函数 -->
      <button @click=${ev => this.count++}>click +1</button>
      
      <!-- 非箭头函数也可以用, 非箭头函数的this, 同样是指向实例本身 -->
      <button @click=${function(ev){ this.count++ }}>click +1</button>
    `
  }

}

事件绑定之 -- 修饰符

v1.9.0起, 采用全新的事件机制, 支持以下修饰符, 可支持同时写多个修饰符。

  • .stop 阻止事件冒泡
  • .prevent 阻止默认事件
  • .self 仅当 event.target 是元素本身时才会触发事件处理器
  • .capture 与原生一致的逻辑
  • .once 与原生一致的逻辑
  • .passive 与原生一致的逻辑

html`<button @click.stop=${this.handleClick}>click me</button>`

html`<button @click.stop.prevent=${this.handleClick}>click me</button>`

特殊属性之 -- ref

v1.5.11起, 完整支持ref绑定, 语法为ref=xx。后续(mounted之后), 可以通过this.$refs.xx, 获取续写的节点对象, 无行为与vue基本一致。

ref绑定, 支持静态值, 支持变量赋值(包括在循环中使用)。

通常用于在组件内部做一些DOM操作, 而无需自己使用选择器来获取节点对象。

特殊属性之 -- #animation

v1.8.0起, 支持动画配置, 并内置fade, scale, bounce, rotate4种简单动画。

v1.8.4额外增加2种动画micro-bounce, slide

v1.10.1增加immediate设定, 允许动画在节点加载时自动执行。

动画配置, 支持以下4个

  • type<String>, 动画类型, 指的是内置的6种动画名字, 默认fade
  • immediate<Boolean>, 是否立即执行, 默认否。
  • duration<Number>, 动画的时长, 单位毫秒, 默认200毫秒。
  • custom<Array/Object>, 自定义动画, 如果内置的动画不满足需求, 可以自定义, 语法详见 HTML的Element.animate文档。
class Foo extends Component {
  static animation = {
    type: 'fade', // 可以是6种内置之一
    
    // 如果内置的动画不满足需求, 可以自定义, 语法详见   HTML的`Element.animate`文档
    custom: [
      {opacity: 0, ...}, // 阶段样式定义
      {opacity: 0.5, ...},
      ...
    ]
  }
}

// 声明之后, 便可在适当时机, 手动触发动画
// 执行出场动画
// this.$animate() 
// 执行离场动画
// this.$animate(true) 

对组件内的节点配置动画

class Foo extends Component {
   
  render() {
    // 配置同上
    let options = {
      type: 'fade'
    }
    return html`<div ref="foo" #animation=${options}>这个是有动画的节点</div>`
  }

}

// 声明之后, 便可在适当时机, 手动触发动画
// 出场动画
// this.$refs.foo.$animate() 
// 离场动画
// this.$refs.foo.$animate(true) 

双向绑定

上面提到过, 由于模板机制的原因, 双向绑定需要借助live()方法实现, 当然, 也可以自行监听事件实现。

注意: live()方法是v1.12.0开始才支持。

import { html, live, Component } from 'wkit'

class Foo extends Component {

  static props = {
    value: ''
  }
   
  render() {
    return html`
      <!-- 自行监听input事件实现 -->
      <input value=${this.value} @input=${ev => (this.value = ev.target.value)} >
      
      <!-- 借助live()实现 -->
      <input value=${live.bind(this, 'value')} >
    
    `
  }

}

属性绑定之样式设置

styleclass这2种设置样式的属性, 比较特殊, 往往会有多个值, 以及多种条件, 所以框架内置了2个方法classMapstyleMap, 用于简化绑定设置。


let classes = classMap({
    foo: true,
    bar: this.bar,
    ['goo-' + this.id]: true
})
let styles = styleMap({
    width: '888px',
    height: '200px',
    backgroundColor: '#f30'
  })

html`
  <div class=${classes} style=${styles}>demo</div>
`