通过s-if指令,我们可以为元素指定条件。只有当条件成立时元素才会渲染,否则元素不会被加载。

s-if无法实现这样的需求:我们需要在符合条件的情况下显示某元素,条件不满足时,元素在页面中隐藏,但依然被挂载到 DOM 。这个时候,元素的展现用 CSS 控制更为合适。

这一需求的本质可以归纳为:如何根据条件实现元素的显示/隐藏。

如何处理

San 提供在视图模板中进行样式处理的方案,详见教程。你可以用不同的 class 控制样式,也可以用 inline 样式实现。

1. 用 class 控制元素的显示与隐藏

  1. <!-- template -->
  2. <div>
  3. <ul class="list{{isHidden ? ' list-hidden' : ' list-visible'}}"></ul>
  4. </div>

注意, class 属性有多个类名时,需要为第一个以后的类名加上空格。

codepen 演示如下:

See the Pen
根据条件添加不同样式-用class控制
by MinZhou (@Mona_) on
CodePen.

CSS 控制着样式的展现,所以 DOM 始终都存在页面节点树中。你可以打开控制台看看。

2. 用内联样式控制元素的隐藏与显示

  1. <!-- template -->
  2. <div>
  3. <ul style="display: {{isHidden ? 'none' : 'block'}}">visible</ul>
  4. </div>

See the Pen
根据条件添加不同样式-用内联样式控制
by MinZhou (@Mona_) on
CodePen.

有时候数据可能并不存在,所以把样式名包含在插值中更为可靠。

  1. <!-- template -->
  2. <div>
  3. <ul style="{{isHidden === false ? 'display: none' : 'display: block'}}">visible</ul>
  4. </div>

3. 使用计算属性

前面的两种方案都可以通过使用计算属性,将判断逻辑从模板中解耦出来,以便更好的应对可能变得更为复杂的需求。下面是基于 class 的例子:

  1. san.defineComponent({
  2. template: `
  3. <div>
  4. <ul class="{{ulClass}}"></ul>
  5. </div>
  6. `,
  7. computed: {
  8. ulClass() {
  9. const isHidden = this.data.get('isHidden');
  10. if (isHidden) {
  11. return 'list list-hidden';
  12. }
  13. return 'list list-visible';
  14. }
  15. }
  16. })

codepen 演示如下:

See the Pen 基于 computed 的元素显示隐藏 by LeuisKen (@LeuisKen) on CodePen.

4. 使用 filter

filter 也可以用于对 class 和 style 进行处理,解耦的效果和 computed 类似,其特点是能够显式地声明属性值与数据的依赖关系。下面是基于 class 的例子:

  1. san.defineComponent({
  2. template: `
  3. <div>
  4. <ul class="{{isHidden | handleHidden}}"></ul>
  5. </div>
  6. `,
  7. filters: {
  8. handleHidden(isHidden) {
  9. if (isHidden) {
  10. return 'list list-hidden';
  11. }
  12. return 'list list-visible';
  13. }
  14. }
  15. })

codepen 演示如下:

See the Pen 基于 filter 的元素显示隐藏 by LeuisKen (@LeuisKen) on CodePen.

我们可以很明显地看出,class 是由 isHidden 控制的。

这里要额外注意的是,如果和 class 关联的有多个 data ,用 filter 的方法可能会有一些问题,比如我在下面的例子中实现了一个tab组件:

  1. san.defineComponent({
  2. template: `
  3. <div class="tab">
  4. <div
  5. s-for="tab in tabs"
  6. class="{{tab.value | mapActive}}"
  7. on-click="tabChange(tab.value)"
  8. >
  9. {{tab.name}}
  10. </div>
  11. </div>
  12. `,
  13. initData() {
  14. return {
  15. active: '',
  16. tabs: [
  17. {
  18. name: '第一项',
  19. value: 'one'
  20. },
  21. {
  22. name: '第二项',
  23. value: 'two'
  24. }
  25. ]
  26. };
  27. },
  28. tabChange(value) {
  29. this.data.set('active', value);
  30. },
  31. filters: {
  32. mapActive(value) {
  33. const active = this.data.get('active');
  34. const classStr = 'sm-tab-item';
  35. if (value === active) {
  36. return classStr + ' active';
  37. }
  38. return classStr;
  39. }
  40. }
  41. });

codepen 演示如下:

See the Pen 没有显式声明依赖的tab bug演示 by LeuisKen (@LeuisKen) on CodePen.

此处当我在点击 tab 的时候,虽然 active 能够正常更新,但是视图不会引起变化,因为 San 的依赖收集机制不认为 active 的修改会影响到视图,因此需要我们在模板中显式声明对 active 的依赖,参考如下代码:

  1. san.defineComponent({
  2. // 将下面的 mapActive 改成 mapActive(active),显示声明视图对 active 的依赖
  3. template: `
  4. <div class="tab">
  5. <div
  6. s-for="tab in tabs"
  7. class="{{tab.value | mapActive(active)}}"
  8. on-click="tabChange(tab.value)"
  9. >
  10. {{tab.name}}
  11. </div>
  12. </div>
  13. `,
  14. initData() {
  15. return {
  16. active: '',
  17. tabs: [
  18. {
  19. name: '第一项',
  20. value: 'one'
  21. },
  22. {
  23. name: '第二项',
  24. value: 'two'
  25. }
  26. ]
  27. };
  28. },
  29. tabChange(value) {
  30. this.data.set('active', value);
  31. this.fire('change', value);
  32. },
  33. filters: {
  34. // 这里就不需要通过 this.data.get('active') 拿到 active 了
  35. mapActive(value, active) {
  36. const classStr = 'sm-tab-item';
  37. if (value === active) {
  38. return classStr + ' active';
  39. }
  40. return classStr;
  41. }
  42. }
  43. });

codepen 演示如下:

See the Pen 显式声明依赖的tab演示 by LeuisKen (@LeuisKen) on CodePen.

通过在模板中显示声明视图对 active 的依赖, San 就能正常更新视图了。这也是为什么我会在一开始说 filter 的特点是能够显式地声明属性值与数据的依赖关系。

结语

隐藏和显示是开发中较为常见的需求,还有一些其他的样式切换需求,使用以上两种方法都可以轻松实现。

总结一下,如果你要控制元素的渲染与否(是否添加到节点树),你需要使用s-if指令;如果你仅仅只想控制 DOM 节点的样式,比如元素的显示/隐藏样式,请使用数据控制 class 或内联样式。