全局 API

Hu.define

  • 用法:   Hu.define( name, options )
  • 参数:
    • { String } name
    • { Object } options
  • 详细:   方法可用于定义一个全局的自定义元素

Hu.observable

  • 用法:   Hu.observable( object )
  • 参数:
    • { Object } object
  • 详细:

方法会返回一个可响应的对象代理 ( 观察者对象 ), Hu 内部会用它来处理 data 函数返回的对象

返回的可响应的对象代理可以直接用于渲染函数和计算属性内, 并且会在发生改变时触发相应的更新, 而对源对象直接进行修改将是不可响应的

也可以作为最小化的跨组件状态存储器

  • 示例:
<div id="app"></div>
const state = Hu.observable({
  count: 0
});

new Hu({
  el: '#app',
  render( html ){
    return html`
      <div @click=${ this.add }>Count is: ${ state.count }</div>
    `;
  },
  methods: {
    add(){
      state.count++
    }
  }
});

Hu.html

  • 用法:   Hu.html`<div></div>`
  • 详细: 方法用于创建 TemplateResult 模板
  • 示例:
const app = document.getElementById('app');

// 一处定义, 处处使用
const result = Hu.html`
  <div>Hello world</div>
`;

// 在 Hu.render 中使用
Hu.render( result, app )

// 在 new Hu 创建的实例中使用
new Hu({
  el: app,
  render( html ){// html === Hu.html
    return result;
  }
});

// 在 Hu.define 创建的自定义元素中使用
Hu.define( 'custom-element', {
  render( html ){// html === Hu.html
    return result;
  }
});

Hu.html.svg

  • 用法:   Hu.html`<div></div>`
  • 详细:

方法用于创建 SVGTemplateResult 模板, 用于解决使用 Hu.html 单独创建出的 svg 内部元素无效的问题

  • 示例:
const text = Hu.html.svg`<text y="50%" dy="30%">123</text>`;

Hu.render( div )`
  <svg>${ text }</svg>
`;

Hu.render

  • 用法:   Hu.render( result, container )
  • 参数:
    • { TemplateResult } result
    • { Element } container
  • 详细:

方法可以在不借助使用 new HuHu.define 的情况下, 渲染一个 HTML 片段

  • 示例:
const app = document.getElementById('app');
let name = '张三';

// 正常使用
Hu.render(
  Hu.html`<div>${ name }</div>`,
  app
);

// 另一种使用方式
Hu.render( div )`
  <div>${ name }</div>
`;

Hu.directive

  • 用法:   Hu.directive( name, directiveClass )
  • 参数:
    • { String } name
    • { Classes } directiveClass
  • 详细:

用于注册或获取全局指令

  • 示例:
  // 注册组件
  Hu.directive( 'my-directive', class {

    /**
     * 指令初始化时调用
     * @param {Element} element 指令所绑定的元素, 可以用来直接操作 DOM
     * @param {string} name 指令名, 不包括 `:` 前缀
     * @param {string[]} strings 有时同一个指令可能是允许使用多个插值绑定的, 该变量就包含了内容中除了插值绑定的部分
     *  - 例如: `:name=${ 'Hu.js' }` 是单个插值绑定的写法, 此时 `string` 将会是 `[ '', '' ]`
     *  - 例如: `:name="${ 'My' } name is ${ 'Hu' }.js"` 是多个插值绑定的写法, 此时 `string` 将会是 `[ '', ' name is ', '.js' ]`
     * @param {{}} modifiers 一个包含修饰符的对象
     *  - 例如: `:name` 中, 修饰符对象为: `{}`
     *  - 例如: `:name.foo.bar` 中, 修饰符对象为: `{ foo: true, bar: true }`
     */
    constructor( element, name, strings, modifiers ){

      /**
       * 在使用多个插值绑定的情况下
       * 就需要返回多个指令类去单独处理每个插值绑定的部分
       * 生成的多个指令类存储在 `parts` 实例属性中
       * 在实例化当前指令类时
       * 如果指令类拥有 `parts` 实例属性将返回 `parts` 内的所有指令类
       * 
       * 注意 !
       * 若当前指令是不允许使用多个插值绑定的, 那么 `this.parts` 是无须定义的, 并且应该抛出一个错误
       * 可以使用以下代码进行判断:
       * 
       *   if( !( strings.length === 2 && strings[0] === '' && strings[1] === '' ) ){
       *     throw new Error('当前指令的传值只允许包含单个表达式 !');
       *   }
       */
      this.parts = [];

    }

    /**
     * 指令被设置值时调用, 有以下情况
     *  1. 正常传值调用
     *  2. 传递指令方法的调用
     *     - 传递指令方法时, 需要将当前指令类传递给指令方法, 然后退出当前方法,
     *       指令方法会将用户的输入处理, 然后使用正常传值调用的方式再次调用当前方法
     * @param {any} value 用户传入值
     * @param {boolean} isDirectiveFn 用户传入值是否是指令方法
     */
    commit( value, isDirectiveFn ){
      if( isDirectiveFn ) return value( this );
    }

    /**
     * 当前指令被释放时调用
     */
    destroy(){

    }

  });

  // 返回已注册的指令
  Hu.directive( 'my-directive' );

  // 使用指令
  Hu.html`
    <div :my-directive=${ 123 }></div>
  `;

Hu.directiveFn

  • 用法:   Hu.directiveFn( directiveFn )
  • 参数:
    • { Function } directiveFn
  • 详细:

用于注册指令方法

  • 示例:
  // 这是方法的入口
  // 正在等待用户调用
  const fn = Hu.directiveFn(( value ) => {
    // 用户已经调用指令方法
    // 生成等待模板解析及指令的调用
    return part => {
      // 指令调用了当前指令方法
      // 处理值后, 将值返回给指令
      Hu.directive.commit( part, value );
    }
  });

  // 指令方法的调用
  Hu.html`
    <div class=${ fn( 123 ) }></div>
  `;

  // 也可以这样
  const fnResult = fn( 123 );

  Hu.html`
    <div class=${ fnResult }></div>
  `;

Hu.nextTick

  • 用法:   Hu.nextTick( callback, context )
  • 参数:
    • { Function } callback
    • { Object } context
  • 详细:

在下次 DOM 更新循环结束之后执行延迟回调. 在修改数据之后立即使用这个方法, 获取更新后的 DOM

  • 示例:
// 修改数据
hu.msg = 'Hello';
// DOM 还没有更新
Hu.nextTick(function(){
  // DOM 更新了
});

// 作为一个 Promise 使用
Hu.nextTick().then(function(){
  // DOM 更新了
});

Hu.noConflict

  • 用法:   Hu.noConflict()
  • 详细:   释放 window.Hu 的控制权, 还原到定义 Hu 之前

Hu.version

  • 详细:   提供字符串形式的 Hu 安装版本号

Hu.util

  • 详细:   共享出来的部分内部使用的方法
/**
 * 绑定事件
 * @param elem 绑定事件的元素对象
 * @param type 事件名称
 * @param listener 事件回调
 * @param options 事件修饰符
 */
addEvent( elem: Element, type: string, listener: function, options: boolean | {} ): void;

/**
 * 解绑事件
 * @param elem 解绑事件的元素对象
 * @param type 事件名称
 * @param listener 事件回调
 * @param options 事件修饰符
 */
removeEvent( elem: Element, type: string, listener: function, options: boolean | {} ): void;

/**
 * 触发事件
 * @param elem 触发事件的元素对象
 * @param type 事件名称
 */
triggerEvent( elem: Element, type: string ): void;

/**
 * 对象遍历方法
 *  - 和 jQuery 的 each 方法不同, 遍历过程无法通过返回 false 进行中断
 *  - 就是个普通的对象遍历方法
 * @param obj 
 * @param callback 
 */
each( obj: {}, callback: function ): void;

/**
 * 判断传入对象是否是纯粹的对象
 * @param obj 需要判断的对象
 */
isPlainObject( obj: any ): boolean;

/**
 * 判断传入对象是否是一个空对象
 * @param obj 需要判断的对象
 */
isEmptyObject( obj: any ): boolean;

/**
 * 判断传入对象是否是原始对象
 * @param obj 需要判断的对象
 */
isPrimitive( obj: any ): boolean;

/**
 * 判断传入的两个值是否相等
 *  - 用于避免 NaN !== NaN 的问题
 * @param value 需要判断的对象
 * @param value2 需要判断的对象
 */
isEqual( value, value2 ): boolean;

/**
 * 判断传入对象是否是 String 类型
 * @param obj 需要判断的对象
 */
isString( obj: any ): boolean;

/**
 * 判断传入对象是否是 Object 类型且不为 null
 * @param obj 需要判断的对象
 */
isObject( obj: any ): boolean;

/**
 * 判断传入对象是否是 Function 类型
 * @param obj 需要判断的对象
 */
isFunction( obj: any ): boolean;

/**
 * 判断传入对象是否是 Symbol 类型
 * @param obj 需要判断的对象
 */
isSymbol( obj: any ): boolean;

/**
 * 返回一个字符串 UID
 */
uid(): string;

/**
 * 创建一个可以缓存方法返回值的方法
 * @param fn 需要缓存值的方法
 */
cached( fn: ( str: string ) => any ): function;

选项 / 数据

data

  • 类型:   Object | Function
  • 限制:   定义自定义元素只接受 Function
  • 详细:  

Hu 实例的数据对象, Hu 会使用 Hu.observable 将对象转为观察者对象, 从而使之能够响应数据变化

实例创建之后, 可以通过 hu.$data 访问该实例完整数据对象, Hu 实例也代理了 data 对象上所有的属性, 因此访问 hu.a 等价于访问 hu.$data.a

$ 开头的属性不会被 Hu 实例代理, 因为它们可能和 Hu 内置的属性、API 方法冲突. 但是你依旧可以使用如 hu.$data.$a 的方式访问这些属性

当一个自定义元素被定义, data 必须声明为一个返回数据对的函数, 因为自定义元素可能被用来创建多个实例. 如果 data 仍然是一个纯粹的对象, 则所有的实例将共享引用同一个数据对象 ! 通过提供 data 函数, 每次创建一个新实例后, 我们能够调用 data 函数, 从而返回初始数据的一个全新副本数据对象

  • 示例:
const data = { a: 1 };

// 创建一个实例
const hu = new Hu({
  data
});

hu.a; // -> 1
hu.$data.a; // -> 1
hu.$data === data // -> false

Hu.define( 'custom-element', {
  // 定义自定义元素时, data 属性必须为一个函数
  data(){
    // this 将会是当前实例
    return {
      data: 1
    };
  },
  // 你也可以使用箭头表达式
  data: () => ({
    data: 1
  })
});

props

  • 类型:   Array<String> | Object
  • 限制:   只有自定义元素创建的实例才能发挥其作用
  • 详细:

props 可以是数组或对象, 用于接收来自定义元素上属性 ( Attribute ) 的数据, props 可以是简单的数组, 或者使用对象作为替代, 对象允许配置高级选项, 如类型转换、来源属性或默认值

你可以基于对象的语法使用以下选项:

  • attr : 定义当前 prop 从自定义元素哪个属性名称上进行取值
  • type : 定义当前 prop 如何从自定义元素的属性转换为实例上的属性, 可以是 StringNumberBoolean 等或者自定义的方法
  • default : 定义当前 prop 的默认值, 如果在创建实例时 prop 没有被传入, 则换做用这个值, 对象或数组的默认值必须从一个工厂函数返回
  • 示例:
// 简单语法
Hu.define( 'custom-element', {
  props: [ 'size', 'myMessage' ]
});

// 对象语法
Hu.define( 'custom-element', {
  props: {
    // 类型转换
    size: Number,
    // 类型转换 + 默认值 + 来源属性
    myMessage: {
      type: String,
      default: '',
      attr: 'message'// 若不设置, 将会从 my-message 上进行取值
    }
  }
});

computed

  • 类型:   { [key: String]: Function | { get: Function, set: Function } }
  • 详细:

计算属性将被混入到 Hu 实例中. 所有 getter 和 setter 的 this 上下文自动地绑定为 Hu 实例

注意如果你为一个计算属性使用了箭头函数, 则 this 不会指向这个组件的实例, 不过你仍然可以将其实例作为函数的第一个参数来访问

computed: {
  aDouble: hu => hu.a * 2
}

计算属性的结果会被缓存, 除非依赖的响应式属性变化才会重新计算. 注意, 如果某个依赖 ( 比如非响应式属性 ) 在实例范畴之外, 则计算属性是不会被更新的

  • 示例:
const hu = new Hu({
  data: {
    a: 1
  },
  computed: {
    // 仅读取
    aDouble(){
      return this.a * 2;
    },
    // 读取和设置
    aPlus: {
      get(){
        return this.a + 1
      },
      set( value ){
        this.a = value - 1;
      }
    }
  }
});

hu.aPlus;   // -> 2
hu.aPlus = 3;
hu.a;       // -> 2
hu.aPlus;   // -> 3
hu.aDouble; // -> 4

methods

  • 类型:   { [key: String]: Function }
  • 详细:

methods 将被混入到 Hu 实例中. 可以直接通过 Hu 实例访问这些方法, 或者在指令表达式中使用. 方法中的 this 自动绑定为 Hu 实例

  • 示例:
const hu = new Hu({
  data: {
    a: 1
  },
  methods:{
    plus(){
      this.a++
    }
  }
});

hu.a; // -> 2
hu.plus();
hu.a; // -> 3

globalMethods

  • 类型:   { [key: String]: Function }
  • 详细:

globalMethods 将会在实例上添加属性内方法的映射, 可以直接通过 Hu 实例访问这些方法, 或者在指令表达式中使用. 方法中的 this 自动绑定为 Hu 实例

和 methods 选项不同的是:

  1. 由自定义元素创建的实例会将方法混入到自定义元素本身, 可以直接调用
  2. 在任何一处修改了方法的映射, 所有地方均会更改
  • 示例:
Hu.define( 'custom-element', {
  data: () => ({
    a: 1
  }),
  globalMethods:{
    plus(){
      this.a++
    }
  }
});

const custom = document.body.appendChild( document.createElement('custom-element') );
const hu = custom.$hu;

hu.a; // -> 2
hu.plus();
hu.a; // -> 3
custom.plus();
hu.a; // -> 4

watch

  • 类型:   { [key: string]: string | Function | Object | Array }
  • 详细:

一个对象, 键是需要观察的表达式, 值是对应回调函数. 值也可以是方法名, 或者包含选项的对象. Hu 实例将会在实例化时调用 $watch(), 遍历 watch 对象的每一个属性

  • 示例:
const hu = new Hu({
  data: {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: {
      f: {
        g: 5
      }
    }
  },
  methods: {
    watchB( value, oldValue ){
      // ...
    }
  },
  watch: {
    // 回调函数方式
    a( value, oldValue ){
      console.log(`value: ${ value }, oldValue: ${ oldValue }`);
    },
    // 方法名
    b: 'watchB'
    // 深度监听
    c: {
      deep: true,
      handler( value, oldValue ){
        // ...
      }
    },
    // 该回调将会在侦听开始之后被立即调用
    d: {
      immediate: true,
      handler( value, oldValue ){
        // ...
      }
    },
    // 监听一系列方法
    e: [
      'watchB',
      function( value, oldValue ){
        // ...
      },
      {
        handler( value, oldValue ){
          // ...
        }
      }
    ],
    // 监听 hu.e.f 的值: { g: 5 }
    'e.f': function( value, oldValue ){
      // ...
    }
  }
});

hu.a = 2; // `value: 2, oldValue: 1`

选项 / DOM

el

  • 类型:   String | Element
  • 限制:   只在由 new 创建的实例中遵守
  • 详细:

提供一个在页面上已存在的 DOM 元素作为 Hu 实例的挂载目标. 可以是 CSS 选择器, 也可以是一个 HTMLElement 实例

在实例挂载之后, 元素可以用 hu.$el 访问

如果在实例化时存在这个选项, 实例将立即进入编译过程, 否则, 需要显式调用 hu.$mount() 手动开启编译

和 Vue 不同的是, 挂载元素不会被 Hu 生成的 DOM 替换, 而是替换掉挂载元素的内容. 但是, 依旧不推荐挂载 root 实例到 <html> 或者 <body>

render

  • 类型:   ( html: () => TemplateResult ): TemplateResult | Array<TemplateResult>
  • 详细:

渲染函数允许你发挥 JavaScript 最大的编程能力, 该渲染函数接收一个 html 方法, 使用类似 JSX 的语法来创建 DOM

这一部分可能是和 Vue 区别最大的地方了, Hu 使用了深度定制的 lit-html 来创建 DOM

相比模板语言, 它提供语义化并且可以移动的标签, 且支持多个根元素

相比 JSX 语法, 它无需通过构建工具就可以使用

  • 示例:
// 正常使用
render( html ){
  return html`<div>Hello world</div>`;
}

// 条件判断
render( html ){
  if( something ){
    return html`<div>Truthy</div>`;
  }else{
    return html`<div>Falsey</div>`;
  }
}

const style = html`
  <style>
    :host > div{ color: red }
  </style>
`;

// 嵌套使用, 且支持多个根元素
render( html ){
  return html`
    ${ style }
    <div>Hi ~</div>
    <div>Hello world ~</div>
  `;
}

// 更多示例, 还在编辑中

styles

  • 类型:   string | string[]
  • 限制:   只在自定义元素创建的实例下可用
  • 详细:

指定自定义元素的样式, 在使用 polyfill 的环境下, 可以解决样式无法生效的问题

选项 / 生命周期钩子

beforeCreate

  • 类型:   Function
  • 详细:

实例初始化后被调用, 计算属性 computed 和数据监听 watch 初始化之前被调用

created

  • 类型:   Function
  • 详细:

实例创建完成后被调用. 但是挂载还未开始, 使用 new 创建的实例 $el 属性目前不可见 ( 由自定义元素创建的实例 $el 属性始终是可见的 )

beforeMount

  • 类型:   Function
  • 详细:

首次挂载开始之前被调用, 相关的 render 函数还未被调用

mounted

  • 类型:   Function
  • 详细:

首次挂载之后被调用, 使用 new 创建的实例 $el 属性已经可见 ( 由自定义元素创建的实例 $el 属性始终是可见的 ), $el 的内容已经被渲染的结果替换

beforeDestroy

  • 类型:   Function
  • 详细:

实例销毁之前调用. 在这一步, 实例仍然完全可用

destroyed

  • 类型:   Function
  • 详细:

实例销毁后调用. 调用后, 实例上的计算属性 / watch 监听数据都会被解绑, 所有的事件监听器会被移除

connected

  • 类型:   Function
  • 限制: 只在自定义元素创建的实例下可用
  • 详细:

自定义元素被添加到文档流, 此时实例完全可用

adopted

  • 类型:   Function
  • 限制:
    • 只在自定义元素创建的实例下可用
    • 在使用 polyfill 的环境下不可用
  • 详细:

自定义元素被移动到新文档时调用, 此时实例完全可用

disconnected

  • 类型:   Function
  • 限制: 只在自定义元素创建的实例下可用
  • 详细:

自定义元素被从文档流移除, 此时实例完全可用

选项 / 组合

mixins

  • 类型:   Array<Object>
  • 详细:

mixins 选项接受一个混入对象的数组. 这些混入实例对象可以像正常的实例对象一样包含选项, 他们将会以一定的逻辑与当前实例对象进行合并. 举例: 如果你的混入包含一个生命周期回调而实例本身也包含一个, 那么两个回调都会被调用

  • 示例:
const mixin = {
  created(){
    // 混入对象的生命周期回调会在实例自身的生命周期回调之前被调用
    console.log( 1 );
  }
};

const hu = new Hu({
  mixins: [ mixin ],
  created(){
    console.log( 2 );
  }
});
// => 1
// => 2

实例属性

hu.$el

  • 类型:   Element | ShadowRoot
  • 只读
  • 详细:

Hu 实例使用的根 DOM 元素
在由自定义元素创建的实例中, 则是当前实例的阴影根 ( ShadowRoot ) 节点

hu.$customElement

  • 类型:   Element
  • 限制:   只在由自定义元素创建的实例中可用
  • 只读
  • 详细

Hu 实例的自定义元素节点

hu.$root

  • 类型:   Hu instance
  • 只读
  • 详细:

为当前实例的根实例, 若当前实例没有父实例, $root 选项会是自己

hu.$parent

  • 类型:   Hu instance
  • 只读
  • 详细:

当前实例的父实例

hu.$children

  • 类型:   Hu instance[]
  • 只读
  • 详细:

当前实例的直接子组件, 注意, $children 并不保证顺序且不是响应式的

hu.$props

  • 类型:   Object
  • 详细:

当前组件接收到的 props 对象, Hu 实例代理了对其 props 对象属性的访问

hu.$data

  • 类型:   Object
  • 详细:

Hu 实例观察的数据对象, Hu 实例代理了对其 data 对象属性的访问

hu.$methods

  • 类型:   Object
  • 详细:

Hu 实例的方法对象, Hu 实例代理了对其 methods 对象属性的访问

hu.$globalMethods

  • 类型:   Object
  • 详细:

Hu 实例的方法对象, Hu 实例代理了对其 globalMethods 对象属性的访问

$methods 实例属性不同的是, $globalMethods 实例属性是响应式的, 若在任何一处修改了方法的映射, 所有地方均会更改, 同时也会触发响应式更新

hu.$computed

  • 类型:   Object
  • 详细:

Hu 实例的计算属性对象, Hu 实例代理了对其 computed 对象属性的访问

hu.$options

  • 类型:   Object
  • 只读
  • 详细:

当前 Hu 实例初始化选项, 需要在初始化选项中包含自定义属性时会有用处

  • 示例:
new Hu({
  customOption: 'foo',
  created(){
    console.log( this.$options.customOption ) // => 'foo'
  }
});

hu.$info

  • 类型:   Object
  • 只读
  • 详细:

当前 Hu 实例信息选项, 包含了当前实例的各种信息及状态:

  • uid: 当前实例的 UID
    • 在所有实例中是唯一的
    • 在由 new 创建的实例中, uid 和 name 是相同的
  • name : 当前自定义元素的名称 | 当前实例的名称
  • isMounted : 标识当前实例的首次挂载是否已完成
  • isCustomElement : 标识当前实例是否是自定义元素
  • isConnected : 标识当前自定义元素是否在文档流中 ( 如果是使用 new 创建的实例, 则作用和 isMounted 一致 )

hu.$refs

  • 类型:   Object
  • 只读
  • 详细:

一个持有注册过 ref 引用特性的所有 DOM 元素的对象

实例方法 / 数据

hu.$watch

  • 用法:   hu.$watch( expOrFn, callback, [ options ] )
  • 参数:
    • { String | Function } expOrFn
    • { Function | Object } callback
    • { Object } [ options ]
      • { Boolean } deep
      • { Boolean } immediate
  • 返回值:   { Function } unWatch
  • 详细:

观察 Hu 实例变化的一个表达式或计算属性函数. 回调函数得到的参数为新值和旧值. 表达式只接受监督的键路径. 对于更复杂的表达式, 可以用一个函数取代

注意: 在变异 ( 不是替换 ) 对象或数组时, 旧值将与新值相同, 因为它们的引用指向同一个对象 / 数组. Hu 不会保留变异之前值的副本

  • 示例:
// 键路径
hu.$watch( 'a.b.c', function( newValue, oldValue ){
  // 做点什么
});

// 函数
hu.$watch(
  function(){
    // 表达式 `this.a + this.b` 每次得出一个不同的结果时
    // 处理函数都会被调用
    // 这就像监听一个未被定义的计算属性
    return this.a + this.b;
  },
  function( newValue, oldValue ){
    // 做点什么
  }
);

hu.$watch 返回一个取消观察函数, 用来停止触发回调

var unWatch = hu.$watch( 'a', callback );
// 取消观察
unWatch();
  • 选项: deep

为了发现对象内部值的变化, 可以在选项参数中指定 deep: true

和 Vue 不同的是, 如果你要监听一个数组内部的变动, 也需要添加 deep: true

hu.$watch( 'someObject', callback, {
  deep: true
});

hu.someObject.nestedValue = 123;
// 回调会被触发
  • 选项: immediate

在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调

hu.$watch( 'a', callback, {
  immediate: true
});

实例方法 / 事件

hu.$on

  • 用法:   hu.$on( event, callback )
  • 参数:
    • { string | Array<string> } event
    • { Function } callback
  • 详细:

监听当前实例上的自定义事件. 事件可以由 hu.$emit 触发. 回调函数会接收所有传入事件触发函数的额外参数

  • 示例:
hu.$on('test', function (msg) {
  console.log( msg )
});

hu.$emit( 'test', 'hi' );
// => "hi"

hu.$once

  • 用法:   hu.$once( event, callback )
  • 参数:
    • { string } event
    • { Function } callback
  • 详细:

监听一个当前实例上的自定义事件, 但是只触发一次, 在第一次触发之后移除监听器

hu.$off

  • 用法:   hu.$off([ event, callback ])

  • 参数:

    • { string | Array<string> } event
    • { Function } callback
  • 详细:

    移除自定义事件监听器

    • 如果没有提供参数, 则移除所有的事件监听器
    • 如果只提供了事件, 则移除该事件所有的监听器
    • 如果同时提供了事件与回调, 则只移除这个回调的监听器

hu.$emit

  • 用法:   hu.$emit( event, [ ...args ] )
  • 参数:
    • { string } event
    • [ ...args ]
  • 详细:

触发当前实例上的事件, 附加参数都会传给监听器回调

实例方法 / 生命周期

hu.$mount

  • 用法:   hu.$mount( elementOrSelector )
  • 参数:
    • { Element | string } elementOrSelector
  • 返回值:   hu - 实例自身
  • 限制:   只在由 new 创建的实例中可用
  • 详细:

如果 Hu 实例在实例化时没有收到 el 选项, 则它处于 "未挂载" 状态, 没有关联的 DOM 元素. 可以使用 hu.$mount() 手动地挂载一个未挂载的实例

这个方法返回实例自身, 因而可以链式调用其它实例方法

  • 示例:
// 创建实例, 但是未挂载实例到文档中
const hu = new Hu({
  render( html ){
    return html`<div>Hello!</div>`;
  }
});

// 挂载到 #app
hu.$mount('#app');

hu.$forceUpdate

  • 用法:   hu.$forceUpdate()
  • 详细:

迫使 Hu 实例立即重新渲染

hu.$nextTick

  • 用法:   hu.$nextTick([ callback ])
  • 参数:
    • { Function } [ callback ]
  • 详细:

将回调延迟到下次 DOM 更新循环之后执行. 在修改数据之后立即使用它, 然后等待 DOM 更新. 它跟全局方法 Hu.nextTick 一样, 不同的是回调的 this 自动绑定到调用它的实例上

如果没有提供回调, 则返回一个 Promise

  • 示例:
new Hu({
  // ...
  methods: {
    doSomethingElse(){
      // ...
    },
    example(){
      // 修改数据
      this.message = 'changed';
      // DOM 还没有更新
      this.$nextTick(function{
        // DOM 现在更新了
        // `this` 绑定到当前实例
        this.doSomethingElse();
      });
    }
  }
});

hu.destroy

  • 用法:   hu.$destroy()
  • 详细:

完全销毁一个实例, 移除所有计算属性和 watch 监听, 解绑它的全部指令及事件监听器
触发 beforeDestroydestroyed 的钩子

自定义元素属性

custom.$hu

  • 只读
  • 详细

自定义元素和自定义元素对应的 hu 实例的映射

  • 示例:
Hu.define( 'custom-element', {
  // ...
});

// 自定义元素
const custom = document.createElement('custom-element');
// 自定义元素对应的 hu 实例
const hu = custom.$hu;

自定义元素方法

custom.$on

  • 用法:   custom.$on( event, callback )
  • 参数:
    • { string | Array<string> } event
    • { Function } callback
  • 只读
  • 详细:

实例 $on 方法的映射

custom.$once

  • 用法:   custom.$once( event, callback )
  • 参数:
    • { string } event
    • { Function } callback
  • 只读
  • 详细:

实例 $once 方法的映射

custom.$off

  • 用法:   custom.$off([ event, callback ])
  • 参数:
    • { string | Array<string> } event
    • { Function } callback
  • 只读
  • 详细:

实例 $off 方法的映射

基础指令

.prop

  • 参数:   prop
  • 预期:   any
  • 详细:

用于动态地绑定 DOM 属性 ( property ), 属性名由参数指定

  • 示例:
html`<div .title=${ 'value' }></div>`;

div.title; // => 'value'

?attr

  • 参数:   attr
  • 预期:   Boolean
  • 详细:

若属性值为 Truthy 则保留 DOM 属性, 否则移除 DOM 属性, 属性名由参数指定

  • 示例:
// 保留 DOM 属性
html`<input ?readonly=${ true } />`;
input.hasAttribute('readonly'); // true;

// 移除 DOM 属性
html`<input ?readonly=${ false } />`;
input.hasAttribute('readonly'); // false;

@event

  • 参数:   event
  • 预期:   Function
  • 修饰符:
    • .stop - 调用 event.stopPropagation()
    • .prevent - 调用 event.preventDefault()
    • .capture - 添加事件侦听器时使用 capture 模式
    • .passive - 添加事件侦听器时使用 passive 模式
    • .once - 只触发一次回调
    • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
    • .left - 只当点击鼠标左键时触发
    • .middle - 只当点击鼠标中键时触发
    • .right - 只当点击鼠标右键时触发
    • .ctrl - 系统修饰键, 只当按下 ctrl 时触发
    • .alt - 系统修饰键, 只当按下 alt 时触发
    • .shift - 系统修饰键, 只当按下 shift 时触发
    • .meta - 系统修饰键, 只当按下 meta 时触发
    • .exact - 控制由精确的 系统修饰键 组合时触发
  • 详细:

绑定事件监听器, 事件类型由参数指定

  • 参数:
function doSomething( event ){
  // ...
}

// 方法处理器
html`<button @click=${ doSomething }></button>`;

// 停止冒泡
html`<button @click.stop=${ doSomething }></button>`;

// 阻止默认行为
html`<button @click.prevent=${ doSomething }></button>`;

// 串联修饰符
html`<button @click.stop.prevent=${ doSomething }></button>`;

// 点击回调只会触发一次
html`<button @click.once=${ doSomething }></button>`;

// 系统修饰键
// 点击时, ctrl 是按下的
html`<button @click.ctrl=${ doSomething }></button>`;

// 精确控制系统修饰键
// 点击时, 四个系统修饰键内只有 ctrl 是按下的
html`<button @click.ctrl.exact=${ doSomething }></button>`;

:name

  • 参数:   name
  • 预期:   按不同指令而不同
  • 详细:

拥有高级功能的指令, 指令名称由参数指定. 详见 功能指令

功能指令

:class

  • 预期:   String | Array | Object
  • 详细:

操作元素的 class 列表是数据绑定的一个常见需求, :class 做了专门的增强, 表达式结果的类型除了字符串之外, 还可以是对象或数组

对象语法

我们可以传给 :class 一个对象, 以动态地切换 class:

html`<div :class=${{ active: isActive }}></div>`

上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActivetruthiness

你可以在对象中传入更多属性来动态切换多个 class. 此外, :class 指令也可以与普通的 class 属性共存

var isActive = true;
var hasError = false;

html`
  <div
    class="static"
    :class=${{ active: isActive, 'text-danger': hasError }}
  ></div>
`

结果将渲染为

<div class="static active"></div>

绑定的数据对象不必内联定义在模板里

const classes = {
  active: true,
  'text-danger': false
};

// 渲染的结果和上面一样
html`<div class="static" :class=${ classes }></div>`;

我们也可以在这里绑定一个返回对象的计算属性. 这是一个常用且强大的模式

new Hu({
  data: {
    isActive: true,
    error: null
  },
  computed: {
    classes(){
      return {
        active: this.isActive && !this.error,
        'text-danger': this.error && this.error.type === 'fatal'
      };
    }
  },
  render( html ){
    return html`
      <div class="static" :class=${ this.classes }></div>
    `;
  }
});

数组语法

我们可以把一个数组传给 :class, 以应用一个 class 列表

var activeClass = 'active';
var errorClass = 'text-danger';

html`<div :class=${[ activeClass, errorClass ]}></div>`;

如果你也想根据条件切换列表中的 class, 可以用三元表达式

html`<div :class=${[ isActive ? activeClass : '', errorClass ]}></div>`;

不过, 当有多个条件 class 时这样写有些繁琐. 所以在数组语法中也可以使用对象语法

html`<div :class=${[ { activeClass: isActive }, errorClass ]}></div>`;

:style

  • 预期:   String | Array | Object
  • 详细:

操作元素的 style 内联样式是数据绑定的一个常见需求, :style 做了专门的增强, 表达式结果的类型除了字符串之外, 还可以是对象或数组.

对象语法

:style 的对象语法十分直观 -- 看着非常像 CSS, 但其实是一个 JavaScript 对象. CSS 属性名可以用驼峰式 ( camelCase ) 或短横线分隔 ( kebab-case, 记得用单引号括起来 ) 来命名

var activeColor = 'red';
var fontSize = 30;

html`<div :style=${{ color: activeColor, fontSize: fontSize + 'px' }}></div>`

直接绑定到一个样式对象通常更好,这会让模板更清晰

var styleObject = {
  color: 'red',
  fontSize: '13px'
};

html`<div :style=${ styleObject }></div>`

同样的, 对象语法常常结合返回对象的计算属性使用

数组语法

:style 的数组语法可以将多个样式对象应用到同一个元素上

html`<div :style=${[ baseStyles, overridingStyles ]}></div>`

:model

  • 预期:   随表单控件类型不同而不同
  • 限制:
    • <input>
    • <select>
    • <textarea>
  • 详细:

在表单控件上创建双向绑定, 可以将控件的值与观察者对象进行绑定

  • 示例:
const div = document.createElement('div');
const hu = new Hu({
  el: div,
  data: {
    value: ''
  },
  render( html ){
    return html`
      <input ref="input" :model=${[ this, 'value' ]}>
    `;
  }
});

hu.value = '1';
hu.$nextTick(() => {
  hu.$ref.input.value;// -> '1'

  hu.value = '2';
  hu.$nextTick(() => {
    hu.$ref.input.value;// -> '2'
  });
});

:show

  • 预期:   any
  • 详细:

根据表达式之真假值, 切换元素的 display CSS 属性

:text

  • 预期:   any
  • 详细:

更新元素的 textContent

:html

  • 预期:   any
  • 详细:

更新元素的 innerHTML, 如果需要更新部分的 innerHTML, 可以使用 unsafe 指令方法

  • 内容将按照普通 HTML 直接进行插入, 不会对内容进行其他编译
  • 在网站上动态渲染任意 HTML 是非常危险的, 因为容易导致 XSS 攻击
  • 只在可信内容上使用 unsafe, 永不用在用户提交的内容上

指令方法

html.repeat

  • 用法:   html.repeat( item, key, template )
  • 参数:
    • { T[] } item
    • { string | ((T) => string) } key
    • { ( item: T, index: number, items: T[] ) => TemplateResult } template
  • 详细:

渲染数组时使用, 若数组发生变动, 将基于 key 的变化重新排列元素顺序而不是替换元素

  • 示例:
const arr = [
  { id: '1', value: 'q' },
  { id: '2', value: 'w' },
  { id: '3', value: 'e' }
]

Hu.render( div )`
  <div>${
    Hu.html.repeat( arr, 'id', ( data, index, arr ) => {
      return Hu.html`<span>${ data.id }: ${ data.value }</span>`;
    })
  }</div>
`;

html.unsafe

  • 用法:   html.unsafe( value )
  • 参数:
    • { string } value
  • 详细:

将内容按普通 HTML 不转义直接插入到当前位置, 如果需要更新完整的的 innerHTML, 也可以使用 html 功能指令

  • 内容将按照普通 HTML 直接进行插入, 不会对内容进行其他编译
  • 在网站上动态渲染任意 HTML 是非常危险的, 因为容易导致 XSS 攻击
  • 只在可信内容上使用 unsafe, 永不用在用户提交的内容上
  • 示例:
const unsafeHTML = `<span>unsafeHTML</span>`;

Hu.render( div )`
  <div>${
    Hu.html.unsafe( unsafeHTML )
  }</div>
`;

html.bind

  • 用法:   html.bind( obj, name )
  • 参数:
    • { object } obj
    • { string | number | symbol } name
  • 详细:

将元素属性或内容与观察者对象的目标对象绑定, 若观察者对象的目标对象更新, 元素属性也会更新

  • 和常规绑定相比, 若是使用此方法绑定的元素属性或内容, 变量更新时可以不触发整体重新渲染
  • 纯渲染实例可以使用此方法达到和常规绑定一样的体验
  • 和常规绑定一样, 观察者对象的目标对象更新后, 属性值会在下一 tick 进行更新, 可以使用 nextTick 方法获取更新后的值
  • 示例:
new Hu({
  data: {
    classes: 'bar'
  },
  render( html ){
    // 可复用
    const bindClasses = html.bind( this, 'classes' );

    // 1. 使用 bind 绑定的写法
    // 若 classes 发生改变, render 方法不会被重新运行, 而是单独更新绑定的位置
    return html`
      <div class=${ bindClasses }>${ bindClasses }</div>
    `;

    // 2. 使用常规绑定的写法
    // 若 classes 发生改变, render 方法会被重新运行以达到更新的目的
    return html`
      <div class=${ this.classes }>${ this.classes }</div>
    `;
  }
});