Vue組件化開發(fā)和異步編程

1. 組件

  • 組件 (Component) 是 Vue.js 最強大的功能之一
  • 組件可以擴展 HTML 元素乌奇,封裝可重用的代碼

1.1 組件注冊

全局注冊

  • Vue.component('組件名稱', { })
  • 全局組件注冊后,任何vue實例都可以用
<div id="example">
    <!-- 2、 組件使用 組件名稱 是以HTML標簽的形式使用  -->  
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>

</div>
<script>
    //   注冊組件 
    // 1借跪、 my-component 就是組件中自定義的標簽名
    Vue.component('my-component', {
        data: function() {
            return {
                count: 0
            }
        }
        template: '<div>{{count}}</div>'
    })

    // 創(chuàng)建根實例
    new Vue({
        el: '// example'
    })

</script>

注意:

  • data值必須是一個函數(shù),同時這個函數(shù)要求返回一個對象
  • 組件模板必須是單個根元素
  • 組件模板的內(nèi)容可以是模板字符串(es6語法张峰,需要瀏覽器提供支持)
  <div id="app">
     <!-- 
4恍风、  組件可以重復(fù)使用多次 
因為data中返回的是一個對象,所以每個組件中的數(shù)據(jù)是私有的
即每個實例可以維護一份被返回對象的獨立的拷貝   
    --> 
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
      <!--必須使用短橫線的方式使用駝峰式命名組件 -->
     <hello-world></hello-world>
  </div>

<script type="text/javascript">
    //(1)如果使用駝峰式命名組件磨总,那么在使用組件的時候嗦明,只能在字符串模板中用駝峰的方式使用組件,
    //(2)但是在普通的標簽?zāi)0逯序窖啵仨毷褂枚虣M線方式使用組件(1)
     Vue.component('HelloWorld', {
      data: function(){
        return {
          msg: 'HelloWorld'
        }
      },
      template: '<div>{{msg}}</div>'
    });
    
    Vue.component('button-counter', {
      // 1娶牌、組件參數(shù)的data值必須是函數(shù) 
      // 同時這個函數(shù)要求返回一個對象  
      data: function(){
        return {
          count: 0
        }
      },
      //  2、組件模板必須是單個根元素
      //  3馆纳、組件模板的內(nèi)容可以是模板字符串  
      template: `
        <div>
          <button @click="handle">點擊了{{count}}次</button>
          <button>測試123</button>
            //   (2)在字符串模板中可以使用駝峰的方式使用組件    
           <HelloWorld></HelloWorld>
        </div>
      `,
      methods: {
        handle: function(){
          this.count += 2;
        }
      }
    })
    var vm = new Vue({
      el: '// app',
      data: {
        
      }
    });
  </script>

補充:組件命名方式有短橫線“ - ”和駝峰式2種

局部注冊

  • 只能在當(dāng)前注冊它的vue實例中使用
  <div id="app">
      <my-component></my-component>
  </div>


<script>
    // 定義組件的模板
    var Child = {
      template: '<div>A custom component!</div>'
    }
    new Vue({
      //局部注冊組件  
      components: {
        // <my-component> 將只在父模板可用  一定要在實例上注冊了才能在html文件中使用
        'my-component': Child
      }
    })
 </script>

1.2 Vue組件之間傳值

父組件向子組件傳值

  • 以屬性的形式綁定值到子組件身上诗良,一種是靜態(tài)的值,一種是動態(tài)綁定(:)
  • 子組件用屬性props鲁驶,以數(shù)組方式接收(單向數(shù)據(jù)傳送)
  • 在props中使用駝峰形式鉴裹,模板中需要使用短橫線的形式(html不區(qū)分大小寫)字符串形式的模板中沒有這個限制
  <div id="app">
    <div>{{pmsg}}</div>
     <!--1、menu-item在APP中嵌套著 故menu-item為子組件  -->
     <!-- 給子組件傳入一個靜態(tài)的值钥弯,類型為字符串 -->
    <menu-item title='來自父組件的值'></menu-item>
    <!-- 2径荔、 需要動態(tài)的數(shù)據(jù)的時候 需要屬性綁定的形式 此時ptitle是父組件data中的數(shù)據(jù)(類型不變). 
          傳的值可以是數(shù)字、布爾脆霎、字符串总处、對象、數(shù)組等等
            html不區(qū)分大小寫,屬性用短橫線的形式
    -->
    <menu-item :title='ptitle' my-content='hello'></menu-item>
  </div>

  <script type="text/javascript">
    Vue.component('menu-item', {
      // 3睛蛛、 子組件用屬性props接收父組件傳遞過來的數(shù)據(jù)  
      props: ['title', 'myContent'],
      data: function() {
        return {
          msg: '子組件本身的數(shù)據(jù)'
        }
      },
      template: '<div>{{msg + "----" + title + "-----" + myContent}}</div>'
    });
    var vm = new Vue({
      el: '// app',
      data: {
        pmsg: '父組件中內(nèi)容',
        ptitle: '動態(tài)綁定屬性'
      }
    });
  </script>

子組件向父組件傳值

  • 子組件用$emit()觸發(fā)事件
  • $emit() 第一個參數(shù)為 自定義的事件名稱 第二個參數(shù)為需要傳遞的數(shù)據(jù)
  • 父組件用v-on 監(jiān)聽子組件的事件
 <div id="app">
    <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
     <!-- 2 父組件用v-on 監(jiān)聽子組件的事件
        這里 enlarge-text是從$emit中的第一個參數(shù)對應(yīng)   handle 為對應(yīng)的事件處理函數(shù) 
    --> 
    <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      子組件向父組件傳值-攜帶參數(shù)
    */
    
    Vue.component('menu-item', {
      props: ['parr'],
      template: `
        <div>
          <ul>
            <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
          </ul>
            // // //   1辨泳、子組件用$emit()觸發(fā)事件
            // // //  第一個參數(shù)為 自定義的事件名稱   第二個參數(shù)為需要傳遞的數(shù)據(jù)  
          <button @click='$emit("enlarge-text", 5)'>擴大父組件中字體大小</button>
          <button @click='$emit("enlarge-text", 10)'>擴大父組件中字體大小</button>
        </div>
      `
    });
    var vm = new Vue({
      el: '// app',
      data: {
        pmsg: '父組件中內(nèi)容',
        parr: ['apple','orange','banana'],
        fontSize: 10
      },
      methods: {
        handle: function(val){
          // 擴大字體大小
          this.fontSize += val;
        }
      }
    });
  </script>

兄弟之間的傳遞

  • 兄弟之間傳遞數(shù)據(jù)需要借助于事件中心虱岂,通過事件中心傳遞數(shù)據(jù)
    • 提供事件中心 var hub = new Vue()
  • 傳遞數(shù)據(jù)方,通過一個事件觸發(fā)hub.$emit(方法名菠红,傳遞的數(shù)據(jù))
  • 接收數(shù)據(jù)方第岖,通過mounted(){} 鉤子中 觸發(fā)hub.$on()方法名
  • 銷毀事件 通過hub.$off()方法名銷毀之后無法進行傳遞數(shù)據(jù)
 <div id="app">
    <div>父組件</div>
    <div>
      <button @click='handle'>銷毀事件</button>
    </div>
    <test-tom></test-tom>
    <test-jerry></test-jerry>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      兄弟組件之間數(shù)據(jù)傳遞
    */
    //1、 提供事件中心
    var hub = new Vue();

    Vue.component('test-tom', {
      data: function(){
        return {
          num: 0
        }
      },
      template: `
        <div>
          <div>TOM:{{num}}</div>
          <div>
            <button @click='handle'>點擊</button>
          </div>
        </div>
      `,
      methods: {
        handle: function(){
          //2试溯、觸發(fā)兄弟組件的事件 事件觸發(fā)hub.$emit(方法名蔑滓,傳遞的數(shù)據(jù))   
          hub.$emit('jerry-event', 2);
        }
      },
      mounted: function() {
       // 3、監(jiān)聽事件:接收數(shù)據(jù)val遇绞,通過mounted(){} 鉤子中  觸發(fā)hub.$on(方法名
        hub.$on('tom-event', (val) => {
          this.num += val;
        });
      }
    });
    Vue.component('test-jerry', {
      data: function(){
        return {
          num: 0
        }
      },
      template: `
        <div>
          <div>JERRY:{{num}}</div>
          <div>
            <button @click='handle'>點擊</button>
          </div>
        </div>
      `,
      methods: {
        handle: function(){
          //2键袱、傳遞數(shù)據(jù)方,通過一個事件觸發(fā)hub.$emit(方法名摹闽,傳遞的數(shù)據(jù))   觸發(fā)兄弟組件的事件
          hub.$emit('tom-event', 1);
        }
      },
      mounted: function() {
        // 3蹄咖、接收數(shù)據(jù)方,通過mounted(){} 鉤子中  觸發(fā)hub.$on()方法名
        hub.$on('jerry-event', (val) => {
          this.num += val;
        });
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {
        
      },
      methods: {
        handle: function(){
          //4付鹿、銷毀事件 通過hub.$off()方法名銷毀之后無法進行傳遞數(shù)據(jù)  
          hub.$off('tom-event');
          hub.$off('jerry-event');
        }
      }
    });
  </script>

1.3 組件插槽

  • 作用:父組件向子組件傳遞內(nèi)容<slot></slot>
  • 組件的最大特性就是復(fù)用性澜汤,而用好插槽能大大提高組件的可復(fù)用能力

匿名插槽

  <div id="app">
    <!-- 組件標簽中的內(nèi)容會替換掉slot內(nèi)容  如果不傳值 則使用 slot 中的默認值  -->  
    <alert-box>有bug發(fā)生</alert-box>
    <alert-box>有一個警告</alert-box>
    <alert-box></alert-box>
  </div>

  <script type="text/javascript">
    /*
      組件插槽:父組件向子組件傳遞內(nèi)容
    */
    Vue.component('alert-box', {
      template: `
        <div>
          <strong>ERROR:</strong>
        //  當(dāng)組件渲染的時候,這個 <slot> 元素將會被替換為“組件標簽中嵌套的內(nèi)容”舵匾。
        //  插槽內(nèi)可以包含任何模板代碼俊抵,包括 HTML
          <slot>默認內(nèi)容</slot>
        </div>
      `
    });
    var vm = new Vue({
      el: '// app',
      data: {
        
      }
    });
  </script>
</body>
</html>

具名插槽

  • 具有名字的插槽
  • 使用 <slot> 中的 "name" 屬性綁定元素
  <div id="app">
    <base-layout>
       <!-- 2、 通過slot屬性來指定, 這個slot的值必須和下面slot組件的name值對應(yīng)上 
                如果沒有匹配到 則放到匿名的插槽中   --> 
      <p slot='header'>標題信息</p>
      <p>主要內(nèi)容1</p>
      <p>主要內(nèi)容2</p>
      <p slot='footer'>底部信息信息</p>
    </base-layout>

    <base-layout>
      <!-- 注意點:template臨時的包裹標簽最終不會渲染到頁面上坐梯,用于包裹多個標簽
     因為一個slot 的name值 只能有一個slot屬性對應(yīng)-->  
      <template slot='header'>
        <p>標題信息1</p>
        <p>標題信息2</p>
      </template>
      <p>主要內(nèi)容1</p>
      <p>主要內(nèi)容2</p>
      <template slot='footer'>
        <p>底部信息信息1</p>
        <p>底部信息信息2</p>
      </template>
    </base-layout>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      具名插槽
    */
    Vue.component('base-layout', {
      template: `
        <div>
          <header>
            // // //    1徽诲、 使用 <slot> 中的 "name" 屬性綁定元素 指定當(dāng)前插槽的名字
            <slot name='header'></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
            // // //   注意點: 
            // // //   具名插槽的渲染順序,完全取決于模板吵血,而不是取決于父組件中元素的順序
            <slot name='footer'></slot>
          </footer>
        </div>
      `
    });
    var vm = new Vue({
      el: '// app',
      data: {
        
      }
    });
  </script>
</body>
</html>

作用域插槽

  • 父組件對子組件的內(nèi)容進行加工處理
  • 既可以復(fù)用子組件的slot谎替,又可以使slot內(nèi)容不一致
<div id="app">
    <!-- 
1、當(dāng)我們希望li 的樣式由外部使用組件的地方定義蹋辅,因為可能有多種地方要使用該組件院喜,
但樣式希望不一樣 這個時候我們需要使用作用域插槽 

-->  
    <fruit-list :list='list'>
        <!-- 2、 父組件中使用了<template>元素,而且包含scope="slotProps",
slotProps在這里只是臨時變量晕翠,用于獲取子組件slot綁定的數(shù)據(jù)   
--->    
        <template slot-scope='slotProps'>
            <strong v-if='slotProps.info.id==3' class="current">
                {{slotProps.info.name}}              
            </strong>
            <span v-else>{{slotProps.info.name}}</span>
        </template>
    </fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
    /*
      作用域插槽
    */
    Vue.component('fruit-list', {
      props: ['list'],
      template: `
        <div>
          <li :key='item.id' v-for='item in list'>
            // // //   3喷舀、 在子組件模板中,<slot>元素上有一個類似props傳遞數(shù)據(jù)給組件的寫法msg="xxx",
            // // //    插槽可以提供一個默認內(nèi)容,如果如果父組件沒有為這個插槽提供了內(nèi)容淋肾,會顯示默認的內(nèi)容硫麻。
                    如果父組件為這個插槽提供了內(nèi)容,則默認的內(nèi)容會被替換掉
            <slot :info='item'>{{item.name}}</slot>
          </li>
        </div>
      `
    });
    var vm = new Vue({
      el: '// app',
      data: {
        list: [{
          id: 1,
          name: 'apple'
        },{
          id: 2,
          name: 'orange'
        },{
          id: 3,
          name: 'banana'
        }]
      }
    });
  </script>
</body>
</html>

購物車案例

1. 實現(xiàn)組件化布局

  • 把靜態(tài)頁面轉(zhuǎn)換成組件化模式
  • 把組件渲染到頁面上
 <div id="app">
    <div class="container">
      <!-- 2樊卓、把組件渲染到頁面上 --> 
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    //  1拿愧、 把靜態(tài)頁面轉(zhuǎn)換成組件化模式
    //  1.1  標題組件 
    var CartTitle = {
      template: `
        <div class="title">我的商品</div>
      `
    }
    //  1.2  商品列表組件 
    var CartList = {
      //   注意點 :  組件模板必須是單個根元素  
      template: `
        <div>
          <div class="item">
            <img src="img/a.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/b.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/c.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/d.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/e.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
        </div>
      `
    }
    //  1.3  商品結(jié)算組件 
    var CartTotal = {
      template: `
        <div class="total">
          <span>總價:123</span>
          <button>結(jié)算</button>
        </div>
      `
    }
    // //  1.4  定義一個全局組件 my-cart
    Vue.component('my-cart',{
      // //   1.6 引入子組件  
      template: `
        <div class='cart'>
          <cart-title></cart-title>
          <cart-list></cart-list>
          <cart-total></cart-total>
        </div>
      `,
      //  1.5  注冊子組件   
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {

      }
    });

  </script>



2、實現(xiàn) 標題和結(jié)算功能組件

  • 標題組件實現(xiàn)動態(tài)渲染
    • 從父組件把標題數(shù)據(jù)傳遞過來 即 父向子組件傳值
    • 把傳遞過來的數(shù)據(jù)渲染到頁面上
  • 結(jié)算功能組件
    • 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值
    • 把傳遞過來的數(shù)據(jù)計算最終價格渲染到頁面上
 <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
     //  2.2  標題組件     子組件通過props形式接收父組件傳遞過來的uname數(shù)據(jù)
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    //  2.3  商品結(jié)算組件  子組件通過props形式接收父組件傳遞過來的list數(shù)據(jù)   
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>總價:{{total}}</span>
          <button>結(jié)算</button>
        </div>
      `,
      computed: {
        //  2.4    計算商品的總價  并渲染到頁面上 
        total: function() {
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '張三',
          list: [{
            id: 1,
            name: 'TCL彩電',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '機頂盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海爾冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手機',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV電視',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      //   2.1  父組件向子組件以屬性傳遞的形式 傳遞數(shù)據(jù)
      //    向 標題組件傳遞 uname 屬性   向 商品結(jié)算組件傳遞 list  屬性  
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {

      }
    });

  </script>

3. 實現(xiàn)列表組件刪除功能

  • 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值
  • 把傳遞過來的數(shù)據(jù)渲染到頁面上
  • 點擊刪除按鈕的時候刪除對應(yīng)的數(shù)據(jù)
    • 給按鈕添加點擊事件把需要刪除的id傳遞過來
      • 子組件中不推薦操作父組件的數(shù)據(jù)有可能多個子組件使用父組件的數(shù)據(jù) 我們需要把數(shù)據(jù)傳遞給父組件讓父組件操作數(shù)據(jù)
      • 父組件刪除對應(yīng)的數(shù)據(jù)
 <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    //   3.2 把列表數(shù)據(jù)動態(tài)渲染到頁面上  
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            //  3.3  給按鈕添加點擊事件把需要刪除的id傳遞過來
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        del: function(id){
           //  3.4 子組件中不推薦操作父組件的數(shù)據(jù)有可能多個子組件使用父組件的數(shù)據(jù) 
          //      我們需要把數(shù)據(jù)傳遞給父組件 讓父組件操作數(shù)據(jù) 
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>總價:{{total}}</span>
          <button>結(jié)算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 計算商品的總價
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '張三',
          list: [{
            id: 1,
            name: 'TCL彩電',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '機頂盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海爾冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手機',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV電視',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      //  3.1 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值  
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          //   3.5  父組件通過事件綁定 接收子組件傳遞過來的數(shù)據(jù) 
          <cart-list :list='list' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        //  3.6    根據(jù)id刪除list中對應(yīng)的數(shù)據(jù)        
        delCart: function(id) {
          // 1碌尔、找到id所對應(yīng)數(shù)據(jù)的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2浇辜、根據(jù)索引刪除對應(yīng)數(shù)據(jù)
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {

      }
    });

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

4. 實現(xiàn)組件更新數(shù)據(jù)功能 上

  • 將輸入框中的默認數(shù)據(jù)動態(tài)渲染出來
  • 輸入框失去焦點的時候 更改商品的數(shù)量
  • 子組件中不推薦操作數(shù)據(jù) 把這些數(shù)據(jù)傳遞給父組件 讓父組件處理這些數(shù)據(jù)
  • 父組件中接收子組件傳遞過來的數(shù)據(jù)并處理
 <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href="">-</a>
                //  1. 將輸入框中的默認數(shù)據(jù)動態(tài)渲染出來
                //  2. 輸入框失去焦點的時候 更改商品的數(shù)量  需要將當(dāng)前商品的id 傳遞過來
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
              <a href="">+</a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        changeNum: function(id, event){
          //  3 子組件中不推薦操作數(shù)據(jù)  因為別的組件可能也引用了這些數(shù)據(jù)
          //   把這些數(shù)據(jù)傳遞給父組件 讓父組件處理這些數(shù)據(jù)
          this.$emit('change-num', {
            id: id,
            num: event.target.value
          });
        },
        del: function(id){
          // 把id傳遞給父組件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>總價:{{total}}</span>
          <button>結(jié)算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 計算商品的總價
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '張三',
          list: [{
            id: 1,
            name: 'TCL彩電',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          }]
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
            //  4  父組件中接收子組件傳遞過來的數(shù)據(jù) 
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function(val) {
          //4.1 根據(jù)子組件傳遞過來的數(shù)據(jù)券敌,跟新list中對應(yīng)的數(shù)據(jù)
          this.list.some(item=>{
            if(item.id == val.id) {
              item.num = val.num;
              // 終止遍歷
              return true;
            }
          });
        },
        delCart: function(id) {
          // 根據(jù)id刪除list中對應(yīng)的數(shù)據(jù)
          // 1、找到id所對應(yīng)數(shù)據(jù)的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2柳洋、根據(jù)索引刪除對應(yīng)數(shù)據(jù)
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {

      }
    });

  </script>

5. 實現(xiàn)組件更新數(shù)據(jù)功能 下

  • 子組件通過一個標識符來標記對用的用戶點擊 + - 或者輸入框輸入的內(nèi)容
  • 父組件拿到標識符更新對應(yīng)的組件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: // fff;*/  
    }
    .container .total {
      background-color: // FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: // DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid // ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              //  1.  + - 按鈕綁定事件 
              <a href="" @click.prevent='sub(item.id)'>-</a>
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
              <a href="" @click.prevent='add(item.id)'>+</a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        changeNum: function(id, event){
          this.$emit('change-num', {
            id: id,
            type: 'change',
            num: event.target.value
          });
        },
        sub: function(id){
          //  2 數(shù)量的增加和減少通過父組件來計算   每次都是加1 和 減1 不需要傳遞數(shù)量   父組件需要一個類型來判斷 是 加一 還是減1  以及是輸入框輸入的數(shù)據(jù)  我們通過type 標識符來標記 不同的操作   
          this.$emit('change-num', {
            id: id,
            type: 'sub'
          });
        },
        add: function(id){
         //  2 數(shù)量的增加和減少通過父組件來計算   每次都是加1 和 減1 不需要傳遞數(shù)量   父組件需要一個類型來判斷 是 加一 還是減1  以及是輸入框輸入的數(shù)據(jù)  我們通過type 標識符來標記 不同的操作
          this.$emit('change-num', {
            id: id,
            type: 'add'
          });
        },
        del: function(id){
          // 把id傳遞給父組件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>總價:{{total}}</span>
          <button>結(jié)算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 計算商品的總價
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '張三',
          list: [{
            id: 1,
            name: 'TCL彩電',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '機頂盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海爾冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手機',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV電視',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>  
        //  3 父組件通過事件監(jiān)聽   接收子組件的數(shù)據(jù)  
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function(val) {
          // 4 分為三種情況:輸入框變更待诅、加號變更、減號變更
          if(val.type=='change') {
            // 根據(jù)子組件傳遞過來的數(shù)據(jù)熊镣,跟新list中對應(yīng)的數(shù)據(jù)
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num = val.num;
                // 終止遍歷
                return true;
              }
            });
          }else if(val.type=='sub'){
            // 減一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num -= 1;
                // 終止遍歷
                return true;
              }
            });
          }else if(val.type=='add'){
            // 加一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num += 1;
                // 終止遍歷
                return true;
              }
            });
          }
        }
      }
    });
    var vm = new Vue({
      el: '// app',
      data: {

      }
    });

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

2.前后端交互

2.1 接口調(diào)用的方式有哪些

原生ajax
基于jQuery的ajax
Fetch
Promise

2.2 url 地址格式有哪些

  • 傳統(tǒng)的url

    協(xié)議+域名/ip地址+端口+路徑+請求參數(shù)+錨點

  • Restful形式的url

    請求方式:增刪改查 post delete put get

3.異步編程與promise

3.1異步

  • JavaScript的執(zhí)行環(huán)境是「單線程」
  • 所謂單線程卑雁,是指JS引擎中負責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個,也就是一次只能完成一項任務(wù)绪囱,這個任務(wù)執(zhí)行完后才能執(zhí)行下一個测蹲,它會「阻塞」其他任務(wù)。這個任務(wù)可稱為主線程
  • 異步模式可以一起執(zhí)行多個任務(wù)
  • JS中常見的異步調(diào)用
    • 定時器
    • ajax
    • 事件函數(shù)

3.2promise

  • 主要解決異步深層嵌套的c問題
  • promise 提供了簡潔的API 使得異步操作更加容易
 
  <script type="text/javascript">
    /*
     1. Promise基本使用
           我們使用new來構(gòu)建一個Promise  Promise的構(gòu)造函數(shù)接收一個參數(shù)鬼吵,是函數(shù)扣甲,并且傳入兩個參數(shù):           resolve,reject齿椅, 分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù)
    */


    var p = new Promise(function(resolve, reject){
      //2. 這里用于實現(xiàn)異步任務(wù)  setTimeout
      setTimeout(function(){
        var flag = false;
        if(flag) {
          //3. 正常情況
          resolve('hello');
        }else{
          //4. 異常情況
          reject('出錯了');
        }
      }, 100);
    });
    //  5 Promise實例生成以后琉挖,可以用then方法指定resolved狀態(tài)和reject狀態(tài)的回調(diào)函數(shù) 
    //  在then方法中,你也可以直接return數(shù)據(jù)而不是Promise對象媒咳,在后面的then中就可以接收到數(shù)據(jù)了  
    p.then(function(data){
      console.log(data)
    },function(info){
      console.log(info)
    });
  </script>

基于Promise發(fā)送Ajax請求

 
  <script type="text/javascript">
    /*
      基于Promise發(fā)送Ajax請求
    */
    function queryData(url) {
     //    1.1 創(chuàng)建一個Promise實例
      var p = new Promise(function(resolve, reject){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
          if(xhr.readyState != 4) return;
          if(xhr.readyState == 4 && xhr.status == 200) {
            //  1.2 處理正常的情況
            resolve(xhr.responseText);
          }else{
            //  1.3 處理異常情況
            reject('服務(wù)器錯誤');
          }
        };
        xhr.open('get', url);
        xhr.send(null);
      });
      return p;
    }
    //  注意:  這里需要開啟一個服務(wù) 
    //  在then方法中粹排,你也可以直接return數(shù)據(jù)而不是Promise對象种远,在后面的then中就可以接收到數(shù)據(jù)了
    queryData('http://localhost:3000/data')
      .then(function(data){
        console.log(data)
        //   1.4 想要繼續(xù)鏈式編程下去 需要 return  
        return queryData('http://localhost:3000/data1');
      })
      .then(function(data){
        console.log(data);
        return queryData('http://localhost:3000/data2');
      })
      .then(function(data){
        console.log(data)
      });
  </script>

3.3 Promise 基本API

實例方法

.then()
  • 得到異步任務(wù)正確的結(jié)果
.catch()
  • 獲取異常信息
.finally()
  • 成功與否都會執(zhí)行(不是正式標準)
  
  <script type="text/javascript">
    /*
      Promise常用API-實例方法
    */
    // console.dir(Promise);
    function foo() {
      return new Promise(function(resolve, reject){
        setTimeout(function(){
          // resolve(123);
          reject('error');
        }, 100);
      })
    }
    // foo()
    //   .then(function(data){
    //     console.log(data)
    //   })
    //   .catch(function(data){
    //     console.log(data)
    //   })
    //   .finally(function(){
    //     console.log('finished')
    //   });

    // --------------------------
    // 兩種寫法是等效的
    foo()
      .then(function(data){
        //  得到異步任務(wù)正確的結(jié)果
        console.log(data)
      },function(data){
        //  獲取異常信息
        console.log(data)
      })
      //  成功與否都會執(zhí)行(不是正式標準) 
      .finally(function(){
        console.log('finished')
      });
  </script>

靜態(tài)方法

.all()
  • Promise.all方法接受一個數(shù)組作參數(shù)涩澡,數(shù)組中的對象(p1、p2肌访、p3)均為promise實例(如果不是一個promise烛亦,該項會被用Promise.resolve轉(zhuǎn)換為一個promise)圣勒。它的狀態(tài)由這三個promise實例決定
.race()
  • Promise.race方法同樣接受一個數(shù)組作參數(shù)。當(dāng)p1, p2, p3中有一個實例的狀態(tài)發(fā)生改變(變?yōu)?code>fulfilled或rejected)粥帚,p的狀態(tài)就跟著改變。并把第一個改變狀態(tài)的promise的返回值限次,傳給p的回調(diào)函數(shù)

區(qū)別:返回數(shù)據(jù)的條件不同芒涡,all是所有執(zhí)行完,race是只要有一個執(zhí)行完就返回結(jié)果

  <script type="text/javascript">
    /*
      Promise常用API-對象方法
    */
    // console.dir(Promise)
    function queryData(url) {
      return new Promise(function(resolve, reject){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
          if(xhr.readyState != 4) return;
          if(xhr.readyState == 4 && xhr.status == 200) {
            // 處理正常的情況
            resolve(xhr.responseText);
          }else{
            // 處理異常情況
            reject('服務(wù)器錯誤');
          }
        };
        xhr.open('get', url);
        xhr.send(null);
      });
    }

    var p1 = queryData('http://localhost:3000/a1');
    var p2 = queryData('http://localhost:3000/a2');
    var p3 = queryData('http://localhost:3000/a3');
     Promise.all([p1,p2,p3]).then(function(result){
       //   all 中的參數(shù)[p1,p2,p3]和 返回的結(jié)果順序一一對應(yīng)["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
       console.log(result) //["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
     })
    Promise.race([p1,p2,p3]).then(function(result){
      // 由于p1執(zhí)行較快卖漫,Promise的then()將獲得結(jié)果'P1'费尽。p2,p3仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄羊始。
      console.log(result) // "HELLO TOM"
    })
  </script>

3.4 fetch

  • Fetch API是新的ajax解決方案 ,fetch不是ajax的進一步封裝旱幼,而是原生js,沒有使用XMLHttpRequest對象
  • 基于標準的Promises實現(xiàn)突委,支持async/await柏卤,F(xiàn)etch會返回Promise
  • 使用fetch(url, options).then()
<script type="text/javascript">  
    //參數(shù):請求路徑冬三,請求參數(shù)   Fetch會返回Promise   所以我們可以使用then 拿到請求成功的結(jié)果 
    fetch('http://localhost:3000/fdata').then(function(res){
        // text()方法屬于fetchAPI的一部分,它返回一個Promise實例對象缘缚,用于獲取后臺返回的數(shù)據(jù)
        return res.text();
    }).then(function(data){
        //   在這個then里面我們能拿到最終的數(shù)據(jù)  
        console.log(data);
    })
    //或者
    fetch('http://localhost:3000/fdata').then(function(res){
        res.text().then(function(data){
            console.log(data);
        });
    })
</script>

fetch API 中的 HTTP 請求

  • fetch(url, options).then()
  • HTTP協(xié)議勾笆,它給我們提供了很多的方法,如POST忙灼,GET匠襟,DELETE,UPDATE该园,PATCH和PUT
    • 默認的是 GET 請求
    • 需要在options對象中指定對應(yīng)的method method:請求使用的方法
    • post和普通請求的時候 需要在options設(shè)置請求頭 headers 和 body
   <script type="text/javascript">
        /*
              Fetch API 調(diào)用接口傳遞參數(shù)
        */
       // 1.1 GET參數(shù)傳遞 - 傳統(tǒng)URL通過url酸舍?的形式傳參 
        fetch('http://localhost:3000/books?id=123', {
                //  省略不寫 默認的是GET 
                method: 'get'
            })
            .then(function(data) {
                //  它返回一個Promise實例對象,用于獲取后臺返回的數(shù)據(jù)
                return data.text();
            }).then(function(data) {
                //  在這個then里面拿到最終的數(shù)據(jù)  
                console.log(data);//123
            });
       // 服務(wù)端:接收請求和響應(yīng)
       // 設(shè)置允許跨域訪問該服務(wù)
        app.all('*', function (req, res, next) {
          res.header("Access-Control-Allow-Origin", "*");
          res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
          res.header('Access-Control-Allow-Headers', 'Content-Type');
          next();
        });
       app.get('/books', (req, res) => {
           res.send(req.query.id);
       })

      // 1.2  GET參數(shù)傳遞  restful形式的URL  通過/的形式傳遞參數(shù)  即id=456和id后臺的配置有關(guān)   
        fetch('http://localhost:3000/books/456', {
                method: 'get'
            })
            .then(function(data) {
                return data.text();
            }).then(function(data) {
                console.log(data)
            });
      // 服務(wù)端:接收請求和響應(yīng)
       // ...        設(shè)置允許跨域訪問該服務(wù)
       app.get('/books/:id', (req, res) => {
           res.send(req.params.id);
       })
       
       // 2.1  DELETE請求方式參數(shù)傳遞      刪除id  是  id=789
        fetch('http://localhost:3000/books/789', {
                method: 'delete'
            })
            .then(function(data) {
                return data.text();
            }).then(function(data) {
                console.log(data)
            });
       // 服務(wù)端:接收請求和響應(yīng)
       // ...        設(shè)置允許跨域訪問該服務(wù)
       app.delete('/books/:id', (req, res) => {
           res.send(req.params.id);
       })

       // 3 POST請求傳參
        fetch('http://localhost:3000/books', {
                method: 'post',
                //  3.1  傳遞數(shù)據(jù) 
                body: 'uname=lisi&pwd=123',
                //   3.2  設(shè)置請求頭 
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            })
            .then(function(data) {
                return data.text();
            }).then(function(data) {
                console.log(data)
            });
       // 服務(wù)端:接收請求和響應(yīng)
       // ...設(shè)置允許跨域訪問該服務(wù)
       const bodyParser = require('body-parse');
       //處理參數(shù)
       app.use(bodyParser.urlencoded({ extended: false }));
       app.post('/books', (req, res) => {
           // 這里的req.body是引入了body-parse模塊處理的結(jié)果
           res.send(req.body.uname + '---' + req.body.pwd);
       })

       //  POST請求傳參
        fetch('http://localhost:3000/books', {
                method: 'post',
                body: JSON.stringify({
                    uname: '張三',
                    pwd: '456'
                }),
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            .then(function(data) {
                return data.text();
            }).then(function(data) {
                console.log(data)
            });
       // 服務(wù)端:接收請求和響應(yīng)
       // ...設(shè)置允許跨域訪問該服務(wù)
       // 處理參數(shù)
       const bodyParser = require('body-parse');
       app.use(bodyParser.json());
       app.post('/books', (req, res) => {
           // 這里的req.body是引入了body-parse模塊處理的結(jié)果
           res.send(req.body.uname + '---' + req.body.pwd);
       })
       
        //  PUT請求傳參     修改id是123的
        fetch('http://localhost:3000/books/123', {
                method: 'put',
                body: JSON.stringify({
                    uname: '張三',
                    pwd: '789'
                }),
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            .then(function(data) {
                return data.text();
            }).then(function(data) {
                console.log(data)
            });
       //...
       app.put('/books/:id', (req, res) => {
           res.send(req.params.id + '---' + req.body.uname + '---' + req.body.pwd);
       })
    </script>

fetchAPI 中 響應(yīng)格式

  • 用fetch來獲取數(shù)據(jù)里初,如果響應(yīng)正常返回啃勉,我們首先看到的是一個response對象,其中包括返回的一堆原始字節(jié)双妨,這些字節(jié)需要在收到后淮阐,需要我們通過調(diào)用方法將其轉(zhuǎn)換為相應(yīng)格式的數(shù)據(jù),比如JSON刁品,BLOB或者TEXT等等
    /*
      Fetch響應(yīng)結(jié)果的數(shù)據(jù)格式
    */
    fetch('http://localhost:3000/json').then(function(res){
      return res.json();   //  將獲取到的數(shù)據(jù)使用 json 轉(zhuǎn)換對象
      // return res.text(); //  將獲取到的數(shù)據(jù) 轉(zhuǎn)換成字符串 
    }).then(function(data){
      // console.log(data.uname)
      // console.log(typeof data)
      console.log(obj.uname,obj.age,obj.gender)
    })

3.5 axios

  • 基于promise泣特,用于瀏覽器和node.js的http庫
  • 支持瀏覽器和node.js
  • 支持promise
  • 能攔截請求和響應(yīng)
  • 自動轉(zhuǎn)換JSON數(shù)據(jù)
  • 能轉(zhuǎn)換請求和響應(yīng)數(shù)據(jù)

axios基礎(chǔ)用法

  • get和 delete請求傳遞參數(shù)
    • 通過傳統(tǒng)的url 以 ? 的形式傳遞參數(shù)
    • restful 形式傳遞參數(shù)
    • 通過params 形式傳遞參數(shù)
  • post 和 put 請求傳遞參數(shù)
    • 通過選項傳遞參數(shù)
    • 通過 URLSearchParams 傳遞參數(shù)
//  1. 發(fā)送get 請求 
axios.get('http://localhost:3000/adata').then(function(res){ 
    //   拿到 res 是一個對象      所有的對象都存在 res 的data 屬性里面
    // 注意:data屬性用于獲取后臺的實際數(shù)據(jù)
    // console.log(res.data)
    console.log(res)
})

//服務(wù)端
app.get('/adata', (req, res) => {
    res.send('ok');
})


//  2.  get 請求傳遞參數(shù)
//  2.1  通過傳統(tǒng)的url  以 ? 的形式傳遞參數(shù)
axios.get('http://localhost:3000/axios?id=123').then(res => {
    console.log(res)
})

//服務(wù)端
app.get('/adata', (req, res) => {
    res.send(req.query.id);
})

//  2.2  restful 形式傳遞參數(shù) 
axios.get('http://localhost:3000/axios/123').then(res => {
    console.log(res)
})

//服務(wù)端
app.get('/adata/:id', (req, res) => {
    res.send(req.params.id);
})

//  2.3  通過params  形式傳遞參數(shù) 
axios.get('http://localhost:3000/axios', {
    params: {
        id: 789
    }
}).then(res => {
    console.log(res)
})

//服務(wù)端(同2.1)
app.get('/adata', (req, res) => {
    res.send(req.query.id);
})

// 3 axios delete 請求傳參     傳參的形式和 get 請求一樣
axios.delete('http://localhost:3000/axios', {
    params: {
        id: 111
    }
}).then(function(ret){
    console.log(ret.data)
})
//服務(wù)端(同2.1)
app.delete('/adata', (req, res) => {
    res.send(req.query.id);
})

//  4  axios 的 post 請求
//  4.1  通過選項傳遞參數(shù)
axios.post('http://localhost:3000/axios', {
    uname: 'lisi',
    pwd: 123
}).then(function(ret){
    console.log(ret.data)
})
//服務(wù)端
app.post('/axios', (req, res) => {
  res.send(req.body.uname + '---' + req.body.pwd)
})

//  4.2  通過 URLSearchParams 傳遞參數(shù) 
var params = new URLSearchParams();
params.append('uname', 'zhangsan');
params.append('pwd', '111');
axios.post('http://localhost:3000/axios', params).then(function(ret){
    console.log(ret.data)
})

// 5  axios put 請求傳參   和 post 請求一樣 
axios.put('http://localhost:3000/axios/123', {
    uname: 'lisi',
    pwd: 123
}).then(function(ret){
    console.log(ret.data)
})
//服務(wù)端
app.put('/axios/:id', (req, res) => {
  res.send(req.params.id + '---' + req.body.uname + '---' + req.body.pwd)
})

axios響應(yīng)結(jié)果

  • data 實際響應(yīng)回來的數(shù)據(jù)

  • headers 響應(yīng)頭

  • status響應(yīng)狀態(tài)碼

axios 全局配置

//   配置公共的請求頭 
axios.defaults.baseURL = 'https://api.example.com';
//   配置 超時時間
axios.defaults.timeout = 2500;
//   配置公共的請求頭
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
//  配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

axios 攔截器

  • 請求攔截器
    • 請求攔截器的作用是在請求發(fā)送前進行一些操作
      • 例如在每個請求體里加上token,統(tǒng)一做了處理如果以后要改也非常容易
  • 響應(yīng)攔截器
    • 響應(yīng)攔截器的作用是在接收到響應(yīng)后進行一些操作
      • 例如在服務(wù)器返回登錄狀態(tài)失效挑随,需要重新登錄的時候状您,跳轉(zhuǎn)到登錄頁
//  1. 請求攔截器 
axios.interceptors.request.use(function(config) {
    console.log(config.url) //http://localhost:3000/adata
    //  1.1  任何請求都會經(jīng)過這一步   在發(fā)送請求之前做些什么   
    config.headers.mytoken = 'nihao';
    //  1.2  這里一定要return   否則配置不成功  
    return config;
}, function(err){
    // 1.3 對請求錯誤做點什么    
    console.log(err)
})

// 2. 響應(yīng)攔截器 
axios.interceptors.response.use(function(res) {
    // 2.1  在接收響應(yīng)做些什么  
    var data = res.data;
    return data;
}, function(err){
    // 2.2 對響應(yīng)錯誤做點什么  
    console.log(err)
})

axios.get('http://localhost:3000/adata').then(function(data){
      console.log(data)//服務(wù)端返回的“Hello axios!”
})

4. async 和 await

async/await是ES7引入的

  • async關(guān)鍵字放到函數(shù)前面
    • async函數(shù)都會隱式返回一個promise
  • await關(guān)鍵字只能在async函數(shù)中使用
    • ? await后面可以直接跟一個 Promise實例對象
    • ? await函數(shù)不能單獨使用
  • async/await 讓異步代碼看起來、表現(xiàn)起來更像同步代碼
    //  1.  async 基礎(chǔ)用法
    //  1.1 async作為一個關(guān)鍵字放到函數(shù)前面
    async function queryData() {
      //  1.2 await關(guān)鍵字只能在使用async定義的函數(shù)中使用      await后面可以直接跟一個 Promise實例對象
      var ret = await new Promise(function(resolve, reject){
        setTimeout(function(){
          resolve('nihao')
        },1000);
      })
      // console.log(ret.data)
      return ret;
    }
    //  1.3 任何一個async函數(shù)都會隱式返回一個promise   我們可以使用then 進行鏈式編程
    queryData().then(function(data){
      console.log(data)
    })

    // 2.  async    函數(shù)處理多個異步函數(shù)
    axios.defaults.baseURL = 'http://localhost:3000';

    async function queryData() {
      //  2.1  添加await之后 當(dāng)前的await 返回結(jié)果之后才會執(zhí)行后面的代碼   
      
      var info = await axios.get('async1');
      // 2.2  讓異步代碼看起來兜挨、表現(xiàn)起來更像同步代碼
      var ret = await axios.get('async2?info=' + info.data);
      return ret.data;
    }

    queryData().then(function(data){
      console.log(data)
    })

5. 圖書列表案例

1. 基于接口案例-獲取圖書列表

  • 導(dǎo)入axios 用來發(fā)送ajax
  • 把獲取到的數(shù)據(jù)渲染到頁面上
  <div id="app">
        <div class="grid">
            <table>
                <thead>
                    <tr>
                        <th>編號</th>
                        <th>名稱</th>
                        <th>時間</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- 5.  把books  中的數(shù)據(jù)渲染到頁面上   -->
                    <tr :key='item.id' v-for='item in books'>
                        <td>{{item.id}}</td>
                        <td>{{item.name}}</td>
                        <td>{{item.date }}</td>
                        <td>
                            <a href="">修改</a>
                            <span>|</span>
                            <a href="">刪除</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    1.  導(dǎo)入axios   
    <script type="text/javascript" src="js/axios.js"></script>
    <script type="text/javascript">
        /*
             圖書管理-添加圖書
         */
        //  2   配置公共的url地址  簡化后面的調(diào)用方式
        axios.defaults.baseURL = 'http://localhost:3000/';
        axios.interceptors.response.use(function(res) {
            return res.data;
        }, function(error) {
            console.log(error)
        });

        var vm = new Vue({
            el: '// app',
            data: {
                flag: false,
                submitFlag: false,
                id: '',
                name: '',
                books: []
            },
            methods: {
                //  3 定義一個方法 用來發(fā)送 ajax 
                //  3.1  使用 async  來 讓異步的代碼  以同步的形式書寫 
                queryData: async function() {
                    // 調(diào)用后臺接口獲取圖書列表數(shù)據(jù)
                    // var ret = await axios.get('books');
                    // this.books = ret.data;
                    //  3.2  發(fā)送ajax請求  把拿到的數(shù)據(jù)放在books 里面   
                    this.books = await axios.get('books');
                }
            },

            mounted: function() {
                //   4 mounted  里面 DOM已經(jīng)加載完畢  在這里調(diào)用函數(shù)  
                this.queryData();
            }
        });
    </script>

2 添加圖書

  • 獲取用戶輸入的數(shù)據(jù) 發(fā)送到后臺
  • 渲染最新的數(shù)據(jù)到頁面上
 methods: {
    handle: async function(){
          if(this.flag) {
            // 編輯圖書
            // 就是根據(jù)當(dāng)前的ID去更新數(shù)組中對應(yīng)的數(shù)據(jù)
            this.books.some((item) => {
              if(item.id == this.id) {
                item.name = this.name;
                // 完成更新操作之后膏孟,需要終止循環(huán)
                return true;
              }
            });
            this.flag = false;
          }else{
            //  1.1  在前面封裝好的 handle 方法中  發(fā)送ajax請求  
            //  1.2  使用async  和 await 簡化操作 需要在 function 前面添加 async   
            var ret = await axios.post('books', {
              name: this.name
            })
            //  1.3  根據(jù)后臺返回的狀態(tài)碼判斷是否加載數(shù)據(jù) 
            if(ret.status == 200) {
             //  1.4  調(diào)用 queryData 這個方法  渲染最新的數(shù)據(jù) 
              this.queryData();
            }
          }
          // 清空表單
          this.id = '';
          this.name = '';
        },        
 }         

3 驗證圖書名稱是否存在

  • 添加圖書之前發(fā)送請求驗證圖示是否已經(jīng)存在
  • 如果不存在 往后臺里面添加圖書名稱
    • 圖書存在與否只需要修改submitFlag的值即可
 watch: {
        name: async function(val) {
          // 驗證圖書名稱是否已經(jīng)存在
          // var flag = this.books.some(function(item){
          //   return item.name == val;
          // });
          var ret = await axios.get('/books/book/' + this.name);
          if(ret.status == 1) {
            // 圖書名稱存在
            this.submitFlag = true;
          }else{
            // 圖書名稱不存在
            this.submitFlag = false;
          }
        }
},

4. 編輯圖書

  • 根據(jù)當(dāng)前書的id 查詢需要編輯的書籍
  • 需要根據(jù)狀態(tài)位判斷是添加還是編輯
 methods: {
        handle: async function(){
          if(this.flag) {
            // 4.3 編輯圖書   把用戶輸入的信息提交到后臺
            var ret = await axios.put('books/' + this.id, {
              name: this.name
            });
            if(ret.status == 200){
              // 4.4  完成添加后 重新加載列表數(shù)據(jù)
              this.queryData();
            }
            this.flag = false;
          }else{
            // 添加圖書
            var ret = await axios.post('books', {
              name: this.name
            })
            if(ret.status == 200) {
              // 重新加載列表數(shù)據(jù)
              this.queryData();
            }
          }
          // 清空表單
          this.id = '';
          this.name = '';
        },
        toEdit: async function(id){
          // 4.1  flag狀態(tài)位用于區(qū)分編輯和添加操作
          this.flag = true;
          // 4.2  根據(jù)id查詢出對應(yīng)的圖書信息  頁面中可以加載出來最新的信息
          //  調(diào)用接口發(fā)送ajax 請求  
          var ret = await axios.get('books/' + id);
          this.id = ret.id;
          this.name = ret.name;
        },

5 刪除圖書

  • 把需要刪除的id書籍 通過參數(shù)的形式傳遞到后臺
   deleteBook: async function(id){
          // 刪除圖書
          var ret = await axios.delete('books/' + id);
          if(ret.status == 200) {
            // 重新加載列表數(shù)據(jù)
            this.queryData();
          }
   }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拌汇,隨后出現(xiàn)的幾起案子柒桑,更是在濱河造成了極大的恐慌,老刑警劉巖噪舀,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魁淳,死亡現(xiàn)場離奇詭異,居然都是意外死亡与倡,警方通過查閱死者的電腦和手機界逛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒸走,“玉大人仇奶,你說我怎么就攤上這事。” “怎么了该溯?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵岛抄,是天一觀的道長。 經(jīng)常有香客問我狈茉,道長夫椭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任氯庆,我火速辦了婚禮蹭秋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堤撵。我一直安慰自己仁讨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布实昨。 她就那樣靜靜地躺著洞豁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荒给。 梳的紋絲不亂的頭發(fā)上丈挟,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機與錄音志电,去河邊找鬼曙咽。 笑死,一個胖子當(dāng)著我的面吹牛挑辆,可吹牛的內(nèi)容都是我干的例朱。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼之拨,長吁一口氣:“原來是場噩夢啊……” “哼茉继!你這毒婦竟也來了咧叭?” 一聲冷哼從身側(cè)響起蚀乔,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菲茬,沒想到半個月后吉挣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡婉弹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年睬魂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镀赌。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡氯哮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出商佛,到底是詐尸還是另有隱情喉钢,我是刑警寧澤姆打,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站肠虽,受9級特大地震影響幔戏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜税课,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一闲延、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧韩玩,春花似錦垒玲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叮雳,卻和暖如春想暗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帘不。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工说莫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寞焙。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓储狭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捣郊。 傳聞我的和親對象是個殘疾皇子辽狈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351