vue整合筆記

vue

前言:整合了以前的筆記,加入新記的筆記信息
javascript

目錄

一 、簡介與初識

是什麼

  • 是一個專一於視圖的漸進式前端框架,若是須要其餘功能可使用它的拓展庫,Vue 的核心庫只關注視圖層。

優缺點

  • 能夠和其餘語言共存,在一個app或者一個頁面視圖中,可使用其餘的語言開發,不會影響,由於他是組件化的

初識

數據綁定v-model
  • 是雙向綁定數據當輸入框數據變化,會讓同一個使用v-model綁定的數據也變化,我理解爲多個引用指向同一塊內存,或者像linux的軟鏈接,能夠進行一塊記憶。
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>test</title>
</head>
<body>
<div id="app">
    <ul>
        <li>{{message}}</li>
    </ul>

    <input type="text" v-model="message" />{{message}}
</div>
<script src="../../js/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        }
    });
</script>
</body>
</html>
  • 效果圖

  • v-model.lazycss

    只有在input失去焦點的時候纔會刷新數據html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="../../js/vue.js"></script>
    </head>
    <body>
    <div id="app">
        <ul>
            <li>{{message}}</li>
        </ul>
        <!--    失去焦點或者enter纔會更新值-->
        <input type="text" v-model.lazy="message">{{message}}
    </div>
    
    <script>
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello world"
            }
        });
    
    </script>
    </body>
    </html>

2、命令-生命週期函數

命令:詳細

order 語法糖 做用
v-bind=attr :attr 將數據和標籤幫頂起來
v-on:action=actionName @action=actionName 綁定事件,事件不須要傳參數能夠省略()
{{expression}} 從vue對象中取值
v-if="boolean" if
v-else-if="boolean" else if
v-else else
key 做爲一種標籤的身份標識
v-show="boolean" 和v-if的區別是,它是display=none標籤還在
v-for="(item, index) in items" for循環
:class="{className:boolean}" 也能夠用數組
v-model="entity" 雙向綁定表單,其實是兩個指令結合v-bind:value和v-on:input
v-html 輸出真正的 HTML
v-once 寫在標籤上只渲染一次
$forceUpdate 強制更新數據

生命週期函數

  1. created():當組件建立成功時
    export default {
      name: "Home",
      created() {
        console.log("Home組件被建立成功");
      }
    };
  2. mounted(): 當組件被掛載到dom上

    未掛載成功的元素不能被獲取到前端

    export default {
      name: "Home",
      created() {
        console.log("Home組件被建立成功");
      },
      mounted(){
        console.log("組件被掛載成功")
      }
    };
  3. updated(): 當組件中發生變化時vue

    export default {
      name: "Home",
      created() {
        console.log("Home組件被建立成功");
      },
      mounted(){
        console.log("組件被掛載成功")
      },
      updated(){
        console.log("組件中發生改變時");
      }
    };
  4. 其餘java

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title></title>
    <script src="vue.js" type="text/javascript" charset="utf-8"></script>
    </head>
    <body>
    <div id="app">
    	{{msg}}
    </div>
    <script type="text/javascript">
    var vm = new Vue({
    	el : "#app",
    	data : {
    		msg : "hi vue",
    	},
    	//在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用。
    	beforeCreate:function(){
    		console.log('beforeCreate');
    	},
    	/* 在實例建立完成後被當即調用。
    	在這一步,實例已完成如下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。
    	然而,掛載階段還沒開始,$el 屬性目前不可見。 */
    	created	:function(){
    		console.log('created');
    	},
    	//在掛載開始以前被調用:相關的渲染函數首次被調用
    	beforeMount : function(){
    		console.log('beforeMount');
    
    	},
    	//el 被新建立的 vm.$el 替換, 掛在成功	
    	mounted : function(){
    		console.log('mounted');
    	
    	},
    	//數據更新時調用
    	beforeUpdate : function(){
    		console.log('beforeUpdate');
    			
    	},
    	//組件 DOM 已經更新, 組件更新完畢 
    	updated : function(){
    		console.log('updated');
    			
    	}
    });
    setTimeout(function(){
    	vm.msg = "change ......";
    }, 3000);
    </script>
    </body>
    </html>

事件傳參問題

  • 綁定事件時沒寫(),可是須要傳參,參數爲undefined
  • 綁定事件寫了(),須要傳參可是沒有傳,vue會默認傳綁定的event對象給第一個參數,第二個就是undefined
  • 若是須要傳event對象,只寫event會被vue解析若是沒又在vue中定義會報錯,須要寫成$event

事件的冒泡、捕獲、其餘屬性

  • 冒泡:@click="action(123)" =>@click.stop="action(123)" , 解決冒泡
  • 捕獲:@click="action(123)" =>@click.prevent="action(123)"
  • 鍵盤:@keyUp【經常使用】|keyDown.enter="action",監聽enter鍵
  • 自定義組件的根元素的事件:@click.native
  • 只監聽一次: @click.once

標籤值被複用了

須要給標籤添加一個key的屬性就能夠了,是由於虛擬dom的問題node

v-for

  • 通常v-for中要指定:key=「通常是一個惟一的東西」 要和每個標籤對應起來,虛擬dom進行diff算法的時候會複用這個標籤
<!-- 當items是數組 -->
    <ul v-for="(item, index) in items" :key="item">
        <li></li>
    </ul>
    <!-- 當items是對象,默認是取value -->
    <ul v-for="value in obj" :key="value">
        <li></li>
    </ul>
    <!-- 當items是對象,默認是取value,key,index -->
    <ul v-for="(value,key,index) in obj" >
        <li></li>
    </ul>
  • 支持響應式的方法python

    • pop()刪除最後一個元素jquery

    • push(a,b...)追加一【多】個元素linux

    • shift()刪除第一個元素

    • unshift(a,b...)添加【多】元素到第一個的位置

  • sort([compare])

    • reverse()

    • splice(起始位置,刪除幾個【只填第一個參數就是從第一個位置刪除到完】,從第一個參數位置添加【能夠多個】的元素)

    • Vue.set(src,index,newValue) 修改src 中index位置值,或者對象賦值,對象直接賦值不起做用

      this.$set(a,'title','列表2');
      //或者
      Vue.set(a,'title','列表2');
  • 不支持響應式的方法

    • items[index]=""

過濾器

在vue的options中定義filters:{run :function(pram),調用 param|run

計算屬性

  • 關鍵字:computed

  • 更加高效由於使用了緩存

  • 計算屬性的響應式是創建在計算一個響應式的數據上的,它變化纔會更新計算屬性,而方法是每次都計算不使用緩存

  • 計算屬性默認只有 getter ,不過在須要時你也能夠提供一個 setter ,給計算屬性賦值的時候會調用setter方法,取值調用getter方法

    computed: {
      fullName: {
        // getter
        get: function () {
          return this.firstName + ' ' + this.lastName
        },
        // setter
        set: function (newValue) {
          var names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[names.length - 1]
        }
      }
    }

3、vue實例、v-model修飾符

vue實例

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一個實例方法
vm.$watch('a', function (newValue, oldValue) {
  // 此回調函數將在 `vm.a` 改變後調用
})

v-model

<input type="text" v-model="message" />{{message}}
v-model.lazy
  • v-model 會和標籤雙向綁定,可是編輯時,時時刻刻都在同步,資源消耗不必,v-model.lazy會監聽enter 或者手標失去焦點的時候才進行數據同步
<!--    失去焦點或者enter纔會更新值--><input type="text" v-model.lazy="message">{{message}}
v-model.number
  • 將輸入框的值轉換爲number類型,默認是字符串處理全部的鍵盤錄入
v-model.trim
  • 將輸入框中的左右兩邊的空格去掉

響應式注意

每當 data 對象發生變化,都會觸發視圖從新渲染。值得注意的是,若是實例已經建立,那麼只有那些 data 中的本來就已經存在的屬性,纔是響應式的。也就是說,若是在實例建立以後,添加一個新的屬性將不是響應式的

4、組件化

什麼是組件

借鑑了將一個大的問題拆分紅一個個的小問題這種思想 , 就是"基礎庫"或者「基礎組件",意思是把代碼重複的部分提煉出一個個組件供給功能使用。

  • 將一個頁面拆分紅一個個的小組件,能夠遞歸的拆分
  • 每一個組件完成本身相關的功能,多個組件共同組成一個頁面或者程序
  • 複用性:下次須要一樣的功能就能夠複用
  • 相似於項目中的一個模塊,只不過更加細化了。

組件的使用

  1. 建立組件的構造器
  2. 註冊組件
  3. 使用組件

組件必須放在vue管理的做用域內,若是是多個標籤必須被一個元素包裹,就是有一個惟一的祖先元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="../../js/vue.js"></script>
<div id="app">
    <cpt></cpt>
    <cpt></cpt>
    <cpt></cpt>
    <cpt></cpt>
</div>

<script>
    // 1. 建立組件構造器
    const component = Vue.extend({
        template: `
            <div>
                hello
            </div>`,
    });
    // 2. 註冊組件 全局組件
    Vue.component('cpt', component);

    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        }
    });

</script>
</body>
</html>

局部組件

<div id="app">11
    <cpt></cpt>
    <cpt></cpt>
</div>

<div id="app2">22
    <cpt></cpt>
</div>
<script>
    // 1. 建立組件構造器
    const component = Vue.extend({
        template: `
            <div>
                hello
            </div>`,
    });

    //局部組件 只在app中的做用域有效
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        },
        components: {
            cpt: component
        }
    });

    //未註冊組件
    const app2 = new Vue({
        el: "#app2",
        data: {
            message: "hello"
        }
    });

</script>

父子組件

<div id="app">11
    <pt></pt>
    <pt></pt>
    <pt></pt>
</div>
<script>

    /*第1個組件構造器*/
    const child = Vue.extend({
        template: `
            <div>
                child
            </div>`
    });
    // 第二建立組件構造器
    const parent = Vue.extend({
        template: `
            <div>
                parent
                <cd></cd>
            </div>`,
        components: {
            cd: child
        }
    });


    //局部組件 只在app中的做用域有效
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        },
        components: {
            pt: parent
        }
    });

</script>

組件的傳遞

組件不會向上級做用域傳遞,只會向下傳遞,孫子沒有在爺爺的做用域註冊的話孫子只能在父親的做用域使用

組件的語法糖

<div id="app">11
  <pt></pt>
  <pt></pt>
  <pt></pt>
</div>
<script>

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      pt: {
        //  語法糖直接能夠放在註冊的地方
        template: `
            <div>
                hello
            </div>`
      }
    }
  });

</script>

模板的分離

<script src="../../js/vue.js"></script>
<div id="app">11
  <pt></pt>
  <pt></pt>
  <pt></pt>
</div>
<!--<script type="text/x-template" id="pt">
  <div>
    <div>我是標題</div>
  </div>
</script>-->

<template id="pt">
  <div>
    <div>我是tempalte</div>
  </div>

</template>
<script>

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      pt: {
        //  語法糖直接能夠放在註冊的地方
        template: "#pt"
      }
    }
  });

</script>

組件訪問數據

  • 組件不能訪問實例中的數據
  • 只能訪問本身的數據
  • 在子組件中data屬性是一個function不是對象,能夠返回一個數據對象供它訪問
  • 組件也有method屬性,它的原型其實是指向vue的實例的
<div id="app">11
  <pt></pt>
  <pt></pt>
  <pt></pt>
</div>

<template id="pt">
  <div>
    <div>我是{{title}}</div>
  </div>
</template>
<script>

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      pt: {
        
        template: "#pt",
        //是一個函數,且只能訪問本身的數據
        data(){
          return {title:"title"};
        }
      }
    }
  });

</script>

組件的data必須是函數

  • 若是寫屬性的話,很容易形成多個組件的數據引用指向同一塊內存,會相互影響
  • 用function的話你只要每次返回一個匿名對象,他是沒有公共引用指向的因此不會影響,若是須要的話你本身能夠return 一個公用的引用就會相互影響的
  • 因此爲了不這種bug,data不是function就會報錯
  • 必須return 一個對象{}

父子組件通訊

父傳子
  • props屬性 : 能夠寫成數組或者對象,對象能夠限制類型,對象更好點,也能夠類型寫成對象添加更多的限制、給默認值
  • 給默認值的時候若是是對象或者是數組,不能直接用{}、[] 須要用工廠(default(){})來建立
  • 自定義validator
  • 能夠自定義一個類做爲類型
<div id="app">
  <pt :msg="msg" :title="title"></pt>
</div>

<template id="pt">
  <div>
    <div>{{title}}</div>
    <div>{{msg}}</div>
  </div>
</template>
<script>
  // 1.註冊組件
  const pt = {
    template:"#pt",
    data() {
      return {};
    },
    methods: {},
    // props:["title","msg"] 能夠寫成數組或者對象,對象能夠限制類型,對象更好點
    props:{
      // title:Array,
      title:{
        type: Array,
        default(){
          return [];
        }
      },
      //也能夠寫成對象的添加更多的限制、給默認值
      msg:{
        type:String,
        default:"",
        required:true,
        //自定義validator 這個待查閱
        validator: function (val) {
          return val == "hello worl";
        }
      }
    }
  }

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      msg: "hello world",
      title:["aaa","bbb","ccc"]
    },
    //字面量簡寫  pt可替換pt:pt
    components:{pt}
  });

</script>
子傳父|自定義事件
  • v-on 事件監聽器在 DOM 模板中會被自動轉換爲全小寫 (由於 HTML 是大小寫不敏感的),因此 v-on:myEvent 將會變成 v-on:myevent——致使 myEvent 不可能被監聽到。
  • $emit --》this.$emit('myevent')會傳遞給父組件的監聽事件要同名
  • 推薦你始終使用 kebab-case 的事件名 my-event
  • 子組件儘可能和本身的data屬性去綁定
<div id="app">
  <!--  不寫參數會默認將$emit事件後傳的參數【可多個】傳出來,寫了參數報錯-->
  <pt @child-click="parentClick"></pt>
</div>

<template id="pt">
  <div>
    <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
  </div>
</template>
<script>
  // 1.註冊組件
  const pt = {
    template: "#pt",
    data() {
      return {
        categories: [
          {id: "aaa", name: "aaa"},
          {id: "bbb", name: "bbb"},
          {id: "ccc", name: "ccc"},
          {id: "ddd", name: "ddd"}
        ]
      };
    },
    methods: {
      btnClick(ite) {
        // js中這樣寫不能駝峯,vue能夠
        this.$emit('child-click', ite,1);
      }
    }
  };

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      msg: "hello world",
      title: ["aaa", "bbb", "ccc"]
    },
    components: {pt},
    methods: {
      parentClick(obj,a) {
        console.log(obj,a);
      }
    }
  });

</script>
練習
  1. num一、num2從父組件傳遞過來
  2. 修改num1,dnum1也變,同時傳dnum1給父組件,父組件改變num1,也改變了prop1
  3. dnum2一直是dnum1的1%
<!--1. num一、num2從父組件傳遞過來
2. 修改num1,dnum1也變,同時傳dnum1給父組件,父組件改變num1,也改變了prop1
3. dnum2一直是dnum1的1%-->
<div id="app">
  <pt :cnum1="num1" :cnum2="num2"
      @change1="cc1"
      @change2="cc2"
  ></pt>
</div>

<template id="pt">
  <div>
    <p>props:{{cnum1}}</p>
    <p>data:{{dnum1}}</p>
    cnum1<input type="text" :value="dnum1" @input="changeProp1"><br>
    <p>props:{{cnum2}}</p>
    <p>data:{{dnum2}}</p>
    cnum2<input type="text" :value="dnum2" @input="changeProp2">
  </div>
</template>
<script>

  //局部組件 只在app中的做用域有效
  const app = new Vue({
    el: "#app",
    data: {
      num1: 1,
      num2: 2
    },
    methods: {
      cc1(eve1) {
        this.num1 = eve1;
      },
      cc2(eve2) {
        this.num2 = eve2;
      }
    },
    components: {
      pt: {
        template: "#pt",
        props: {
          cnum1: {
            type: Number,
            default: 3
          },
          cnum2: {
            type: Number,
            default: 4
          }
        },
        data() {
          return {
            dnum1: this.cnum1,
            dnum2: this.cnum2,
          };
        }, 
        methods: {
          changeProp1(event1) {
            this.dnum1 = event1.target.value;
            console.log(this.dnum1)
            if (this.dnum1) {
              this.dnum1 = parseInt(this.dnum1)
              this.dnum2 = this.dnum1 / 100;
              this.$emit('change1', this.dnum1);
            } else {
              this.dnum2 = "";
            }

          },
          changeProp2(event2) {
            this.dnum2 = event2.target.value;
            this.$emit('change2', parseInt(this.dnum2));
          }

        }
      }
    }
  });

</script>

watch

  • watch監聽對象不能直接監聽,能夠用computed代替對象

  • 語法:

    watch:{
    	監聽的屬性名(newValue, oldValue){
    
    	}
    }
<script src="../../js/vue.js"></script>
<div id="app">
  {{message}}
  <input type="text" v-model="message">
  {{demo.name}}
  <input type="text" v-model="demo.name">
</div>

<template id="cd">
  <div>
    aaaaa
  </div>

</template>
<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world",
      demo: {
        name: "nameObj"
      }
    },
    computed:{
      demoName(){
        return this.demo.name;
      }
    },
    watch: {
      message(newVal, oldVal) {
        console.log(newVal, oldVal);
      },
      //不能直接監聽對象
      // demo(val) {
      //   console.log(val);
      // }
      demoName(val) {
        console.log(val);
      }
    },
    components: {
      cd: {
        template: "#cd"
      }
    }
  });

</script>
  • 若是是鍵的路徑須要用引號包裹

  • 也能夠外部調用

  • immediate和handler

    watch有一個特色,就是當值第一次綁定的時候,不會執行監聽函數,只有值發生改變纔會執行。若是咱們須要在最初綁定值的時候也執行函數,則就須要用到immediate屬性。

    好比當父組件向子組件動態傳值時,子組件props首次獲取到父組件傳來的默認值時,也須要執行函數,此時就須要將immediate設爲true。

  • **deep: **當須要監聽一個對象的改變時,普通的watch方法沒法監聽到對象內部屬性的改變,只有data中的數據纔可以監聽到變化,此時就須要deep屬性對對象進行深度監聽

<div id="app">
  {{demo1.name}}
  <input type="text" v-model="demo1.name">
  {{demo.name}}
  <input type="text" v-model="demo.name">
  <input type="text" v-model="demo2">
</div>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world",
      demo: {
        name: "nameObj"
      },
      demo1: {
        name: "nameObj"
      },
      demo2:"qweqw"
    },
    computed: {
      demoName() {
        return this.demo.name;
      }
    },
    watch: {
      //若是是鍵的路徑須要用引號包裹
      "demo.name": function (val) {
        console.log(val);
      },
        
      // childrens: {  //監聽的屬性的名字
      //   handler:function(val){
      //     console.log(val.name);
      //   },
      //   deep: true, //能夠監聽到一個對象的內部屬性變化
       //  immediate: true
      // },
      // "childrens.name":function (val) {
      //   console.log(val);
      // }
    }
  });
  //外部調用
  app.$watch("demo2",function (val) {
    console.log(val)
  })
</script>

訪問子組件實例 $children和$refs

  • 通常不會用$children來取子組件
  • $refs.refName | $refs['refName']
    • 若是多個相同的引用會取最後一個
    • 若是綁定的是一個普通標籤拿到的就是一個dom對象
<div id="app">
  <tmp ref="a"></tmp>
  <tmp ref="a"></tmp>
  <tmp ref="b"></tmp>
  <button @click="btnClick">打印子組件</button>
</div>
<template id="tmp">
  <div>
    <p>哈哈哈</p>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    methods:{
      btnClick(){
        //1. 通常不會用$children來取子組件
        // console.log("第一個子組件:",this.$children[0]);
        // console.log("全部子組件:",this.$children);

        // 2.$refs.refName|['refName']
        console.log("全部組件有ref屬性的組件:",this.$refs);
        //若是多個相同的引用會取最後一個
        console.log("取得固定的ref的元素:",this.$refs["a"]);
        console.log("取得固定的ref的元素:",this.$refs.b);
      }
    },
    components: {
      tmp: {
        template: "#tmp"
      }
    },

  });

</script>

訪問父組件實例

  • 不建議使用this.$parent,會讓組件的耦合加強不夠獨立
  • 祖先組件this.$root
<div id="app">
  <tmp></tmp>
</div>
<template id="tmp">
  <div>
    <p>哈哈哈</p>
    <button @click="btnClick">打印父組件</button>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      tmp: {
        template: "#tmp",
        methods: {
          btnClick() {
            //1. 不建議使用,會讓組件的耦合加強不夠獨立
            console.log("打印直系父組件:", this.$parent);
            //祖先組件
            console.log("打印root組件:", this.$root);
          }
        }
      },
    },

  });

插槽slot

  • 拓展組件像回調函數同樣,usb接口同樣
  • 插槽的基本使用
  • 插槽的默認值 默認值
<!--1. 插槽的基本使用 <slot></slot>-->
<!--2. 插槽的默認值 <slot>默認值</slot>-->
<div id="app">
  <tmp></tmp><br>
  <tmp></tmp><br>
  <tmp></tmp><br>
  <tmp><div>我是插槽</div></tmp>
  <tmp><i>我是插槽i</i></tmp>
</div>
<template id="tmp">
  <div>
    <p>哈哈哈</p>
    <slot><p>我是默認值*******</p></slot>
    <p>娃娃</p>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      tmp: {
        template: "#tmp"
      },
    }

  });

</script>
具名插槽
<div id="app">
  <tmp ><a slot="right" href="#">我替換右邊</a></tmp><br>
  <tmp ><a slot="left" href="#">我替換左邊</a></tmp><br>
  <tmp><a href="#">我替換沒名字的</a></tmp><br>
</div>
<template id="tmp">
  <div>
    <slot name="left"><p>我是默認值left</p></slot>
    <slot name="center"><p>我是默認值center</p></slot>
    <slot name="right"><p>我是默認值right</p></slot>
    <slot><p>我是默認值沒有名字</p></slot>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world"
    },
    components: {
      tmp: {
        template: "#tmp"
      },
    }

  });

編譯做用域

  • 始終使用本身組件中的變量
<div id="app">
<!--    在誰的做用域用誰的變量-->
  <cp v-show="isShow"></cp>
</div>
<template id="cp">

  <div v-show="isShow"><!-- div父元素初始化的時候不受影響 -->
    <a href="">aaa</a>
    <button v-show="isShow">按鈕</button>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world",
      isShow: true
    },
    components: {
      cp: {
        template: "#cp",
        data() {
          return {
            isShow: false
          };
        }
      }
    }
  });

</script>
做用域插槽
  • 父組件想要替換子組件的插槽的數據,數據的具體值仍是由子組件來決定
  • slot-scope="slotData",相似於該組件的對象,2.5以前要用template標籤
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<script src="../../js/vue.js"></script>
<div id="app">
  <cp>
    <!--    slotData:相似於該組件的對象,2.5以前要用template-->
    <template slot-scope="slotData">
      <!--      取得綁定在組件中的數據-->
      <span v-for="item in slotData.datas">{{item}}-</span>
    </template>
  </cp>

  <cp>
    <template slot-scope="slotData">
      <!--      join方法將數組拼接成字符串-->
      <span>{{slotData.datas.join(' * ')}}</span>
    </template>
  </cp>
</div>
<template id="cp">

  <div>
    <!--    做爲傳遞的數據-->
    <slot :datas="languages">
      <ul>
        <li v-for="item in languages">{{item}}</li>
      </ul>
    </slot>
  </div>
</template>

<script>
  const app = new Vue({
    el: "#app",
    data: {
      message: "hello world",
    },
    components: {
      cp: {
        template: "#cp",
        data() {
          return {
            languages: ['java', 'javascript', 'css', 'html', 'vb', 'python']
          };
        }
      }
    }
  });

</script>
</body>
</html>

5、es6模塊化

阮一峯的ES6---Module的加載實現

把功能進行劃分,將同一類型的代碼整合在一塊兒,因此模塊的功能相對複雜,但都同屬於一個業務

爲何有模塊化

  • js是按順序加載的,因此通常相互依的js是具備強制性的
  • 多個js文件定義的引用會污染全局變量,多人協做開發可能會有衝突
  • 能夠用閉包

閉包解決多人協做開發

  • 只須要寫好本身的模塊化的命名,就能夠很好的避免衝突了,至關於把全部的容錯點都聚焦在一個點上,犯錯的機會就少了,
  • 可是代碼的複用性仍是不好
// ;是爲了防止其餘的導入js相互影響
;var xm01 = (function xiaoming01() {
  return {
    aa:"asdas",
    flag: true
  };
}())


//js文件2
;(function () {
  if (xm01.flag) {
    alert("xm01.flag:" + xm01.flag);
  }
}());

組件化相似模塊化的更細粒度,組件充當了基本類庫同樣的東西目的是複用拓展性,模塊主要是以功能區分類別劃分儘可能隔離其餘業務

模塊化練習

  • xiaoming01.js

    // es6的導出,02中導入
    export let exa = "222";
    let exa1 = "333";
    let exb = "333";
    
    export {exb, exa1};
    export function fun(){
      console.log("asasddsds");
    }
    
    
    //export default :import的時候能夠自定義命名,一個js中只能有一個default
    let aaa="export default";
    export default aaa;
  • xiaoming02.js

    // 導入 ,這裏須要寫上.js
    import {exa, exa1, exb} from "./xiaoming01.js";
    // 01
    console.log(exa1, exb);
    
    //導入default能夠自定義命名
    import asd from "./xiaoming01.js";
    
    console.log('export:',asd);
    
    //導入所有的導出,而且重命名
    import * as all from "./xiaoming01.js";
    
    console.log(all);
    console.log(all.default)
  • 01-es6.html

    • script須要指定type=module
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script src="./xiaoming01.js" type="module"></script>
      <script src="./xiaoming02.js" type="module"></script>
    </head>
    <body>
    沒有使用導入導出的話:
    Uncaught ReferenceError: flag is not defined
    at xiaoming02.js:3
        之前是能夠執行的先在不知道怎麼執行不了了
    </body>
    </html>

6、npm、webpack

npm

  • node package manager , node包管理工具

  • 查看版本:npm -v

  • 卸載安裝的包 npm uninstall 包名 或 npm remove 包名

  • 查看包的詳細信息, npm info 包名

  • 查看一個包存在的全部版本號 npm view 包名 versions

  • 查看指定包當前的最新版本 npm view 包名 version

  • 下載指定版本的包 npm install 包名@1.8

  • npm list 查看項目安裝了哪些包 或 npm ls

  • npm install jquery --save 或 npm i jquery -S 下載生產包

  • npm install jquery --save-dev 或 npm i jquery -D 下載開發依賴包

  • npm ls jquery 查看當前安裝包的版本

  • npm config set registry https://registry.npm.taobao.org 更改 npm 的下載鏡像爲淘寶鏡像

  • npm help npm幫助文檔

  • 使用和linux有的很像

webpack起步

*webpack* 是一個現代 JavaScript 應用程序的*靜態模塊打包器(module bundler)*。當 webpack 處理應用程序時,它會遞歸地構建一個*依賴關係圖(dependency graph)*,其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 *bundle*(捆,束),它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其打包爲合適的格式以供瀏覽器使用。
  • 入口js

    //commonjs規範
    const {add} = require('./mathUtil.js');
    
    console.log(add(1,3));
    
    //es6規範
    import {result} from "./es6.js";
    
    console.log(result);
  • es6規範

const result = 45456;
export {result};
  • common規範
function add(a, b) {
  return a + b;
}

module.exports = {add};

webpack配置

  • 導出的時候es6和commonjs不能在一個模塊中混用
  • 配置webpack.config.js:要使用commonjs規範
//node的包裏面的path模塊,用來拼接絕對路徑
const path = require('path');

//這裏要用commonjs導出,不能用es6
module.exports = {
    //打包轉換的調用入口和main方法相似
  entry: './src/main.js',
  ouput: {
    //必須使用絕對路徑,path.resolve(__dirname,'dist')返回絕對路徑
    path: path.resolve(__dirname,'dist'),
    filename: 'bundle.js'
  }
};
  • package.json配置:json不能有註釋
{
  "name": "meetpackage",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    //npm run build 會在這個配置文件中找webpack命令,這個使用的是本地的命令,
    //不是全局的webpack,本地是針對於你的這個開發項目
    "build":"webpack"
  },
  "author": "",
  //開發的依賴
   "devDependencies": {
    "webpack": "^3.6.0"
  },
  //開源才須要這個,json中不能註釋
  "license": "ISC"
}
  • 本地安裝:開發時依賴 npm install webpack@3.6.0 --save-dev
  • 終端terminal裏敲的命令都是全局的
爲何使用--save-dev而不是--save?

--save 會把依賴包名稱添加到 package.json 文件 dependencies 下;

--save-dev 則添加到 package.json 文件 devDependencies 鍵下;

webpack-loader

  • 官網能夠找到對應的loader安裝
    • 例:npm install style-loader[@version] --save -dev[表示開發環境用]
    • npm install babel-loader@7 babel-core babel-preset-es2015 --save-dev es6轉es5
//node的包裏面的path模塊,用來拼接絕對路徑
const path = require('path');

//這裏要用commonjs導出,不能用es6
module.exports = {
  entry: './src/main.js',
  output: {
    //必須使用絕對路徑
    path: path.resolve(__dirname,'dist'),
    filename: 'bundle.js',
    //爲全部的url相關的添加路徑
    publicPath:'dist/'
  },
  module:{
    rules: [
      {
        test: /\.css$/,
        // style-loader將模塊的導出做爲樣式添加到 DOM 中
        // loader解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼
        // 從右到左的順序加載
        use: [ 'style-loader', 'css-loader' ]
      },
      // {
      //   test: /\.(png|jpg|gif)$/,
      //   use: [
      //     {
      //       loader: 'url-loader',
      //       options: {
      //         //限制圖片大小,大於limit會找file-loader
      //         limit: 9999
      //       }
      //     }
      //   ]
      // },
      // 在使用webpack進行打包時,對圖片路徑的處理方法經常使用的有兩種,一種是file-loader,
      // 一種是url-loader,當咱們使用其中一種是,請把另外一種刪掉,否則會出現圖片沒法正常顯示的問題
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              //name是文件名,hash取8位,ext是拓展名
              name:'img/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      }
    ]
  }
};

webpack-vue

  1. npm install vue -save

  2. 不寫路徑默認從node_modules引入 import Vue from 'vue'

  3. runtime-only:是運行的時候代碼不能包含任意一個template標籤

  4. runtime-compiler:代碼中能夠有template標籤

    1. 解決3.4碰到的問題
    module:{
    
      resolve:{
        alias:{
          // vue$正則,表示導入的時候會檢測vue指向的文件夾,若是這裏不指定,會去找默認的runtime-only
          'vue$':'vue/dist/vue.esm.js'
        }
      }
//使用vue
import Vue from 'vue';


const App = {
    template: `
    <h2>{{msg}}</h2>
    `,
    data() {
        return {
            msg: 'hello world'

        };
    }
};

new Vue({
    el: '#app',
    // template和el關係是,這裏的template會替換el的標籤
    template: `<App/>`,
    components: {
        App
    }
});
將組件的代碼提出去
  • 新建一個.vue文件
<template>
  <h2>{{msg}}</h2>
  <span class="title">{{tit}}</span>
</template>

<script>
  export default {
    name: "App",
    data() {
      return {
        msg: 'hello world',
        tit:'title'
      };
    }
  }
</script>

<style scoped>
.title{
  color: red;
}
</style>
  • npm install vue-loader vue-template-compiler --save -dev
    • 會出現版本太高的問題 安裝一個低版本的
    • 編輯package.json中的版本號,會根據你的大版本找一個合適的,必須從新npm install

webpack-plugin

  • 安裝打包靜態文件:npm install --save-dev html-webpack-plugin
  • 壓縮js文件替換變量爲更簡單的:npm install uglifyjs-webpack-plugin@1.1.1 --save -dev 指定的vueCli 2
webpack-dev-server
  • 全局安裝:能夠不用

    npm install webpack-dev-server -g
  • 開發環境:

    npm install webpack-dev-server -save -dev
  • 配置參數:

    --content-base //設定webpack-dev-server的director根目錄。若是不進行設定的話,默認是在當前目錄下。
    --quiet: //控制檯中不輸出打包的信息,開發中通常設置爲false,進行 打印,這樣查看錯誤比較方面
    --no-info: // 不顯示任何信息
    --colors: //對信息進行顏色輸出
    --no-colors: //對信息不進行顏色輸出
    --compress:  //開啓gzip壓縮
    --host <hostname/ip>: //設置ip
    --port <number>: //設置端口號,默認是:8080
    --inline: //webpack-dev-server會在你的webpack.config.js的入口配置文件中再添加一個入口,
    --hot: //開發熱替換
    --open: //啓動命令,自動打開瀏覽器
    --history-api-fallback: //查看歷史url
  • 兩種方式:

    1. 直接scripts中使用:"dev": "webpack-dev-server --contentBase src --port 80 --hot --colors"
    2. 配置文件:
    plugins: [
        new webpack.BannerPlugin('最終版權是小明'),
        //打包靜態資源,而且指定模板
        new htmlWebpackPlugin({
          template:`index.html`
        }),
        //壓縮js
        new UglifyJsWebpackPlugin(),
        //熱加載,不會所有加載,只加載改動的地方,配置了hot就須要配置,直接在命令中使用--hot就不須要配置這個插件
        // new webpack.HotModuleReplacementPlugin()
      ],
      // devServer: {
      //   contentBase: 'src',
      //   port: 80,
      //   hot:true
      // },
  • 報錯多是版本問題

webpack.config.js配置文件

//node的包裏面的path模塊,用來拼接絕對路徑
const path = require('path');
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');

//這裏要用commonjs導出,不能用es6
module.exports = {
  entry: './src/main.js',
  output: {
    //必須使用絕對路徑
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    //爲全部的url相關的添加路徑
    // publicPath: 'dist/'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // style-loader將模塊的導出做爲樣式添加到 DOM 中
        // loader解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼
        // 從右到左的順序加載
        use: ['style-loader', 'css-loader']
      },
      // {
      //   test: /\.(png|jpg|gif)$/,
      //   use: [
      //     {
      //       loader: 'url-loader',
      //       options: {
      //         //限制圖片大小,大於limit會找file-loader
      //         limit: 9999
      //       }
      //     }
      //   ]
      // },
      // 在使用webpack進行打包時,對圖片路徑的處理方法經常使用的有兩種,一種是file-loader,
      // 一種是url-loader,當咱們使用其中一種是,請把另外一種刪掉,否則會出現圖片沒法正常顯示的問題
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
            //name是文件名,hash取8位,ext是拓展名
              name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015']
          }
        }
      },
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader'
        }
      }
    ]
  },
  resolve: {
    // 這寫拓展名能夠省略
    extensions: ['.css', '.js', '.vue'],
    alias: {
      // vue$正則,表示導入的時候會檢測vue指向的文件夾,若是這裏不指定,會去找默認的runtime-only
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new webpack.BannerPlugin('最終版權是小明'),
    //打包靜態資源,而且指定模板
    new htmlWebpackPlugin({
      template:`index.html`
    }),
    //壓縮js
    new UglifyJsWebpackPlugin(),
    //熱加載,不會所有加載,只加載改動的地方,配置了hot就須要配置,直接在命令中使用--hot就不須要配置這個插件
    // new webpack.HotModuleReplacementPlugin()
  ],
  // devServer: {
  //   contentBase: 'src',
  //   port: 80,
  //   hot:true
  // },
};

抽取分離配置文件

  • 建立三個配置文件:
    1. base.config.js : 存放公共的配置
    //node的包裏面的path模塊,用來拼接絕對路徑
    const path = require('path');
    const webpack = require('webpack');
    const htmlWebpackPlugin = require('html-webpack-plugin');
    
    //這裏要用commonjs導出,不能用es6
    module.exports = {
        entry: './src/main.js',
        output: {
            //必須使用絕對路徑
            path: path.resolve(__dirname, '../dist'),
            filename: 'bundle.js',
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    // style-loader將模塊的導出做爲樣式添加到 DOM 中
                    // loader解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼
                    // 從右到左的順序加載
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                //name是文件名,hash取8位,ext是拓展名
                                name: 'img/[name].[hash:8].[ext]'
                            }
                        }
                    ]
                },
                {
                    test: /\.js$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: ['es2015']
                        }
                    }
                },
                {
                    test: /\.vue$/,
                    use: {
                        loader: 'vue-loader'
                    }
                }
            ]
        },
        resolve: {
            // 這寫拓展名能夠省略
            extensions: ['.css', '.js', '.vue'],
            alias: {
                // vue$正則,表示導入的時候會檢測vue指向的文件夾,若是這裏不指定,會去找默認的runtime-only
                'vue$': 'vue/dist/vue.esm.js'
            }
        },
        plugins: [
            new webpack.BannerPlugin('最終版權是小明'),
            //打包靜態資源,而且指定模板
            new htmlWebpackPlugin({
                template: `index.html`
            })
        ],
    };
    1. dev.config.js : 存放開發時配置

      const WebpackMerge = require('webpack-merge');
      const baseConfig = require('./base.config');
      
      module.exports = WebpackMerge(baseConfig, {
          devServer: {
              contentBase: 'src',
              port: 80,
              inline: true
          }
      });
    2. prod.config.js : 存放生產時配置

      const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');
      const WebpackMerge = require('webpack-merge');
      const baseConfig = require('./base.config');
      
      module.exports = WebpackMerge(baseConfig, {
          plugins: [
              //壓縮js
              new UglifyJsWebpackPlugin()
          ]
      });
  • 修改scripts
    • 可刪除默認的webpack.config.js
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack-dev-server --config ./build/dev.config.js",
        "build": "webpack --config ./build/prod.config.js"
      },

打包

  • 使用webpack,也能夠不用配置文件本身在命令後指定參數,如下是使用配置文件

  • 使用npm,會找到package.json找到對應的script裏的命令執行,實際上仍是調用了webpack命令

7、vuecli

  • 介紹:

    • vue command interface 命令行界面:使用一些命令搭建項目的基礎結構
    • 都使用會規範不少配置,易維護,減小出錯率
    • 依賴 webpack and npm-》npm-》依賴node
  • 安裝:

    • npm install -g @vue/cli
  • 卸載以前版本

    • npm uninstall vue-cli -g or npm uninstall -g @vue/cli
  • 拉取v2的模板

    • npm install -g @vue/cli-init
v2使用:
  • 基於webpack3
建立project
vue init webpack projectName

v3使用:
  • 零配置

  • 隱藏build和config目錄,能夠在node-modules/@vue/cli-service

    • 要修改配置須要根目錄建立一個vue.config.js

      module.exports={};
  • 基於webpack4

  • 提供vue ui命令,圖形化操做

  • 移除static,新增public目錄將index.html移動到下面

  • 項目名不能有大寫

建立project
vue create projectName
  1. 會默認建立一個.git文件夾

  2. 自定義配置:

    1. 根目錄新建 vue.config.js
    module.exports = {
      configureWebpack: {
        resolve: {
          // extensions:[],
          //配置別名
          alias: {
            'assets': '@/assets',
            'components': '@/components',
            'network': '@/network',
            'common': '@/commom',
            'views': '@/views',
          }
        }
      }
    };
    1. 配置.editorconfig : 代碼格式
    root = true
    
    [*]
    charset = utf-8
    indent_style = space
    indent_size = 2
    end_of_line = lf
    insert_final_newline = true
    trim_trailing_whitespace = true
vueUi
vue ui  打開圖形管理界面
el
  • el:'#app'最後執行的仍是$mount('#app')

8、runtime-only 和 runtime-compiler

  1. runtime-only:是運行的時候代碼不能包含任意一個template標籤

    • render(h)- 》 virtual Dom - 》UI真實dom
  2. runtime-compiler:代碼中能夠有template標籤

    • template加載過程:

      template - 》parse - 》ast 抽象語法樹 - 》compiler - 》render(h)- 》 virtual Dom - 》UI真實dom

    1比2性能更高,代碼更少(少6kb)

//runtime-compiler
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})




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

Vue.config.productionTip = false

//runtime-only,這個h是一個createElement('tagName',{attrName:'attrVal'},['innerHtml'])
//在vue中也能夠傳一個template對象靠vue-template-compiler解析成render(),也能夠遞歸建立
/* eslint-disable no-new */
new Vue({
  el: '#app',
  render: h => h(App)
})

9、路由 vue-router

詳情

通常使用vue的插件都要用Vue.use(插件)

  • 介紹:
    • 互聯的網絡將信息傳輸到目標地址的活動
    • 路由器提供兩種機制:
      1. 路由:決定數據包從源到目的地的路徑
      2. 將輸入端的數據轉送到合適的輸出端
    • 路由表:是一個映射表,決定了數據包的指向
  • 後端路由--前端路由
    • 後端處理url和頁面的映射,jsp是後端渲染,到前端的時候頁面就確認好了
    • 前端處理url和頁面的跳轉映射
  • 非嵌套路由必須包含一個前導斜槓字符

改編url不刷新頁面

  • 改location.hash='aaa';
  • history:
    • 改history.pushState({},'','aaa');相似壓棧,history.back()相似彈棧
    • 改history.replaceState({},'','aaa'),不能back()
    • 改history.go(-1) = history.back(),前進或者後退
    • 改history.forword()=history.go(1)
  1. 安裝路由:npm install vue-router --save 由於生產也須要路由

  2. 導入:

    • router/index.js
    import Vue from 'vue'
    //1. 導入插件
    import Router from 'vue-router'
    import HelloWorld from '@/components/HelloWorld'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 建立路由配置
    const routes = [
      {
        path: '/',
        name: 'HelloWorld',
        component: HelloWorld
      }
    ];
    
    //4. 傳入路由配置,導出路由對象
    export default new Router({
      routes
    })
    • main.js
    import Vue from 'vue'
    import App from './App'
    //只寫目錄默認會找  index.js
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
  • 替換a標籤
<div id="app">
    <router-link to="/home">首頁</router-link>
    <!-- 至關於佔位符 -->
    <router-view></router-view>
    <router-link to="/about">詳情</router-link>
  </div>
  • 經常使用屬性

    • tag 、replace

      <!-- tag設置替換成什麼標籤 -->
          <!-- replace表示禁用了返回前進按鈕,是使用了history.replaceState() -->
          <router-link to="/home" tag='button' replace>首頁</router-link>
    • 配置默認的active的樣式

      .router-link-active{
        color: #f00
      }
    • 自定義樣式:手動一個一個標籤的寫

      <!--active-class 自定義點擊後的樣式 -->
          <router-link to="/home" tag='button' replace active-class="active">首頁</router-link>
    • 配置全局的active-class

    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })

默認重定向

const routes = [
  {
    path:'/',
    redirect:'/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path:'/about',
    name:'About',
    component:About
  }
];

設置router的默認方式爲history

  • 自己默認hash
  • history:url不會顯示#號
//4. 傳入路由配置,導出路由對象
export default new Router({
  routes,
  mode:'history'
})

手動寫路由跳轉

  • router會給每一個組件傳$router
<template>
  <div id="app">
    <button @click="homeClick">首頁</button>
    <button @click="aboutClick">詳細</button>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods:{
    //router會給每一個組件傳$router
    homeClick(){
      // this.$router.push('/home');
      this.$router.replace('/home');
    },
    aboutClick(){
      // this.$router.push('/about');
      this.$router.replace('/about');
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

動態路由

01動態路由
  • 須要調用$route.params來獲取參數
  1. 建立一個vue組件:User.vue

    <template>
      <div>
          <h2>我的信心</h2>
          <h3></h3>
      </div>
    </template>
    
    <script>
    export default {
        name:'User',
    }
    </script>
    
    <style>
    
    </style>
  2. 配置路由:index.js

    import Vue from 'vue'
    import User from '../components/User.vue'
    
    //1. 導入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 建立路由配置
    const routes = [
      {
        path:'/user',
        component:User
      }
    ];
    
    //4. 傳入路由配置,導出路由對象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
  3. 加入路由到目標組件:Vue.vue

    <template>
      <div id="app">
        <router-link to="/user" replace>用戶</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
    }
    </script>
    
    <style>
    .active{
      color: rgb(209, 15, 25)
    }
    </style>
  4. 導入組件到入口 : main.js

    import Vue from 'vue'
    import App from './App'
    //只寫目錄默認會找  index.js
    import router from './router'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
  5. 設置動態路由:index.js

    import Vue from 'vue'
    import User from '../components/User.vue'
    
    //1. 導入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 建立路由配置
    const routes = [
      {
        path:'/user/:userName',
        component:User
      }
    ];
    
    //4. 傳入路由配置,導出路由對象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })
  6. 配置頁面的url: Vue.vue

    <template>
      <div id="app">
        <router-link v-bind:to="'/user/'+userName" replace>用戶</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
      data(){
          return {
              userName:'xiaoming'
          }
      }
    }
    </script>
    
    <style>
    .active{
      color: rgb(209, 15, 25)
    }
    </style>
  7. 獲取動態路由中的參數:User.vue

    • $route是當前活躍的路由
    <template>
      <div>
          <h2>我的信心</h2>
          <h3>{{userName}}</h3>
      </div>
    </template>
    
    <script>
    export default {
        name:'User',
        computed:{
            userName(){
                return this.$route.params.userName;
            }
        }
    }
    </script>
    
    <style>
    
    </style>
02傳參

路由配置中props 被設置爲 trueroute.params 將會被設置爲組件屬性。

路由方式

編程式路由

// 字符串
router.push('home')

// 對象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

注意: path和params 不共存params會被忽略,path和query 能夠。name和params也能夠

const userId = '123'

//命名路由
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123

// 這裏的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

一樣的規則也適用於 router-link 組件的 to 屬性

route 、router 、$router 、$route

  • $router:路由組件對象 配置的路由對象
  • $route:當前活躍路由

匹配優先級

有時候,同一個路徑能夠匹配多個路由,此時,匹配的優先級就按照路由的定義順序:誰先定義的,誰的優先級就最高。

懶加載

  • 若是把全部的js都打包到app中,js將會很大,訪問的時候會有等待時間,因此把不一樣的路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候加載對應的資源,就更加高效了

    import Vue from 'vue'
    
    //替換成懶加載
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User.vue'
    
    //懶加載:
    const Home = ()=>import('../components/Home.vue')
    const About = ()=>import('../components/About.vue')
    const User = ()=>import('../components/User.vue')
    
    
    //1. 導入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 建立路由配置
    const routes = [
      {
        path:'/',
        redirect:'/home'
      },
      {
        path: '/home',
        name: 'Home',
        component: Home
      },
      {
        path:'/about',
        name:'About',
        component:About
      },
      {
        path:'/user/:userName',
        component:User
      }
    ];
    
    //4. 傳入路由配置,導出路由對象
    export default new Router({
      routes,
      mode:'history',
      linkActiveClass:'active'
    })

子路由

  • 有默認子路由的時候父漏油去掉name,轉發的時候進不了子路由
index.js
import Vue from 'vue'

//替換成懶加載
// import Home from '../components/Home.vue'
// import About from '../components/About.vue'
// import User from '../components/User.vue'

//懶加載:
const Home = () => import('../components/Home.vue')
const About = () => import('../components/About.vue')
const User = () => import('../components/User.vue')
const HomeChild = () => import ('../components/HomeChild.vue')


//1. 導入插件
import Router from 'vue-router'

//2. 使用插件
Vue.use(Router)

//3. 建立路由配置
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    children: [
      {
        path: '',
        // redirect:'child'
      },
      {
        //這裏不能同/開頭,會自動加上
        path: 'child',
        name: 'HomeChild',
        component: HomeChild
      }]
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  {
    path: '/user/:userName',
    component: User
  }
];

//4. 傳入路由配置,導出路由對象
export default new Router({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})
Home.vue
<template>
  <div>
      <h2>首頁11</h2>
      <router-link to="/home/child">child</router-link>
      <router-view></router-view>
  </div>
</template>

<script>
export default {
    name:'Home'
}
</script>

<style>

</style>

傳參到另外一個組件

profile.vue
<template>
  <div><span>我的檔案</span>
    <span>{{$route.query}}</span><br>
    <span>query.name: {{$route.query.name}}</span>

  </div>
</template>

<script>
  export default {
    name: "Profile"
  }
</script>

<style scoped>

</style>
配置路由:index.js
const Profile = () => import('../components/Profile') 

{
    path: '/profile',
    component: Profile
  }
顯示位置的路由傳參
<template>
  <div id="app">
    <router-link to="/home" tag='button' replace >首頁</router-link>
    <router-link to="/about" replace>詳情</router-link>
    <router-link :to="'/user/'+userName" replace>用戶</router-link>
    <router-link :to="{path:'/profile',query:{name:'lisa',age:18},fragment:'4d5as46s'}" replace>檔案</router-link>

    <!-- <button @click="homeClick">首頁</button>
    <button @click="aboutClick">詳細</button> -->
    <router-view></router-view>
  </div>
</template>

爲何這樣傳參:

  • 符合url的規範:

  • 因此能夠用query對象傳參

導航守衛

注意導航守衛並無應用在跳轉(redirect)路由上,而僅僅應用在其目標上。爲redirect的路由添加一個 beforeEachbeforeLeave 守衛並不會有任何效果

全局守衛

全部的路由都會被過濾,也能夠在特定的組件內建立局部守衛

  • 主要監聽頁面的跳轉

  • from從哪一個組件來的

  • to去跳轉到哪一個組件

  • next()

    • next(false)中斷路由
    • next(path)跳轉到哪一個頁面,可用來作一些條件判斷的跳轉,好比login
    • 其餘的用的時候查官網
    import Vue from 'vue'
    
    //替換成懶加載
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User.vue'
    
    //懶加載:
    const Home = () => import('../components/Home.vue')
    const About = () => import('../components/About.vue')
    const User = () => import('../components/User.vue')
    const HomeChild = () => import('../components/HomeChild.vue')
    const Profile = () => import('../components/Profile')
    
    
    //1. 導入插件
    import Router from 'vue-router'
    
    //2. 使用插件
    Vue.use(Router)
    
    //3. 建立路由配置
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: '/home',
        name: 'Home',
        component: Home,
        meta: {
          title: '首頁'
        },
        children: [
          {
            path: '',
            // redirect:'child'
          },
          {
            //這裏不能同/開頭,會自動加上
            path: 'child',
            name: 'HomeChild',
            component: HomeChild,
          }]
      },
      {
        path: '/about',
        name: 'About',
        component: About,
        meta: {
          title: '詳情'
        },
      },
      {
        path: '/user/:userName',
        component: User,
        meta: {
          title: '用戶'
        },
      },
      {
        path: '/profile',
        component: Profile,
        meta: {
          title: '檔案'
        },
      }
    ];
    const router = new Router({
      routes,
      mode: 'history',
      linkActiveClass: 'active'
    })
    
    router.beforeEach((to, from, next) => {
      next()
      //匹配path中的meta對象的title
      document.title = to.matched[0].meta.title
      console.log(to);
      // console.log(from);
      // console.log("next: "+next);
    })
    
    //4. 傳入路由配置,導出路由對象
    export default router
獨享守衛
import Vue from 'vue'

//懶加載:
const Home = () => import('../components/Home.vue')

//1. 導入插件
import Router from 'vue-router'

//2. 使用插件
Vue.use(Router)

//3. 建立路由配置
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    meta: {
      title: '首頁'
    },
    children: [
      {
        path: '',
        // redirect:'child'
      },
      {
        //這裏不能同/開頭,會自動加上
        path: 'child',
        name: 'HomeChild',
        component: HomeChild,
        beforeEnter: (to, from, next) => {
          console.log("獨享守衛");
            next()
        }
      }]
  }
];
const router = new Router({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

//4. 傳入路由配置,導出路由對象
export default router
前置鉤子和後置鉤子
import Vue from 'vue'

//懶加載:
const Home = () => import('../components/Home.vue')

//1. 導入插件
import Router from 'vue-router'

//2. 使用插件
Vue.use(Router)

//3. 建立路由配置
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    meta: {
      title: '首頁'
    }
  },
  
];
const router = new Router({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})

//前置鉤子 hook,像filter同樣
router.beforeEach((to, from, next) => {
  next()
  //匹配path中的meta對象的title
  document.title = to.matched[0].meta.title
  console.log(to);
})

//後置鉤子
router.afterEach((to,from)=>{
  console.log("在跳轉以後調用");
})

//4. 傳入路由配置,導出路由對象
export default router

10、打包js的結構圖

  • app*.js全部的業務代碼
  • mainifest*.js代碼轉換的依賴的底層支持
  • vendor*.js第三方插件
  • .map文件:項目打包後,代碼都是通過壓縮加密的,若是運行時報錯,輸出的錯誤信息沒法準確得知是哪裏的代碼報錯。有了map就能夠像未加密的代碼同樣,準確的輸出是哪一行哪一列有錯,能夠設置:config/index.js productionSourceMap:false

11、keep-alive組件

  1. 是vue的一個組件:保證一些組件進入緩存不用你每次請求解析資源,提升效率,在顯示的地方配置

    <template>
      <div id="app">
        <router-link to="/home" tag="button" replace>首頁</router-link>
        <router-link to="/about" replace>詳情</router-link>
        <router-link :to="'/user/'+userName" replace>用戶</router-link>
        <router-link :to="{path:'/profile',query:{name:'lisa',age:18},fragment:'4d5as46s'}" replace>檔案</router-link>
        <button @click="toProfile">檔案2</button>
        <!-- <button @click="homeClick">首頁</button>
        <button @click="aboutClick">詳細</button>-->
        <!-- <router-view></router-view> -->
    
        <!-- 保存到緩存中 -->
        <keep-alive>
          <router-view></router-view>
        </keep-alive>
      </div>
    </template>
  2. keep-alive的組件纔可使用activated()、deactivated()

    <template>
      <div>
        <h2>首頁11</h2>
        <router-link :to="{path:'/home/child',query:{content:'child1'}}">child</router-link>
        <router-link :to="toChild2">child2</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: "Home",
      data() {
        return {
          toChild2: {
            path: "/home/child2",
            query: {
              content: "child2"
            }
          },
          path: "/home/child",
          query:{
            childContent:'child1'
          }
        };
      },
      methods: {},
      created() {
        console.log("Home組件被建立成功");
      },
      mounted() {
        console.log("組件被掛載成功");
      },
      updated() {
        console.log("組件中發生改變時");
      },
      destroyed() {
        console.log("home destroyed");
      },
      activated() {
        console.log("home 激活");
        this.$router.push(this.path)
      },
      deactivated() {
        console.log("home 離開");
      },
      beforeRouteLeave(to, from, next) {
        console.log('before leave home');
        this.path = this.$route.path;
        console.log(this.path);
        next();
      }
    };
    </script>
    
    <style>
    </style>
  3. keep-alive 的exclude、include屬性

    1. exclude=「componentName,componentName...」,被排除在緩存以外,不能加空格
    <keep-alive exclude="Profile">
          <router-view></router-view>
        </keep-alive>
    export default {
      name: "Profile",
      created() {
        console.log("profile created");
      },
      destroyed() {
        console.log("profile destroyed");
      }
    };

12、自定義tab-bar

  1. /*style中引用要用@import */

    準備好tabbar.vue,調好樣式,預留出來一個插槽,用來放具體的tabbar的item

    <template>
      <div id="tab-bar">
        <slot></slot>
      </div>
    </template>
    
    <script>
    
      export default {
        name: "TabBar",
      }
    </script>
    
    <style scoped>
    
      #tab-bar {
        display: flex;
        background-color: #fdfdff;
    
        /*顯示在最下面和屏幕等寬*/
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
    
        /*陰影  fgba(最後是0.1表示透明度)*/
        box-shadow: 0 -1px 1px rgba(100, 100, 100, .1);
      }
    
    </style>
  2. 封裝tab-bar-item

    <template>
      <div class="tab-bar-item" @click="itemClick">
        <div v-if="!isActive">
          <slot name="item-icon"></slot>
        </div>
        <div v-else>
          <slot name="item-icon-active"></slot>
        </div>
        <div :class="{active:isActive}">
          <slot name="item-text"></slot>
        </div>
    
      </div>
    </template>
    
    <script>
      export default {
        name: "TabBarItem",
        props:{
          path:{
            type:String
          }
        },
        data() {
          return {
            // isActive: true
          }
        },
        computed:{
          isActive(){
            return this.$route.path.indexOf(this.path) !== -1
          }
        },
        methods:{
          itemClick(e){
            this.$router.replace(this.path)
          }
        }
      }
    </script>
    
    <style scoped>
    
      .tab-bar-item {
        flex: 1;
        text-align: center;
        /*通常移動端的tabbar都是49px*/
        height: 49px;
        font-size: 14px;
      }
    
      .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 3px;
        margin-bottom: 2px;
        /*能夠去掉圖片下面的三個像素*/
        vertical-align: bottom;
      }
    
      .active {
        color: red;
      }
    </style>
  3. 註冊到app.vue中

    <template>
      <div id="app">
        <router-view></router-view>
        <tab-bar>
          <tab-bar-item path="/home">
            <img slot="item-icon" src="./assets/images/tabbar/home.png" alt="首頁">
            <img slot="item-icon-active" src="./assets/images/tabbar/home_active.png" alt="">
            <div slot="item-text">首頁</div>
          </tab-bar-item>
          <tab-bar-item path="/category">
            <img slot="item-icon" src="./assets/images/tabbar/category.png" alt="">
            <img slot="item-icon-active" src="./assets/images/tabbar/category_active.png" alt="">
            <div slot="item-text">分類</div>
          </tab-bar-item>
          <tab-bar-item path="/cart">
            <img slot="item-icon" src="./assets/images/tabbar/cart.png" alt="">
            <img slot="item-icon-active" src="./assets/images/tabbar/cart_active.png" alt="">
            <div slot="item-text">購物車</div>
          </tab-bar-item>
          <tab-bar-item path="/profile">
            <img slot="item-icon" src="./assets/images/tabbar/profile.png" alt="">
            <img slot="item-icon-active" src="./assets/images/tabbar/profile_active.png" alt="">
            <div slot="item-text">個人</div>
          </tab-bar-item>
        </tab-bar>
      </div>
    </template>
    
    <script>
      import TabBar from "./components/tabbar/TabBar";
      import TabBarItem from "./components/tabbar/TabBarItem";
    
      export default {
        name: 'App',
        components: {
          TabBar,
          TabBarItem
        }
      }
    </script>
    
    <style>
      /*style中引用要用@*/
      @import "./assets/css/base.css";
    </style>

    能夠優化class,顏色直接寫死不合適

    還能夠從父組件傳過來,而後綁定style來設置

十3、路徑配置別名

vue-cli2

  1. 配置別名:\build\webpack.base.conf.js

    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          '@': resolve('src'),
          'assets': resolve('src/assets'),
          'components': resolve('src/components'),
          'views': resolve('src/views'),
        }
      },
  2. 項目中使用

    1. html中: 前面要加 ~

      <tab-bar-item path="/home" activeColor="blue">
            <img slot="item-icon" src="~assets/images/tabbar/home.png" alt="首頁" />
            <img slot="item-icon-active" src="~assets/images/tabbar/home_active.png" alt />
            <div slot="item-text">首頁</div>
          </tab-bar-item>
    2. import中使用

      import TabBarItem from "components/tabbar/TabBarItem";

vue-cli3

  1. 根目錄下新建vue.config.js

  2. 在vue.config.js中的chainWebpack中配置config.resolve.alias.set('@', resolve('src')).set('components', resolve('src/components'));

十4、promise

是什麼?

是異步編程的一種解決方案

何時使用異步呢?

  1. 網絡請求
  2. 回調函數的時候

promise用法

  1. 構造器有一個參數,是函數,這個函數有兩個參數都是函數
  2. resolve:異步請求成功調的函數,被調用以後會調用then()
  3. then:來處理業務代碼,參數是一個函數,可經過resolve來傳入data
  4. reject:異步失敗的時候調的函數,也能夠傳輸數據到catch
new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('傳入then 中的 data')
            }, 1500)
        }).then(data => {
            console.log(data);

            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    // resolve('內部的resolve')
                    reject('內部的reject')
                }, 1500)
            })
        }).catch(data => {
            console.log(data);
        })
  1. promise異步完成後會有三種狀態

    1. pendding等待
    2. fullfill 徹底知足
    3. reject 拒絕|次品
  2. promise的另外一種寫法

    1. then中也能夠傳兩個函數,第一個是成功,第二個是失敗
    new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('傳入then 中的 data')
                    // reject('失敗')
                }, 1500)
            }).then(data => {
                console.log(data);
            },reject => {
                console.log(reject);
            })
  3. 再簡化

    1. new Promise(resolve) ==>Promise.resolve(data) ==> data
    2. throw 'msg'也會被catch()捕獲
// new Promise(resolve) ==>Promise.resolve(data) ==> data
        //throw 'msg'也會被catch()捕獲

        new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('第一層...')
            }, 1500)
        }).then(data => {
            console.log(data);
            return Promise.resolve('第二層...')
            // return Promise.reject('額鵝鵝鵝')
            throw 'dsadsa'
        }).then(data=>{
            console.log(data);
            return 'aaa'
        }).then(data=>{
            console.log(data);
        }).catch(err=>{
            console.log(err);
        })
  1. Promise.all([PromiseInstance...])
    1. 多個異步請求同時等待成功後才執行後續代碼
    2. 像是java-juc的柵欄
Promise.all([
                new Promise((resolve, reject)=>{
                    setTimeout(()=>{
                        resolve('1111111')
                    },1000)
                }),
                new Promise((resolve, reject)=>{
                    setTimeout(()=>{
                        resolve('222222')
                    },2000)
                })
        ]).then(data=>{
            //1111111,222222
            console.log(data.toString())
        })

十5、vuex

介紹

  1. 是什麼?
    1. 是爲vue程序提供一個集中狀態管理模式和庫
    2. 充當應用程序中全部組件的特殊共享變量的集中存儲
    3. 這些共享狀態都是響應式的

  1. vuex修改狀態的流程

  • 經過提交 mutation 的方式,而非直接改變 store.state.count,是由於咱們想要更明確地追蹤到狀態的變化。這個簡單的約定可以讓你的意圖更加明顯,這樣你在閱讀代碼的時候能更容易地解讀應用內部的狀態改變。此外,這樣也讓咱們有機會去實現一些能記錄每次狀態改變,保存狀態快照的調試工具。有了它,咱們甚至能夠實現如時間穿梭般的調試體驗。

    因爲 store 中的狀態是響應式的,在組件中調用 store 中的狀態簡單到僅須要在計算屬性中返回便可。觸發變化也僅僅是在組件的 methods 中提交 mutation。

  • actions步驟能夠省略,通常異步的操做放在actions中完成後放在mutations中

  • mutations只能是同步的操做,devtools監聽不到異步操做

使用步驟

store用法

state用法

state中全部的已定義的屬性都是響應式的,新加入的不被響應:由於屬性初始化後,都被一個dep對象=【watcher,watcher..】監控,後面加入的不受監控

  1. npm install vuex --save
  2. 新建、src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

//1.安裝,底層會調用Vuex.install
Vue.use(Vuex)

// 2.建立對象
const store = new Vuex.Store({
  state: {
    count: 0
  }, mutations: {
    //state必須傳,默認會傳進來
    increment(state) {
      state.count++
    }
  }, actions: {}, getters: {}, modules: {}
})

// 3.導出store對象
export default store
  1. main.js掛載插件
import Vue from 'vue'
import App from './App'
import store from "./store";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  render: h => h(App)
})
  1. App.vue
<template>
  <div id="app">
    <h2>{{$store.state.count}}</h2>
    <button @click="increment">+</button>
    <hello-vuex></hello-vuex>
  </div>
</template>

<script>
import HelloVuex from "./components/HelloVuex";


export default {
  name: 'App',
  methods:{
    increment(){
      this.$store.commit('increment')
    }
  },
  components: {
    HelloVuex
  }
}
</script>

<style>
</style>
  1. HelloVuex.vue
<template>
  <div>
    <h2>{{$store.state.count}}</h2>
  </div>
</template>

<script>
  export default {
    name: "HelloVuex"
  }
</script>

<style scoped>

</style>
getters用法

有點像computed的概念

  1. App.vue
<template>
  <div id="app">
    <h2>{{$store.state.count}}</h2>
    <button @click="increment">+</button>
    <h2>年齡大於20:{{$store.getters.more20Person}}</h2>
    <h2>年齡大於20個數:{{$store.getters.more20PersonCount}}</h2>
    <h2>年齡大於age個數:{{$store.getters.moreAgePerson(13)}}</h2>
    <hello-vuex></hello-vuex>
  </div>
</template>

<script>
import HelloVuex from "./components/HelloVuex";


export default {
  name: 'App',
  methods:{
    increment(){
      this.$store.commit('increment')
    }
  },
  components: {
    HelloVuex
  }
}
</script>

<style>
</style>
  1. store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

//1.安裝,底層會調用Vuex.install
Vue.use(Vuex)

// 2.建立對象
const store = new Vuex.Store({
  state: {
    count: 0,
    persons: [
      {name: 'a', age: 12},
      {name: 'b', age: 23},
      {name: 'c', age: 32},
      {name: 'd', age: 24}
    ]
  }, mutations: {
    //state必須傳,默認會傳進來
    increment(state) {
      state.count++
    }
  },
  actions: {},
  //最多隻能寫兩個參數時state,getters,默認會傳進來
  getters: {
    more20Person(state) {
      return state.persons.filter(per=>per.age>20)
    },
    more20PersonCount(state,getters){
      // 這裏不用寫括號
      return getters.more20Person.length
    },
    //返回一個函數能夠傳動態的參數
    moreAgePerson(state){
      return (age)=>{
        return state.persons.filter(per=>per.age>age)
      }
    }

  },
  modules: {}
})

// 3.導出store對象
export default store
mutations
  1. 官方規定修改state只能用mutations
  2. 分爲兩部分函數名叫作字符串時間類型
  3. 代碼塊叫作回調函數
  4. 能夠傳多個參數第一個是store,後面的自定義叫作payload(負載 )
//store/index.js 
mutations: {
    //state必須傳,默認會傳進來
    increment(state) {
      state.count++
    },
    add(state,num){
      state.count +=num
    }
  }

//app.vue
methods:{
    increment(){
      this.$store.commit('increment')
    },
    add(num){
      this.$store.commit('add',num)
    }
  }

第二種提交風格

  1. 提交的參數會當成一個對象來取
inc(num){
      this.$store.commit({
        type:'inc',
        num
      })
    }
mutations: {
    //state必須傳,默認會傳進來
    increment(state) {
      state.count++
    },
    add(state,num){
      state.count +=num
    },
        //當成對象處理參數
    inc(state,payLoad){
      state.count +=payLoad.num
    }
  }
響應式操做
update(state){
      //響應式
      // state.persons.push({name:'e',age:99})
      //響應式
      // state.person={name:'f',age:101}

      //新加的屬性不會被監控,只有在其餘任意的屬性變化一次後他會刷新一次
      // state.person.add=111
      // state.person['address']=222
      //刪除一個對象的屬性
      // delete state.person.age

      //vue set value是響應式的,key必須是字符串
      // Vue.set(state.person,'asd','vue set value是響應式的')
      Vue.delete(state.person,'age')
    }
mutations中方法的官方定義
  1. 避免寫錯,定義一個常量對象,在使用的文件中導入

  2. 定義

    [const](){}
//mutation-type.js
export const INCREMENT='increment'
export const ADD='add'
export const INC='inc'
export const UPDATE='update'


import {INCREMENT,ADD,UPDATE,INC} from "./mutation-type";

//app.vue
update(){
      this.$store.commit({
        type:UPDATE,
      })
    }


//index.js
mutations: {
    //state必須傳,默認會傳進來
    [INCREMENT](state) {
      state.count++
    },
    [ADD](state,num){
      state.count +=num
    },
    //當成對象處理參數
    [INC](state,payLoad){
      state.count +=payLoad.num
    },
    [UPDATE](state){
      Vue.delete(state.person,'age')
    }
  }
actions
  1. mutations的異步方法修改的數據,插件是跟蹤不到的
  2. 全部的異步操做都應該放在actions中處理,處理後的回調放在mutations中處理
  3. 修改state的惟一途徑就是mutations
  4. actions中的默認參數是上下文context(context=store)

action處理異步操做:

//app.vue
aUpdate(){
      // this.$store.dispatch('aUpdate',{
      //   msg:'參數信息',
      //  success:(data)=>{console.log(data)}
      // })

      //第二種方法,異步函數返回的promise對象
      this.$store.dispatch('aUpdate',{
        msg:'參數信息'
      }).then(res=>{
        console.log('完成異步操做');
        console.log(res);
      })
    }


//index.js
actions: {
    // aUpdate(context,payload) {
    //   // console.log('默認參數是上下文對象: ',context)
    //   setTimeout(function () {
    //     context.commit('aUpdate',payload)
    //   }, 1000)
    // }

    //第二種方式返回一個promise對象,在調用處可使用
    aUpdate(context, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          context.commit('aUpdate', payload)
          resolve(12312)
        }, 1000)
      })
    }

  }
modules
  1. 只在一個state中存數據可能由於數據量過大而臃腫,因此在modules中分多個模塊
  2. 取值$store.state. modulesName .propertise
  3. 子模塊:
    1. state:須要指定模塊名,能夠和父模塊同名
    2. getters : 和父模塊同名會報錯,能夠直接訪問不須要指定模塊名
    3. actions|mutations : 和父模塊名字相同都會調用,先調用父模塊的,因此不要定義相同的名字

顯示:app.vue

<h2>-------------state--modules的內容---------</h2>
    <h2>{{$store.state.a.name}}</h2>
    <h2>{{$store.getters.getModuleA}}</h2>
    <h2>{{$store.getters.getModuleA_add('age')}}</h2>
    <h2>{{$store.getters.getModuleA_add_root}}</h2>
    <button @click="moduleA">模塊a修改name</button>
    <button @click="asyncUpdateModuleA">異步模塊a修改name</button>


methods:{
	  moduleA() {
        this.$store.commit('aUpdate','模塊a名字修改')
      },
      asyncUpdateModuleA(){
        this.$store.dispatch('asyncUpdateModuleA')
      }
}

index.js

modules: {
    a:{
      //須要指定模塊名,能夠和父模塊同名
      state:{name:'module_a',person:123},
      //和父模塊同名會報錯,能夠直接訪問不須要指定模塊名
      getters:{
        getModuleA(state){
          return state.name+'_getModuleA'
        },
        getModuleA_add(state,getters){
          return (age) => {
            return getters.getModuleA+age
          }

        },
        //三個默認參數
        getModuleA_add_root(state,getters,rootState){
          return state.name+getters.getModuleA+'_add_'+rootState.count
        }
      },
      // 和mutations使用差很少
      actions:{
          //也可使用對象的解構,詳見es6
        asyncUpdateModuleA(context){
          setTimeout(()=>{
            context.commit('aUpdate','異步修改子模塊')
          },1000)
        }
      },
      mutations:{
        //和父模塊名字相同都會調用,先調用父模塊的,因此不要定義相同的名字
        aUpdate(state,payload){
          state.name=payload
          console.log('child mutations 被調用')
        }
      },
      modules:{}
    },
        
    //模塊b
    b:ModuleB
  }

抽離index.js

  1. state通常是不抽取出來的
  2. modules是新建一個./modules/文件夾,在裏面創建模塊
  3. 抽離好的文件
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from "./mutations";
import actions from "./actions";
import getters from "./getters";
import module_a from "./modules/module_a";

//1.安裝,底層會調用Vuex.install
Vue.use(Vuex)

// 2.建立對象
const store = new Vuex.Store({
  state: {
    count: 0,
    persons: [
      {name: 'a', age: 12},
      {name: 'b', age: 23},
      {name: 'c', age: 32},
      {name: 'd', age: 24}
    ],
    person: {
      name: 'g',
      age: 100
    }
  },
  mutations,
  actions,
  getters,
  modules: {
    a: module_a
  }
})

// 3.導出store對象
export default store

刷新頁面store丟失

  • stote/index.js
export default new Vuex.Store({
    state:sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')): {
        enterprise: {},
        grid: [],
        iofficeUserRoleID: '',
        userVO: {},
    },
  • app.vue
mounted() {
            window.addEventListener('unload', this.saveState)
        },
        methods: {
            saveState() {
                sessionStorage.setItem('state', JSON.stringify(this.$store.state))
            }
        }

十6、axios

介紹

  1. 爲何選則它
    • vue做者再也不維護vue-resource,推薦使用vxios
    • 能夠在node環境中使用
    • 能夠攔截請求
    • 能夠攔截響應數據處理
  2. 支持的請求方式,知足resful,和jq的有點像
    1. axios(config) :默認是get,參數是一個對象
    2. axios.request(config)
    3. axios.get(url [, config])
    4. axios.delete(url [, config])
    5. axios.head(url [, config])
    6. axios.post(url [, data [, config] ])
    7. axios.put(url [, data [, config] ])
    8. axios.patch(url [, data [, config] ])
  3. 內部封裝了promise
  4. 會根據提供的參數數據自動轉換:content-type
  5. 會在跨域並使用content-type:application、json變成非簡單請求的時候,預請求一個options方法到後臺,不處理的話形成後臺傳參錯誤

使用

初步使用
import axios from 'axios'

axios.defaults.baseURL = 'https://httpbin.org'
axios.defaults.timeout = 5000
axios({
  // url:'http://123.207.32.32:8080/home/mutidata',
  url: 'post',
  method: 'post',
  // 拼接在URL後
  params: {
    name: 1
  },
  // 請求體中的參數
  data: {
    type: 'sell',
    page: 3
  },
  //攔截請求
  transformRequest:[function (query) {

  }],
  //攔截返回數據
  transformResponse:[function (response) {

  }],
}).then(res => {
  console.log(res);
})

// 同時處理多個異步請求,最後返回一個數據數組,像java的柵欄
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})]).then(res => {
  console.log(res);
})

//處理返回的結果數組,使用的是數組的解構是根據下標解構的
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})])
  .then(([res1, res2]) => {
    console.log(res1);
    console.log(res2);
  })
// 這樣也能夠
axios.all([axios({url: 'post', method: 'post'}), axios({url: 'get'})])
  .then(
    axios.spread((res1, res2) => {
      console.log(res1);
      console.log(res2);
    }))
進一步封裝

避免使用全局的axios,可能每一個模塊的請求是不同的

  1. 使用的時候導入就能夠了
  2. transformrequest和axiosInstance.interceptors.request.use 不衝突後者先調用
  3. transformResponse和axiosInstance.interceptors.response前者先調用

新建/network/request.js

import axios from "axios";

export function request(config) {
  if (!config.baseURL) {
    config.baseURL = 'https://httpbin.org'
  }
  if (!config.timeout) {
    config.timeout = 5000;
  }
  const axiosInstance = axios.create(config);
  //req是請求參數對象
  axiosInstance.interceptors.request.use(req => {
    console.log(req);
    //1.能夠修改一些請求的參數
    // 2.能夠設置一個加載圖片
    return req
  })
  //res是返回的對象
  axiosInstance.interceptors.response.use(res => {
    console.log(res.data);
    return res.data
  })
  return axiosInstance(config);
}

十7、細節和雜項

this.$refs.[refName]

只會取當前模塊的引用

style標籤裏的scoped

只會做用當前的組件的css

組件是不能直接監聽原生事件的,須要:@click.native=""

main.js裏設置Vue.config.productionTip = false和true的區別

  • 是一個全局配置屬性2.2.0 新增
  • 設置爲 false 以阻止 vue 在啓動時生成生產提示。

vue文件能夠首字母大寫,若是首字母大寫你使用組件的時候能夠用-爲分隔符,TabBar標籤使用的時候可以使用tab-bar

使用':style'

:style 後面是對象的時候裏面的屬性值是字符串格式

vue表單提交:其餘方式

controller

@RequestMapping("save")
public ResponseModel savenew(@RequestBody @Validated SysUser user, BindingResult 		result) {
        if (result.hasErrors()) {
            return ResponseModel.FAIL()
                    .setMsg(result.getAllErrors()
                            .stream()
                            .map(err->err.getDefaultMessage())
                                    .collect(Collectors.joining(";"))
                            );
        }
        String password = user.getPassword();
        if (password.length() < 32) {
            user.setPassword(CryptUtil.shiroEncry(user));
        }
        userService.save(user);
        return ResponseModel.SUCCESS();
}

vue

<template>
	<div id="user">
		<div>
			姓名:
			<input type="text" name="username" v-model="entity.username"/>
		</div>
		<div>
			密碼:
			<input type="password" v-model="entity.password" />
		</div>
		<div>
			電話:
			<input type="text" v-model="entity.phone" />
		</div>
		<div>
			電話:
			<input type="text" v-model="entity.role.roleName" />
		</div>
		<button @click="saveUserInfo">保存</button>
	</div>
</template>

<script>
	import {
		saveUser
	} from 'network/module/user.js';

	export default {
		name: "User",
		methods: {
			saveUserInfo() {
				saveUser(this.entity).then(res=>alert(res.msg));
			}
		},
		data(){
			return {
				entity:{
					role:{}
				}
			}
		}
	};
</script>

415Unsupported Media Type錯誤

前端工具請求接口的類型和後端服務器定義的類型不一致形成

vue跨域

vue.config.js

//vue-cli3配置這個作代理
	devServer: {
		proxy: {
			'/api': {
				target: 'http://localhost:8080/mall/api', //API服務器的地址
				changeOrigin: true, // 是否跨域
				pathRewrite: {
					'^/api': ''
				}
			}
		},
	},
	//vue-cli2使用這個
	// dev: {
	// 	proxyTable: {
	// 		'/api': {
	// 			target: 'http://localhost:8080/mall/api', //API服務器的地址
	// 			changeOrigin: true, // 是否跨域
	// 			pathRewrite: {
	// 				'^/api': ''
	// 			}
	// 		}
	// 	}
	// },

qs的嵌套對象序列化問題

qs官網

  • 手寫嵌套對象:

    {
        a:{
            'b.c':d
        }
    }
  • qs默認嵌套對象的序列化會用 ‘[]’

    //{ allowDots: true }會將【】轉換成 ‘.’
    this.$qs.stringify(obj,{ allowDots: true })

簡單請求和非簡單請求

阮一峯跨域

package.js中的^和~區別

npm官網

~1.15.2 := >=1.15.2 <1.16.0 匹配到第二位,大於當前版本小於1.16.0

^3.3.4 := >=3.3.4 <4.0.0 匹配到第一位全部大於當前版本且小於4

vue經常使用插件

詳情

Vue.nextTick

this.$nextTick(function)將回調延遲到下次 DOM 更新循環以後執行,vm.$nextTick() 實例方法特別方便,由於它不須要全局 Vue,而且回調函數中的 this 將自動綁定到當前的 Vue 實例上

@load的時候老是不生效

  • image src有值的時候,纔開始加載
  • onload事件是在src的資源加載完畢的時候,纔會觸發
相關文章
相關標籤/搜索