英文原版:learn-vuejs
繁體中文翻譯:yoyoys
簡體中文翻譯:ZYSzys
此頁面集結了許多有用的 Vue 實作模式、技術、技巧、以及有幫助的參考連結。
- Vue 實作模式 (learn-vuejs) 中文版
<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>
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,
},
});
},
});
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 classes)
<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>
基本上,Vue 元件是依照單向資料流傳遞資料,這就是屬性進、事件出(props down, event up)請參考官方說明)。
(譯註:也有稱作props in, emit out。)
屬性(props)是唯讀的資料,所以不可能從子元件內部修改他的值,若是外部傳入的屬性變化了,子元件將會自動重繪(因為屬性是反應性資料)。
子元件只能將事件發送到父代,藉此父代可以修改資料(data
) ,這個資料也對應到子元件的屬性(props
)。
<template>
<button @click="$emit('click')">{{text}}</button>
</template>
<script>
export default {
name: 'v-btn',
props: {
text: String,
},
};
</script>
<template>
<v-btn :text="buttonText" @click="handleClick"></v-btn>
</template>
<script>
export default {
data() {
return {
clickCount: 0,
buttonText: 'initial button text',
};
},
methods: {
handleClick() {
this.buttonText = `Button clicked ${++this.clickCount}`;
console.log('clicked', this.buttonText);
}
}
};
</script>
- Vue.js Component Communication Patterns
- Creating Custom Inputs With Vue.js
- Vue Sibling Component Communication
- Managing State in Vue.js
- Leveraging Vue events to reduce prop declarations
- Vue.js Component Hooks as Events
- Creating a Global Event Bus with Vue.js
- Vue.js Event Bus + Promises
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>
元素不會實際渲染一個 DOM。
<template v-if="true">
<h1>所有元素</h1>
<p>都會被渲染成為 DOM</p>
<p>除了 `template` 元素</p>
</template>
如果你在你的 Vue 應用程式中使用 JSX,你可以使用所有 javascript 語句,例如 if else
、 switch case
、三元運算 (ternary
) 與 邏輯運算式 (logical operator)
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 />;
},
};
三元運算子 (ternary operator)
export default {
data() {
return {
isTruthy: true,
};
},
render(h) {
return (
<div>
{this.isTruthy ? (
<h1>值為真時渲染</h1>
) : (
<h1>值為假時渲染</h1>
)}
</div>
);
},
};
邏輯運算子 (logical operator)
export default {
data() {
return {
isLoading: true,
};
},
render(h) {
return <div>{this.isLoading && <h1>Loading ...</h1>}</div>;
},
};
<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>
<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 default {
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>
- Understanding Component Slots with Vue.js
- Composing Custom Elements With Slots And Named Slots
- Writing Abstract Components with Vue.js
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>
- Getting Your Head Around Vue.js Scoped Slots
- Understanding scoped slots in Vue.js
- Scoped Component Slots in Vue.js
- The Trick to Understanding Scoped Slots in Vue.js
- The Power of Scoped Slots in Vue
大多數狀況下,你可以優先使用作用域插槽 (Scoped Slots) 之於渲染屬性 (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;
有時候你想要傳遞所有參數 (props) 與事件 (listeners) 到子元件,但又不想要宣告所有子元件的參數。
你可以直接將 $attrs
與 $listeners
綁定在子元件上,並設定 inheritAttrs
為 false
(若不設定後者,div
元素也會收到這些屬性)。
<template>
<div>
<h1>{{title}}</h1>
<child-component v-bind="$attrs" v-on="$listeners"></child-component>
</div>
</template>
<script>
export default {
name: 'PassingPropsSample'
inheritAttrs: false,
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>
- Higher Order Components in Vue.js
- Do we need Higher Order Components in Vue.js?
- Higher-Order Components in Vue.js
Vue 支援 提供 與 注入 (Provide / inject) 機制來傳遞一個物件到所有子代元件中,不管結構有多深,只要都基於同一個父代即可。
注意: provide
和 inject
並沒有響應能力 (reactive) ,除非你傳遞的物件本身就帶有響應能力。
<parent-component> // 父元件
<child-component> // 子元件
<grand-child-component></grand-child-component> // 孫元件
</child-component>
</parent-component>
上述的元件結構,若要從 父元件
取得資料,你必須要透過 參數(props
) 傳遞資料到 子元件
與 孫元件
之中。
但如果 父元件
提供 (provide
) 資料(或物件), 孫元件
可以透過宣告直接 注入 (inject
) 父元件
中所定義的資料(或物件)。
- Official API
- Official Guide
- Component Communication
- Dependency Injection in Vue.js App with TypeScript
// 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>
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 事件時就有效
// 不要這樣做
created() {
this.fetchUserList();
},
watch: {
searchText: 'fetchUserList',
}
// 這樣做
watch: {
searchText: {
handler: 'fetchUserList',
immediate: true,
}
}
- Refactoring Vue: Cleaning Up a List of Posts With Better Component Splitting and More ES6
- Clean up your Vue modules with ES6 Arrow Functions
- Examples of Vue’s Clean Code
- Optimizing Performance with Computed Properties
- Decouple Vuex modules with the Mediator pattern
- Vuex getters are great, but don’t overuse them
- Reusable Vuex Mutation Functions
- A pattern to handle ajax requests in Vuex
- [vuex Mutations] Single Changes vs. Single Responsibility
- Components and How They Interact in Vue and Vuex
- Why VueX Is The Perfect Interface Between Frontend and API
- Composing actions with Vuex
- How to Build Complex, Large-Scale Vue.js Apps With Vuex
- Should I Store This Data in Vuex?
- How you can improve your workflow using the JavaScript console
- How to Structure a Vue.js Project
- Large-scale Vuex application structures
- Vue.js Application Structure and CSS Architecture
- How To Build Vue Components Like A Pro 😎
- Four tips for working with Vue.js
- Tips from a Lowly VueJS Developer
- Throttling and Debouncing Events with Vue.js and lodash
- Are partially applied functions in event handlers possible?
- Vue.js — Considerations and Tricks
- Six random issues and their solutions in VueJS.
- When VueJS Can't Help You
- Things that won’t work using Vue
- Tip#15 Delay execution with _.debounce
- Chris Fritz - Vue.js Anti-Patterns (and How to Avoid Them)
- Common mistakes to avoid while working with Vue.js
- Avoid This Common Anti-Pattern In Full-Stack Vue/Laravel Apps
- [Video] - VueNYC - Three Vue code smells, and what you can do about them - Matt Rothenberg (@mattrothenberg)
- 81: Evan You - Advanced Vue Component Design
- 7 Secret Patterns Vue Consultants Don’t Want You to Know
- Vue + TypeScript: A Match Made in Your Code Editor
- Writing Class-Based Components with Vue.js and TypeScript
- Creating an Interpose Vue component from a React implementation
- Composing computed properties in Vue.js
- 4 AJAX Patterns For Vue.js Apps
- 3 Code Splitting Patterns For VueJS and Webpack
- The easiest way to improve your Vue.js application. Part 1
- Using JSX with Vue and Why You Should Care
- Compound components
- Creating Multi-root Vue.js Components
- Understanding Vue.js Reactivity in Depth with Object.defineProperty()
- Templating in Vue: Separation of Concerns or Separation of Technology or something else?
- Stashing Vue components data
- Creating Reusable Transitions in Vue
- vue-advanced-workshop
- Do it with Elegance: How to Create Data-Driven User Interfaces in Vue
- Creating Vue.js Component Instances Programmatically
- Managing User Permissions in a Vue.js App
- Render Functional Components in Vue.js
- Looping through Object Properties
- Cancelling async operations in Vue.js
- Scoped styles with v-html
- Pagination With Vuejs
- What does the ‘h’ stand for in Vue’s render method?
- How To Build Vue Components That Play Nice
- Making responsive Vue components with ResizeObserver
- An imperative guide to forms in Vue.js
- Vue.js: the good, the meh, and the ugly
- Dynamic Vue.js Layout Components
- Advanced Vue.js concepts: mixins, custom directives, filters, transitions, and state management
- Introducing the Single Element Pattern
- Control DOM Outside Your Vue.js App with portal-vue
- Add i18n and manage translations of a Vue.js powered website