day5
1.组件的生命周期 创建到销毁的整个过程
v-if创建并且删除元素
一个组件 标签就是一个组件的实例
v-if会造成组件的创建和销毁
2.钩子函数
初始 beforeCreate 创建实例过程 created
挂载 beforeMount 挂载过程 mounted 挂载完成 已经有dom了
更新节点 beforeUpdate 更新过程 updated 数据驱动视图
销毁阶段 beforeDestory 销毁过程 destoryed 一般在beforeDestroy中销毁 因为beforeDestory方法中是已经确定了要销毁的数据了
3.初始化阶段
created才可以用data和methods方法
beforeCreated是鸡肋
CRUD 查询 created-data和methods已经使用了
查询网络请求在created中
4.挂载阶段 模板编译 虚拟dom到真实dom 挂载到页面
挂载 beforeMount 挂载过程 mounted 挂载完成 已经有dom了
beforeMount 是鸡肋 几乎不用
mounted 获取真实dom --echarts
echarts.init(dom)//一般在mouted中进行 获取真实dom
5.更新阶段 数据变化 触发钩子函数
export default(){
data(){
return{
a:1,
b:2
},
beforeUpdate(){
},
update(){
}
}}
watch侦听单个的变量,二update是所有变量的触发 都会执行
还是watch更精细
created 和mounted 虚拟dom到真实dom的和销毁之前的阶段是常用的
6.销毁阶段
beforeDestory 销毁所有的监视器 和事件 destroyed
销毁被全局引用的变量 --定时器
created获取数据 mounted获取dom beforeDestroy--销毁定时器相关的引用
7.axios的介绍
请求的库 --客户端使用 nodejs也可以使用
支持promise
主流的请求工具库
npm i axios
用法:
axios({
method:"get/post/put/delete/patch",
url:"",//请求的接口地址
data:{}, //请求体参数 body参数 post/put/delete/patch
parms:{}//地址参数 url参数 拼接到url地址上
//箭头函数 一个参数不用写括号
}).then(result=>{
//result是返回结果
}).catch(error=>{
//error 报错结果
})
get获取数据 没有请求体参数
post提交数据
put 更新数据 完整更新{id:1,name:"aijing"} 数据完全覆盖
patch 增量更新 部分更新 只更新name属性
delete 删除
axios.get('地址').then({retsult获取结果})
解析:axios.then().catch()
axios返回一个Promise对象
三种状态: pending(等待) 成功 失败
new Promist(function(resolve,reject){
resolve()//成功
reject()//失败
}).then(result=>{}).catch(error=>{
})
axios的实现机制 源代码
function axios(){
其结果返回一个 Promise 成功和失败
return new Promise (function (resolve,reject){
//resolve()
XMLHttpRequest xml=new XMLHttpRequest()
xml.open('get'," ")
xml.send()
xml.οnlοad=function(){
resolve(JSON.parse(xml.response))
}
xml.οnerrοr=function(){
reject()
}
})
}
axios().then(result=>{
})
npm i axios
import axios from 'axios'
channels:[],//频道列表
created(){
创建的时候调用方法
this.getChannleds()
},
methods:{
getChannleds(){
axios({
url:"http://toutiao.itheima.net/v1_0/channels"
}).then(({data})=>{ //解构赋值 解构数据
log(data.data.channels)
this.channels=data.data.channels
}
}
}
<li v-for="(item,index) in channels" :key="index">
{{ item }}
</li>
2.axios 发起post请求
绘制页面结构
login.vue
在login里面
<template>
<div>
<!-- 登录页面结构 -->
<h1>短信登录</h1>
<div class="form_item">
<input
class="input_item"
type="text"
v-model.trim="mobile" //双向绑定输入的手机号
placeholder="请输入手机号"
/>
</div>
<div class="form_item">
<input
class="input_item code"
type="text"
placeholder="请输入短信验证码"
v-model.trim="code" //双向绑定 输入的验证码 输入了就保存到data的code数据中
/>
<span class="send_code">发送验证码</span>
</div>
<div class="form_item">
<button class="btn_login" @click="login">登录</button> //点击登录按钮 触发事件
</div>
</div>
</template>
<script>
import axios from 'axios' //导入axios
export default {
name: "LoginCom",
data() {
return {
mobile: "19906398784",
code: "246810",
};
},
methods: {
// 校验手机号的格式和验证码的格式
// 手机号
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/app/v1_0/authorizetions
axios.post('http://toutiao.itheima.net/v1_0/authorizations',{
mobile:this.mobile,
code:this.code,
}).then(()=>{
alert('登录成功')
}).catch(()=>{
alert('用户的手机号或密码错误')
});
// axios({
// url:"",
// data:{},
// method:{}
// })
//默认的验证码 246810
}else{
alert("用户的手机号或密码错 ")
}
},
},
};
</script>
发送验证码倒计时功能
默认 没有发送验证码状态 初始为0
timerCount:0//默认为0
在发送验证码 大于0就是显示倒计时 =0和<0就是重置 可以再次请求验证码
{{timerCount >0 ? `${timerCount}秒` : '发送验证码'}}
给发送验证码的按钮添加点击事件
@click='sendCode'
sendCode(){
const mobileReg=/^1[3-9]\d{9}$/
const codeReg=/^\d{6}$/
//判断当前的时间>0 不能发送 请求地址
// https://toutiao.itheima.net/v1_0/sms/codes/19906398784
if(this.timerCount ===0 &&mobileReg.test(this.mobile){
只有在=0和 输入的手机号 符合手机号的规则 的情况下 才能发送验证码
axios.get(`https://toutiao.itheima.net/v1_0/sms/code/${this.mobile}`)//发起请求校验输入的手机号 发送验证码
this.timerCount=10//开启倒计时
this.timer=setInterval(()=>{
if(this.timerCount===0){
clearInterval(this.timer)
}else{
this.timer--
}
},1000)
}
}
11.配置axios的基地址
axios的路径要完整 不易维护 只需要配置一个地方 所有的请求都会生效
axios.defaults.baseURL='https://toutiao.itheima.net'
axios可以创建实例 //相当于new创建实例
const instance=axios.create({
baseURL='https://toutiao.itheima.net'
})
instance.get()
instance.post()
这样的好处 不会影响原始数据
在导入的地方配置基地址
axios.defaults.baseURL='https://toutiao.itheima.net'
12.ref获取dom元素
因为双向绑定 数据绑定视图 数据驱动视图
给想要获取dom 的标签上一个ref属性
div ref='abc'
<login-com ref='efg'//组件的标签 获取组件的实例对象
必须在mounted和以后的生命周期函数
this.$refs.abc 这个就是真实的dom对象
this.$refs.属性 父组件拿到子组件的this
this.$parent 子组件拿到父组件 的this
13 ref实现父传子
确定父子关系
给子组件的标签添加一个ref属性
this.$refs.属性获取子组件的实例this
通过实例就可以调用方法 来传值
14.$nextTick方法
等到数据渲染完成 dom更新之后执行
this.$nextTick(函数体)
Vue.nextTIck(函数体) 两个一样
export default{
methods:{
update(){
this.count++//更新数据
this.$nextTick(()=>{
渲染更新完成之后 此时获取最新的数据
}
}
}
}
nextTick应用场景: 在需要获取更新的dom时候,或者props传值
因为props更新要重新渲染 也是一个异步过程
父组件调用子组件的方法
ref-获取子组件的实例 this 通过this就可以获取子组件的方法 和属性
子组件的属性有Props属性 不能保证已经更新完成
nextTick执行的时候 说明渲染完成 数据也更新完成了
当你遇到获取的数据是上一次的时候 说明你遇到了异步 可以使用 nextTick
15.json-server的使用
json 开启服务器
是一个npm包 快速生成8个接口 包括增删改查
npm i -g json-server
新建json文件
{
这里的数据写在这里
}
启动服务
json-server ./da.json --watch
相当于有了后台服务 有接口了
17.获取数据显示到购物车上面
json-server
axios
安装axios
npm i axios
在main.js入口文件 引入
imoort axios from 'axios'
配置基地址 我这里是json-server启动的服务端口 里面是接口数据
axios.deafults.baseURL='http://localhost:3000'
在app.vue
导入axios
list:[]
created(){
this.getGoodsList()
}
getGoodsList(){
axios.get('/goodsList').then(({data}){
this.list=data
})
}
得到数据 在goods-item组件中渲染数据 父传子
<goods-item v-for='item in list' :key='item.id' :item='item'>
在goods-item组件 接受数据
props:['item']
得到数据 渲染数据
{{item.goods_name}}
{{item.goods_price}}
{{item.goods_count}}
img :src='goods.img'
1.购物车选中问题
1.checkbox想要点击图片也选中 通过label的属性for='id' id不能写死 因为商品列表是循环出来的 应该生成动态的id和动态的for
只有label标签的for绑定多选框 二者要一致
input type='checkbox' :id=`input_${item.id}`>
label :for='`input_${item.id}`'
2.绑定单选框影响的购物车数据
Props数据单向数据流 修改的数据 回到父组件 改变父组件的属性
这个id是为了绑定label checked=接收其状态 因为是单向 不能监听其值 只是负责展示 绑定改变事件
input type='checkbox' :id=`input_${item.id}` :checked='item.goods_state'
@change='changeChecked'>
changeChecked(event){
event.target.checked//得到当前的状态
这里是我是子组件 通过props获取到item项 通过this指向获取props的id 传递当前的这个input
this.$emit('updateChecked',this.item.id,event.target.checked) //id是当前任务项 第二个参数是当前任务项的状态 是true还是false
}
在父组件
goods-item v-for='item in list' :key='item.id' :item='item' @updateChecked='updateChecked'
updateChecked(id,godds_state){
//修改数组的状态
push pop shift unshift splice sort reverse
重新赋值一个新数组 新数组和原数组的长度一样
this.list=this.list.map((item)=>{
数组有几条 遍历多少次
if(item.id===id)//找到了在子组件点击的那个input的id项
将子组件传递的状态给新数组 再渲染到页面上
item.goods_state=goods_state;
return item;
})
}
在子组件中 v-model绑定props数据不允许 是单向数据流
将数据显示到组件上 绑定了input的checked数据
监听input的change事件
通过子传父 this.$emit(事件名称,参数)将改变的值和id传递给父组件
在父组件中监听这个传递的自定义事件 再改变自己的list数组
重新生成一个新数组 找到对应的项的id 从而改变其状态
3.全选状态的设定
全选组件在footer组件中 状态的展示依赖list数组中的状态state是否全部都勾选了
通过计算属性来监听其list的state值的状态
computed(){
isAll(){
计算是不是所有的state都勾选了
有这个一个方法every() 判断是不是所有的item项都等于true
[true,true,false].every(item =>item===true) //false
// every方法会返回 true和false 这里的item.goods_state 是item.goods_state===true的简写
return this.list.every(item =>item.goods_state)
}
}
将计算属性得到的值传递给子组件 cart-footer 值会返回ture还是false
cart-footer :isAll='isAll'
在子组件接受props:['isAll']
给input全选多选框动态绑定 :checked='isAll' 这个会返回true和false true就是勾选 false反之
绑定全选多选框的状态和 传递一个事件 实现反选的功能
input :checked='isAll' @change='updateAll'
updateAll(event){
这里没有id 这是全选按钮只有一个 true就是true false就是false
this.$emit("updateAll",event.target.checked)
}
在父组件监听
cart-footer :isAll='isAll' @updateAll='updateAll'
要将所有的状态都跟新传过来的状态 实现全选按钮的反选
updateAll(goods_state){
重新赋值一个新数组 新数组和原数组的长度一样
this.list=this.list.map((item)=>{
数组有几条 遍历多少次
这里是所有的item的state状态 不是true 就是false 绑定的所有的state
item.goods_state=goods_state
return item
})
}
全选状态的设定 小结
全选组件在footer组件中 将状态的展示依赖list中的item项是否全部都勾选了 其子组件的状态是父组件传递过来的 因为是单向数据流 不能在子组件去修改父组件的值
父组件中定义计算属性 判断所有的item项map一下 看有没有全部都选中
将计算属性的结果 传递给footer组件 赋值给checked属性 如果list的state状态都勾选就是true 那么传递过去的就是true 拿全选按钮就是true勾选
监听input的值去改变事件
触发自定义事件 将true/false传递给父组件
父组件监听事件
根据true/false来影响list全部的state状态
5.算出总数量 和总价格 显示在footer组件上
找到所有勾选的item项 && 价格*数量的总和就是=总价格
总数量=选中的商品的数量总和
在父组件的计算属性 计算总价格和总数量
computed(){
//总价格
totalPrice(){
这里的item是对象 返回reduce方法的结果
return this.list.reduce((preValue,item)=>{
这里要是勾选的item.goods_state为true就算到总数量中
我这里的return是回调函数的return
//return preValue+item.goods_count;
对返回的结果修改一下 状态为true就计算 false就+0 再去乘以价格
return preValue +(item.goods_state? item.goods_count:0) *item.goods_price
},0)
要把三元表达式包裹起来 再跟之前的preValue加
这里的总价格 就是把总数量的复制过来 *总价格
因为 reduce方法是前面两个数加的和再跟后一位值再加 数量再*价格就是总价格的数量
}
},
//总数量
totalCount(){
reduce方法 第一个参数是回调函数(preValue,currentItem){
},0)
[1,2,3].reduce(function(preValue,currentItem){
return preValue+currentItem //1+2的结果+3 =6
},0)
这里的item是对象 返回reduce方法的结果
return this.list.reduce((preValue,item)=>{
这里要是勾选的item.goods_state为true就算到总数量中
我这里的return是回调函数的return
return preValue+item.goods_count;
对返回的结果修改一下 状态为true就计算 false就+0
return preValue +(item.goods_state?item.goods_count:0)
},0)
要把三元表达式包裹起来 再跟之前的preValue加
}
}
将总数量的计算属性结果返回给子组件 显示到页面上
cart-footer :totalCount='totalCount' :totalPrice='totalPrice' :isAll='isAll' @updateAll='updateAll'
在footer子组件得到值总数量的结果 totalCount是总数量 totalPrice是总价格
props:['isAll','totalCount','totalPrice']
得到数据 渲染页面
{{totalCount}}
{{totalPrice}}
购物车案例 到此结束 感谢观看
5.动态组件
一个位置可以切换不同类型的组件
用到vue的内置组件 component
<component :is='变量'
变量是组件的名字 --注册组件 的名字
1.如果要展示多少个组件 就要创建多少个vue组件
在views目录下新建组件
在app引入组件 注册组件 写组件标签
在components目录新建8个组件
在views目录下的changeFace组件导入8个组件
changeFace组件写:
动态组件:
data数据 faceName=''
<component :is='faceName'></component>
button @click='changeFace'
方法
changeFace(){
获取随机数 随机8 +1是得到组件8
const value=~~(Math.random()*8)+1
this.faceName=`CommonFace${value}`
}
<component is='组件名称'></component>是内置组件 需要is绑定组件名称
6.组件缓存:
目的是提示性能 创建一次 就不会销毁了
缓存我们的组件实例 保留虚拟dom 销毁真实dom
vue提供一个内置组件 <keep-alive>包裹组件 就不会销毁
<keep-alive>
<alive-test></ alive-test>
</keep-alive>
用keep-alive包裹的组件 就会保留虚拟dom 不会销毁 提示性能
想让谁缓存 就用keep-alive包裹
7.组件缓存的钩子函数
用keep-alive包裹的组件不再执行 生命周期的函数
激活事件 -从缓存中再次被引用 activated()
休眠事件-将其放入缓存 deactivated()
export default{
activated(){
组件被激活
}
deactivated(){
组件被休眠
}
}
8.插槽
组件展示不同结构的内容 使用插槽
挖坑:代表使用vue内置组件 <slot></slot>
填坑:组件标签内部的内容就是坑的内容
组件标签中的内容就是插槽的内容
<alive-test>
插槽的内容
</alive-test>
插槽这里的课程有噪音 直接跳过
12.自定义指令
自己编写指令
if else show bind on for model text html slot once pre
v-once只渲染一次 响应式的更新
v-pre 跳过当前标签的扫描 写了很多层级的页面结构
可以自定义指令
全局 局部
export default{
directives:{
focus:{
//inserted 作用的标签被插入到dom之后执行这个函数
inserted(el,options){
el.focus();
}
}
}
}
day5的小结:
1.生命周期 四个阶段 八个钩子函数
初始化阶段 beforeCreate created 实例化前后触发
created获取数据 因为次时的data和methods已经初始化
挂载阶段:beforeMount mounted dom挂载前后触发
mounted 获取dom对象
数据更新阶段:beforeUpdate updated 数据更新前后触发
很少用 监听所有的数据变化 一般使用 watch代替
watch可以监听某一个数据 数据更新的钩子会监听整个页面的数据变化
组件销毁阶段 beforeDestroy destroy 销毁
beforeDestroy 确定要销毁 还没有销毁
http的请求工具 ——axios
axios是市场占有率高 用法很好
支持Promise 支持在nodejs使用
通用写法
axios({
method:"get/post/delete/put/patch",//请求类型
url:'',//请求地址
data:{},//请求体参数 body参数 get只有地址参数
params:{} //地址参数 最终参数会拼接到url地址上
}).then(result=>{}).catch()
别名写法:
axios.get(url)
axios.post(url,data{})//data是请求体参数
axios.delete(url)
axios.put(url,data{})
axios.patch(url,data{})
$.ajax({
})
组件的ref属性和$nextTick
ref是获取真实的dom元素 组件的实例对象
先给组件标签一个ref属性
<counter-com ref='efg'
h1 ref='abc'
methods:{
getRef(){
this.$refs.abc //h1的dom元素
this.$refs.efg //获取组件的实例对象 得到其组件的this指向 可以使用其内部的属性和方法
}
}
$nextTick vue的渲染是异步的
setTimeout setInterva ajax Promise.then.catch
先执行同步代码 等到主线程执行完毕 再去(按照到时顺序)执行任务对列的异步代码
数据更新之后 立刻获取dom结构 dom结构是上一次的
this.$nextTick(()=>{})//异步更新完成 执行这个函数确保异步更新已经完成
购物车的案例 json-server 讲一个json文件变成一个服务器 开启一个服务 提供一个接口
restful接口的规范--业界通用的规范
同样的地址 不同类型处理不同的业务
用户业务--新增-删除-更新-查询
查询用户:查询用户的列表 和单个用户的详情
get-/user 查询用户的列表 /user/:id 动态参数 查询单个用户的详情
post-/user 请求体参数 新增用户
put-/user/:id 请求体参数 更新用户
delete/user/:id 删除用户
获取购物车的数据
axios.defaults.baseURL=''//配置基础地址 不用写地址的前缀
因为前缀是一样的
在created中获取数据
list:[],
created(){
this.getGoodList()
}
methods:{
getGoodList(){
axios.get().then(result=>{
this.list=result.data
}).catch()
}
}
day6:
1.购物车案例 父传子props 子传父 自定义事件
2.动态组件-<component :is='组件名称'></component>
多个不同的组件显示在一个位置
3.组件缓存
组件被缓存 没有被销毁 存放在虚拟dom中 销毁真实dom
用<keep-alive>标签包裹组件 就缓存虚拟dom 保存组件实例
组件只会创建一次 提示性能 created只执行一次
只有激活和休眠两个函数 activated() deactivated()
4.插槽
占位:<slot></slot> 在组件中定义坑的位置
填补内容:<child> 填补内容</child>
匿名插槽 没有定义name名称的
<slot></slot>
具名插槽
<slot name='before'></slot>
填补的时候需要指定名称 template不起任何作用 代表这是标签
<child> <template v-slot:before 或者 slot='before'>123</template> </child>
后备内容
没有人传入插槽内容的时候 后备内容显示
<slot>后备内容</slot>
作用域插槽
父组件传递插槽内容的时候 需要使用子组件的数据
1.在子组件传递出你想让父组件使用的数据
<slot a='1' b='2' :d='变量'></slot>
<slot name='after' a='1' b='2' :d='变量'></slot>
2.传递插槽内容时 获取变量
<child>
obj是传递出所有属性的几何
<template v-slot:after='obj' >
template slot='after' slot-scope='obj'
<div>
a:{{obj.a}}
b:{{obj.b}}
</div>
</template>
</child>
使用具名插槽的时候
<child>
div slot='名字' slot-scope='变量名 //第一种语法
<template v-slot:'名字'='变量名' //第二种语法
</child>
自定义指令
开发者可以封装指令
局部注册 全局注册
export default{
directive:[
"focus":{
//指令描述
inserted(){
此时表示此指令作用的dom元素已经被插入到页面上
}
}
}
}
.sync 修饰符
子传父 子组件触发 父组件监听
.sync 子组件触发