Как реализовать производную реализацию рендеринга в Vue.js с использованием классов ES6 - PullRequest
2 голосов
/ 05 марта 2019

Заранее извиняюсь за длину этого запроса ..

В React я могу использовать функцию рендеринга для любого стандартного класса ES6, который возвращает JSX.Element. Это замечательно, потому что позволяет мне иметь производные классы ES6, где каждый производный класс реализует свою собственную реализацию рендеринга JSX - см. Пример (ts) ниже:

export class Colour {
  public name: string = "";

  constructor(name) {
    this.name = name;
  }

  public render(): JSX.Element {
    return <span>Error, no colour specified</span>;
  }
}

export class Red extends Colour {
  constructor() {
    super("red");
  }
  public render(): JSX.Element {
    return <div style={{ color: this.name }}>Hi, I am {this.name}!</div>;
  }
}

export class Blue extends Colour {
  constructor() {
    super("blue");
  }
  public render(): JSX.Element {
    return <h2 style={{ color: this.name }}>Hi, I am {this.name}!</h2>;
  }
}  

Затем в компоненте React я могу создать список объектов Color , которые можно легко визуализировать следующим образом:

function App() {
  const list = [];
  list.push(new Red());
  list.push(new Blue());
  list.push(new Red());

  const listItems = list.map(item => <li key={item}>{item.render()}</li>);

  return (
    <div className="App">
      <h1>React derived rendering </h1>
      <ul>{listItems}</ul>
    </div>
  );
} 

В результате получается такой вывод:

enter image description here

и все хорошо ...

Теперь мой вопрос: есть ли способ сделать это так легко в Vue.js? Пожалуйста, обратите внимание - для меня важно создать несколько производных экземпляров в коде и добавить их в список для визуализации!

Сейчас самое близкое, что я могу сделать - это создать фиктивный компонент Vue, и когда он рендерится, я на самом деле вызываю производную реализацию, передающую дескриптор рендеринга 'h'.

Vue.component("fusion-toolbar", {
  props: ["items"],
  template: `
            <div>
                <div v-for="item in items" 
                    v-bind:key="item.code" >
                        <f-dummy v-bind:item='item'></f-dummy>
                </div>
            </div>
        `
});

Vue.component("f-dummy", {
  props: ["item"],
  render(h) {
    return this.item.render(h);
  }
});

export class Colour {
    public colour: string = "";

    constructor(colour: string) {
        this.colour = colour;
    }

    render(h: any) {
        return h("h1", "Hello, I am " + this.colour);
    }

}

export class Red extends Colour {
    constructor() {
        super("red");
    }

    render(h: any) {
        return h("h2", "Hello, I am " + this.colour);
    }
}

export class Blue extends Colour {
    private _isDisabled = true;

    constructor(disabled: boolean) {
        super("blue");
        this._isDisabled = disabled;
    }

    render(h: any) {
        return h('div',
            [
                h('h4', {
                    class: ['example-class', { 'conditional-class': this._isDisabled }],
                    style: { backgroundColor: this.colour }
                }, "Hello, I am " + this.colour)
            ]
        )
    }
}

Тогда в моем родительском компоненте листинга у меня есть:

<template>
  <div id="app"><fusion-toolbar v-bind:items="myitems"> </fusion-toolbar></div>
</template>

<script>
import { Red, Blue } from "./Colour";

export default {
  name: "App",
  data: function() {
    return {
      myitems: []
    };
  },
  mounted() {
    this.myitems.push(new Red());
    this.myitems.push(new Blue(false));
    this.myitems.push(new Blue(true));
  }
};
</script>

Это лучший способ сделать это? Любые отзывы приветствуются ...

Ответы [ 2 ]

0 голосов
/ 13 марта 2019

Имея некоторое время, чтобы обдумать это, я должен заключить, что мое раздражение вызвано необходимостью введения промежуточного компонента 'f-dummy' просто для того, чтобы я смог захватить функцию рендеринга Vue, которую я мог затем передать в pojo.

Что я изменил сейчас, так это то, что я отображаю сам список , используя дескриптор render (createElement) (или 'h') нижнего уровня, который затем я могу передать потомку без необходимости прибегать к промежуточному компоненту .

Вот новый код, во-первых, я определяю классы ES6 (обратите внимание, что Color, Orange, Blue являются только примерами):

export class Colour {
    public colour: string = "";

    constructor(colour: string) {
        this.colour = colour;
    }

    render(h: any) {
        return h("h1", "Error - please provide your own implementation!");
    }
}

export class Orange extends Colour {
    constructor() {
        super("orange");
    }

    public getStyle() {
        return {
            class: ['example-class'],
            style: { backgroundColor: this.colour, border: '3px solid red' }
        }
    }

    render(h: any) {
        return h('h4', this.getStyle(), "This is my colour: " + this.colour)
    }
}

export class Blue extends Colour {
    private _isDisabled = true;

    constructor(disabled: boolean) {
        super("lightblue");
        this._isDisabled = disabled;
    }

    render(h: any) {
        return h('div',
            [
                h('h4', {
                    class: ['example-class', { 'is-disabled': this._isDisabled }],
                    style: { backgroundColor: this.colour }
                }, "Hello, I am " + this.colour)
            ]
        )
    }
}

Чтобы отобразить список, я теперь делаю это:

Vue.component("fusion-toolbar", {
  props: ["items"],
  render(h: any) {
    return h('div', this.items.map(function (item: any) {
      return item.render(h);
    }))
  }
});

Теперь я могу просто добавить оранжевый или синий 'pojo' экземпляры в мой список, и они будут отображаться правильно.См. Содержание App.vue ниже:

<template>
  <div id="app">
    <fusion-toolbar v-bind:items="myitems"> </fusion-toolbar>
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import { Colour, Orange, Blue } from "./components/DropDownItem";

export default Vue.extend({
  name: "app",
  data: function() {
    return {
      myitems: [] as Colour[]
    }
  },
  mounted() {
    this.myitems.push(new Orange());
    this.myitems.push(new Blue(false));
    this.myitems.push(new Blue(true));
  }
});
</script>

<style lang="scss">
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  text-align: center;
}

.example-class {
  width: 250px;
  padding: 3px;
}

.is-disabled {
  color: gray;
}

</style>

Вот как выглядит образец:

enter image description here

Очень аккуратно.Я понимаю, что могу использовать плагин Babel для использования JSX вместо низкоуровневого рендеринга Vue, но я вполне наслаждаюсь мощью, которую он мне дает!

0 голосов
/ 07 марта 2019

Я сделал это решение от руки, поэтому оно может потребовать некоторых настроек.Поскольку единственная переменная - это цвет / имя, кажется, что вы могли бы легко сделать универсальный компонент Color следующим образом:

<template>
    <div>
        <span v-if="name === ''">Error, no colour specified</span>
        <h2 v-else :style="{ color: name }">Hi, I am {{name}}!</h2>     
    </div>
</template>

<script>
export default {
    data() {
        return {
            name: ''
        }
    }
}
</script>

Вы бы создали родительский элемент со слотом.

<template>
    <div>
        <slot></slot>
    </div>
</template>

Затем вы поместите его в родительский элемент с помощью директивы v-for ниже.Вам не нужно было бы myItems быть списком компонентов - просто список имен цветов строк:

<template>
    <parent-element>
        <Color v-for="item in myItems" :name="item" />
    </parent-element>
</template>


<script>
import ParentElement from './ParentElement.vue'
import Color from './Color.vue'

export default {
    components: {
        Color, ParentElement
    },
    data() {
        return {
            myItems: ['red', 'red', 'blue']
        }
    }
}

</script>

Обновлено: если все, что вам действительно нужно, это наследуемость, создайтеmixin:

var myColorMixin = {
  data() {
    return {
        name: ''
    }
  },
  computed: {
    message() {
        return 'Error, no colour specified';
    }
  }
}

Затем переопределите свойства, которые вы хотите, когда вы наследуете:

export default {
    mixins: [myColorMixin],
    computed: {
        message() {
            return `Hi, I am ${this.name}`;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...