├── LICENSE
├── README.md
└── generate-toc.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ilkwon Sim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue 實作模式 (vue-patterns) 中文版
2 |
3 | 英文原版:[learn-vuejs](https://github.com/learn-vuejs/vue-patterns)
4 | 繁體中文翻譯:[yoyoys](https://github.com/yoyoys/vue-patterns-cht)
5 | 簡體中文翻譯:[ZYSzys](https://github.com/ZYSzys/vue-patterns-cn)
6 |
7 | 此頁面集結了許多有用的 Vue 實作模式、技術、技巧、以及有幫助的參考連結。
8 |
9 | - [Vue 實作模式 (learn-vuejs) 中文版](#vue-%E5%AF%A6%E4%BD%9C%E6%A8%A1%E5%BC%8F-learn-vuejs-%E4%B8%AD%E6%96%87%E7%89%88)
10 | - [元件宣告](#%E5%85%83%E4%BB%B6%E5%AE%A3%E5%91%8A)
11 | - [單文件組件(Single File Component, SFC) - 最為常見](#%E5%96%AE%E6%96%87%E4%BB%B6%E7%B5%84%E4%BB%B6single-file-component-sfc---%E6%9C%80%E7%82%BA%E5%B8%B8%E8%A6%8B)
12 | - [字串樣板 (String Template) (或是 es6 樣板字面值 (Template Literal)))](#%E5%AD%97%E4%B8%B2%E6%A8%A3%E6%9D%BF-string-template-%E6%88%96%E6%98%AF-es6-%E6%A8%A3%E6%9D%BF%E5%AD%97%E9%9D%A2%E5%80%BC-template-literal)
13 | - [渲染函式 (Render Function)](#%E6%B8%B2%E6%9F%93%E5%87%BD%E5%BC%8F-render-function)
14 | - [JSX](#jsx)
15 | - [vue-class-component (使用 es6 classes)](#vue-class-component-%E4%BD%BF%E7%94%A8-es6-classes)
16 | - [參考連結](#%E5%8F%83%E8%80%83%E9%80%A3%E7%B5%90)
17 | - [元件之間的溝通(Component Communication)](#%E5%85%83%E4%BB%B6%E4%B9%8B%E9%96%93%E7%9A%84%E6%BA%9D%E9%80%9Acomponent-communication)
18 | - [屬性與事件(Props and Events)](#%E5%B1%AC%E6%80%A7%E8%88%87%E4%BA%8B%E4%BB%B6props-and-events)
19 | - [元件事件處理方法(Component Events Handling)](#%E5%85%83%E4%BB%B6%E4%BA%8B%E4%BB%B6%E8%99%95%E7%90%86%E6%96%B9%E6%B3%95component-events-handling)
20 | - [元件條件渲染 (Component Conditional Rendering)](#%E5%85%83%E4%BB%B6%E6%A2%9D%E4%BB%B6%E6%B8%B2%E6%9F%93-component-conditional-rendering)
21 | - [指令 (Directives) (`v-if` / `v-else` / `v-else-if` / `v-show`)](#%E6%8C%87%E4%BB%A4-directives-v-if--v-else--v-else-if--v-show)
22 | - [JSX](#jsx)
23 | - [動態元件](#%E5%8B%95%E6%85%8B%E5%85%83%E4%BB%B6)
24 | - [元件組合](#%E5%85%83%E4%BB%B6%E7%B5%84%E5%90%88)
25 | - [基本組合 (Basic Composition)](#%E5%9F%BA%E6%9C%AC%E7%B5%84%E5%90%88-basic-composition)
26 | - [繼承 (Extends)](#%E7%B9%BC%E6%89%BF-extends)
27 | - [混入 (Mixins)](#%E6%B7%B7%E5%85%A5-mixins)
28 | - [預設插槽 (Slots (Default))](#%E9%A0%90%E8%A8%AD%E6%8F%92%E6%A7%BD-slots-default)
29 | - [具名插槽(Named Slots)](#%E5%85%B7%E5%90%8D%E6%8F%92%E6%A7%BDnamed-slots)
30 | - [作用域插槽 (Scoped Slots)](#%E4%BD%9C%E7%94%A8%E5%9F%9F%E6%8F%92%E6%A7%BD-scoped-slots)
31 | - [渲染屬性 (Render Props)](#%E6%B8%B2%E6%9F%93%E5%B1%AC%E6%80%A7-render-props)
32 | - [參數傳遞 (Passing Props)](#%E5%8F%83%E6%95%B8%E5%82%B3%E9%81%9E-passing-props)
33 | - [高優先元件 (Higher Order Component, HOC)](#%E9%AB%98%E5%84%AA%E5%85%88%E5%85%83%E4%BB%B6-higher-order-component-hoc)
34 | - [相依注入 (Dependency injection)](#%E7%9B%B8%E4%BE%9D%E6%B3%A8%E5%85%A5-dependency-injection)
35 | - [**提供** 與 **注入** (Provide / Inject)](#%E6%8F%90%E4%BE%9B-%E8%88%87-%E6%B3%A8%E5%85%A5-provide--inject)
36 | - [注入裝飾器模式 (@Provide / @Inject Decorator)](#%E6%B3%A8%E5%85%A5%E8%A3%9D%E9%A3%BE%E5%99%A8%E6%A8%A1%E5%BC%8F-provide--inject-decorator)
37 | - [錯誤處理 (Handling Errors)](#%E9%8C%AF%E8%AA%A4%E8%99%95%E7%90%86-handling-errors)
38 | - [`errorCaptured` 事件](#errorcaptured-%E4%BA%8B%E4%BB%B6)
39 | - [生產力小技巧](#%E7%94%9F%E7%94%A2%E5%8A%9B%E5%B0%8F%E6%8A%80%E5%B7%A7)
40 | - [有用的連結](#%E6%9C%89%E7%94%A8%E7%9A%84%E9%80%A3%E7%B5%90)
41 | - [重構技巧](#%E9%87%8D%E6%A7%8B%E6%8A%80%E5%B7%A7)
42 | - [狀態管理](#%E7%8B%80%E6%85%8B%E7%AE%A1%E7%90%86)
43 | - [Vuex](#vuex)
44 | - [Mobx](#mobx)
45 | - [不須渲染的元件 (Renderless Component)](#%E4%B8%8D%E9%A0%88%E6%B8%B2%E6%9F%93%E7%9A%84%E5%85%83%E4%BB%B6-renderless-component)
46 | - [目錄結構](#%E7%9B%AE%E9%8C%84%E7%B5%90%E6%A7%8B)
47 | - [小技巧](#%E5%B0%8F%E6%8A%80%E5%B7%A7)
48 | - [路由(Router)](#%E8%B7%AF%E7%94%B1Router)
49 | - [不良示範 (反模式)](#%E4%B8%8D%E8%89%AF%E7%A4%BA%E7%AF%84-%E5%8F%8D%E6%A8%A1%E5%BC%8F)
50 | - [影片與音訊課程](#%E5%BD%B1%E7%89%87%E8%88%87%E9%9F%B3%E8%A8%8A%E8%AA%B2%E7%A8%8B)
51 | - [專案範例](#%E5%B0%88%E6%A1%88%E7%AF%84%E4%BE%8B)
52 | - [付費課程](#%E4%BB%98%E8%B2%BB%E8%AA%B2%E7%A8%8B)
53 | - [Typescript](#typescript)
54 | - [Flowtype](#flowtype)
55 | - [GraphQL](#graphql)
56 | - [其他資訊](#%E5%85%B6%E4%BB%96%E8%B3%87%E8%A8%8A)
57 |
58 | ## 元件宣告
59 |
60 | ### 單文件組件(Single File Component, SFC) - 最為常見
61 |
62 | ```html
63 |
64 |
67 |
68 |
69 |
83 |
84 |
89 | ```
90 |
91 | ### 字串樣板 (String Template) (或是 es6 樣板字面值 (Template Literal))
92 |
93 | ```js
94 | Vue.component('my-btn', {
95 | template: `
96 |
99 | `,
100 | data() {
101 | return {
102 | text: 'Click me',
103 | };
104 | },
105 | methods: {
106 | handleClick() {
107 | console.log('clicked');
108 | },
109 | },
110 | });
111 | ```
112 |
113 | ### 渲染函式 (Render Function)
114 |
115 | ```js
116 | Vue.component('my-btn', {
117 | data() {
118 | return {
119 | text: 'Click me',
120 | };
121 | },
122 | methods: {
123 | handleClick() {
124 | console.log('clicked');
125 | },
126 | },
127 | render(h) {
128 | return h('button', {
129 | attrs: {
130 | class: 'btn-primary'
131 | },
132 | on: {
133 | click: this.handleClick,
134 | },
135 | });
136 | },
137 | });
138 | ```
139 |
140 | ### JSX
141 |
142 | ```jsx
143 | Vue.component('my-btn', {
144 | data() {
145 | return {
146 | text: 'Click me',
147 | };
148 | },
149 | methods: {
150 | handleClick() {
151 | console.log('clicked');
152 | },
153 | },
154 | render() {
155 | return (
156 |
159 | );
160 | },
161 | });
162 | ```
163 |
164 | ### [vue-class-component](https://github.com/vuejs/vue-class-component) (使用 es6 classes)
165 |
166 | ```html
167 |
168 |
171 |
172 |
173 |
186 |
187 |
192 | ```
193 |
194 | #### 參考連結
195 |
196 | * [7 Ways To Define A Component Template in VueJS](https://medium.com/js-dojo/7-ways-to-define-a-component-template-in-vuejs-c04e0c72900d)
197 |
198 | ## 元件之間的溝通(Component Communication)
199 |
200 | ### 屬性與事件(Props and Events)
201 |
202 | 基本上,Vue 元件是依照單向資料流傳遞資料,這就是屬性進、事件出(props down, event up)[請參考官方說明](https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow))。
203 | (譯註:也有稱作props in, emit out。)
204 | 屬性(props)是唯讀的資料,所以不可能從子元件內部修改他的值,若是外部傳入的屬性變化了,子元件將會自動重繪(因為屬性是反應性資料)。
205 | 子元件只能將事件發送到父代,藉此父代可以修改資料(`data`) ,這個資料也對應到子元件的屬性(`props`)。
206 |
207 | ```html
208 |
209 |
210 |
211 |
212 |
220 | ```
221 |
222 | ```html
223 |
224 |
225 |
226 |
227 |
243 | ```
244 |
245 | #### 參考連結:
246 |
247 | * [Vue.js Component Communication Patterns](https://alligator.io/vuejs/component-communication/)
248 | * [Creating Custom Inputs With Vue.js](https://www.smashingmagazine.com/2017/08/creating-custom-inputs-vue-js/)
249 | * [Vue Sibling Component Communication](https://vegibit.com/vue-sibling-component-communication/)
250 | * [Managing State in Vue.js](https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87)
251 |
252 | ## 元件事件處理方法(Component Events Handling)
253 |
254 | #### 參考連結:
255 |
256 | * [Leveraging Vue events to reduce prop declarations](https://itnext.io/leveraging-vue-events-to-reduce-prop-declarations-e38f5dce2aaf)
257 | * [Vue.js Component Hooks as Events](https://alligator.io/vuejs/component-event-hooks/)
258 | * [Creating a Global Event Bus with Vue.js](https://alligator.io/vuejs/global-event-bus/)
259 | * [Vue.js Event Bus + Promises](https://medium.com/@jesusgalvan/vue-js-event-bus-promises-f83e73a81d72)
260 |
261 | ## 元件條件渲染 (Component Conditional Rendering)
262 |
263 | ### 指令 (Directives) (`v-if` / `v-else` / `v-else-if` / `v-show`)
264 |
265 | `v-if`
266 |
267 | ```html
268 |
只在 v-if 值為 true 時渲染
269 | ```
270 |
271 | `v-if` 與 `v-else`
272 |
273 | ```html
274 | 只在 v-if 值為 true 時渲染
275 | 只在 v-if 值為 false 時渲染
276 | ```
277 |
278 | `v-else-if`
279 |
280 | ```html
281 | 只在 `type` 等於 `A` 時渲染
282 | 只在 `type` 等於 `B` 時渲染
283 | 只在 `type` 等於 `C` 時渲染
284 | 只在 `type` 不等於>fmf `A` 或 `B` 或 `C` 時渲染
285 | ```
286 |
287 | `v-show`
288 |
289 | ```html
290 | 永遠都會渲染,但是只在 `v-show` 值為 true 時顯示
291 | ```
292 |
293 | 如果你需要同時在多個元素上面做條件式渲染,你可以在 `` 元素上使用這些指令 (`v-if` / `v-else` / `v-else-if` /`v-show`)。
294 | 注意:`` 元素不會實際渲染一個 DOM。
295 |
296 | ```html
297 |
298 | 所有元素
299 | 都會被渲染成為 DOM
300 | 除了 `template` 元素
301 |
302 | ```
303 |
304 | ### JSX
305 |
306 | 如果你在你的 Vue 應用程式中使用 JSX,你可以使用所有 javascript 語句,例如 `if else` 、 `switch case` 、三元運算 (`ternary`) 與 邏輯運算式 (logical operator)
307 |
308 | `if else` 語句
309 |
310 | ```jsx
311 | export default {
312 | data() {
313 | return {
314 | isTruthy: true,
315 | };
316 | },
317 | render(h) {
318 | if (this.isTruthy) {
319 | return 值為真時渲染
;
320 | } else {
321 | return 值為假時渲染
;
322 | }
323 | },
324 | };
325 | ```
326 |
327 | `switch case` 語句
328 |
329 | ```jsx
330 | import Info from './Info';
331 | import Warning from './Warning';
332 | import Error from './Error';
333 | import Success from './Success';
334 |
335 | export default {
336 | data() {
337 | return {
338 | type: 'error',
339 | };
340 | },
341 | render(h) {
342 | switch (this.type) {
343 | case 'info':
344 | return ;
345 | case 'warning':
346 | return ;
347 | case 'error':
348 | return ;
349 | default:
350 | return ;
351 | },
352 | }
353 | };
354 | ```
355 |
356 | 你也可以透過物件的對應來簡化 `switch case`
357 |
358 | ```jsx
359 | import Info from './Info';
360 | import Warning from './Warning';
361 | import Error from './Error';
362 | import Success from './Success';
363 |
364 | const COMPONENT_MAP = {
365 | info: Info,
366 | warning: Warning,
367 | error: Error,
368 | success: Success,
369 | };
370 |
371 | export default {
372 | data() {
373 | return {
374 | type: 'error',
375 | };
376 | },
377 | render(h) {
378 | const Comp = COMPONENT_MAP[this.type || 'success'];
379 |
380 | return ;
381 | },
382 | };
383 | ```
384 |
385 | 三元運算子 (ternary operator)
386 |
387 | ```jsx
388 | export default {
389 | data() {
390 | return {
391 | isTruthy: true,
392 | };
393 | },
394 | render(h) {
395 | return (
396 |
397 | {this.isTruthy ? (
398 |
值為真時渲染
399 | ) : (
400 | 值為假時渲染
401 | )}
402 |
403 | );
404 | },
405 | };
406 | ```
407 |
408 | 邏輯運算子 (logical operator)
409 |
410 | ```jsx
411 | export default {
412 | data() {
413 | return {
414 | isLoading: true,
415 | };
416 | },
417 | render(h) {
418 | return {this.isLoading &&
Loading ...
};
419 | },
420 | };
421 | ```
422 | #### 參考連結
423 |
424 | * [Conditional Rendering](https://vuejs.org/v2/guide/conditional.html)
425 | * [Difference Between v-if and v-show [With Video at End]](https://dzone.com/articles/difference-between-v-if-and-v-show-with-a-video)
426 |
427 | ## 動態元件
428 |
429 | ### 使用 `is` 屬性在 `` 元素上
430 |
431 | * [範例 1](https://jsfiddle.net/chrisvfritz/o3nycadu/)
432 | * [範例 2](https://jsfiddle.net/chrisvfritz/b2qj69o1/)
433 | * [範例 3](https://alligator.io/vuejs/dynamic-components/)
434 |
435 | ```html
436 |
437 | ```
438 |
439 | 上面的範例,原有 `` 中的元件,在切換元件的同時將會被消滅。
440 | 如果你需要切換後仍保留 `` 中元件的實體,而不被消滅的話,可以包裹一個 `` 標籤,如下:
441 |
442 | ```html
443 |
444 |
445 |
446 | ```
447 | #### 參考連結
448 |
449 | * [Dynamic Component Templates with Vue.js](https://medium.com/scrumpy/dynamic-component-templates-with-vue-js-d9236ab183bb)
450 |
451 | #### 元件庫
452 |
453 | * [Proppy - Functional props composition for components](https://proppyjs.com/)
454 |
455 | ## 元件組合
456 |
457 | ### 基本組合 (Basic Composition)
458 |
459 | ```html
460 |
461 |
462 |
463 |
464 |
465 |
466 |
475 | ```
476 |
477 | #### 參考連結
478 |
479 | * [Official - Composing with Components](https://vuejs.org/v2/guide/#Composing-with-Components)
480 |
481 |
482 | ### 繼承 (Extends)
483 | ```html
484 |
485 |
488 |
489 |
490 |
498 | ```
499 |
500 | #### 參考連結
501 |
502 | * [Official - extends](https://vuejs.org/v2/api/#extends)
503 | * [Extending VueJS Components](https://medium.com/js-dojo/extending-vuejs-components-42fefefc688b)
504 |
505 | ### 混入 (Mixins)
506 |
507 | ```js
508 | // closableMixin.js
509 | export default {
510 | props: {
511 | isOpen: {
512 | default: true
513 | }
514 | },
515 | data: function() {
516 | return {
517 | shown: this.isOpen
518 | }
519 | },
520 | methods: {
521 | hide: function() {
522 | this.shown = false;
523 | },
524 | show: function() {
525 | this.shown = true;
526 | },
527 | toggle: function() {
528 | this.shown = !this.shown;
529 | }
530 | }
531 | }
532 | ```
533 |
534 | ```html
535 |
536 |
537 | {{text}}
538 |
539 |
540 |
541 |
542 |
550 | ```
551 |
552 | #### 參考連結
553 |
554 | * [Official - mixins](https://vuejs.org/v2/guide/mixins.html)
555 | * [Practical use of Components and Mixins in Vue JS](http://www.qcode.in/practical-use-of-components-and-mixins-in-vue-js/)
556 |
557 |
558 | ### 預設插槽 (Slots (Default))
559 |
560 | ```html
561 |
562 |
565 |
566 |
567 |
572 | ```
573 |
574 | ```html
575 |
576 |
577 |
578 | Login
579 |
580 |
581 |
582 |
591 | ```
592 |
593 | #### 參考連結
594 |
595 | * [Understanding Component Slots with Vue.js](https://alligator.io/vuejs/component-slots/)
596 | * [Composing Custom Elements With Slots And Named Slots](https://alligator.io/web-components/composing-slots-named-slots/)
597 | * [Writing Abstract Components with Vue.js](https://alligator.io/vuejs/vue-abstract-components/)
598 |
599 | ### 具名插槽(Named Slots)
600 |
601 | BaseLayout.vue
602 |
603 | ```html
604 |
605 |
608 |
609 |
610 |
611 |
614 |
615 | ```
616 |
617 | App.vue
618 |
619 | ```html
620 |
621 |
622 | 這裡是頁面標題
623 |
624 |
625 | 一段文件主體內的文字
626 | 另外一段文字
627 |
628 |
629 | 一些聯絡資訊
630 |
631 |
632 | ```
633 |
634 | ### 作用域插槽 (Scoped Slots)
635 |
636 | ```html
637 |
638 |
639 | -
643 |
644 |
645 |
646 | {{ todo.text }}
647 |
648 |
649 |
650 |
651 |
652 |
663 | ```
664 |
665 | ```html
666 |
667 |
668 |
669 | ✓
670 | {{ todo.text }}
671 |
672 |
673 |
674 |
675 |
694 | ```
695 |
696 | #### 參考資料
697 |
698 | * [Getting Your Head Around Vue.js Scoped Slots](https://medium.com/js-dojo/getting-your-head-around-vue-js-scoped-slots-281bf82a1e4e)
699 | * [Understanding scoped slots in Vue.js](https://medium.com/corebuild-software/understanding-scoped-slots-in-vue-js-db5315a42391)
700 | * [Scoped Component Slots in Vue.js](https://alligator.io/vuejs/scoped-component-slots/)
701 | * [The Trick to Understanding Scoped Slots in Vue.js](https://adamwathan.me/the-trick-to-understanding-scoped-slots-in-vuejs/)
702 | * [The Power of Scoped Slots in Vue](https://pineco.de/power-scoped-slots-vue/)
703 |
704 | ### 渲染屬性 (Render Props)
705 |
706 | 大多數狀況下,你可以優先使用作用域插槽 (Scoped Slots) 之於渲染屬性 (Render Props),但是,在某些狀況下渲染屬性還是很有用的。
707 |
708 | 於單文件組件:
709 |
710 | ```html
711 |
712 |
713 |
714 |
715 |
716 |
717 |
735 |
742 | ```
743 |
744 | 於 `JSX`
745 |
746 | ```js
747 | const Mouse = {
748 | name: "Mouse",
749 | props: {
750 | render: {
751 | type: Function,
752 | required: true
753 | }
754 | },
755 | data() {
756 | return {
757 | x: 0,
758 | y: 0
759 | };
760 | },
761 | methods: {
762 | handleMouseMove(event) {
763 | this.x = event.clientX;
764 | this.y = event.clientY;
765 | }
766 | },
767 | render(h) {
768 | return (
769 |
770 | {this.$props.render(this)}
771 |
772 | );
773 | }
774 | };
775 |
776 | export default Mouse;
777 | ```
778 |
779 | #### 參考連結
780 |
781 | * [Leveraging Render Props in Vue](https://medium.com/@dillonchanis/leveraging-render-props-in-vue-7eb9a19c262d)
782 | * [Use a Vue.js Render Prop!](https://medium.com/js-dojo/use-a-vue-js-render-prop-98880bc44e05)
783 |
784 | ## 參數傳遞 (Passing Props)
785 |
786 | 有時候你想要傳遞所有參數 (props) 與事件 (listeners) 到子元件,但又不想要宣告所有子元件的參數。
787 | 你可以直接將 `$attrs` 與 `$listeners` 綁定在子元件上,並設定 `inheritAttrs` 為 `false` (若不設定後者,`div` 元素也會收到這些屬性)。
788 |
789 | ```html
790 |
791 |
792 |
{{title}}
793 |
794 |
795 |
796 |
797 |
809 | ```
810 |
811 | 在父元件上,你可以這樣做:
812 | ```html
813 |
814 |
819 |
820 |
821 |
822 |
836 | ```
837 |
838 | #### 參考資料
839 |
840 | * [Transparent Wrapper Components in Vue](https://zendev.com/2018/05/31/transparent-wrapper-components-in-vue.html)
841 |
842 | ## 高優先元件 (Higher Order Component, HOC)
843 |
844 | #### 參考連結
845 |
846 | * [Higher Order Components in Vue.js](https://medium.com/bethink-pl/higher-order-components-in-vue-js-a79951ac9176)
847 | * [Do we need Higher Order Components in Vue.js?](https://medium.com/bethink-pl/do-we-need-higher-order-components-in-vue-js-87c0aa608f48)
848 | * [Higher-Order Components in Vue.js](https://medium.com/tldr-tech/higher-order-components-in-vue-js-38b500c6d49f)
849 |
850 | ## 相依注入 (Dependency injection)
851 |
852 | Vue 支援 **提供** 與 **注入** (Provide / inject) 機制來傳遞一個物件到所有子代元件中,不管結構有多深,只要都基於同一個父代即可。
853 | 注意: `provide` 和 `inject` 並沒有響應能力 (reactive) ,除非你傳遞的物件本身就帶有響應能力。
854 |
855 | ```html
856 | // 父元件
857 | // 子元件
858 | // 孫元件
859 |
860 |
861 | ```
862 |
863 | 上述的元件結構,若要從 `父元件` 取得資料,你必須要透過 參數(`props`) 傳遞資料到 `子元件` 與 `孫元件` 之中。
864 | 但如果 `父元件` **提供** (`provide`) 資料(或物件), `孫元件` 可以透過宣告直接 **注入** (`inject`) `父元件` 中所定義的資料(或物件)。
865 |
866 |
867 | #### 參考連結
868 |
869 | * [Official API](https://vuejs.org/v2/api/#provide-inject)
870 | * [Official Guide](https://vuejs.org/v2/guide/components-edge-cases.html#Dependency-Injection)
871 | * [Component Communication](https://alligator.io/vuejs/component-communication/#provide--inject)
872 | * [Dependency Injection in Vue.js App with TypeScript](https://blog.kloud.com.au/2017/03/22/dependency-injection-in-vuejs-app-with-typescript/)
873 |
874 | ### **提供** 與 **注入** (Provide / Inject)
875 |
876 | ```js
877 | // ParentComponent.vue
878 |
879 | export default {
880 | provide: {
881 | theme: {
882 | primaryColor: 'blue',
883 | },
884 | },
885 | };
886 | ```
887 |
888 | ```html
889 | // GrandChildComponent.vue
890 |
891 |
892 |
895 |
896 |
897 |
908 | ```
909 |
910 | ### 注入裝飾器模式 ([@Provide / @Inject Decorator](https://github.com/kaorun343/vue-property-decorator))
911 |
912 | ```js
913 | // ParentComponent.vue
914 |
915 | import { Component, Vue, Provide } from 'vue-property-decorator';
916 |
917 | @Component
918 | export class ParentComponent extends Vue {
919 | @Provide
920 | theme = {
921 | primaryColor: 'blue',
922 | };
923 | }
924 | ```
925 |
926 | ```html
927 | // GrandChildComponent.vue
928 |
929 |
930 |
933 |
934 |
935 |
945 | ```
946 |
947 | ## 錯誤處理 (Handling Errors)
948 |
949 | ### `errorCaptured` 事件
950 |
951 | ```js
952 | export default {
953 | name: 'ErrorBoundary',
954 | data() {
955 | return {
956 | error: false,
957 | errorMessage: '',
958 | };
959 | },
960 | errorCaptured (err, vm, info) {
961 | this.error = true;
962 | this.errorMessage = `${err.stack}\n\nfound in ${info} of component`;
963 |
964 | return false;
965 | },
966 | render (h) {
967 | if (this.error) {
968 | return h('pre', { style: { color: 'red' }}, this.errorMessage);
969 | }
970 |
971 | return this.$slots.default[0]
972 | }
973 | };
974 | ```
975 |
976 | ```
977 |
978 |
979 |
980 | ```
981 |
982 | #### 範例
983 |
984 | * [Example 1](https://jsfiddle.net/Linusborg/z84wspcg/)
985 |
986 | #### 參考連結
987 |
988 | * [Handling Errors in Vue with Error Boundaries](https://medium.com/@dillonchanis/handling-errors-in-vue-with-error-boundaries-91f6ead0093b)
989 |
990 | ## 生產力小技巧
991 |
992 | 讓監聽器在 created 事件時就有效
993 |
994 | ```js
995 | // 不要這樣做
996 | created() {
997 | this.fetchUserList();
998 | },
999 | watch: {
1000 | searchText: 'fetchUserList',
1001 | }
1002 | ```
1003 |
1004 | ```js
1005 | // 這樣做
1006 | watch: {
1007 | searchText: {
1008 | handler: 'fetchUserList',
1009 | immediate: true,
1010 | }
1011 | }
1012 | ```
1013 |
1014 | ## 有用的連結
1015 |
1016 | ### 風格指南
1017 | * [Official - Style Guide](https://vuejs.org/v2/style-guide/)
1018 | * [Vue.js Component Style Guide](https://github.com/pablohpsilva/vuejs-component-style-guide)
1019 |
1020 | ### 重構技巧
1021 |
1022 | * [Refactoring Vue: Cleaning Up a List of Posts With Better Component Splitting and More ES6](https://mattstauffer.com/blog/refactoring-vue-cleaning-up-a-list-of-posts-with-better-component-splitting-and-more-es6/?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=Vue.js%20Feed)
1023 | * [Clean up your Vue modules with ES6 Arrow Functions](https://gist.github.com/JacobBennett/7b32b4914311c0ac0f28a1fdc411b9a7)
1024 | * [Examples of Vue’s Clean Code](https://webdesign.tutsplus.com/tutorials/examples-of-vues-clean-code--cms-29619)
1025 | * [Optimizing Performance with Computed Properties](https://codingexplained.com/coding/front-end/vue-js/optimizing-performance-computed-properties)
1026 |
1027 | ### 狀態管理
1028 |
1029 | * [Managing State in Vue.js](https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87)
1030 |
1031 |
1032 | ### Vuex
1033 |
1034 | * [Decouple Vuex modules with the Mediator pattern](https://itnext.io/decouple-vuex-actions-with-the-mediator-pattern-58a8879de1b4)
1035 | * [Vuex getters are great, but don’t overuse them](https://codeburst.io/vuex-getters-are-great-but-dont-overuse-them-9c946689b414)
1036 | * [Reusable Vuex Mutation Functions](https://itnext.io/reusable-vuex-mutation-functions-9b4920aa737b)
1037 | * [A pattern to handle ajax requests in Vuex](https://medium.com/@lachlanmiller_52885/a-pattern-to-handle-ajax-requests-in-vuex-2d69bc2f8984)
1038 | * [[vuex Mutations] Single Changes vs. Single Responsibility](https://forum.vuejs.org/t/vuex-mutations-single-changes-vs-single-responsibility/16396)
1039 | * [Components and How They Interact in Vue and Vuex](https://dzone.com/articles/how-do-components-interact-in-vue-and-what-is-vuex)
1040 | * [Why VueX Is The Perfect Interface Between Frontend and API](https://codeburst.io/why-vuex-is-the-perfect-interface-between-frontend-and-api-271d92161709)
1041 | * [Composing actions with Vuex](https://codeburst.io/composing-actions-with-vuex-b63466264a37)
1042 | * [How to Build Complex, Large-Scale Vue.js Apps With Vuex](https://code.tutsplus.com/tutorials/how-to-build-complex-large-scale-vuejs-applications-with-vuex--cms-30952)
1043 | * [Should I Store This Data in Vuex?](https://markus.oberlehner.net/blog/should-i-store-this-data-in-vuex/)
1044 |
1045 | ### Mobx
1046 |
1047 | * [Build A View-Framework-Free Data Layer Based on MobX — Integration With Vue (1)](https://itnext.io/build-a-view-framework-free-data-layer-based-on-mobx-integration-with-vue-1-8b524b86c7b8)
1048 |
1049 | ### 不須渲染的元件 (Renderless Component)
1050 |
1051 | * [Renderless Components in Vue.js](https://adamwathan.me/renderless-components-in-vuejs/)
1052 | * [Building Renderless Components to Handle CRUD Operations in Vue.js](https://markus.oberlehner.net/blog/building-renderless-components-to-handle-crud-operations-in-vue/)
1053 |
1054 | #### 範例
1055 |
1056 | * [Renderless Calendar component](https://codesandbox.io/s/v65lx0xvy5)
1057 |
1058 | ### 目錄結構
1059 |
1060 | * [How you can improve your workflow using the JavaScript console](https://medium.freecodecamp.org/how-you-can-improve-your-workflow-using-the-javascript-console-bdd7823a9472)
1061 | * [How to Structure a Vue.js Project](https://itnext.io/how-to-structure-a-vue-js-project-29e4ddc1aeeb)
1062 | * [Large-scale Vuex application structures](https://medium.com/3yourmind/large-scale-vuex-application-structures-651e44863e2f)
1063 | * [Vue.js Application Structure and CSS Architecture](https://markus.oberlehner.net/blog/vue-application-structure-and-css-architecture/)
1064 |
1065 | ### 小技巧
1066 |
1067 | * [How To Build Vue Components Like A Pro 😎](https://blog.bitsrc.io/how-to-build-vue-components-like-a-pro-fd89fd4d524d)
1068 | * [Four tips for working with Vue.js](https://itnext.io/four-tips-for-working-with-vue-js-b362d97de852)
1069 | * [Tips from a Lowly VueJS Developer](https://medium.com/@denny.headrick/tips-from-a-lowly-vuejs-developer-381b6956aece)
1070 | * [Throttling and Debouncing Events with Vue.js and lodash](https://alligator.io/vuejs/lodash-throttle-debounce/)
1071 | * [Are partially applied functions in event handlers possible?](https://forum.vuejs.org/t/are-partially-applied-functions-in-event-handlers-possible/10187)
1072 | * [Vue.js — Considerations and Tricks](https://blog.webf.zone/vue-js-considerations-and-tricks-fa7e0e4bb7bb)
1073 | * [Six random issues and their solutions in VueJS.](https://medium.com/@stijlbreuk/six-random-issues-and-their-solutions-in-vuejs-b16d470a6462)
1074 | * [When VueJS Can't Help You](https://vuejsdevelopers.com/2017/05/01/vue-js-cant-help-head-body/)
1075 | * [Things that won’t work using Vue](https://winnercrespo.com/things-that-wont-work-using-vue/)
1076 | * [Tip#15 Delay execution with _.debounce](https://medium.com/vuejs-tips/tip-15-delay-execution-with-debounce-6a93b759bb06)
1077 |
1078 | ### 路由(Router)
1079 |
1080 | * [Navigation Guards - Official](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-guards)
1081 | * [Vue Router Navigation Guards with Vuex](https://serversideup.net/vue-router-navigation-guards-vuex/)
1082 |
1083 | ### 不良示範 (反模式)
1084 |
1085 | * [Chris Fritz - Vue.js Anti-Patterns (and How to Avoid Them)](http://www.fullstackradio.com/87)
1086 | * [Common mistakes to avoid while working with Vue.js](https://medium.freecodecamp.org/common-mistakes-to-avoid-while-working-with-vue-js-10e0b130925b)
1087 | * [Avoid This Common Anti-Pattern In Full-Stack Vue/Laravel Apps](https://vuejsdevelopers.com/2017/08/06/vue-js-laravel-full-stack-ajax/)
1088 | * [[Video] - VueNYC - Three Vue code smells, and what you can do about them - Matt Rothenberg (@mattrothenberg)](https://www.youtube.com/watch?v=z5UWVOeIsUQ)
1089 |
1090 | ### 影片與音訊課程
1091 |
1092 | * [81: Evan You - Advanced Vue Component Design](https://player.fm/series/series-1401837/81-evan-you-advanced-vue-component-design)
1093 | * [7 Secret Patterns Vue Consultants Don’t Want You to Know](https://www.youtube.com/watch?v=7YZ5DwlLSt8)
1094 |
1095 | ### 專案範例
1096 |
1097 | * [vue-enterprise-boilerplate](https://github.com/chrisvfritz/vue-enterprise-boilerplate)
1098 | * [7-secret-patterns](https://github.com/chrisvfritz/7-secret-patterns)
1099 | * [Vue.js-2-Design-Patterns-and-Best-Practices](https://github.com/PacktPublishing/Vue.js-2-Design-Patterns-and-Best-Practices)
1100 |
1101 | ### 付費課程
1102 |
1103 | * [Advanced Vue Component Design](https://adamwathan.me/advanced-vue-component-design/)
1104 | * [Advanced Vue.js Features from the Ground Up](https://frontendmasters.com/courses/advanced-vue/)
1105 |
1106 |
1107 | ### Typescript
1108 |
1109 | * [Vue + TypeScript: A Match Made in Your Code Editor](https://css-tricks.com/vue-typescript-a-match-made-in-your-code-editor/)
1110 | * [Writing Class-Based Components with Vue.js and TypeScript](https://alligator.io/vuejs/typescript-class-components/)
1111 |
1112 | ### Flowtype
1113 |
1114 | ### GraphQL
1115 |
1116 | ---
1117 |
1118 | ### 其他資訊
1119 |
1120 | * [Creating an Interpose Vue component from a React implementation](https://itnext.io/creating-an-interpose-vue-component-from-a-react-implementation-80d367a695c6)
1121 | * [Composing computed properties in Vue.js](https://medium.com/@kevin_peters/composing-computed-properties-in-vue-js-87b4507af079)
1122 | * [4 AJAX Patterns For Vue.js Apps](https://medium.com/js-dojo/4-ajax-patterns-for-vue-js-apps-add915fc9168)
1123 | * [3 Code Splitting Patterns For VueJS and Webpack](https://medium.com/js-dojo/3-code-splitting-patterns-for-vuejs-and-webpack-b8fff1ea0ba4)
1124 | * [The easiest way to improve your Vue.js application. Part 1](https://codeburst.io/the-easiest-way-to-improve-your-vue-js-application-part-1-51f068652872)
1125 | * [Using JSX with Vue and Why You Should Care](https://scotch.io/tutorials/using-jsx-with-vue-and-why-you-should-care?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=Vue.js%20News)
1126 | * [Compound components](https://forum.vuejs.org/t/compound-components/30139)
1127 | * [Creating Multi-root Vue.js Components](https://zendev.com/2018/05/07/multi-root-vue-components.html)
1128 | * [Understanding Vue.js Reactivity in Depth with Object.defineProperty()](https://www.timo-ernst.net/blog/2017/07/26/understanding-vue-js-reactivity-depth-object-defineproperty/)
1129 | * [Templating in Vue: Separation of Concerns or Separation of Technology or something else?](https://medium.com/@s.molinari/templating-separation-of-concerns-or-separation-of-technology-or-something-else-123a3d41f0b4)
1130 | * [Stashing Vue components data](https://medium.com/@kelin2025/components-stash-f2e14603a874)
1131 | * [Creating Reusable Transitions in Vue](https://vuejsdevelopers.com/2018/02/26/vue-js-reusable-transitions/)
1132 | * [vue-advanced-workshop](https://github.com/d-levin/vue-advanced-workshop)
1133 | * [Do it with Elegance: How to Create Data-Driven User Interfaces in Vue](https://blog.rangle.io/how-to-create-data-driven-user-interfaces-in-vue/)
1134 | * [Creating Vue.js Component Instances Programmatically](https://css-tricks.com/creating-vue-js-component-instances-programmatically/)
1135 | * [Managing User Permissions in a Vue.js App](https://dzone.com/articles/managing-user-permissions-in-a-vuejs-app)
1136 | * [Render Functional Components in Vue.js](https://alligator.io/vuejs/render-functional-components/)
1137 | * [Looping through Object Properties](https://codingexplained.com/coding/front-end/vue-js/looping-object-properties)
1138 | * [Cancelling async operations in Vue.js](https://codeburst.io/cancelling-async-operations-in-vue-js-3d0f3c2de598)
1139 | * [Scoped styles with v-html](https://medium.com/@brockreece/scoped-styles-with-v-html-c0f6d2dc5d8e)
1140 | * [Pagination With Vuejs](https://medium.com/@obapelumi/pagination-with-vuejs-1f505ce8d15b)
1141 | * [What does the ‘h’ stand for in Vue’s render method?](https://css-tricks.com/what-does-the-h-stand-for-in-vues-render-method/)
1142 | * [How To Build Vue Components That Play Nice](https://vuejsdevelopers.com/2018/06/18/vue-components-play-nicely/)
1143 | * [Making responsive Vue components with ResizeObserver](https://itnext.io/making-adaptive-vue-components-with-resizeobserver-123b5ebb20ae)
1144 | * [An imperative guide to forms in Vue.js](https://blog.logrocket.com/an-imperative-guide-to-forms-in-vue-js-7536bfa374e0)
1145 | * [Vue.js: the good, the meh, and the ugly](https://medium.com/@Pier/vue-js-the-good-the-meh-and-the-ugly-82800bbe6684)
1146 | * [Dynamic Vue.js Layout Components](https://markus.oberlehner.net/blog/dynamic-vue-layout-components/)
1147 | * [Advanced Vue.js concepts: mixins, custom directives, filters, transitions, and state management](https://blog.logrocket.com/advanced-vue-js-concepts-mixins-custom-directives-filters-transitions-and-state-management-ca6955905156)
1148 | * [Introducing the Single Element Pattern](https://medium.freecodecamp.org/introducing-the-single-element-pattern-dfbd2c295c5d)
1149 | * [Control DOM Outside Your Vue.js App with portal-vue](https://alligator.io/vuejs/portal-vue/)
1150 | * [Add i18n and manage translations of a Vue.js powered website](https://medium.com/hypefactors/add-i18n-and-manage-translations-of-a-vue-js-powered-website-73b4511ca69c)
1151 |
--------------------------------------------------------------------------------
/generate-toc.md:
--------------------------------------------------------------------------------
1 | # To generate table of contents
2 |
3 | ```js
4 | var toc = '';
5 |
6 | readme.querySelectorAll('h2, h3')
7 | .forEach((elem) => {
8 | const { tagName, innerText } = elem;
9 | const anchor = elem.querySelector('a').getAttribute('href');
10 |
11 | if (tagName === 'H2') {
12 | toc += `- [${innerText}](${anchor})\n`;
13 | }
14 |
15 | if (tagName === 'H3') {
16 | toc += ` - [${innerText}](${anchor})\n`;
17 | }
18 | });
19 |
20 | console.log(toc);
21 | ```
22 |
--------------------------------------------------------------------------------