OWL教程3 OWL五大组成部分之二组件系统

OWL教程3 OWL五大组成部分之二组件系统

参考文档:https://github.com/odoo/owl/blob/master/doc/reference/component.md

粗略的讲,Owl有五个主要部分:

  1. 虚拟DOM系统(src/blockdom)
  2. 组件系统(src/component)
  3. 模板编译器(src/compiler 目录)
  4. 一个小的运行时让各层组合在一起(src/app)
  5. 响应式系统(src/reactivity.ts)

2.组件

2.1 概览

​ 一个Owl组件是一个小的类,描述了部分用户接口,它是组件树的一部分,它有一个环境变量(env),从父组件传播到子组件. (被所有组件共享)

​ Owl组件通过继承Component类来定义,例如,这里是一个Counter组件的实现:

const { Component, xml, useState } = owl;

class Counter extends Component {
  static template = xml`
    <button t-on-click="increment">
      Click Me! [<t t-esc="state.value"/>]
    </button>`;

  state = useState({ value: 0 });

  increment() {
    this.state.value++;
  }
}

这个例子中,我们使用xml助手定义了内联模板,使用了useState钩子,它返回一个响应式的参数.

2.2 属性和方法

Component类有一个很小的API

env(object) 组件环境变量

props(object)

父组件传递给子组件的属性值. 注意: props由父组件拥有,所以它不应该由子组件修改.(否则可能会意想不到的风险,因为父组件不知道它被修改了)

props可以动态的被父组件修改, 在这种情况下,子组件会依次调用下列声明周期函数

willUpdateProps, willPatch and patched.

render(deep=false)

调用这个方法会直接触发一次重新渲染. 注意在响应式系统中,这很少需要手动去做. 另外,渲染操作是异步的,所以Dom只会有一点点延迟(在下一个动画帧,如果没有组件延迟的话)

默认情况下, 只渲染组件自己本身,如果需要渲染所有的子组件,需要设置deep=true

2.3 静态属性

  • template (string): 组件渲染使用的模板名称,注意可以使用xml助手来定义一个内联模板.

  • components (object, optional): 模板中使用的所有子组件

    class ParentComponent extends owl.Component {
      static components = { SubComponent };
    }
    
  • props (object, optional): 用于验证父组件传递给子组件的参数, (开发模式下) See Props Validation for more information.

    class Counter extends owl.Component {
      static props = {
        initialValue: Number,
        optional: true,
      };
    }
    
  • defaultProps (object, optional): 默认属性, 当props传递给对象的 时候,如果有参数没有赋值,会用默认值代替,注意一点,不会修改初始化的对象,而是新建一个对象来代替See default props for more information

    class Counter extends owl.Component {
      static defaultProps = {
        initialValue: 0,
      };
    }
    

2.4 生命周期循环

一个坚实的,健壮的组件系统需要完整的生命周期系统来帮助开发者来编写组件,这里是关于Owl组件生命周期的完整描述

MethodHookDescription
setupnonesetup
willStartonWillStartasync, before first rendering
willRenderonWillRenderjust before component is rendered
renderedonRenderedjust after component is rendered
mountedonMountedjust after component is rendered and added to the DOM
willUpdatePropsonWillUpdatePropsasync, before props update
willPatchonWillPatchjust before the DOM is patched
patchedonPatchedjust after the DOM is patched
willUnmountonWillUnmountjust before removing component from DOM
willDestroyonWillDestroyjust before component is destroyed
erroronErrorcatch and handle errors (see error handling page)

总结一波:

11个钩子可以分成五组:

1 error 处理错误
2 setup willDestroy 总是被执行,setup类似构造函数, willDestroy 最后做一些清理操作.
3 mounted unmounted 一对相反的方法,不一定会被执行,但是要么都执行,要不都不执行, 适合添加事件监听器
4 willstart(只执行一次,可以用来异步加载资源) willrender rendered
5 willupdateprops( 更新props的时候可以异步加载资源) willpatch patched

fatux:
owl的渲染分为两部分,虚拟dom的渲染和物理dom的渲染
mouted、patched、unmouont是针对物理dom的,其他的是针对虚拟dom的,个人理解,不知道对不对。

​ 在组件构建好之后会立刻执行setup函数,这是一个生命周期方法,类似于构造函数,除了它不接收任何参数.

​ 这里是调用钩子函数的合适地方, 请注意,在组件生命周期中使用setup钩子的一个主要原因是可以对其进行monkey patch。这是Odoo生态系统中的一个常见需求。

setup() {
  useSetupAutofocus();
}

什么叫monkey patch ?( 来自chatgpt)

“Monkey patch”(猴子补丁)是指在运行时修改现有的代码或类的行为的一种技术。猴子补丁是动态语言(如Python)中的一种常用技巧,允许程序员在不修改原始源代码的情况下进行临时或局部的修改。

通过猴子补丁,您可以在运行时动态地更改类、方法或函数的行为,添加新的功能,修改现有的行为或修复bug。这种技术在开发过程中非常有用,尤其是当您无法修改或无权访问原始源代码时。

猴子补丁的原理是利用动态类型语言的特性,可以在运行时修改对象的方法和属性。这允许您在不修改原始类定义的情况下,通过重新定义方法或添加属性来改变类的行为。

以下是一个简单的Python示例,演示了如何使用猴子补丁来修改现有类的行为:

class MyClass:
    def my_method(self):
        return "Original behavior"

def modified_method(self):
    return "Modified behavior"

obj = MyClass()
print(obj.my_method())  # 输出:Original behavior

MyClass.my_method = modified_method  # 猴子补丁,将my_method重新定义为modified_method

print(obj.my_method())  # 输出:Modified behavior

在这个例子中,我们定义了一个类MyClass和一个名为my_method的方法。然后,我们使用猴子补丁技术,将my_method重新定义为modified_method。这样,实例化的对象obj调用my_method时,将输出"Modified behavior",而不是原始的"Original behavior"。

猴子补丁虽然可以灵活地修改现有的代码行为,但也需要谨慎使用。过度使用猴子补丁可能导致代码难以理解、调试和维护。因此,应在慎重思考和评估后,选择是否使用猴子补丁来解决特定的问题。

fatux: 为啥叫猴子补丁呢? 中国有“猴子偷桃”的故事,猴子补丁的意思也类似吧,偷偷的修改代码…

​ willstart是一个异步的钩子在初始化渲染组件之间用来执行一些操作(大多数时候是异步的)

​ 它在初始化渲染之前只执行一次, 有时候很有用,比如,

1 在渲染组件之前加载一些js文件

, 2 或者从服务器加载数据

  setup() {
    onWillStart(async () => {
      this.data = await this.loadData()
    });
  }

在这一时刻,组件还没有渲染出来,注意, 过慢的willstart代码会减慢用户界面的渲染. 因此,有时候要让这个方法尽快执行.

不太常用,但是如果在需要渲染组件之前执行一些代码的话(更准确的说,它编译好的模板函数执行时),

  setup() {
    onWillRender(() => {
      // do something
    });
  }

willRender 钩子在渲染模板之前执行,父组件先,然后子组件.

不太常用,但是如果需要在组件渲染后,(更准确的说,当它的编译模板函数执行完后)调用

  setup() {
    onRendered(() => {
      // do something
    });
  }

rendered钩子仅仅在渲染完模板后调用,注意: 在这一刻,真实的dom可能还不存在(特别是第一次渲染),或者还没有更新, 这将在下一个动画帧中被显示,只要所有的组件都准备好了.

fatux: 所以willrender和rendered指的是在虚拟内存中渲染。 而mounted是真实的在页面上渲染,此时dom元素已经生成。可以添加各种事件处理函数。 所以,owl的渲染是分两部分的,虚拟渲染和物理渲染。

​ mounted钩子在每次组件被附加到dom的时候执行, 在初始化渲染完成后, 在这一刻,组件已经激活.

​ 这是个合适的位置用来添加监听器,或者跟Dom交互

​ 跟它相反的是willUnmount, 如果一个组件加载了,那么他在未来某一时刻一定会执行unmount.

​ mounted方法会在子组件递归的调用,首先子组件,然后父组件.

​ 修改Mounted钩子的状态是被允许的(但是不鼓励). 这样做会引发重新渲染,这将不会被用户察觉,但是会稍微减慢组件的速度.

  setup() {
    onMounted(() => {
      // do something here
    });
  }

​ willUpdateProps是一个异步的钩子,在新的props设置之前被调用, 如果组件需要执行一些异步的任务这将很有用,例如, 假设props是记录id,根据id获取记录数据.

onWillUpdateProps 钩子用来注册一个函数在这一刻执行.

  setup() {
    onWillUpdateProps(nextProps => {
      return this.loadData({id: nextProps.id});
    });
  }

注意: 它将接收新的props作为参数

这个钩子在第一次被渲染的时候不会执行, 但是willstart会执行来类似的事情,另外,跟大多数钩子一样,根据通常的顺序执行,先父组件,然后子组件.

这个钩子在DOM patching进程开始的时候执行,第一次渲染的时候不执行, 这可以用来从DOM中获取信息,例如,当前scrollbar的位置.

注意, 不允许修改状态, 这个方法仅仅在给真实DOM patch的时候调用,仅仅用来保存真实DOM的状态,另外组件不在真实dom中也不允许调用.

  setup() {
    onWillPatch(() => {
      this.scrollState = this.getScrollSTate();
    });
  }

跟大多数钩子一样,根据通常的顺序执行,先父组件,然后子组件.

当组件更新完DOM的时候会调用这个钩子(最有可能通过改变了它的状态/props或者环境)

这个组件在第一次渲染的时候不会调用, 它用来跟DOM交互, 如果组件不在真实dom中,它不会执行.

  setup() {
    onPatched(() => {
      this.scrollState = this.getScrollSTate();
    });
  }

在这个钩子里更新组件状态是可能的,但是不鼓励. 注意,这里的更新会产生额外的重新渲染,这里一定要小心,要避免死循环式的重新渲染.

像mounted一样,patched钩子的执行顺序 先子后父.

​ 当组件从DOM卸载的时候会调用这个钩子,这里是移除监听器的好地方.

  setup() {
    onMounted(() => {
      // add some listener
    });
    onWillUnmount(() => {
      // remove listener
    });
  }

这个方法是mounted方法相反的方法,注意,如果一个组件在mount之前就被销毁了,那么这个方法就不执行了.

执行顺序,先父后子.

有时候,组件需要在setup中做些动作,然后当他们不活跃的时候需要清理. 然后willUnmount钩子并不适合用来做清理操作,因为,组件可能在安装之前就被销毁了, willDestroy就很有用了,因为它总是被执行.

  setup() {
    onWillDestroy(() => {
      // do some cleanup
    });
  }

执行顺序: 先子后父

不幸的是,组件可能会在运行时崩溃, 这是一个不幸的消息, 这就是Owl为啥要提供一种方式来处理这些错误

当我们需要拦截并正确响应某些子组件中发生的错误时,onError钩子非常有用。有关详细信息,请参阅有关错误处理的页面。

  setup() {
    onError(() => {
      // do something
    });
  }

2.5 子组件

用子组件来定义父组件是很方便的,这叫做合成,实践中很有用. 在Owl中要做到这点只需要两点:

1 在模板中通过首字母大写的标签来引用子组件并且可以传递参数

2 在组件的静态属性Component中注册子组件

class Child extends Component {
  static template = xml`<div>child component <t t-esc="props.value"/></div>`;
}

class Parent extends Component {
  static template = xml`
    <div>
      <Child value="1"/>
      <Child value="2"/>
    </div>`;

  static components = { Child };
}

2.6 动态子组件

这不常用,但是有时候我们需要一个动态的子组件名称,这种情况, t-component指令可以用来接收动态的值.这应该是一个表达式用来计算组件类,例如

class A extends Component {
  static template = xml`<div>child a</div>`;
}
class B extends Component {
  static template = xml`<span>child b</span>`;
}
class Parent extends Component {
  static template = xml`<t t-component="myComponent"/>`;

  state = useState({ child: "a" });

  get myComponent() {
    return this.state.child === "a" ? A : B;
  }
}

2.7 status助手

有时候需要一种方法来查看当前的组件处于哪种状态,要做到这点,可以使用status助手

const { status } = owl;
// assume component is an instance of a Component

console.log(status(component));
// logs either:
// - 'new', if the component is new and has not been mounted yet
// - 'mounted', if the component is currently mounted
// - 'destroyed' if the component is currently destroyed
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用 Owl Carousel 实现大图和小图联动轮播的例子,你可以根据自己的需求进行修改和定制: HTML 代码: ```html <div class="owl-carousel owl-theme"> <div class="item"> <img src="big-image-1.jpg" alt="Big Image 1"> </div> <div class="item"> <img src="big-image-2.jpg" alt="Big Image 2"> </div> <div class="item"> <img src="big-image-3.jpg" alt="Big Image 3"> </div> </div> <div class="owl-carousel owl-theme thumbs"> <div class="item"> <img src="small-image-1.jpg" alt="Small Image 1"> </div> <div class="item"> <img src="small-image-2.jpg" alt="Small Image 2"> </div> <div class="item"> <img src="small-image-3.jpg" alt="Small Image 3"> </div> </div> ``` CSS 代码: ```css .owl-carousel { margin-bottom: 20px; } .thumbs .item { margin-right: 10px; } .thumbs .item:last-child { margin-right: 0; } .thumbs .item img { width: 100%; height: auto; cursor: pointer; opacity: 0.5; } .thumbs .item.active img { opacity: 1; } ``` JavaScript 代码: ```javascript $(document).ready(function() { var bigCarousel = $('.owl-carousel'); var thumbsCarousel = $('.thumbs'); bigCarousel.owlCarousel({ items: 1, loop: true, dots: false, nav: true, navText: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'] }); thumbsCarousel.owlCarousel({ items: 3, loop: true, dots: false, nav: false, margin: 10, responsive: { 0: { items: 3 }, 600: { items: 4 }, 1000: { items: 5 } } }); thumbsCarousel.on('click', '.item', function(e) { e.preventDefault(); var index = $(this).index(); bigCarousel.trigger('to.owl.carousel', [index, 300, true]); thumbsCarousel.find('.item.active').removeClass('active'); $(this).addClass('active'); }); bigCarousel.on('changed.owl.carousel', function(event) { var index = event.item.index; thumbsCarousel.find('.item.active').removeClass('active'); thumbsCarousel.find('.item').eq(index).addClass('active'); }); }); ``` 在上面的例子中,我们使用了两个 Owl Carousel 实例,一个用于显示大图,另一个用于显示小图。我们通过 thumbsCarousel.on('click', '.item', function(e) {}); 和 bigCarousel.on('changed.owl.carousel', function(event) {}); 两个事件来实现大图和小图的联动效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值