获取列表元素(Directive,selector,ContentChildren)

http://www.open-open.com/lib/view/open1461113267205.html

如果你想获取的元素不在你的组件模版定义里怎么办?举个例子,假设你有个列表组件,允许用户自定义各列表项,然后你想跟踪列表项的数量。

当然你可以用 @ContentChildren 来获取组件里的“内容”(那些用户自定义,然后映射到你组件里的内容),但因为这些内容可以是任意值,所以是没办法向刚才那样通过 local variable 来追踪她们的。

一种方法是,要求用户给他将要映射的列表项都加上预定义的 local variable 。这样的话,代码可以从上面例子改成这样:

@ContentChildren 和 local variable (不推荐)

// user code
<my-list>
   <li *ngFor="#item of items" #list-item> {{item}} </li>
</my-list>

// component code
@Component({
  selector: 'my-list',
  template: `
    <ul>
      <ng-content></ng-content>
    </ul>
  `
})
export class MyList implements AfterContentInit {
  @ContentChildren('list-item') items: QueryList<ElementRef>;

  ngAfterContentInit() {
     // do something with list items
  }
}

可是,这需要用户写些额外的内容( #list-item ),真心不怎么优雅!你可能希望用户就只写 <li> 标签,不要什么 #list-item 属性,那肿么办?

解决方案: @ContentChildren 配合 li 选择器指令

介绍一个好方案,用 @Directive 装饰器,配合他的 selector 功能。定义一个能查找/选择 <li> 元素的指令,然后用 @ContentChildren 过滤用户映射进当前组件里的内容,只留下符合条件的 li 元素。

@ContentChildren 配合 @Directive (推荐)

// user code
<my-list>
   <li *ngFor="#item of items"> {{item}} </li>
</my-list>

@Directive({ selector: 'li' })
export class ListItem {}

// component code
@Component({
  selector: 'my-list'
})
export class MyList implements AfterContentInit {
  @ContentChildren(ListItem) items: QueryList<ListItem>;

  ngAfterContentInit() {
     // do something with list items
  }
}

注:看起来只能选择 <my-list> 里的 li 元素(例如: my-list li ),需要注意的是,目前 angular2 尚不支持"parent-child"模式的选择器。如果需要获取组件里的元素,用 @ViewChildren 、 @ContentChildren 这类queries是最好的选择

<think>嗯,我现在需要了解如何在Vue中获取元素的大小。首先,我应该回忆一下在原生JavaScript中是怎么做的。通常,我们会用getBoundingClientRect()方法或者offsetWidth、offsetHeight这些属性来获取元素的尺寸。那在Vue里,有没有什么特别的方法或者更方便的途径呢? 可能Vue提供了某些内置的方法或指令来简化这个过程。比如,使用ref属性来获取DOM元素的引用。没错,在Vue中,可以通过给元素添加ref属性,然后在组件实例中通过this.$refs来访问对应的DOM元素。这样就能像原生JS那样操作了。 那具体步骤应该是怎样的呢?首先,在模板里给目标元素加上ref,比如ref="myElement"。然后在Vue实例的方法里,通过this.$refs.myElement来获取这个元素。接下来,调用getBoundingClientRect()或者访问offsetWidth等属性。不过需要注意的是,在生命周期钩子中,比如mounted之后才能确保元素已经渲染完成,这时候才能安全地访问DOM。 另外,Vue的响应式系统可能对DOM操作有影响。比如,如果在数据变化后立即获取元素大小,可能需要使用nextTick来等待DOM更新完成。比如,在修改数据后调用this.$nextTick(() => { ... }),然后在回调中获取尺寸,这样可以确保获取的是更新后的尺寸。 还有,如果需要在窗口大小变化时动态获取元素的大小,可能需要监听resize事件,并在事件处理函数中更新尺寸数据。不过要注意,频繁的resize事件可能会影响性能,所以最好加上防抖处理。 另外,有没有Vue特定的指令或库可以帮助更方便地获取元素尺寸呢?比如,vue-resize这个第三方库,或者使用自定义指令来监听元素尺寸的变化。比如,创建一个自定义指令,在元素插入和组件更新时触发,使用ResizeObserver来监测尺寸变化,这样可以更高效地处理动态变化的情况。 不过,ResizeObserver可能在旧版浏览器中不支持,需要考虑polyfill。或者,使用Vue的官方推荐方法,结合ref和生命周期钩子来处理。 总结一下,步骤大概是:使用ref获取元素引用,在合适的生命周期钩子(如mounted)或nextTick回调中访问元素的尺寸属性,或者监听resize事件,考虑性能优化,或者使用自定义指令和ResizeObserver来监测变化。 需要注意的常见问题包括:在元素未渲染时访问导致的undefined错误,数据变化后DOM未及时更新导致的尺寸不准确,以及频繁事件监听带来的性能问题。需要确保在正确的时机获取元素,并使用nextTick或ResizeObserver来处理动态变化的情况。</think>在Vue中获取元素大小,可以通过以下步骤实现: --- ### 一、使用 `ref` 获取DOM引用 1. **模板中定义元素引用** 在需要获取大小的元素上添加 `ref` 属性: ```html <template> <div ref="myElement">需要测量的元素</div> </template> ``` 2. **通过 `$refs` 访问元素** 在Vue实例的`mounted`生命周期钩子中访问元素(确保DOM已渲染): ```javascript export default { mounted() { const element = this.$refs.myElement; const width = element.offsetWidth; // 包含边框和内边距的宽度 const height = element.offsetHeight; // 包含边框和内边距的高度 } } ``` --- ### 二、获取精确尺寸:`getBoundingClientRect()` ```javascript mounted() { const element = this.$refs.myElement; const rect = element.getBoundingClientRect(); console.log('宽度:', rect.width, '高度:', rect.height); } ``` - **返回值**:包含`width`、`height`、`top`、`left`等属性的对象。 - **特点**:精确测量布局后的实际尺寸(包含边框,不包含外边距)。 --- ### 三、响应式更新尺寸(动态监听) #### 方法1:监听窗口变化 ```javascript mounted() { window.addEventListener('resize', this.handleResize); }, beforeDestroy() { window.removeEventListener('resize', this.handleResize); // 避免内存泄漏 }, methods: { handleResize() { const rect = this.$refs.myElement.getBoundingClientRect(); console.log('新尺寸:', rect.width, rect.height); } } ``` - **优化**:添加防抖函数(如`lodash.debounce`)避免频繁触发。 #### 方法2:使用 `ResizeObserver`(推荐) ```javascript mounted() { this.observer = new ResizeObserver(entries => { const entry = entries[0]; console.log('新宽度:', entry.contentRect.width); }); this.observer.observe(this.$refs.myElement); }, beforeDestroy() { this.observer.disconnect(); // 清理监听 } ``` - **优点**:直接监听元素尺寸变化,无需依赖窗口事件。 - **兼容性**:需引入[polyfill](https://github.com/que-etc/resize-observer-polyfill)支持旧浏览器。 --- ### 四、结合 `nextTick` 确保DOM更新 在数据变更后立即获取尺寸时,需等待DOM更新完成: ```javascript methods: { updateSize() { this.someData = newValue; // 修改数据触发DOM更新 this.$nextTick(() => { const width = this.$refs.myElement.offsetWidth; console.log('更新后的宽度:', width); }); } } ``` --- ### 五、自定义指令封装(高阶用法) 创建可复用的尺寸监听指令: ```javascript // main.js Vue.directive('size-observer', { bind(el, binding) { const callback = binding.value; const observer = new ResizeObserver(entries => { callback(entries[0].contentRect); }); observer.observe(el); el._resizeObserver = observer; }, unbind(el) { el._resizeObserver?.disconnect(); } }); ``` ```html <!-- 使用指令 --> <template> <div v-size-observer="onResize"></div> </template> <script> export default { methods: { onResize(rect) { console.log('宽度:', rect.width, '高度:', rect.height); } } } </script> ``` --- ### 常见问题 1. **`$refs` 未定义?** - 确保在`mounted`或之后的生命周期钩子中访问。 - 检查`ref`名称是否拼写一致。 2. **动态渲染的元素** 使用`v-if`控制的元素,需在其显示后通过`nextTick`获取。 3. **组件元素** 如果`ref`指向Vue组件,需通过`this.$refs.component.$el`访问根DOM元素。 --- ### 总结 | 方法 | 场景 | 特点 | |--------------------|-----------------------------|-----------------------------------| | `offsetWidth` | 快速获取简单尺寸 | 不包含滚动条,包含边框和内边距 | | `getBoundingClientRect` | 精确测量布局后尺寸 | 包含`transform`缩放后的结果 | | `ResizeObserver` | 动态监听尺寸变化 | 高效,推荐结合自定义指令使用 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值