前端筆記之Vue(二)組件&案例&props&計算屬性

1、Vue組件(.vue文件)

組件 (Component) Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠表現爲用 is 特性進行了擴展的原生 HTML 元素。javascript

全部的 Vue 組件同時也都是 Vue 的實例,因此可接受相同的選項對象 (除了一些根級特有的選項) 並提供相同的生命週期鉤子。css

說白了,就是HTMLCSSJS行爲的一個封裝。html

 

Vue中組件的實例化是對程序員不透明的,組件必須在components中進行「註冊」才能使用。vue

Vue中用K:V對的形式定義類,讓你自由的註冊名字,Vue官方推薦使用含有短橫的名字來表示自定義組件。java

 

翻譯.vue文件須要安裝vue-loader依賴:webpack

https://vue-loader.vuejs.org/zh-cn/程序員

https://vue-loader-v14.vuejs.org/zh-cn/configurations/pre-processors.htmlweb

 

安裝其餘4個開發依賴:算法

npm install --save-dev css-loader
npm install --save-dev vue-loader
npm install --save-dev vue-style-loader
npm install --save-dev vue-template-compiler

 

修改webpack的配置(關於vue-loader的配置)官網找:npm

https://vue-loader-v14.vuejs.org/zh-cn/configurations/pre-processors.html


1.1組件的寫法1

.vue文件的結構:

<template></template>     html結構
<script></script>            js程序
<style></style>            樣式表

 App.vue父組件:

<template>
    <h1>我是App父組件{{a}}</h1>
</template>
<script>
    export default {
        data(){
            return {
                a:100
            }
        }
    }
</script>
<style></style>
示例代碼

 

有了App.vue組件就能夠在main.js中經過import引入,並註冊:

import Vue from 'vue';
import App from './App.vue';

new Vue({
    el : "#app",
    data : {

    },
    components : { App }
})

 

index.html頁面中使用便可:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
</body>
<script type="text/javascript" src="dist/all.js"></script>
</html>

此時看見App父組件的內容了


1.2組件的寫法2

使用vue2新增的render()函數

index.html頁面中就不用放在<App></App>自定義標籤了,也不須要註冊了。

main.js

import Vue from 'vue';
import App from './App.vue';

new Vue({
    el : "#app",
 render : (h)=> h(App)
})

 

Vue中不容許出現片斷標籤,必須用一個標籤包裹全部

<template>
    <div>
        <h1>我是app組件{{a}}</h1>
        <h1>我是app組件{{a}}</h1>
    </div>
</template>
錯誤寫法:
<template>
    <h1>我是app組件{{a}}</h1>
    <h1>我是app組件{{a}}</h1>
</template>

 

若是有兩個組件就須要有一個components的文件夾 裏面能夠放.vue組件

mian.vue

<style>
</style>
<template>
    <div>
        <h1>我是mian組件{{a}}</h1>
    </div>
</template>
<script>
    export default{
        data(){
            return{
                a : 100
            }
        }
    }
</script>

 

組件在App.vue裏面使用

<style>
</style>
<template>
    <div>
        <vue-main></vue-main>
        <VueMain></VueMain>
    </div>
</template>
<script> import VueMain from "./components/main.vue"
export default {
    data(){
        return {
            a: 100
        }
    },
    components:{  VueMain }
}
</script>

 

使用的時候如下兩種方法:

<VueMain></VueMain> 

等價於:

<vue-main></vue-main>

1.3關於data

在前面看到,在new Vue()的時候,建立或註冊模板時,傳入一個data屬性做爲用來綁定的數據。是能夠給data直接賦值爲一個對象的。可是在組件中,data必須是一個函數,而不能直接把一個對象賦值給它。

 

1種,在main.js主入口中的寫法:

new Vue({
    el:'#app',
    data:{ }
})

 

2種,在組件中data選項必須是一個函數:

new Vue({
    el:'#app',
    data(){ return { //返回一個惟一的對象,不要和其餘組件共用一個對象進行返回
 } }
})

 

【區別】:

1)在簡單的Vue實例中,沒什麼區別,由於你new出的對象不會被複用。

new Vue({...})

2)但在組件中,由於可能在多處調用同一組件,因此爲了避免讓多處的組件共享同一data對象,只能返回函數。


 

2、 案例

2.1調色板

<template>
    <div>
        <div class="box" :style="{background:`rgb(${r},${g},${b})`}"></div>
        <p>
            <input type="range" min="0" max="255" v-model="r">
            <input type="number" min="0" max="255" v-model="r">
        </p>
        <p>
            <input type="range" min="0" max="255" v-model="g">
            <input type="number" min="0" max="255" v-model="g">
        </p>
        <p>
            <input type="range" min="0" max="255" v-model="b">
            <input type="number" min="0" max="255" v-model="b">
        </p>
    </div>
    
</template>
<script>
    export default {
        data(){
            return {
                r : 100,
                g : 100,
                b : 100
            }
        }
    }
</script>
<style>
    .box{
        width: 200px;
        height: 200px;
    }
</style>
示例代碼

2.2購物車

<template>
    <div>
        <table>
            <tr>
                <th>號碼</th>
                <th>東西</th>
                <th>價格</th>
                <th>數量</th>
                <th>小計</th>
            </tr>
            <tr v-for="item in carts">
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.price}}</td>
                <td>
                    <button @click="minus(item.id)">-</button>
                    <input  type="number" min="0" v-model="item.number">
                    <button @click="add(item.id)">+</button>
                </td>
                <td>{{item.number * item.price}}</td>
            </tr>
        </table>
        <h1>總價格:{{this.carts.reduce((a,b)=>a + b.price * b.number , 0)}}</h1>
    </div>
</template>
<script>
    export default {
        data(){
            return {
                carts : [
                    {"id" : 1 , "title" : "空調" , "price" : 5000, "number" : 1},
                    {"id" : 2 , "title" : "手機" , "price" : 3000, "number" : 1},
                    {"id" : 3 , "title" : "鼠標" , "price" : 200 , "number" : 1}
                ]
            }
        },
        methods : {
            add(id){
                //this.carts.filter(item=>item.id == id)[0].number++
                //vue不能識別數組某個項單獨修改!!要改必須直接改變數組!!
                this.carts = this.carts.map(item=>item.id == id ? {...item , "number" : item.number +}: item);
            },
            minus(id){
                //this.carts.filter(item=>item.id == id)[0].number--
                this.carts = this.carts.map(item=>item.id == id ? {...item , "number" : item.number -}: item);
            }
        }
    }
</script>
<style>
     table,tr,td,th{border:1px solid red;}
     td{width:200px;height:60px;}
</style>
示例代碼

2.3選項卡

<style>
.box {
    width: 600px;height: 400px;
    margin: 10px auto;
    border: 1px solid #333;
        header {
        ul {
            overflow: hidden;
            li {
                float: left;
                width: 33.333%;
                height: 40px;
                line-height: 40px;
                text-align: center;
            }
            li.cur {
                background: red; color: #fff;
            }
        }
    }
}
</style>
<template>
    <div class="box">
        <header>
            <ul>
                <li v-for="(item,index) in tabNav" :class="{cur:item.click}" @click="changeTab(dex)">{{item.title}}</li>
            </ul>
        </header>
        <div class="content">
            <div v-show="tabIndex == 0">
                新聞新聞新聞新聞新聞
            </div>
            <div v-show="tabIndex == 1">
                軍事軍事軍事軍事軍事
            </div>
            <div v-show="tabIndex == 2">
                圖片圖片圖片圖片圖片
            </div>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            tabNav: [
                {title: "新聞", click: true},
                {title: "軍事", click: false},
                {title: "圖片", click: false}
            ],
            tabIndex: 0
        }
    },
    methods:{
      changeTab(index){
          // 遍歷tabNav數組 進行循環 去掉全部類
          this.tabNav.forEach(function(item){
              item.click = false
          });
          // 點擊的那個tab 加類
          this.tabNav[index].click = true
          // 改變索引
          this.tabIndex = index
      }
    }
}
</script>
示例代碼

2.4三級聯動

<template>
    <div>
        <select v-model="sheng">
            <option v-for="item in info" :value="item.name">
                {{item.name}}
            </option>
        </select>

        <select v-model="shi">
            <option 
                v-for="item in info.filter(i=>i.name == sheng)[0].city" 
                :value="item.name"
            >
                {{item.name}}
            </option>
        </select>

         <select v-model="xian">
            <option 
v-for="item in info.filter(i=>i.name==sheng)[0].city.filter(i=>i.name==shi)[0].area" 
                :value="item"
            >
                {{item}}
            </option>
        </select>
        <h1>你的地址{{sheng}}{{shi}}{{xian}}</h1>
    </div>
</template>
<script>
    import info from "./info.js"; //引入全國省市數據
    export default {
        data(){
            return {
                info ,
                sheng:"廣東省",
                shi : "廣州市",
                xian: "花都區"
            }
        },
        watch : {
            sheng(){
                //當data中的sheng變化的時候,觸發
              this.shi = info.filter(i=>i.name == this.sheng)[0].city[0].name;
              this.xian=info.filter(i=>i.name==this.sheng)[0].city.filter(i=>i.name==this.shi)[0].area[0]
            }
        }
    }
</script>
示例代碼

簡化:

<template>
    <div>
         <!-- <select v-model="sheng" @change="changeSheng($event)"> -->
         <select v-model="sheng">
             <option v-for="item in info" :value="item.name">
                 {{item.name}}
             </option>
         </select>

         <select v-model="shi">
             <option v-for="item in allShi()" :value="item.name">
                 {{item.name}}
             </option>
         </select>

         <select v-model="xian">
             <option v-for="item in allXian()" :value="item">
                 {{item}}
             </option>
         </select>
    </div>
</template>
<script>
    import info from "../lib/info.js";
    export default {
        data(){
            return {
                info,
                sheng:"廣東省",
                shi:"廣州市",
                xian:"天河區"
            }
        },
        methods:{
            // changeSheng(e){
            //     this.shi = info.filter(i=>i.name == this.sheng)[0].city[0].name;
            //     this.xian = info.filter(i=>i.name == this.sheng)[0].city.filter(i=>i.name == this.shi)[0].area[0];
            // }
            allShi(){
                return info.filter(i=>i.name == this.sheng)[0].city;
            },
            allXian(){
                return info.filter(i=>i.name == this.sheng)[0].city.filter(i=>i.name == this.shi)[0].area
            }
        },
        watch:{
            //當data中的sheng變化的時候,觸發這個函數
            sheng(){
                this.shi = info.filter(i=>i.name == this.sheng)[0].city[0].name;
                this.xian = info.filter(i=>i.name == this.sheng)[0].city.filter(i=>i.name == this.shi)[0].area[0];
            }
        }
    }
</script>
示例代碼

3、props(★)

使用 Prop 傳遞數據

組件實例的做用域是孤立的。這意味着不能 (也不該該) 在子組件的模板內直接引用父組件的數據。父組件的數據須要經過 prop 才能下發到子組件中。

動態 Prop

與綁定到任何普通的 HTML屬性相相似,能夠用v-bind來動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件

 

1) 組件的使用:子組件不能直接改變父組件傳入的值,必須調用父組件的函數來改變(引用類型值能直接改)

2) 組件的思惟:全部子組件不須要對其它兄弟組件負責,只對父組件負責便可。


 

3.1傳基本類型值

若是想讓父組件data中的數據傳遞給子組件,須要使用標籤屬性傳遞(若是是動態值用v-bind

子組件須要使用props接收父組件的值,若是父組件中修改a值,同時會影響子組件。

 

App.vue父組件

<template>
    <div>
        <Haha a="8"></Haha>
        <Haha :b="a"></Haha>
    </div>
</template>
<script>
    import Haha from "./components/Haha.vue";
    export default {
        data(){
            return {
                a : 100
            }
        },
        components : {
            Haha
        }
    }
</script>

 

Haha.vue子組件

<template>
    <div>
        <h1>我是子組件{{a}} {{b}}</h1>
    </div>
</template>
<script>
    export default {
        props : ["a","b"],  //接收父組件傳過來的屬性的值
        data(){
            return {
          a : 100   //從父組件接收有a,這裏就不能有同名的a了
            }
        }
    }
</script>

3.2子組件要改props必須調用父親傳的函數

子組件不能直接改變父組件傳入的值,必須調用父組件的函數來改變

傳值就要傳它的改變函數給子組件,本質上仍是調用父親的函數去改變。

 

App.vue父組件傳值:

<template>
    <div>
        <h1>父組件{{a}}</h1>
        <button @click="add">父組件按鈕+</button>
        <button @click="minus">父組件按鈕-</button>

        <Haha :a="a" :add="add" :minus="minus"></Haha>
    </div>
</template>
<script>
    import Haha from "./components/Haha.vue";
    export default {
        data(){
            return {
                a : 100
            }
        },
        components : {
            Haha
        },
        methods : {
            add(){
                this.a++;
            },
            minus(){
                this.a--;
            }
        }
    }
</script>

 

Haha.vue子組件接收

<template>
    <div>
        <h1>我是子組件{{a}}</h1>
        <button @click="add">子組件按鈕+</button>
        <button @click="minus">子組件按鈕-</button>
    </div>
</template>
<script>
    export default {
        //子組件不能直接改父組件的數據,要改就必須傳父組件的函數來改變
 props : ["a","add","minus"], //接收父組件傳過來的屬性的值
        data(){
            return {

            }
        }
    }
</script>


3.3傳引用類型值能夠直接改

若是傳入引用類型值,子組件是能夠直接改父組件的值,而不報錯的。

 

App.vue父組件

<template>
    <div>
        <h1>父組件{{obj.a}}</h1>
        <Haha :obj="obj"></Haha>
        <button @click="add">父組件按鈕+</button>
    </div>
</template>
<script>
    import Haha from "./components/Haha.vue";
    export default {
        data(){
            return {
                obj : { a : 100 }
            }
        },
        components : {
            Haha
        },
        methods : {
            add(){ this.obj.a++; }
        }
    }
</script>

 

Haha.vue子組件

<template>
    <div>
        <h1>我是子組件{{obj.a}}</h1>
        <button @click="add">子組件按鈕+</button>
    </div>
</template>
<script>
    export default {
        // 子組件不能直接改父組件的數據,要改就必須傳父組件的函數來改變
 props : ["obj"], //接收父組件傳過來的屬性的值
        methods:{
            add(){ this.obj.a++ }
        }
    }
</script>

l 單向數據流

Prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,可是反過來不會。這是爲了防止子組件無心間修改了父組件的狀態,來避免應用的數據流變得難以理解。

另外,每次父組件更新時,子組件的全部 prop 都會更新爲最新值。這意味着你不該該在子組件內部改變 prop。若是你這麼作了,Vue 會在控制檯給出警告。

 


3.4字面量語法 vs 動態語法

使用v-bind是動態語法,不使用就是字符串

app.vue父組件:

<div>
    <Haha a="a"></Haha>
</div>
<script>
import Haha from "./components/Haha.vue"
export default {
    data() {
        return {
            a: 100
        }
    }
    components:{
        Haha
    }
}
</script>

 

Haha.vue子組件:

<template>
    <div> 
        <h1>{{typeof a}}</h1> 
    </div>
</template>
<script>
    export default{
        props:["a"]
    }
</script>

這樣傳遞獲得的是字符串的 "a"

 

初學者常犯的一個錯誤是使用字面量語法傳遞數值:

由於它是一個字面量 prop,它的值是字符串 "a" 而不是一個數值。若是想傳遞一個真正的 JavaScript 數值,則須要使用 v-bind,從而讓它的值被看成 JavaScript 表達式計算。


3.5組件化思惟-調色板案例

用組件從新作昨天的調色板和購物車案例。

v-model能夠綁定一個引用類型值。

若是props是一個引用類型值,而不是基本類型值,此時v-model能夠直接綁定修改,由vue內部實現對父組件的更改。

App.vue父組件

<template>
    <div>
        <div class="box" :style="{background:`rgb(${color.r},${color.g},${color.b})`}"></div>
        <!-- <Bar :v="r" name="r"></Bar>
        <Bar :v="g" name="g"></Bar>
        <Bar :v="b" name="b"></Bar> -->
        <Bar :color="color" name="r"></Bar>
        <Bar :color="color" name="g"></Bar>
        <Bar :color="color" name="b"></Bar>
    </div>
</template>
<script>
    import Bar from "./components/Bar.vue";
    export default{
        data(){
            return {
                // 這裏爲何要封裝一個對象
                // 由於vue有一個機制,若是子組件的v-model與父組件傳入的引用類型值綁定
                // 會自動幫你改父組件的值,而不會報錯。
                color : {
                    r : 100,
                    g : 100,
                    b : 100
                }
            }
        }
        components : {
            Bar
        }
    }
</script>
<style>
    .box{
        width: 200px;
        height: 200px;
    }
</style>
示例代碼

 

Bar.vue子組件

<template>
    <div>
        <input type="range" max="255" v-model="color[name]">
        <input type="number" max="255" v-model="color[name]">
    </div>
</template>
<script>
    export default{
        props:["color","name"],
        data(){
            return {

            }
        }
    }
</script>
示例代碼

3.6組件化思惟-購物車案例

is屬性解釋:https://segmentfault.com/q/1010000007205176

說白了,就是讓它顯示哪一個組件。

Bar.vue子組件:

<template>
    <tr>
        <td>{{item.id}}</td>
        <td>{{item.title}}</td>
        <td>{{item.price}}</td>
        <td>
            <button @click="minus">-</button>
            <input type="number" min="0" v-model.number="item.number">
            <button @click="add">+</button>
        </td>
        <td>
            {{item.number * item.price}}
        </td>
    </tr>
</template>
<script>
    export default {
        props : ["item"],
        data(){
            return {

            }
        },
        methods : {
            add(){
                // 能夠直接改,由於父親傳入引用類型值
                this.item.number++
            },
            minus(){
                if(this.item.number <= 0 ) return;
                this.item.number--
            }
        }
    }
</script>
示例代碼

 

App.vue父組件

<template>
    <div>
        <table>
            <tr>
                 <th>編號</th>
                 <th>商品</th>
                 <th>價格</th>
                 <th>數量</th>
                 <th>小計</th>
            </tr>
            <!-- <Bar v-for="item in carts" :item="item"></Bar> -->
            <tr is="Bar" v-for="item in carts" :item="item"></tr>
        </table>
        <h1>總價格:{{this.carts.reduce((a,b)=>a + b.price * b.number , 0)}}</h1>
    </div>
</template>
<script>
    import Bar from "./components/Bar.vue";
    export default {
        data(){
            return {
                carts : [
                    {"id":1,"title":"空調", "price":5000, "number":1},
                    {"id":2,"title":"手機", "price":4999, "number":1},
                    {"id":3,"title":"電腦", "price":6000, "number":1},
                    {"id":4,"title":"冰箱", "price":8000, "number":1}
                ]
            }
        },
        components : {
            Bar
        }
    }
</script>
示例代碼

3.7組件化思惟-全部子組件不須要對其它兄弟組件負責

組件的思惟:全部子組件不須要對其它兄弟組件負責,只對父組件負責便可。

教室:黑板、桌椅、側面宣傳欄、後黑板

只對父組件負責,不對其餘組件負責!!!

 

App.vue父組件:

<template>
    <div>
        <Heiban class="heiban" :banzhang="banzhang"></Heiban>
        <Zhuoyi :students="students" :changeBanzhang="changeBanzhang"></Zhuoyi>
        <Xuanchuanlan class="xuanchuanlan" :banzhang="banzhang"></Xuanchuanlan>
        <Houheiban class="houheiban" :banzhang="banzhang"></Houheiban>
    </div>
</template>
<script>
    import Zhuoyi from "./components/Zhuoyi.vue";
    import Houheiban from "./components/Houheiban.vue";
    import Xuanchuanlan from "./components/Xuanchuanlan.vue";
    import Heiban from "./components/Heiban.vue";
    export default {
        data(){
            return {
                students : ["小明","小紅","小強","小黑","小剛"], banzhang : "小明"
            }
        },
        components : { //註冊組件
 Heiban,Zhuoyi,Xuanchuanlan,Houheiban
        },
        methods : {
            changeBanzhang(banzhang){ this.banzhang = banzhang; //改變班長
 }
        }
    }
</script>
<style>
    .heiban, .houheiban{ width:650px; height:100px; border:1px solid #000; }
    .zhuoyi{ float:left; width:400px;height:300px; border:1px solid #000; }
    .xuanchuanlan{float:left; width:250px;height:300px; border:1px solid #000; }
</style>

 

Heiban.vueHouheiban.vueXuanchuanlan.vue子組件,都同樣:

<template>
    <div>
        我是黑板。我班班長:{{banzhang}} </div>
</template>
<script>
    export default {
        props : ["banzhang"]
    }
</script>

 

Zhuoyi.vue子組件:

<template>
    <div>
        <ul>
            <li v-for="item in students">
                {{item}}
                <button @click="changeBanzhang(item)">成爲班長</button>
            </li>
        </ul>
    </div>
</template>
<script>
    export default {
        props : ["students" , "changeBanzhang"]
    }
</script>
<style>
    li{float:left;width:100px;height:100px;margin:10px;background:orange;}
</style>

4、計算屬性(computed )★

4.1 computed屬性的使用

咱們如今export default(){ }中只寫了:data(){}methodscomponentspropswatch

如今學習computed,表示計算後的值,作實驗看看什麼意思。

注意:{{}}中若是放computed的值,是沒有圓括號的,雖然它是函數。

 

計算屬性的結果會被緩存,除非依賴的屬性(dataprops中的值)變化纔會從新計算。

data/props中對應數據發生改變時,計算屬性的值也會發生改變。

 

若是想觸發computed的函數,必須在頁面中使用,哪怕是display:none

computed就是返回一些和dataprops相關的值,dataprops的值變化時可以自動觸發。

computed就是計算最多的一個地方,這裏算法很重。


4.2關於methodscomputed的區別

http://www.javashuo.com/article/p-zynvanie-gr.html

l computed就是要監聽哪些數據變化時要用到的。當監聽的數據發生變化時,馬上會執行計算,並返回結果。

l methods只是定義函數的。如要執行,還得本身手動執行,來動態看成方法來用的。

 

l 相同:二者達到的效果是一樣的。

l 不一樣:

1.最明顯的不一樣,就是調用的時候,methods要加上()

2.可使用methods替代computed,效果上兩個都是同樣的,可是 computed 是基於它的依賴緩存,只有相關依賴發生改變時纔會從新取值。只要相關依賴未改變,只會返回以前的結果,再也不執行函數。

而使用 methods,在從新渲染時,函數總會從新調用執行。

能夠說使用 computed 性能會更好,可是若是你不但願緩存,你可使用 methods 屬性。

computed 屬性默認只有 getter ,不過在須要時你也能夠提供一個 setter :因此其實computed也是能夠傳參的。

 

<template>
    <div>
        <h1>{{a}}</h1>
        <h2>{{pingfang}}</h2>
        <button @click="add">+</button>
    </div>
</template>
<script>
    export default {
        data(){
            return {
                a:100
            }
        },
        computed:{
            pingfang(){
                return this.a * this.a
            }
        },
        methods:{
            add(){
                this.a++
            }
        }
    }
</script>
<style></style>
相關文章
相關標籤/搜索