1. 程式人生 > >VUE快速入門與實踐(上)

VUE快速入門與實踐(上)

簡介

vue官網 vue.js是一套構建使用者介面的漸進式框架。 vue全家桶:vue.js+vue-router+vuex+axios

特點

  1. 核心只關注檢視層(view)
  2. 易學,輕量,靈活的特點
  3. 適用於移動端專案
  4. 漸進式框架

漸進式的理解

  1. 宣告式渲染(無需關係如何實現)
  2. 組建系統
  3. 客戶端路由(vue-router)
  4. 大規模狀態管理(vuex)
  5. 構建工具(vue-cli)

核心

  1. 響應的資料變化。當資料發生改變->試圖的自動更新
  2. 組合的檢視元件。ui頁面對映為組建樹,劃分組建可維護、可複用、可測試

安裝vue

  1. 安裝vue npm install vue
  2. 安裝bootstrap npm install bootstrap
  3. 安裝 axiosnpm install axios
  4. 初始化npm npm init -y,初始化生成一個新的 package.json 檔案,如果使用了 -f(代表force)、-y(代表yes),則跳過提問階段,直接生成一個新的 package.json 檔案。

指令

  1. v-model:用於表單中的input ,textarea中,會忽略value,checked,selected,將資料繫結在試圖上,檢視修改後會影響資料的變化。同angularJS中的ng-model
  2. v-text:雙向繫結資料,同{{}}。<div v-text="msg"></div>
  3. v-cloak
    :解決{{}}閃爍問題(作用於塊區域),要賦予樣式才起作用,[v-cloak]{display:none;}
  4. v-once:一次繫結資料。<div v-once>{{msg}}</div>
  5. v-html:繫結html,一定是安全資料。<div v-html="msg"></div>
  6. v-for:迴圈陣列,同angularJS中的指令ng-repeat 例: o in arr 或 (o,index) in arr a in obj 或 (value,key,index) in obj
  7. v-bind:動態繫結屬性。v-bind:src="" 或 :src=""
  8. v-if:同angularJS ng-if,if操作的是dom
  9. v-show:同angularJS ng-show,show操作的是樣式。如果頻繁的切換dom使用v-show,如果一開始就確定下來使用v-if更好,如果if不通過內部指令不會再執行。只用dom從顯示到隱藏或隱藏到顯示菜使用vue動畫
  10. 繫結事件 v-on:click="fn" 或 @click="fn"
 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件</title>
</head>
<body>
<div id="app">
    <!-- 如果不傳遞引數,則不要寫括號會自動傳入事件源,如果寫括號要手動傳入$event -->
    <div v-on:mousedown="fn()">點我</div><br>
    <!-- 事件繫結可以用@代替-->
    <div @mousedown="fn2(2)">點我</div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
    let vm=new Vue({//根例項
        el:'#app',
        methods:{//methods和data中的資料會全部放到vm上,而且名字不能衝突,衝突會報錯,methods中的this指向的都是例項,不能使用箭頭函式
            fn(){alert(this.a)},
            fn2(i){alert(i)}
        },
        data:{
            a:[1,2,3,4,5],
        }
    });
</script>
</body>
</html>

資料響應的變化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>資料響應的變化(物件)</title>
</head>
<body>
<div id="app">
    {{msg}}{{a.school}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
    //引入vue後會給一個Vue的建構函式
    //vue會迴圈data中的資料(資料劫持),依次的增加getter和setter
    let vm=new Vue({//vm===viewModel
        el:'#app',//vue控制的div
        data:{//data的中的資料會被vue代理
            msg:'hello world!',//可以通過vm.msg取得相對應的資料
            a:{school:''}//1.使用變數時先進行初始化,否則新新增的屬性不會導致頁面重新整理(推薦)
        }
    });
    //2.如果沒有初始化資料,可以通過$set方法來給物件新增響應式資料
    vm.$set(vm.a,'school',1);
    //3.通過替換原物件初始化資料
    vm.a={school:''};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>資料響應的變化(陣列)</title>
</head>
<body>
<div id="app">
    {{arr}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        data:{
            arr:[1,2,3,4,5],
        }
    });
    //去改變陣列中的某一項時監控不到的,也不能使用改變陣列長度的方法
    //錯誤:vm.arr[0]=100;vm.arr.length=2;
    //可以使用變異方法:pop push shift unshift sort reserve splice
    vm.arr=vm.arr.map(item=>item*3);//filter map
</script>
</body>
</html>

checkbox,select,radio

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>checkbox,select,radio</title>
</head>
<body>
<div id="app">
    <div>
        <input type="checkbox" v-model="hobby" value="0">游泳
        <input type="checkbox" v-model="hobby" value="1">跑步
        <input type="checkbox" v-model="hobby" value="2">射箭<br>
        愛好:{{hobby}}
    </div><br>
    <div>
        <select v-model="address">
            <option value="" disabled>請選擇</option>
            <option value="1">上海</option>
            <option value="2">北京</option>
            <option value="3">天津</option>
        </select><br>
        地址:{{address}}
    </div><br>
    <div>
        <input type="radio" v-model="sex" value="1">男
        <input type="radio" v-model="sex" value="2">女
        性別:{{sex}}
    </div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        data:{
            hobby:[],
            address:'',
            sex:''
        }
    });
</script>
</body>
</html>

Promise

//回撥函式:將後續的處理邏輯傳入到當前要做的事,事情做好後呼叫此函式
function buy(callback) {
    setTimeout(()=>{
        let a='蘑菇';
        callback(a);
    },2000);
}
buy(function (val) {
    console.log(val);
});
//可用promise處理回撥問題,共三個狀態:成功、失敗、等待
//resolve對應data,reject對應err,resolve和reject均是函式
//promise的例項就是一個then方法,then方法中有兩個引數
var p=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        let a='青椒';
        resolve(a);
    },2000);
});
p.then((data)=>{
    console.log(data);
},(err)=>{

});
//promise示例
function buyPack() {
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            if(Math.random()>0.5){
                resolve('買');
            }else {
                reject('不買');
            }
        },Math.random()*1000);
    });
}
buyPack().then((data)=>{
    console.log(data);
},(data)=>{
    console.log(data);
});

Promise-ajax

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>promise-ajax</title>
</head>
<body>
<div id="app">

</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="assets/js/promise-ajax.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        created(){
            ajax({url:'carts.json'}).then((data)=>{
                console.log(data);
            })
        },
        data:{
            products:[]
        }
    });
    function ajax({url='',type='get',dataType='JSON'}) {
	    return new Promise((resolve,reject)=>{
	        let xhr=new XMLHttpRequest();
	        xhr.open(type,url,true);
	        xhr.responseType=dataType;
	        xhr.onload=function () {//shr.readyState=4  shr.status=200
	            if(xhr.status==200){
	                resolve(xhr.response);//成功呼叫成功的方法
	            }else{
	                reject('not found');//失敗呼叫失敗的方法
	            }
	
	        };
	        xhr.onerror=function (err) {
	            reject(err);//失敗呼叫失敗的方法
	        };
	        xhr.send();
	    });
	}
</script>
</body>
</html>

carts.json檔案

[
  {
    "isSelected":true,
    "name":"由淺入深學習vue",
    "img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
    "info":"小米(MI) 米家自動傘 男女通用晴雨兩用 一鍵自動開合 黑色",
    "price":99,
    "count":3
  },
  {
    "isSelected":true,
    "name":"由淺入深學習vue",
    "img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
    "info":"小米(MI) 米家自動傘 男女通用晴雨兩用 一鍵自動開合 黑色",
    "price":99,
    "count":3
  }
]

axios

更多關於axios,點選這裡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>axios</title>
</head>
<body>
<div id="app">
<input type="number" v-model.number="num">
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        //專門用來發送Ajax的方法
        created(){//再資料被初始化後會呼叫(鉤子函式),this指向是vm例項
            let that=this;
            axios.get('carts.json').then(function (res) {
                that.products=res.data;
            },function (err) {
                console.log(err);
            });
            //也可以用箭頭函式
            axios.get('carts.json').then(res=> {
                this.products=res;
            },err=> {
                console.log(err);
            })
        },
        data:{
            products:[],
            num:0
        }
    });
</script>
</body>
</html>

filter,created,computed

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>購物車</title>
    <link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
    <div class="container">
        <table class="table table-bordered table-hover">
            <tr>
                <th>全選<input type="checkbox" v-model="selectAll"></th>
                <th>商品</th>
                <th>描述</th>
                <th>單價</th>
                <th>數量</th>
                <th>總價</th>
                <th>操作</th>
            </tr>
            <tr v-for="(product,index) in products">
                <td><input type="checkbox" v-model="product.isSelected"></td>
                <td><img :src="product.img">{{product.name}}</td>
                <td>{{product.info}}</td>
                <td>{{product.price}}</td>
                <td><input type="number" v-model.number="product.count" min="1"></td>
                <td>{{product.price*product.count|toFixed(2)}}</td>
                <td><input type="button" class="btn btn-danger" value="刪除" @click="remove(product)"></td>
            </tr>
            <tr>
                <td>總價</td>
                <td>{{sum}}</td>
            </tr>
        </table>
    </div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        computed:{//用來計算
            selectAll:{//放棄賦予複選框change方法後,可以通過“計算屬性”實現全選與單選
                //當products值變化時會重新計算
                get(){//get和set中的this指向例項,預設v-model會獲取selectAll的值,多以呼叫get方法
                    return this.products.every(p=>p.isSelected);
                },set(val){//當給checkbox賦值時
                    this.products.forEach(p=>p.isSelected=val);
                }
            },
            sum(){//如果計算屬性寫成函式,預設呼叫get方法,等價於下面的sum屬性
                return this.products.reduce((pre,next)=>{
                    if(!next.isSelected) return pre;
                    return pre+next.price*next.count;
                },0)
            },
            // sum:{//總價,sum結果會被快取,如果依賴的資料沒有變化就不會重新執行
            //     get(){
            //         return this.products.reduce((pre,next)=>{
            //             if(!next.isSelected) return pre;
            //             return pre+next.price*next.count;
            //         },0)
            //     }
            // }
        },
        filters:{//過濾器
            toFixed(n,s){//這裡的this指的是window
                return '¥'+n.toFixed(s);
            }
        },
        created(){//在資料被初始化後會呼叫(鉤子函式),this指向是vm例項
            this.getData();
        },
        methods:{
            // checkOne(){//單選
            //     this.selectAll=this.products.every(item=>item.isSelected);
            // },
            // checkAll(){//全選/反選
            //     this.products.forEach(item=>item.isSelected=this.selectAll);
            // },
            // sum(){//總價,資料只要有變化就會呼叫此方法,不會快取上一次的結果,computed會解決這個問題
            //     return this.products.reduce((pre,next)=>{
            //         return pre+next.price*next.count;
            //     },0)
            // },
            remove(p){//刪除
                this.products=this.products.filter(item=>item!=p);
            },
            getData(){
                axios.get('carts.json').then(res=> {
                    this.products=res.data;
                    // this.checkAll();
                },err=> {
                    console.log(err);
                })
            }
        },
        data:{
            products:[]
            // selectAll:false
        }
    });
</script>
</body>
</html>

全域性filter

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>全域性filter</title>
</head>
<body>
<div id="app1">{{info|select}}</div>
<div id="app2">{{info|select}}</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
    Vue.filter('select',(data)=>data+'小紅');//全域性filter
    new Vue({
        el:'#app1',
        // filters:{
        //     choose(data){
        //         return data+'小紅';
        //     }
        // },
        data:{
            info:'hello'
        }
    });
    new Vue({
        el:'#app2',
        data:{
            info:'hello'
        }
    });
</script>
</body>
</html>

vue動畫

  • v-if:操作dom,可以和v-else-if,v-else連寫
  • v-show:操作的是樣式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue動畫</title>
    <link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
    <style>
        .one-enter{opacity: 1;}
        .one-leave-active{opacity: 0;transition: 1s linear}
        /*如果只有一個動畫可這樣寫*/
        /*.v-enter{opacity: 1;}*/
        /*.v-leave-active{opacity: 0;transition: 1s linear}*/
    </style>
</head>
<body>
<div id="app">
    <input type="button" @click="flag=!flag" value="切換">
    <transition name="one"><!-- 當存在多個動畫時,可設定name區分 -->
        <div style="width: 100px;height: 100px;background: red;" v-show="flag"></div>
    </transition>
    <input type="text" v-model="query">
    <transition-group name="two" enter-active-class="animated bounceInUp" leave-active-class="animated bounceOutDown">
        <div v-for="(a,index) in newArr" :key="Math.random()">{{a}}</div>
    </transition-group>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        computed:{
            newArr(){
                return this.arr.filter(item=>item==this.query);
            }
        },
        data:{
            query:'',
            arr:[1,2,3,4,5],
            flag:true
        }
    });
</script>
</body>
</html>

v-ifv-else 例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-if,else</title>
</head>
<body>
<div id="app">
<div v-if="flag">紅色</div>
<div v-else>藍色</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        data:{
            flag:false
        }
    });
</script>
</body>
</html>

修飾符

可進入官網 檢視修飾符內容

表單輸入繫結修飾符

  1. .lazy:在預設情況下,v-model 在每次 input 事件觸發後將輸入框的值與資料進行同步 (除了上述輸入法組合文字時)。你可以新增 lazy 修飾符,從而轉變為使用 change事件進行同步:
  2. .number:限制輸入框只能填寫數字 。<input type="number" v-model.number="num">
  3. .trim:自動過濾使用者輸入的首尾空白字元。<input v-model.trim="msg">

事件修飾符

  1. .stop:阻止繼續冒泡,向上傳播。阻止事件傳播。同e.stopPropagationcancelBubble=true 效果相同
  2. .capture:停止捕獲,向下傳播。阻止事件傳播。類似:xxx.addEventListener('clicl',fn,true)
  3. .prevent:阻止預設行為。如a標籤的跳轉。類似:e.preventDefaultreturnValue=false
  4. .once:只觸發一次
  5. .self:只有觸發自身事件源才會呼叫。類似:e.srcElement&&e.target
<!-- 新增事件監聽器時使用事件捕獲模式 -->
<!-- 即元素自身觸發的事件先在此處處理,然後才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只當在 event.target 是當前元素自身時觸發處理函式 -->
<!-- 即事件不是從內部元素觸發的 -->
<div v-on:click.self="doThat">...</div>

computed

  • computed 計算屬性,它不是方法。
  • 方法不會快取,computed 會根據依賴的屬性進行快取(歸vue管理的資料,可以響應式變化的)。
  • getset 兩部分組成(不能只寫set,可以只寫get)。一般情況下,通過js賦值影響其他元素或者表單元素設定值的時候會呼叫set方法。
  • computed預設呼叫get方法,必須要有return,而且不支援非同步
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>computed</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        computed:{//computed預設呼叫get方法,必須要有return,而且不支援非同步
            msg(){
                if(this.a.length<3) return '少了';
                if(this.a.length>6) return '多了';
                return '';
            }
        },
        data:{
            a:''
        }
    });
</script>
</body>
</html>

watch

  • 偵聽器
  • 只有值變化的時候才會觸發,並且支援非同步。其他情況我們更善於使用computed
  • watch的屬性名字要和觀察的名字一樣
  • 當你有一些資料需要隨著其它資料變動而變動時,你很容易濫用 watch——特別是如果你之前使用過 AngularJS。然而,通常更好的做法是使用計算屬性而不是命令式的 watch 回撥。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>watch</title>
</head>
<body>
<div id="app">
    <input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        watch:{//只有值變化的時候才會觸發,並且支援非同步。其他情況我們更善於使用computed
            a(newVal,oldVal){//watch的屬性名字要和觀察的名字一樣
                if(newVal.length<3){
                    this.msg='太少';
                }else if(newVal.length>6){
                    this.msg='太多';
                }else{
                    this.msg='';
                }
            }
        },
        data:{
            a:'',
            msg:''
        }
    });
</script>
</body>
</html>

template

  • template標籤是vue提供給我們的沒有任何意義,只是用來包裹元素用的。
  • v-show不支援template
  • 預設情況下切換dom時相同的結構會被複用,如果不需要複用,需要新增key屬性
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-if,else</title>
</head>
<body>
<div id="app">
    <!-- template標籤是vue提供給我們的沒有任何意義,只是用來包裹元素用的。v-show不支援template-->
    <template v-if="flag">
        <div>紅色</div>
        <div>紅色</div>
        <div>紅色</div>
        <div>紅色</div>
        <div>紅色</div>
    </template>
    <div v-else>藍色</div>
    <br><br>
     <!-- 預設情況下切換dom時相同的結構會被複用,如果不需要複用,需要新增key-->
    <input type="button" value="切換" @click="cut=!cut">
    <template v-if="cut">
        <label>登入</label>
        <input type="text" key="1">
    </template>
    <template v-else>
        <label>註冊</label>
        <input type="text" key="2">
    </template>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        data:{
            flag:true,
            cut:true
        }
    });
</script>
</body>
</html>

:class :style

class和style的繫結有兩種形式:一、物件,二、陣列

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-bind-class,v-bind-style</title>
    <style>
        .x {color: red;}
        .y {font-size: 30px;}
        .z {background: antiquewhite;}
    </style>
</head>
<body>
<div id="app">
    <p :class="{x:flag,y:true,z:false}">測試</p>
    <p :class="[class1,class2,{z:true}]">測試</p>

    <p :style="{color:'red',fontSize:'30px',background:'antiquewhite'}">測試</p>
    <p :style="[style1,style2,{background:'antiquewhite'}]">測試</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        data:{
            class1:'x',
            class2:'y',
            style1:{color:'red'},
            style2:{fontSize:'30px'},
            flag:true,
            cut:true
        }
    });
</script>
</body>
</html>

實現單頁開發的方式

  • 通過hash(#)記錄跳轉的路徑(可以產生歷史管理)
  • 瀏覽器自帶的歷史管理的方法history(history。pushState()),但可能會導致404錯誤
  • 開發時使用hash的方法,上線的時候使用history的方法

自定義指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定義指令</title>
    <style>
        .box {
            width: 100px;
            height: 100px;
            background: red;
            position: absolute;;
        }
    </style>
</head>
<body>
<div id="app">
    <button v-color="flag">變色</button>
    <div class="box" v-drag></div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let vm=new Vue({
        el:'#app',
        directives:{
            color(el,bindings){//el指代的是元素本身,這裡指的是button
                el.style.background=bindings.value;
            },
            drag(el,bindings){
                el.onmousedown=function(e){
                    var disX=e.pageX-el.offsetLeft;
                    var disY=e.pageY-el.offsetTop;
                    document.onmousemove=function (e) {
                        el.style.left=e.pageX-disX+'px';
                        el.style.top=e.pageY-disY+'px';
                    };
                    e.preventDefault();
                }
            }
        },
        data:{
            flag:'red'
        }
    });
</script>
</body>
</html>

元件

  • 分類是頁面級的,1.一個頁面是一個元件;2.將可複用的部分抽離出來形成的基礎元件
  • 全域性元件:可以宣告一次在任何地方使用
  • 區域性元件:必須告訴這個元件屬於誰
  • 一般寫外掛用的是全域性元件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>全域性元件</title>
</head>
<body>
<div id="app">
   <my-remark></my-remark>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //1.元件名不要帶有大寫,多個單詞用-連線
    //2.只要元件名和定義名字相同就可以,但只有首字母可以大寫
    //3.html採用短橫線隔開命名法,js中轉駝峰也是可以的
    Vue.component('my-remark',{
        template:'<div>{{msg}}測試啦</div>',
        data(){//元件中的資料必須是函式型別,返回一個例項作為元件的資料
             return {msg:'啦啦啦,'}
        }
    });
    let vm=new Vue({
        el:'#app',
    });
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>區域性元件</title>
</head>
<body>
<div id="app">
   <component1></component1>
   <component2></component2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //區域性元件使用三部曲:1.建立元件,2.註冊元件,3.使用元件
    //元件是相互獨立的,不能之間跨作用域。例項也是一個元件,元件中擁有宣告周期函式
    let obj={school:'lalala'};//如果元件公用了資料,會導致同時更新(獨立性)
    //子元件不能直接使用父元件的資料(元件之間的資料互動)
    //元件理論上可以無限巢狀
    let component1={
        template:'<div @click="fn">元件1{{school}}</div>',
        data(){
            return obj;
        },
        methods:{
            fn(){
                this.school='hello'
            }
        }
    };
    let component2={
        template:'<div>元件2{{school}}</div>',
        data(){
            return obj;
        }
    };
    let vm=new Vue({
        el:'#app',
        components:{
            component1,component2
        }
    });
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>巢狀元件</title>
</head>
<body>
<div id="app">
   <parent></parent>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //如果要再一個元件使用另一個元件,1.先保證使用的元件是真實存在的,2.在需要引用這個元件的例項上通過components註冊這個元件,3.元件需要在父級的模板中通過標籤的形式引入
    let grandson={
        template:'<h3>grandson</h3>'
    };
    let son={
        template:'<h2>son<grandson></grandson></grandson></h2>',
        components:{
            grandson
        }
    };
    let parent={
        template:'<h1>parent<son></son></h1>',
        components:{
            son
        }
    };
    let vm=new Vue({
        el:'#app',
        components:{
            parent
        }
    });
</script>
</body>
</html>
  • 父傳子
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父傳子</title>
</head>
<body>
<div id="app">
    父親:{{money}}
    <!-- 當前元件的屬性=父級的值。給兒子加了一個m屬性,屬性對應的資料是屬於父親的-->
    <child :m="money"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //父傳遞子
    let vm=new Vue({//parent
        el:'#app',
        data:{money:200},
        components:{
            child:{
                props:{//屬性名和data中名字不能相同,校驗時不能阻斷程式碼的指向,只是警告而已
                    m:{//校驗屬性的型別,如果不帶:得到的肯定時字串型別:m='1'  :m='true'
                        type:[String,Function,Object,Array,Number,Boolean],
                        // default:0,//可以給m賦予預設值,如果不傳預設會呼叫default
                        required:true,//次屬性是限制必須傳遞,但不能給default同用
                        validator(val){//第一個引數是當前傳遞的值,返回true標識通過,false不通過
                            return val>300;//自定義校驗器(用的不是很多)
                        }
                    }
                },//物件的形式可以通過校驗
                // props:['m'],//this.m=200;會在當前子元件上宣告一個m屬性值是父親的
                template:'<div>兒子:{{m}}</div>'
            }
        }
    });
</script>
</body>
</html>
  • 釋出訂閱
//釋出emit 訂閱on
function Girl() {
    this._events={};
}
Girl.prototype.on=function (eventName,callback) {
    if(this._events[eventName]){//不是第一次
        this._events[eventName].push(callback);//{失戀:[cry,shopping,eat]}
    }else{
        this._events[eventName]=[callback]//{失戀:[cry]}
    }
};
Girl.prototype.emit=function (eventName,...args) {
    //[我,你,他]
    //[].slice.call(arguments,1);
    //Array.from(arguments).slice(1);
    this._events[eventName].forEach(cb=>cb(args));
};
let girl=new Girl();
let cry=(who)=>{console.log(who+'哭')};
let shopping=(who)=>{console.log(who+'購物')};
let eat=(who)=>{console.log(who+'吃')};
girl.on('失戀',cry);//{失戀:[cry]}
girl.on('失戀',shopping);//{失戀:[cry,shopping]}
girl.on('失戀',eat);//{失戀:[cry,shopping,eat]}
girl.emit('失戀','我','你');

arguments 剩餘引數

  • sync修飾符
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>sync修飾符</title>
</head>
<body>
<div id="app">
    <!-- 使用.sync修飾符同步資料 -->
    父親:{{money}}
    <!--<child :m="money" @update:m="val=>this.money=val"></child>-->
    <child :m.sync="money"></child>
    <!-- 寫的時候還是按原有的方法來寫即可,即註釋的方法 -->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //父親繫結一些事件,兒子觸發這個事件將引數傳遞過去,單向資料流,父親資料重新整理,兒子就重新整理
    let vm=new Vue({//parent
        el:'#app',
        data:{money:200},
        components:{
            child:{
                props:['m'],
                template:'<div>兒子:{{m}}<button @click="getMoney">多要錢</button></div>',
                methods:{
                    getMoney(){
                        this.$emit('update:m',400);//觸發自定義事件
                    }
                }
            }
        }
    });
</script>
</body>
</html>
  • 例項
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>彈窗</title>
    <style>
        .mask {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0,0,0,.3);}
        .pop {position: fixed;top: 50%;left: 50%;width: 500px;height: 200px;background: #fff;transform: translate3d(-50%,-50%,0);}
    </style>
</head>
<body>
<div id="app">
    <button @click="open">點選</button>
    <pop :shows="flag" @close="()=>flag=false"></pop>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let pop={
        props:['shows'],
        template:' <div class="mask" v-show="shows">' +
                    '<div class="pop">' +
                        '<button @click="down">關閉彈窗</button>' +
                    '</div>' +
                 '</div>',
        methods:{
            down(){
                this.$emit('close');
            }
        }
    };
    let vm=new Vue({//parent
        el:'#app',
        data:{flag:false},
        components:{
            pop
        },
        methods:{
            open(){
                this.flag=true;
            }
        }
    });
</script>
</body>
</html>
  • slot插槽
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot插槽</title>
</head>
<body>
<div id="app">
    <container><h1 slot="title">標題</h1><p slot="content">內容</p><span>預設內容嗎</span></container>
    <!--<box>測試啦</box>-->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let container={
        template:'<div><slot name="title"></slot><slot name="content"></slot><slot name="default"></slot></div>',
    };
    let vm=new Vue({//parent
        el:'#app',
        components:{
            container
        }
    });
    //最基本的方法
    // let box={
    //     //slot中的內容為元件標籤內容為空時的預設內容,可不填
    //     template:'<div><slot>預設內容</slot></div>',//預設slot擁有name為default的屬性,若只有一個slot,可不填寫name屬性
    // };
    // let vm=new Vue({//parent
    //     el:'#app',
    //     components:{
    //         box
    //     }
    // });
</script>
</body>
</html>
  • ref 父呼叫子方法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父呼叫子方法,ref獲取真實dom</title>
</head>
<body>
<div id="app">
   <loading ref="load"></loading>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //父呼叫子方法
    let loading={
        data(){
            return {flag:true};
        },
        template:'<div v-show="flag">瘋狂載入中...</div>',
        methods:{
            hide(){
                this.flag=false;
            }
        }
    };
    let vm=new Vue({
        el:'#app',
        mounted(){
            console.log(this.$refs.load);
            this.$refs.load.hide();
            // this.$refs.load.$el.style.color='red';
        },
        components:{
            loading
        },
    });
</script>
</body>
</html>
  • keep-alive <keep-alive> 包裹動態元件時, 會快取不活動的元件例項,而不是銷燬它們。和 <transition> 相似,<keep-alive> 是一個抽象元件:它自身不會渲染一個 DOM 元素,也不會出現在父元件鏈中。 當元件在 <keep-alive> 內被切換,它的 activateddeactivated 這兩個生命週期鉤子函式將會被對應執行。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>keep-alive,component標籤</title>
</head>
<body>
<div id="app">
    <input type="radio" v-model="flag" value="home" name="flag">home
    <input type="radio" v-model="flag" value="list" name="flag">list
    <!-- component是vue自帶的標籤,template,slot,transition同樣也是-->
    <!-- 這種方法在跳轉的時候會銷燬前一個元件-->
    <component :is="flag"></component>
    <!-- 可以新增keep-alive標籤,就不會銷燬元件-->
    <!-- 一般用作快取,為的是後面的路由做準備-->
    <keep-alive>
        <component :is="flag"></component>
    </keep-alive>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //父呼叫子方法
    let home={
        template:'<div>home</div>',
        beforeDestroy(){
            alert('銷燬');
        }
    };
    let list={
        template:'<div>list</div>',
        beforeDestroy(){
            alert('銷燬');
        }
    };
    let vm=new Vue({
        el:'#app',
        data:{
            flag:'home'
        },
        components:{
            home,list
        },
    });
</script>
</body>
</html>

元件非同步渲染問題

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>元件渲染掛載問題$nextTick</title>
</head>
<body>
<div id="app">
    <child ref="child"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    //子元件和父元件同時擁有mounted,先執行子元件,後執行父元件
    let child={
        data(){
            return {arr:[1,2,3]}
        },
        template:'<div><li v-for="a in arr">{{a}}</li></div>',
        mounted(){
            this.arr=[4,5,6];//此處是非同步渲染dom
            alert('child');
        },
    };
    let vm=new Vue({
        el:'#app',
        mounted(){
            alert('parent');
            console.log(this.$refs.child.$el.innerHTML);//結果是<li>1</li><li>2</li><li>3</li>
            //頁面顯示是4,5,6,為什麼父元件列印的是1,2,3。因為mounted是非同步渲染的,所以此時需要$nextTick,使用mounted一定要使用$nextTick
            this.$nextTick(()=>{
                console.log(this.$refs.child.$el.innerHTML);
            })
        },
        components:{
            child
        },
    });
</script>
</body>
</html>
  • slot實踐
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot</title>
    <link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
    <panel type="primary" @say-title="parent">
        <div slot="title">怎樣學習vue</div>
        <div slot="body">
            <p>js基礎</p>
            <p>vue基礎</p>
            <p>vue進階</p>
        </div>
        <div slot="foot">作者:小明</div>
    </panel>
</div>
<template id="panel">
    <div class="panel" :class="[color]">
        <div class="panel-heading" ref="head">
            <slot name="title"></slot>
        </div>
        <div class="panel-body">
            <slot name="body"></slot>
        </div>
        <div class="panel-footer">
            <slot name="foot">無名</slot>
        </div>
        <button class="btn btn-default" @click="getTitle">點選獲取標題</button>
    </div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let panel={
        template:'#panel',
        computed:{
            color(){
                return 'panel-'+this.type
            }
        },
        methods:{
            getTitle(){
                this.$emit('say-title',this.$refs.head.innerText);
            }
        },
        props:{
            type:{//this.type='primary',子不能更改父元件傳遞的屬性
                type:[String],
                default:'default'
            }
        }
    };
    let vm=new Vue({
        el:'#app',
        components:{
            panel
        },
        methods:{
            parent(val){
                alert(val);
            }
        }
    });
</script>
</body>
</html>
  • 元件的迴圈
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>元件的迴圈</title>
    <link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
    <!-- 在2.2.0版本以上,當元件使用v-for時,key是必須的 -->
    <panel :type="article.type" @say-title="parent" v-for="(article,index) in articles" :key="index">
        <div slot="title" v-html="article.title"></div>
        <div slot="body">{{article.content}}</div>
        <div slot="foot">{{article.auth}}</div>
    </panel>
</div>
<template id="panel">
    <div class="panel" :class="[color]">
        <div class="panel-heading" ref="head">
            <slot name="title"></slot>
        </div>
        <div class="panel-body">
            <slot name="body"></slot>
        </div>
        <div class="panel-footer">
            <slot name="foot">無名</slot>
        </div>
        <button class="btn btn-default" @click="getTitle">點選獲取標題</button>
    </div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
    let panel={
        template:'#panel',
        computed:{
            color(){
                return 'panel-'+this.type
            }
        },
        methods:{
            getTitle(){
                this.$emit('say-title',this.$refs.head.innerText);
            }
        },
        props:{
            type:{//this.type='primary',子不能更改父元件傳遞的屬性
                type:[String],
                default:'default'
            }
        }
    };
    let vm=new Vue({
        el:'#app',
        components:{
            panel
        },
        methods:{
            parent(val){
                alert(val);
            }
        },
        data:{
            articles:[
                {type:'primary',title:'<h1>vue</h1>',content:'這是vue文章',auth:'作者:小明'},
                {type:'success',title:'<h1>react</h1>',content:'這是react文章',auth:''},
                {type:'info',title:'<h1>angularJs</h1>',content:'這是angularJs文章',auth:'作者:小紅'},
            ]
        }
    });
</script>
</body>
</html>

路由

  • 宣告式路由
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>宣告式路由</title>
    <style>
        /*.router-link-active {color: red;}*/
        .active {color: red;}
    </style>
</head>
<body>
<!-- hash模式#,開發時使用,不會導致404,但不支援seo-->
<!-- h5的history.pushState,上線後採用h5的跳轉方式-->
<div id="app">
    <!-- 宣告式導航 -->
    <router-link to="/home" tag="button">首頁</router-link>
    <router-link to="/list" tag="button">列表頁</router-link>
    <router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
    let home={template:'<div>首頁</div>'};
    let list={template:'<div>列表頁</div>'};
    let routes=[//路由的對映表,配置路徑和元件的關係
        {path:'/home',component:home},//配置的關係就是頁面級元件
        {path:'/list',component:list}//路徑必須加/
    ];
    let router=new VueRouter({//引入vue-router自帶VueRouter類
        //vue-router 預設 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新載入。如果不想要很醜的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新載入頁面。
        mode:'history',
        routes,
        linkActiveClass:'active'//將router-link按鈕自帶觸發類名改為active
    });
    let vm=new Vue({
        el:'#app',
        router
    });
</script>
</body>
</html>
  • 程式設計式路由
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>程式設計式路由</title>
    <style>
        /*.router-link-active {color: red;}*/
        .active {color: red;}
    </style>
</head>
<body>
<div id="app">
    <!-- 程式設計式導航 -->
    <router-link :to="{path:'/home'}" tag="button">首頁</router-link>
    <router-link :to="{path:'/list'}" tag="button">列表頁</router-link>
    <router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
    let home={
        template:'<div>首頁<button @click="goList">列表頁</button></div>',
        methods:{
            goList(){
                this.$router.push('/list');
                // this.$router.replace('/list');不常用
            }
        }
    };
    let list={
        template:'<div>列表頁<button @click="back">返回</button></div>'
        ,methods:{
            back(){
                this.$router.go(-1);//返回上一級
            }
        }
    };
    let routes=[//路由的對映表,配置路徑和元件的關係
        {path:'',component:home},//預設的路由
        {path:'/home',component:home},//配置的關係就是頁面級元件
        {path:'/list',component:list},//路徑必須加/
        // {path:'*',component:list}//當沒有可匹配的頁面時,展示這個頁面。但這個地方路徑不會變,只是切換了元件而已
        {path:'*',redirect:'/home'}//路徑變,元件也會變。常用於404
    ];
    let router=new VueRouter({//引入vue-router自帶VueRouter類
        routes,
        linkActiveClass:'active'//將router-link按鈕自帶觸發類名改為active
    });
    let vm=new Vue({
        el:'#app',
        router//定義router後,每個元件都會擁有$router(有r放的都是方法)和$route(沒有r放的都是屬性)
    });
</script>
</body>
</html>
  • 路由巢狀
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>路由巢狀</title>
</head>
<body>
<div id="app">
    <router-link to="/home">首頁</router-link>
    <router-link to="/detail">詳情頁</router-link>
    <router-view></router-view>
</div>
<template id="detail">
    <div>
        <router-link to="/detail/profile">個人中心</router-link>
        <router-link to="/detail/about">關於我們</router-link>
        <router-view></router-view>
    </div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
    let home={template:'<div>home</div>'};
    let detail={template:'#detail'};
    let profile={template:'<div>profile</div>'};
    let about={template:'<div>about</div>'};
    let routes=[
        {path:'/home',component:home},
        {
            path:'/detail',
            component:detail,
            children:[//children中的路徑永遠不帶/,如果帶/表示1級路由
                {path:'profile',component:profile},
                {path:'about',component:about}
            ]
        }
    ];
    let router =new VueRouter({
        routes
    });
    let vm=new Vue({
        el:'#app',
        router
    });
</script>
</body>
</html>
  • 路由引數
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>路由引數</title>
</head>
<body>
<div id="app">
    <!-- 如果用物件作為to的屬性,並且使用了引數,必須給路由起一個名字,通過名字跳轉-->
    <router-link :to="{name:'pro',params:{num:1,id:'a'}}">商品1</router-link>
    <router-link to="/article/2/b">商品2</router-link>
    <router-link to="/article/3/c">商品3</router-link>
    <router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
    let article={
        template:'<div>這是第{{$route.params.num}}篇文章</div>',
        watch:{//路徑引數發生變化,監控引數的變化來發送Ajax
            $route(){
                alert('ajax');
            }
        }
    };
    let routes=[
        {path:'/article/:num/:id',component:article,name:'pro'},
    ];
    let router =new VueRouter({
        routes
    });
    let vm=new Vue({
        el:'#app',
        router
    });
</script>
</body>
</html>
  • 路由動畫
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>路由動畫</title>
    <link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
</head>
<body>
<div id="app">
    <router-link to="/home" tag="button">首頁</router-link>
    <router-link to="/list" tag="button">列表頁</router-link>
    <!-- mode="out-in"動畫的模式,預設是in-out-->
    <!-- 要想呈現相對流暢的動畫,可以設定檢視為絕對位置-->
    <transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
        <router-view style="position: absolute;top: 50px;left: 0;width: 100%;height: 50px;"></router-view>
    </transition>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
    let home={template:'<div style="background: red">首頁</div>'};
    let list={template:'<div style="background: green">列表頁</div>'};
    let routes=[
        {path:'/home',component:home},
        {path:'/list',component:list}
    ];
    let router=new VueRouter({
        routes
    });
    let vm=new Vue({
        el:'#app',
        router
    });
</script>
</body>
</html>