主頁 > 軟體設計 > Vue.js 組件化開發

Vue.js 組件化開發

2021-04-23 10:25:16 軟體設計

三、組件化開發

  • 1.1 組件化的實作和使用步驟
    • 組件注冊步驟決議
  • 1.2 全域組件和區域組件
  • 1.3 父組件和子組件
  • 1.4 注冊組件語法糖
  • 1.5 組件模板抽離的寫法
  • 1.6 組件資料存放
  • 1.7 父子組件通信
    • 父組件向子組件傳遞資料
    • 子組件向父組件傳遞資料
  • 1.8 組件訪問
    • 父組件訪問子組件
      • children
      • refs
    • 子組件訪問父組件
  • 1.9 插槽
    • slot
    • 具名插槽
    • 作用域插槽

人面對復雜問題的處理方式:

  • 任何一個人處理資訊的邏輯能力都是有限的
  • 所以,當面對一個非常復雜的問題時,我們不太可能—次性搞定一大堆的內容,
  • 但是,我們人有一種天生的能力,就是將問題進行拆解,如果將一個復雜的問題,拆分成很多個可以處理的小問題,再將其放在整體當中,你會發現大的問題也會迎刃而解,

組件化也是類似的思想:

  • 如果我們將一個頁面中所有的處理邏輯全部放在一起,處理起來就會變得非常復雜,而且不利于后續的管理以及擴展,
  • 但如果,我們講一個頁面拆分成一個個小的功能塊,每個功能塊完成屬于自己這部分獨立的功能,那么之后整個頁面的管理和維護就變得非常容易了,

組件化是Vue.js中的重要思想:

  • 它提供了一種抽象,讓我們可以開發出一個個獨立可復用的小組件來構造我們的應用,
  • 任何的應用都會被抽象成一顆組件樹,

在這里插入圖片描述
組件化思想的應用:

  • 有了組件化的思想,我們在之后的開發中就要充分的利用它,
  • 盡可能的將頁面拆分成一個個小的、可復用的組件,
  • 這樣讓我們的代碼更加方便組織和管理,并且擴展性也更強,

1.1 組件化的實作和使用步驟

組件的使用分成三個步驟:

  1. 創建組件構造器
  2. 注冊組件
  3. 使用組件

在這里插入圖片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <!-- 3、使用組件 -->
        <my-cpn></my-cpn>
        <my-cpn></my-cpn>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        // 1、創建組件構造器
        const cpnC = Vue.extend({
            template: `
                <div>
                    <h2>我是標題</h2>
                    <p>我是內容,哈哈哈哈</p>
                    <p>我是內容,呵呵呵呵</p>
                </div>`,
        });
        // 2、注冊組件
        Vue.component('my-cpn', cpnC);
        const app = new Vue({
            el: '#app', 
            data: {    
            }
        })
    </script>
</body>
</html>

在這里插入圖片描述

組件注冊步驟決議

  1. Vue.extend():

    • 呼叫Vue.extend()創建的是一個組件構造器,
    • 通常在創建組件構造器時,傳入template代表我們自定義組件的模板,該模板就是在使用到組件的地方,要顯示的HTML代碼,
    • 事實上,這種寫法在Vue2.x的檔案中幾乎已經看不到了,它會直接使用下面我們會講到的語法糖,但是在很多資料還是會提到這種方式,而且這種方式是學習后面方式的基礎,
  2. Vue.component():

    • 呼叫Vue.component()是將剛才的組件構造器注冊為一個組件,并且給它起一個組件的標簽名稱,
    • 所以需要傳遞兩個引數:1、注冊組件的標簽名,2、組件構造器
  3. 組件必須掛在在某個Vue實體下,否則它不會生效

    • 下面的實體中,我們使用了三次<my-cpn></my-spn>
    • 但是第三次并沒有生效

在這里插入圖片描述

1.2 全域組件和區域組件

  • 全域組件:在注冊之后可以用在任何新創建的 Vue 根實體 (new Vue) 的模板中,
  • 區域組件:區域組件只有在所注冊的Vue實體中才能使用,并不能在其他未進行注冊的Vue實體中使用,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <!-- 3、使用組件 -->
        <my-cpn></my-cpn>
        <cpn></cpn>
    </div>
    <hr>
    <div id="app2">
        <my-cpn></my-cpn>
        <cpn></cpn>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        // 1、創建組件構造器
        const cpnC = Vue.extend({
            template: `
                <div>
                    <h2>我是標題</h2>
                    <p>我是內容,哈哈哈哈</p>
                    <p>我是內容,呵呵呵呵</p>
                </div>`,
        });
        // 2、注冊組件(全域組件,意味著可以在多個 Vue 實體下面使用)
        Vue.component('my-cpn', cpnC);
        const app = new Vue({
            el: '#app', 
            data: {    
            },
            // 區域組件
            components: {
                cpn: cpnC
            }
        })
        const app2 = new Vue({
            el: '#app2', 
            data: {    
            }
        })
    </script>
</body>
</html>


在上面的代碼中,我們創建了兩個Vue實體物件appapp2,同時我們使用注冊了全域組件my-cpn以及在app內注冊了區域組件cpn,我們在appapp2中使用上面的兩個組件,可以看到區域組件cpn并沒有在app2中生效,符合我們的預期,

在這里插入圖片描述

1.3 父組件和子組件

在前面我們看到了組件樹:

  • 組件和組件之間存在層級關系
  • 而其中一種非常重要的關系就是父子組件的關系

我們來通過代碼看這種層級關系:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn2></cpn2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        // 1、創建第一個組件構造器(子組件)
        const cpnC1 = Vue.extend({
            template: `
                <div>
                    <h2>我是標題1</h2>
                    <p>我是內容,哈哈哈哈</p>
                </div>`,
        });
        // 2、創建第二個組件構造器(父組件)
        const cpnC2 = Vue.extend({
            template: `
                <div>
                    <h2>我是標題2</h2>
                    <p>我是內容,呵呵呵呵</p>
                    <cpn1></cpn1>
                </div>`,
            components: {
                cpn1: cpnC1,
            },
        });
        const app = new Vue({
            el: '#app', 
            data: {    
            },
            components: {
                cpn2: cpnC2,
            }
        })
    </script>
</body>
</html>


在這里插入圖片描述
在這里插入圖片描述

1.4 注冊組件語法糖

通過語法糖的方式,我們可以跳過Vue.extend()方法,直接通過Vue.component()方法實作組件的注冊,


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <!-- 3、使用組件 -->
        <cpn1></cpn1>
        <cpn2></cpn2>
    </div>
    <script src="../js/vue.js"></script>
    <script>
        // 1、全域組件注冊語法糖
        //  1.1 創建組件構造器
        // const cpn1 = Vue.extend({
        //     template: `
        //         <div>
        //             <h2>我是標題</h2>
        //             <p>我是內容,哈哈哈哈</p>
        //             <p>我是內容,呵呵呵呵</p>
        //         </div>`,
        // });
        //  1.2 注冊組件
        // Vue.component('cpn1', cpn1);
        Vue.component('cpn1', {
            template: `
                <div>
                    <h2>我是標題</h2>
                    <p>我是內容,哈哈哈哈</p>
                    <p>我是內容,呵呵呵呵</p>
                </div>`,
        });
        const app = new Vue({
            el: '#app', 
            data: {    
            },
            components: {
                cpn2: {
                    template: `
                        <div>
                            <h2>我是標題</h2>
                            <p>我是內容,哈哈哈哈</p>
                            <p>我是內容,呵呵呵呵</p>
                        </div>`,
                }
            }
        })
    </script>
</body>
</html>

在這里插入圖片描述

1.5 組件模板抽離的寫法

IDE中,寫組件的template時,由于其是一個字串,所以沒有代碼提示,寫起來非常不方便,并且template的代碼直接耦合在Vue的代碼中,非常的凌亂,為此,我們可以將組件的模板抽離出來,有兩種抽離的方式,


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn1></cpn1>
        <hr>
        <cpn2></cpn2>
    </div>
    <!-- 1、第一種寫法 -->
    <script type="text/x-template" id="cpn">
        <div>
            <h2>我是標題</h2>
            <p>我是內容,哈哈哈哈</p>
        </div>
    </script>
    <!-- 2、第二種寫法 -->
    <template id="cpn2">
        <div>
            <h2>我是標題</h2>
            <p>我是內容,哈哈哈哈</p>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        Vue.component('cpn1', {
            template: '#cpn',
        });
        Vue.component('cpn2', {
            template: '#cpn2',
        });
        const app = new Vue({
            el: '#app', 
            data: {    
            },
        })
    </script>
</body>
</html>


在這里插入圖片描述
通常,我們采用的是方式二,可以看到,組件模板抽離后的代碼看起來非常簡潔,

1.6 組件資料存放

組件是一個單獨功能模塊的封裝:

這個模塊有自己的 HTML 模板,也應該有屬于自己的資料data

組件中的資料是保存在哪里呢?頂層的Vue實體中嗎?

我們可以通過下面的代碼測驗,組件中能不能直接訪問Vue實體中的data


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn></cpn>
    </div>
    <template id="a">
        <div>
            <h2>{{ message}}</h2>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        Vue.component('cpn', {
            template: '#a',
        });
        const app = new Vue({
            el: '#app', 
            data: {
            	message: 'Hello'
            },
        })
    </script>
</body>
</html>


在這里插入圖片描述
我們發現并不能訪問,而且即使可以訪問,如果將所有的資料都放在Vue實體中,Vue實體會變的非常臃腫,

結論:Vue 組件應該有自己保存資料地方

組件自己的資料存放在哪里呢?

  • 組件物件也有一個data屬性(也可以有methods屬性)
  • 只是這個data屬性必須是一個函式
  • 而且這個函式回傳一個物件,物件內部保存著資料

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn></cpn>
    </div>
    <template id="a">
        <div>
            <h2>{{ title }}</h2>
            <p>我是內容,哈哈哈哈</p>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        Vue.component('cpn', {
            template: '#a',
            data() {
                return {
                    title: 'Hello'
                }
            }
        });
        const app = new Vue({
            el: '#app', 
            data: {    
            },
        })
    </script>
</body>
</html>


在這里插入圖片描述

1.7 父子組件通信

子組件是不能參考父組件或者Vue實體的資料的,

但是,在開發中,往往一些資料確實需要從上層產地到下層:

  • 比如在一個頁面中,我們從服務器請求到了很多的資料
  • 其中一部分資料,并非是我們整個頁面的大組件來展示的,而是需要下面的子組件進行展示
  • 這個時候,并不會讓子組件再次發送要給網路請求,而是直接讓大組件(父組件)將資料傳遞給小組件(子組件)

如果進行父子組件間的通信呢?官方提到

  • 通過props向子組件傳遞資料
  • 通過事件向父組件發送訊息
    在這里插入圖片描述

Vue實體同樣是組件,所以Vue實體與子組件通信和父組件與子組件通信的程序是一樣的,

父組件向子組件傳遞資料

在子組件中,使用選項props來宣告需要從父組件接收到的資料,

props的值有兩種方式:

  • 方式一:字串陣列,陣列中的字串就是傳遞時的名稱,
  • 方式二:物件,物件可以設定傳遞時的型別,也可以設定默認值等,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
       <cpn :cmovies="movies" :cmessage="message"></cpn>
       <!-- 如果要傳遞的是 data 屬性,則必須用 v-bind -->
       <cpn cmovies="movies" cmessage="message"></cpn>
    </div>
    <template id="a">
        <div>
            {{cmessage}}
            <ul>
                <li v-for="movie in cmovies">{{movie}}</li>
            </ul>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const cpn = {
            template: '#a',
            props: ['cmovies', 'cmessage'],
        }
        const app = new Vue({
            el: '#app', 
            data: {
                message: 'Hello',
                movies: ['a', 'b', 'c', 'd']   
            },
            components: {
                cpn
            }
        })
    </script>
</body>
</html>


在這里插入圖片描述
在上面,我們的props選項是使用一個陣列,

我們說過,處理陣列之外,我們也可以使用物件,當需要對props進行型別等驗證時,就需要物件寫法了,

驗證都支持如下資料型別:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
       <cpn :cmovies="movies" ></cpn>
       <!-- 錯誤語法:必須用 v-bind -->
       <!-- <cpn cmovies="movies" cmessage="message"></cpn> -->
    </div>
    <template id="a">
        <div>
            {{cmessage}}
            <ul>
                <li v-for="movie in cmovies">{{movie}}</li>
            </ul>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const cpn = {
            template: '#a',
            // props: ['cmovies', 'cmessage'],
            props: {
                // 1、型別檢查
                // cmovies: Array,
                // cmessage: String
                // 2、提供一些默認值
                cmessage: {
                    type: String,
                    default: 'aaaa',
                    required: false
                },
                cmovies: {
                    type: Array,
                }
            }
        }
        const app = new Vue({
            el: '#app', 
            data: {
                message: 'Hello',
                movies: ['a', 'b', 'c', 'd']   
            },
            components: {
                cpn
            }
        })
    </script>
</body>
</html>

子組件向父組件傳遞資料

props用于父組件向子組件傳遞資料,還有一種比較常見的是子組件傳遞資料或事件到父組件中,

我們應該如何處理呢?這個時候,我們需要使用自定義事件來完成,

什么時候需要自定義事件呢?

  • 當子組件需要向父組件傳遞資料時,就要用到自定義事件了,
  • 我們之前學習的v-on不僅僅可以用于監聽DOM事件,也可以用于組件間的自定義事件,

自定義事件的流程︰

  • 在子組件中,通過$emit('事件名', [引數])來觸發事件,
  • 在父組件中,通過v-on來監聽子組件事件,
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <!-- 父組件模板 -->
    <div id="app">
       <cpn v-on:itemclick="cpnClick"></cpn>
    </div>
    <!-- 子組件模板 -->
    <template id="a">
        <div>
            <button v-for="item in categories" @click="btnClick(item)">
                {{item.name}}
            </button>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const cpn = {
            template: '#a',
            data() {
                return {
                    categories: [
                        {id: 1, name: '熱門推薦'},
                        {id: 2, name: '手機數碼'},
                        {id: 3, name: '家用家電'},
                        {id: 4, name: '電腦辦公'},
                    ]
                }
            },
            methods: {
                btnClick(item) {
                    // 產生事件:自定義事件
                    this.$emit('itemclick', item);
                }
            }
        }
        const app = new Vue({
            el: '#app', 
            data: {
            },
            components: {
                cpn
            },
            methods: {
                cpnClick(item) {
                    console.log(item)
                }
            }
        })
    </script>
</body>
</html>

上面代碼的含義如下:

  1. 父組件監聽itemclick事件,該事件產生時,執行cpnClick方法
  2. 點擊子組件內部的按鈕時,執行btnClick方法
  3. btnClick方法內部,產生itemclick事件
  4. 由此便完成了子組件向父組件的資料傳遞

在這里插入圖片描述

1.8 組件訪問

有時候,我們需要父組件直接訪問子組件,子組件直接訪問父組件,或者是子組件訪問根組件,

  • 父組件訪問子組件:使用$children$refs
  • 子組件訪問父組件:使用$parent

父組件訪問子組件

children

  • this.$children是一個陣列型別,它包含所有子組件物件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn></cpn>
        <button @click="btnClick">點擊</button>
    </div>
    <template id="cpn">
        <div>
            <h2>我是子組件</h2>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            methods: {
                btnClick() {
                    console.log(this.$children);
                    this.$children[0].showMessage();
                }
            },
            components: { 
                cpn: {
                    template: '#cpn',
                    methods: {
                        showMessage() {
                            console.log("showMessage")
                        }
                    }
                }
            },
        })
    </script>
</body>
</html>

在這里插入圖片描述
可以看到,我們通過this.$children成功訪問了cpn組件,并且呼叫了cpn所擁有的methods,當然也可以訪問cpn的其他屬性,

refs

通過this.$children存在這樣一個問題,便是我們要訪問一個組件時,可能并不知道其索引,需要進行遍歷查找,

this.$refs的作用相當于給組件一個id,這樣我們便能根據這個id直接找到對應的組件了,


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn ref="aaa"></cpn>
        <button @click="btnClick">點擊</button>
    </div>
    <template id="cpn">
        <div>
            <h2>我是子組件</h2>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            methods: {
                btnClick() {
                    console.log(this.$refs);
                    console.log(this.$refs.aaa);
                    this.$refs.aaa.showMessage();
                }
            },
            components: { 
                cpn: {
                    template: '#cpn',
                    methods: {
                        showMessage() {
                            console.log("showMessage")
                        }
                    }
                }
            },
        })
    </script>
</body>
</html>


在這里插入圖片描述

子組件訪問父組件

  • this.$parent獲得父組件
  • this.$root獲得根組件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <div id="app">
        <cpn></cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>我是子組件</h2>
            <button @click="btnClick">點擊</button>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            methods: {
                showMessage() {
                    console.log("showMessage")
                }
            },
            components: { 
                cpn: {
                    template: '#cpn',
                    methods: {
                        btnClick() {
                            console.log(this.$parent);
                            this.$parent.showMessage();

                            // 訪問根組件
                            console.log(this.$root);
                        }
                    }
                }
            },
        })
    </script>
</body>
</html>


在這里插入圖片描述

1.9 插槽

slot翻譯為插槽:

  • 在生活中很多地方都有插槽,電腦的USB插槽,插板當中的電源插槽,口插槽的目的是讓我們原來的設備具備更多的擴展性,
  • 比如電腦的USB我們可以插入U盤、硬碟、手機、音響、鍵盤、滑鼠等等,

組件的插槽︰

  • 組件的插槽也是為了讓我們封裝的組件更加具有擴展性,
  • 讓使用者可以決定組件內部的一些內容到底展示什么,

例子∶移動網站中的導航欄,

  • 移動開發中,幾乎每個頁面都有導航欄,
  • 導航欄我們必然會封裝成一個插件,比如nav-bar組件,一旦有了這個組件,我們就可以在多個頁面中復用了,

如何去封裝這類的組件呢?

  • 它們也很多區別,但是也有很多共性,
  • 如果,我們每一個單獨去封裝一個組件,顯然不合適∶比如每個頁面都回傳,這部分內容我們就要重復去封裝,
  • 但是,如果我們封裝成一個,好像也不合理∶有些左側是選單,有些是回傳,有些中間是搜索,有些是文字,等等,

如何封裝合適呢?抽取共性,保留不同

  • 最好的封裝方式就是將共性抽取到組件中,將不同暴露為插槽,
  • 一旦我們預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什么內容,
  • 是搜索框,還是文字,還是選單,由呼叫者自己來決定,

這就是為什么我們要學習組件中的插槽slot的原因,

slot


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 
        1、插槽的基本使用 <slot></slot>
        2、插槽的默認值 <slot>button</slot>
        3、如果有多個值,同時放入到組件進行替換時,一起作為替換元素
     -->
    
    <div id="app">
        <cpn><button>按鈕</button></cpn>
        <cpn><div>哈哈哈</div></cpn>
        <cpn><a href="">百度</a></cpn>
        <cpn></cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>我是組件</h2>
            <div>我是組件,哈哈哈</div>
            <slot></slot>
            <!-- 可以給 slot 默認值,如果父組件沒有插入,則使用默認值 -->
            <!-- <slot><button>按鈕</button></slot> -->
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            components: {
                cpn: {
                    template: '#cpn',
                }
            }
        })
    </script>
</body>
</html>


在這里插入圖片描述
在上面的代碼中,我們在組件中使用<slot></slot>創建插槽,當我們使用該組件時,組件標簽內的內容會自動替換掉<slot></slot>

具名插槽

當子組件的功能復雜時,子組件的插槽可能并非是一個,

  • 比如我們封裝一個導航欄的子組件,可能就需要三個插槽,分別代表左邊、中間、右邊,
  • 那么,外面在給插槽插入內容時,如何區分插入的是哪—個呢?
  • 這個時候,我們就需要給插槽起一個名字

如何使用具名插槽呢?

  • 非常簡單,只要給slot元素一個name屬性即可<slot name='myslot'> </slot>

我們來給出一個案例︰

  • 這里我們先不對導航組件做非常復雜的封裝,先了解具名插槽的用法,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <cpn>
            <span slot="center">標題</span>
        </cpn>
    </div>
    <template id="cpn">
        <div>
            <slot name="left"><span>左邊</span></slot>
            <slot name="center"><span>中間</span></slot>
            <slot name="right"><span>右邊</span></slot>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            components: {
                cpn: {
                    template: '#cpn',
                }
            }
        })
    </script>
</body>
</html>


在這里插入圖片描述
在上面的代碼中,我們在組件中創建了三個具名插槽,分別為leftcenterright.

我們使用該組件時,通過slot="center"指定要將內容插入到center的插槽,

作用域插槽

在了解作用域插槽之前,我們需要西安了解一個概念:編譯作用域

官方對于編譯的作用域決議比較簡單,我們自己來通過一個例子來理解這個概念:

我們來考慮下面的代碼是否最終是可以渲染出來的︰

  • <my-cpn v-show="isShow"></my-cpn>中,我們使用了isShow屬性,
  • isShow屬性包含在組件中,也包含在Vue實體中,

答案︰最終可以渲染出來,也就是使用的是Vue實體的屬性,

為什么呢?

  • 官方給出了一條準則∶父組件模板的所有東西都會在父級作用域內編譯;子組件模板的所有東西都會在子級作用域內編譯
  • 而我們在使用<my-cpn v-show="isShow"></my-cpn>的時候,整個組件的使用程序是相當于在父組件中出現的,
  • 那么他的作用域就是父組件,使用的屬性也是屬于父組件的屬性,
  • 因此,isShow使用的是Vue實體中的屬性,而不是子組件的屬性,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <cpn v-show="isShow"></cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>我是組件</h2>
            <div>我是組件,哈哈哈</div>
            <button v-show="isShow">按鈕</button>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
                isShow: true
            },
            components: {
                cpn: {
                    template: '#cpn',
                    data() {
                        return {
                            isShow: false
                        }
                    }
                }
            }
        })
    </script>
</body>
</html>


在這里插入圖片描述

作用域插槽是slot—個比較難理解的點,而且官方檔案說的又有點不清晰,

這里,我們用一句話對其做一個總結,然后我們在后續的案例中來體會︰

  • 父組件替換插槽的標簽,但是內容由子組件來提供

我們先提一個需求︰

  • 子組件中包括一組資料:比如: pLanguages: ['JavaScript', 'Python', 'Swift', 'Go','C++']
  • 需要在多個界面進行展示:
    • 某些界面是以水平方向展示的
    • 某些界面是以串列形式展示的
    • 某些界面直接展示一個陣列
  • 內容在子組件,希望父組件告訴我們如何展示,怎么辦呢?
  • 利用slot作用域插槽就可以了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <cpn></cpn>
        <cpn>
            <!-- 目的是獲取子組件中的 pLanguages -->
            <template slot-scope="slot">
                <span v-for="item in slot.data">{{item}} - </span>
            </template>
        </cpn>
    </div>
    <template id="cpn">
        <div>
            <h2>我是組件</h2>
            <slot :data="pLanguages">
                <ul>
                    <li v-for="item in pLanguages">
                        {{item}}
                    </li>
                </ul>
            </slot>
        </div>
    </template>
    <script src="../js/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
            },
            components: {
                cpn: {
                    template: '#cpn',
                    data() {
                        return {
                            pLanguages: ['Python', 'Java', 'Go', 'C++']
                        }
                    }
                }
            }
        })
    </script>
</body>
</html>

在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/279229.html

標籤:其他

上一篇:谷粒商城專案簡介

下一篇:華三華為無線系列排障-----AP間無法實作自動漫游

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more