[TOC]

昨日回顾

  1. 组件-一个单vue文件就是一个组件-是一个独立的个体-数据独立-data返回的是一个新对象

    html + css + js- template-html script- js style-css-(scoped)- 加scoped会给所有的标签加上一个唯一的hash值- 样式只对当前组件生效

  2. 既然组件相互独立,组件的通讯。父组件给子组件传值 子组件给父祖件传值 非父子关系传值

  3. 父 => 子传值-确定父子关系- 被引入的组件-子组件
  4. 父 => 子 props

    <template>
    <div>
     给谁传就在谁的标签上写属性
     <child :name="name" />
    </div>
    </template>
    <script>
    import Child from  './Child',
    export default {
    name: 'parent',
    data() {
      return {
         name: '章三'
      }
    },
    props: ["username"],
    computed: {
      firstName() {
        return this.name.substr(0, 3)
      }
    },
    // 局部注册-只能在当前注册的组件使用
    components: {
      Child
    }
    
    }
    </script>
    

    子组件

    <template>
    <div>{{ name }}</div>
    </template>
    <script>
    export default {
    name: 'child',
    props: {
      name: {
         // 这里是对name的要求
        type: String,
        required: true, // 必须传这个属性
        default: "李四" // 这个值会在没有传name属性时生效
    
      }
    }
    // props: ["name"] // 数组-对象
    }
    </script>
    

    要有父子关系,要有子组件的标签,给谁传就在谁的标签上写属性, 子组件要接收属性

  5. 子组件 - 父组件传值- 三种用法

官方-自定义事件

  1. 子组件触发事件

    <template>
    <div>{{ name }}</div>
    <button @click="updateName">改变父组件的名字</button>
    </template>
    <script>
    export default {
    name: 'child',
    props: {
      name: {
         // 这里是对name的要求
        type: String,
        required: true, // 必须传这个属性
        default: "李四" // 这个值会在没有传name属性时生效
    
      }
    },
    methods: {
     updateName () {
      // 触发自定义事件
       this.$emit("updateName", "王武")
       //this.name = "" // 这种方法是错误的 单向数据流
    
     }
    
    }
    // props: ["name"] // 数组-对象
    }
    </script>
    

    父组件监听事件

    <template>
    <div>
     给谁传就在谁的标签上写属性
     <child :name="name" @updateName="updateName" />
    </div>
    </template>
    <script>
    import Child from. './Child',
    export default {
    name: 'parent',
    data() {
      return {
         name: '章三'
      }
    },
    props: ["username"],
    computed: {
      firstName() {
        return this.name.substr(0, 3)
      }
    },
    // 局部注册-只能在当前注册的组件使用
    components: {
      Child
    },
    methods: {
      updateName (name) {
        this.name = name // 修改值
      }
    
    }
    
    }
    </script>
    

    子组件要触发事件, 父组件要监听事件

$emit(“自定义事件名称”, 参数)
v-on:自定义事件名=”方法”

  1. 非关系型组件传值

    eventBus- 事件总线- 将触发事件和监听事件都放在了一个实例上

  • 新建事件总线的一个实例 ```javascript import Vue from ‘vue’

export default new Vue() // 事件总线


- 触发事件
```javascript
import eventBus from ''

methods: {
  test() {
     eventBus.$emit("abc", 123)

  }

}
  • 监听事件-要越早越好-组件一创建就监听 ```javascript import eventBus from ‘’

export default { created() { eventBus.$on(“abc”, (value) => { // value就是123 }) }

}

TodoMVC的案例

```vue
<template>
  <div :name="name" @updateName=""></div>
</template>
<script>
  export default {
    name: 'App',
    components: {}, // 注册组件
    data() {
      return {}
    },
    filters: {
      // 过滤器
    },
    computed: {
      // 计算属性

    },
    watch: {
     // 侦听器
    },
    methods: {
      // 存储方法的

    },
    created() {
     // 会在组件创建时触发
    }

  }

</script>

真正的应用 都是综合的结合

1. 组件的生命周期

vue的组件生命周期-从创建到销毁的整个过程

  • v-if 会创建并删除元素

    一个组件的标签就是一个组件的实例

  • v-if会造成组件的创建或者销毁

    2.组件的生命周期的钩子函数

  • 初始化阶段- beforeCreate - 创建实例过程 - created

  • 挂载阶段- beforeMount - 挂载过程 - mounted (挂载完成-已经有dom了)
  • 更新阶段 - beforeUpdate - 更新过程 - updated (数据驱动视图)
  • 销毁阶段 - beforeDestroy - 销毁过程 - destroyed(一般都在beforeDestroy)

3. 初始化阶段的过程

  • 初始化阶段- beforeCreate - 创建实例过程 - created

    鸡肋-beforeCreate- data和methods不能用-几乎不用

  • created才可以用data和methods方法

    created-CRUD- 增删改查- 查询-created-data和methods已经使用了

  • 一般的查询网络请求放在created

4. 挂载阶段

  • 挂载阶段- 模版编译-虚拟dom-真实dom- 挂载到页面
  • 挂载阶段- beforeMount - 挂载过程 - mounted (挂载完成-已经有dom了)

    beforeMount 鸡肋事件,几乎不用

mounted可以获取真实dom -echarts

echarts.init(dom) // 一般放在mounted中进行

5. 更新阶段

数据变化-就会触发钩子函数

export default {
  data () {
    return {
      a:1,
      b:2,
      c:3,
      d:4
      ...
    }
  },
  beforeUpdate() {},
  updated() {}


}

watch-侦听单个的变量

任意变量的触发-都会执行钩子函数

  • 鸡肋函数

6.销毁阶段

beforeDestroy- 销毁所有的监视器和事件 destroyed

beforeDestroy- 销毁被全局引用的变量- 定时器

<template>
  <div>
    <h1>倒计时{{ count }}</h1>
  </div>
</template>

<script>
export default {
  name: "TimerCount",
  data() {
    return {
      count: 60,
    };
  },
  beforeCreate() {
    console.log("beforeCreate");
  },
  created() {
    console.log("created");
    // 记录标记
    this.flag = setInterval(() => {
      this.count--;
    }, 1000);
  },
  beforeMount() {
    console.log("beforeMount");
  },
  mounted() {
    console.log("mounted");
  },
  beforeUpdate() {
    console.log("beforeUpdate");
  },
  updated() {
    console.log("updated");
  },
  beforeDestroy() {
    clearInterval(this.flag); // 清理定时器
    console.log("beforeDestroy");
  },
  destroyed() {
    console.log("destroyed");
  },
};
</script>

<style>
</style>

created-获取数据
mounted- 获取dom
beforeDestroy- 销毁定时器相关的引用

7. axios的介绍

  • 请求的库- (浏览器)客户端使用- nodejs也可以使用
  • 支持Promise
  • 主流的请求工具库-90%的项目react-vue都使用它

http://www.axios-js.com/ axios的官网地址

  1. 安装axios

    $ npm i axios
    
  2. 用法 ```javascript axios({ method: ‘get/post/put/delete/patch’, // 类型 url: ‘’, // 请求的接口地址 data: {}, // 请求体参数 body参数 post/put/delete/patch params: {} // 参数- 地址参数-url参数- 拼接到url地址上 }).then(result => { // result就是返回结果

}).catch(error => { // 报错的信息

})


- get - 获取数据-没有请求体参数
- post- 提交数据
- put- 更新数据- 完整更新- { id: 1, name: '章三' }- 数据会完全覆盖
- patch- 增量更新- 部分更新- { id: 1, name: '章三' }-只更新name属性
- delete-删除
> 简写  

axios.get("地址")   // get请求<br />axios.post("地址") // post请求<br />axios.delete("地址") // delete请求<br />axios.patch("地址") // patch请求<br />axios.put("地址") // put请求

解析- axios().then().catch()
> axios返回的是一个Promise对象

- 三种状态-pending(等待) 成功  失败
```javascript
new Promise(function (resolve, reject) {
  resolve() // 成功
 // reject() // 失败
}).then(result => {}).catch(error=> {
})
  • axios的实现机制 ```javascript function axios () { return new Promise(function (resolve, reject) { XMLHttpRequest xml = new XMLHttpRequest() xml.open(“get”, “”) xml.send() xml.onload = function () { resolve(JSON.parse(xml.response))
    } xml.onerror = function () { reject() }

    })

}

axios().then(result => {

})


<a name="ulKU9"></a>
## 8. axios获取数据
1.安装axios
```bash
$ npm i axios
  1. 在你需要的组件中引入axios
    import axios from 'axios'
    

    axios的返回结果数据中 默认包了一层data数据

<template>
  <div id="app">
    <ul>
      <li v-for="item in channels" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  components: {},
  data() {
    return {
      channels: [], // 频道列表
    };
  },
  created() {
    // this
    this.getChannels();
  },
  methods: {
    getChannels() {
      axios({
        // method: "get",
        url: "http://toutiao.itheima.net/v1_0/channels",
      }).then(({ data }) => {
        // console.log(data.data.channels);
        this.channels = data.data.channels;
      });
    },
  },
};
</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;
}
ul li {
  list-style: none;
  width: 80px;
  text-align: center;
}
ul {
  display: flex;
  flex-direction: row;
}
</style>

9. axios发送post请求

image.png

  1. 绘制页面结构 ```vue

``` 2. 双向绑定input输入值-拿到验证码和手机号 ```vue ``` 3. 校验手机号的格式和验证码的格式 > 手机号 1 3-9 9位数字 > 验证码 6位数字 ```javascript login() { const mobileReg = /^1[3-9]\d{9}$/; const codeReg = /^\d{6}$/; if (mobileReg.test(this.mobile) && codeReg.test(this.code)) { // 此时说明两个都通过了校验 } }, ``` 4. 调用接口- 登录的接口 ```javascript login() { const mobileReg = /^1[3-9]\d{9}$/; const codeReg = /^\d{6}$/; if (mobileReg.test(this.mobile) && codeReg.test(this.code)) { // 此时说明两个都通过了校验 // http://toutiao.itheima.net/v1_0/authorizations axios .post("http://toutiao.itheima.net/v1_0/authorizations", { mobile: this.mobile, code: this.code, }) .then(() => { alert("用户登录成功"); }) .catch(() => { alert("用户的手机号或者密码错误"); }); // axios({ // url: '', // data: {}, // method: 'post' // }) } else { alert("手机号或者验证码格式不正确"); } }, ``` ## 10.发送验证码倒计时功能 > 倒计时时间是60秒 - 由于默认是没有发送验证码状态,也就意味着 倒计时状态 是 0 > 当点击的时候-变成60,开启倒计时,每次减一秒钟,直到减到0,再次显示发送验证码 ```vue ``` ## 11.配置axios的基地址 > axios的路径要完整,不易维护,只需要配置一个地方,所有请求都会生效 ```javascript axios.defaults.baseURL = "https://toutiao.itheima.net" // 直接对原始数据进行操作 ``` > axios可以创建实例 ```javascript const instance = axios.create({ baseURL: 'https://toutiao.itheima.net' }) // 相当于new了一下 instance.get() instance.post() instance.put() 〉这样的好处 不会影响原始数据 ``` ## 12.ref获取dom元素 > 一般情况下- 不需要获取dom的,数据就是视图 > echarts-init(Dom) - ref 1. 给想要获取dom的标签一个ref属性- h5标签上 ```html // 获取的是dom对象
// 获取的是组件的实例对象 ``` 2. 必须在mounted及以后 ```javascript this.$refs.abc # 就是真实的dom对象 this.$refs.efg # 指的是login-com的实例对象-也就是该组件的this ``` this.$parent- 子组件可以拿到父组件的this
this.$refs.属性 - 父组件可以拿到子组件的this ## 13.使用ref实现父传子 1. 确定父子关系 1. 给子组件的标签一个ref属性 1. this.$refs.属性获取子组件的实例(this) 1. 通过实例就可以调用方法 传值 ```vue

<a name="wclnF"></a>
## 14. $nextTick方法
> 会等到数据渲染完成,dom更新之后执行

this.$nextTick(函数体)<br />Vue.nextTick(函数体)

```vue
export default {

  methods: {
    update() {
       this.count++ // 更新数据
      this.$nextTick(() => {
         // 渲染更新完成之后,此时可以获取最新的数据
      })

   }

  }

}

场景: 一般用在需要获取更新的dom的时候,又或者 props传值

props更新要重新渲染,也是一个异步过程

父组件 =》 子组件的方法
ref- 获取子组件的实例-this

  • 子组件的属性有props属性-不能保证已经更新完了
  • nextTick 执行的时候 说明渲染完成了 数据已经更新

    当你遇到你获取的数据的是上一次的时候,说明你遇到了异步,就可以使用nextTick

15.json-server的使用

json 开启一个服务器

  • json-server它是一个npm包,快速生成8个左右的接口,包含增删改查
  1. 安装json-server

    $ npm i -g json-server
    
  2. 新建一个json文件

    {
    "goodsList": [
     {
       "id": 1,
       "goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
       "goods_img": "https://www.escook.cn/vuebase/pics/1.png",
       "goods_price": 108,
       "goods_count": 1,
       "goods_state": true
     },
     {
       "id": 2,
       "goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
       "goods_img": "https://www.escook.cn/vuebase/pics/2.png",
       "goods_price": 129,
       "goods_count": 1,
       "goods_state": true
     },
     {
       "id": 3,
       "goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
       "goods_img": "https://www.escook.cn/vuebase/pics/3.png",
       "goods_price": 198,
       "goods_count": 1,
       "goods_state": false
     },
     {
       "id": 4,
       "goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
       "goods_img": "https://www.escook.cn/vuebase/pics/4.png",
       "goods_price": 99,
       "goods_count": 1,
       "goods_state": false
     },
     {
       "id": 5,
       "goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
       "goods_img": "https://www.escook.cn/vuebase/pics/5.png",
       "goods_price": 156,
       "goods_count": 1,
       "goods_state": true
     },
     {
       "id": 6,
       "goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
       "goods_img": "https://www.escook.cn/vuebase/pics/6.png",
       "goods_price": 142.8,
       "goods_count": 1,
       "goods_state": true
     },
     {
       "id": 7,
       "goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
       "goods_img": "https://www.escook.cn/vuebase/pics/7.png",
       "goods_price": 219,
       "goods_count": 2,
       "goods_state": true
     },
     {
       "id": 8,
       "goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
       "goods_img": "https://www.escook.cn/vuebase/pics/8.png",
       "goods_price": 178,
       "goods_count": 1,
       "goods_state": true
     },
     {
       "id": 9,
       "goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
       "goods_img": "https://www.escook.cn/vuebase/pics/9.png",
       "goods_price": 128,
       "goods_count": 1,
       "goods_state": false
     },
     {
       "id": 10,
       "goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
       "goods_img": "https://www.escook.cn/vuebase/pics/10.png",
       "goods_price": 153,
       "goods_count": 1,
       "goods_state": false
     }
    ]
    }
    
  3. 启动服务

    $ json-server ./db.json --watch
    

    相当于后台服务,已经有接口了

image.png

16. 初始化项目-拆分组件

image.png

  1. 创建vue项目

    $ vue create cart
    
  2. 安装bootstrap

    $ npm i bootstrap
    
  3. 在main.js中引入bootstrap的样式

    // 导入 bootstrap 样式文件:
    import 'bootstrap/dist/css/bootstrap.css';
    
  4. 启动服务

  5. 头部组件

CartHeader.vue

<template>
  <div class="cart-header">购物车</div>
</template>

<script>
export default {};
</script>

<style>
.cart-header {
  z-index: 999;
  height: 45px;
  line-height: 45px;
  text-align: center;
  background-color: #1d7bff;
  color: #fff;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
}
</style>
  1. CartFooter.vue ```vue
``` 7. GoodItem.vue ```vue ``` 8. App.vue ```vue

<a name="KuBES"></a>
## 17.获取数据显示到购物车上

- json-server
- axios
1. 安装axios
```bash
$ npm i axios
  1. 配置axios的基础地址 main.js ```javascript axios.defaults.baseURL = “http://localhost:3000

3. App.vue 获取商品数据
```vue
<template>
  <div id="app">
    <cart-header></cart-header>
    <goods-item v-for="item in list" :key="item.id" :item="item"></goods-item>
    <cart-footer></cart-footer>
  </div>
</template>

<script>
import CartHeader from "./components/CartHeader.vue";
import CartFooter from "./components/CartFooter.vue";
import GoodsItem from "./components/GoodItem.vue";
import axios from "axios";
export default {
  name: "App",
  components: {
    CartHeader,
    CartFooter,
    GoodsItem,
  },
  data() {
    return {
      list: [],
    };
  },
  created() {
    this.getGoodsList();
  },
  methods: {
    getGoodsList() {
      axios.get("/goodsList").then(({ data }) => {
        this.list = data;
      });
    },
  },
};
</script>

<style>
#app {
  padding-top: 50px;
  padding-bottom: 50px;
}
</style>
  1. GoodsItem中接收数据,并渲染 ```vue

```