【Перевод】 Шаблоны Vue

Vue.js
【Перевод】 Шаблоны Vue

Английский оригинал:learn-vuejs
Китайский перевод:yoyoys

Эта страница представляет собой набор полезных шаблонов реализации Vue, методов, советов и полезных справочных ссылок.

Объявление компонента

Однофайловый компонент (SFC) — наиболее распространенный

<template>
  <button class="btn-primary" @click.prevent="handleClick">
    {{text}}
  </button>
</template>

<script>
export default {
  data() {
    return {
      text: 'Click me',
    };
  },
  methods: {
    handleClick() {
      console.log('clicked');
    },
  },
}
</script>

<style scoped>
.btn-primary {
  background-color: blue;
}
</style>

Строковый шаблон (или литерал шаблона es6)

Vue.component('my-btn', {
  template: `
    <button class="btn-primary" @click.prevent="handleClick">
      {{text}}
    </button>
  `,
  data() {
    return {
      text: 'Click me',
    };
  },
  methods: {
    handleClick() {
      console.log('clicked');
    },
  },
});

Функция рендеринга

Vue.component('my-btn', {
  data() {
    return {
      text: 'Click me',
    };
  },
  methods: {
    handleClick() {
      console.log('clicked');
    },
  },
  render(h) {
    return h('button', {
      attrs: {
        class: 'btn-primary'
      },
      on: {
        click: this.handleClick,
      },
    });
  },
});

JSX

Vue.component('my-btn', {
  data() {
    return {
      text: 'Click me',
    };
  },
  methods: {
    handleClick() {
      console.log('clicked');
    },
  },
  render() {
    return (
      <button class="btn-primary" onClick={this.handleClick}>
        {{this.text}}
      </button>
    );
  },
});

vue-class-component(с использованием классов es6)

<template>
  <button class="btn-primary" @click.prevent="handleClick">
    {{text}}
  </button>
</template>

<script>
import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default MyBtn extends Vue {
  text = 'Click me';

  handleClick() {
    console.log('clicked');
  }
}
</script>

<style scoped>
.btn-primary {
  background-color: blue;
}
</style>

справочная ссылка

Условный рендеринг компонентов

Директивы (v-if / v-else / v-else-if / v-show)

v-if

<h1 v-if="true">只在 v-if 值為 true 時渲染</h1>

v-ifа такжеv-else

<h1 v-if="true">只在 v-if 值為 true 時渲染</h1>
<h1 v-else>只在 v-if 值為 false 時渲染</h1>

v-else-if

<div v-if="type === 'A'">只在 `type` 等於 `A` 時渲染</div>
<div v-else-if="type === 'B'">只在 `type` 等於 `B` 時渲染</div>
<div v-else-if="type === 'C'">只在 `type` 等於 `C` 時渲染</div>
<div v-else>只在 `type` 不等於>fmf `A` 或 `B` 或 `C` 時渲染</div>

v-show

<h1 v-show="true">永遠都會渲染,但是只在 `v-show` 值為 true 時顯示</h1>

Если вам нужно выполнить условный рендеринг для нескольких элементов одновременно, вы можете<template>используйте эти директивы для элементов (v-if / v-else / v-else-if /v-show). Уведомление:<template>Элементы на самом деле не оказывают домо.

<template v-if="true">
  <h1>所有元素</h1>
  <p>都會被渲染成為 DOM</p>
  <p>除了 `template` 元素</p>
</template>

JSX

Если вы используете JSX Vue в своем приложении, вы можете использовать все операторы javascript, например,if else,switch case, тернарная операция (ternary) и логические операторы

if elseутверждение

export default {
  data() {
    return {
      isTruthy: true,
    };
  },
  render(h) {
    if (this.isTruthy) {
      return <h1>值為真時渲染</h1>;
    } else {
      return <h1>值為假時渲染</h1>;
    }
  },
};

switch caseутверждение

import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';

export default {
  data() {
    return {
      type: 'error',
    };
  },
  render(h) {
    switch (this.type) {
      case 'info':
        return <Info text={text} />;
      case 'warning':
        return <Warning text={text} />;
      case 'error':
        return <Error text={text} />;
      default:
        return <Success text={text} />;
    },
  }
};

Вы также можете упростить сопоставление объектовswitch case

import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';

const COMPONENT_MAP = {
  info: Info,
  warning: Warning,
  error: Error,
  success: Success,
};

export default {
  data() {
    return {
      type: 'error',
    };
  },
  render(h) {
    const Comp = COMPONENT_MAP[this.type || 'success'];

    return <Comp />;
  },
};

тернарный оператор

export default {
  data() {
    return {
      isTruthy: true,
    };
  },
  render(h) {
    return (
      <div>
        {this.isTruthy ? (
          <h1>值為真時渲染</h1>
        ) : (
          <h1>值為假時渲染</h1>
        )}
      </div>
    );
  },
};

логический оператор

export default {
  data() {
    return {
      isLoading: true,
    };
  },
  render(h) {
    return <div>{this.isLoading && <h1>Loading ...</h1>}</div>;
  },
};

справочная ссылка

динамический элемент

использоватьisсобственность в<component>элемент

<component :is="currentTabComponent"></component>

Пример выше, оригинал<component>Элементы в , будут уничтожены одновременно с элементами переключения. Если вам нужно переключить и сохранить его<component>Сущность элемента посередине, не разрушаясь, может обернуть<keep-alive>маркировка следующим образом:

<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>

Комбинация компонентов

Базовый состав

<template>
  <div class="component-b">
    <component-a></component-a>
  </div>
</template>

<script>
import ComponentA from './ComponentA';

export default {
  components: {
    ComponentA,
  },
};
</script>

Расширяет

Может использоваться, когда вам нужно расширить компонент с одним файлом (SFC).

<template>
  <button class="button-primary" @click.prevent="handleClick">
    {{buttonText}}
  </button>
</template>

<script>
import BaseButton from './BaseButton';

export default {
  extends: BaseButton,
  props: ['buttonText'],
};
</script>

справочная ссылка

Миксины

// closableMixin.js
export default {
  props: {
    isOpen: {
      default: true
    }
  },
  data: function() {
    return {
      shown: this.isOpen
    }
  },
  methods: {
    hide: function() {
      this.shown = false;
    },
    show: function() {
      this.shown = true;
    },
    toggle: function() {
      this.shown = !this.shown;
    }
  }
}
<template>
  <div v-if="shown" class="alert alert-success" :class="'alert-' + type" role="alert">
    {{text}}
    <i class="pull-right glyphicon glyphicon-remove" @click="hide"></i>
  </div>
</template>

<script>
import closableMixin from './mixins/closableMixin';

export deafult {
  mixins: [closableMixin],
  props: ['text']
};
</script>

справочная ссылка

Слоты (по умолчанию)

<template>
  <button class="btn btn-primary">
    <slot></slot>
  </button>
</template>

<script>
export default {
  name: 'VBtn',
};
</script>
<template>
  <v-btn>
    <span class="fa fa-user"></span>
    Login
  </v-btn>
</template>

<script>
import VBtn from './VBtn';
  
export default {
  components: {
    VBtn,
  }
};
</script>

справочная ссылка

Именованные слоты

BaseLayout.vue

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

App.vue

<base-layout>
  <template slot="header">
    <h1>這裡是頁面標題</h1>
  </template>

  <p>一段文件主體內的文字</p>
  <p>另外一段文字</p>

  <template slot="footer">
    <p>一些聯絡資訊</p>
  </template>
</base-layout>

Слоты с ограниченной областью действия

<template>
  <ul>
    <li
      v-for="todo in todos"
      v-bind:key="todo.id"
    >
      <!-- 保留一個插槽供每一個 todo 使用,-->
      <!-- 並將 將 `todo` 物件作為插槽參數傳遞給它,供外部元件使用。-->
      <slot v-bind:todo="todo">
        {{ todo.text }}
      </slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TodoList',
  props: {
    todos: {
      type: Array,
      default: () => ([]),
    }
  },
};
</script>
<template>
  <todo-list v-bind:todos="todos">
      <template slot-scope="{ todo }">
        <span v-if="todo.isComplete">✓</span>
        {{ todo.text }}
      </template>
  </todo-list>
</template>

<script>
import TodoList from './TodoList';

export default {
  components: {
    TodoList,
  },
  data() {
    return {
      todos: [
        { todo: 'todo 1', isComplete: true },
        { todo: 'todo 2', isComplete: false },
        { todo: 'todo 3', isComplete: false },
        { todo: 'todo 4', isComplete: true },
      ];
    };
  },
};
</script>

использованная литература

Рендер реквизит

В большинстве случаев вы можете использовать Scoped Slots вместо Render Props, однако бывают ситуации, когда Render Props может быть полезен.

Для однофайловых компонентов:

<template>
  <div id="app">
    <Mouse :render="__render"/>
  </div>
</template>

<script>
import Mouse from "./Mouse.js";
export default {
  name: "app",
  components: {
    Mouse
  },
  methods: {
    __render({ x, y }) {
      return (
        <h1>
          The mouse position is ({x}, {y})
        </h1>
      );
    }
  }
};
</script>
<style>
* {
  margin: 0;
  height: 100%;
  width: 100%;
}
</style>

ВJSX

const Mouse = {
  name: "Mouse",
  props: {
    render: {
      type: Function,
      required: true
    }
  },
  data() {
    return {
      x: 0,
      y: 0
    };
  },
  methods: {
    handleMouseMove(event) {
      this.x = event.clientX;
      this.y = event.clientY;
    }
  },
  render(h) {
    return (
      <div style={{ height: "100%" }} onMousemove={this.handleMouseMove}>
        {this.$props.render(this)}
      </div>
    );
  }
};

export default Mouse;

справочная ссылка

Передача реквизита

Иногда вы хотите передать все параметры (реквизиты) и события (слушатели) дочернему элементу, но не хотите объявлять все параметры дочернего элемента. вы можете напрямую$attrsа также$listenersПривязать к дочернему элементу.

<template>
  <div>
    <h1>{{title}}</h1>
    <child-component v-bind="$attrs" v-on="$listeners"></child-component>
  </div>
</template>

<script>
export default {
  name: 'PassingPropsSample'
  props: {
    title: {
      type: String,
      default: 'Hello, Vue!'
    }
  }
};
</script>

В родительском элементе вы можете сделать это:

<template>
  <passing-props-sample
    title="Hello, Passing Props"
    childPropA="This props will properly mapped to <child-component />"
    @click="handleChildComponentClick"
  >
  </passing-props-sample>
</template>

<script>
import PassingPropsSample from './PassingPropsSample';

export default {
  components: {
    PassingPropsSample
  },
  methods: {
    handleChildComponentClick() {
      console.log('child component clicked');
    }
  }
};
</script>

использованная литература

Компонент высшего порядка (HOC)

справочная ссылка

Внедрение зависимости

Поддержка Vueпоставкаа такжеинъекция(Предоставить/внедрить) механизм для передачи объекта всем дочерним компонентам, независимо от того, насколько глубока структура, если они основаны на одном и том же родительском компоненте. Уведомление:provideа такжеinjectНет реактивной способности, если только объект, который вы передаете, не является реактивным по своей природе.

<parent-component>  // 父元件
  <child-component>  // 子元件
    <grand-child-component></grand-child-component>  // 孫元件
  </child-component>
</ancestor-component>

Вышеупомянутая структура компонентов, если вы хотите от父元件Для получения данных необходимо передать параметр (props) передать данные子元件а также孫元件среди. но если父元件 поставка (provide) данные (или объекты),孫元件непосредственно, объявивинъекция (inject) 父元件Данные (или объекты), определенные в .

справочная ссылка

поставкаа такжеинъекция (Provide / Inject)

// ParentComponent.vue

export default {
  provide: {
    theme: {
      primaryColor: 'blue',
    },
  },
};
// GrandChildComponent.vue

<template>
  <button :style="{ backgroundColor: primary && theme.primaryColor }">
    <slot></slot>
  </button>
</template>

<script>
export default {
  inject: ['theme'],
  props: {
    primary: {
      type: Boolean,
      default: true,
    },
  },
};
</script>

Внедрить шаблон декоратора (@Provide / @Inject Decorator)

// ParentComponent.vue

import { Component, Vue, Provide } from 'vue-property-decorator';

@Component
export class ParentComponent extends Vue {
  @Provide
  theme = {
    primaryColor: 'blue',
  };
}
// GrandChildComponent.vue

<template>
  <button :style="{ backgroundColor: primary && theme.primaryColor }">
    <slot></slot>
  </button>
</template>

<script>
import { Component, Vue, Inject, Prop } from 'vue-property-decorator';

export class GrandChildComponent extends Vue {
  @Inject() theme;

  @Prop({ default: true })
  primary: boolean;
};
</script>

Обработка ошибок

errorCapturedмероприятие

export default {
  name: 'ErrorBoundary',
  data() {
    return {
      error: false,
      errorMessage: '',
    };
  },
  errorCaptured (err, vm, info) {
    this.error = true;
    this.errorMessage = `${err.stack}\n\nfound in ${info} of component`;
    
    return false;
  },
  render (h) {
    if (this.error) {
      return h('pre', { style: { color: 'red' }}, this.errorMessage);
    }

    return this.$slots.default[0]
  }
};
<error-boundary>
  <another-component/>
</error-boundary>
скопировать код

пример

справочная ссылка

Советы по продуктивности

Сделать слушателя активным в созданном событии

// 不要這樣做
created() {
  this.fetchUserList();
},
watch: {
  searchText: 'fetchUserList',
}
// 這樣做
watch: {
  searchText: {
    handler: 'fetchUserList',
    immediate: true,
  }
}

Полезные ссылки

общение между группами

Советы по рефакторингу

Vuex

Mobx

Компонент без рендеринга

пример

Структура каталогов

Советы

Пример проекта

Плохая демонстрация (анти-шаблон)

Видео и аудио уроки

Платные курсы


Дополнительная информация