vue生命周期与侦听器
回顾
知识点
v-for v-if的使用
修饰符
事件修饰符
冒泡
self stop capture
效率
once
不常用,滚动条
passive
默认
prevent
按键修饰符
KeyCode
Vue.config.KeyCodes.自定义按键修饰符=keycode
表单修饰符
lazy
number
trim
指令
文本 效率 属性 流程
作业
学员管理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> *{ margin: 0; padding: 0; } table{ width: 1000px; margin: 0 auto; background-color: black; } th,td{ text-align: center; background-color: white; line-height: 50px; } h1{ text-align: center; line-height: 60px; } input{ margin: 0 auto; display: inline-block; width: 200px; } </style> </head> <body> <div id="app"> <h1>学员信息</h1> <table> <tr> <th>学号</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>成绩</th> <th>操作</th> </tr> <tr v-for="(s,index) in students"> <td>{{ s.id }}</td> <td>{{ s.name }}</td> <td>{{ s.gender }}</td> <td>{{ s.age }}</td> <td>{{ s.score }}</td> <td> <button @click="update(s)">修改</button> <button @click="del(index)">删除</button> </td> </tr> </table> <hr /> <input placeholder="学员姓名" v-model="obj.name"> <input placeholder="学员性别" v-model="obj.gender"> <input placeholder="学员年龄" v-model="obj.age"> <input placeholder="学员成绩" v-model="obj.score"> <input type="button" value="提交" @click="get_score"> </div> <script> var vue = new Vue({ el: "#app", data: { obj: { id: 0, name: "", gender: "", age: "", score: "" }, students: [ { id: 5, name: "张三", gender: "男", age: 18, score: 80 }, { id: 4, name: "李四", gender: "女", age: 21, score: 90 }, { id: 3, name: "王五", gender: "男", age: 18, score: 60 }, { id: 2, name: "赵六", gender: "男", age: 19, score: 85 }, { id: 1, name: "候七", gender: "男", age: 18, score: 80 }, ] }, methods: { save(){ //新增的代码逻辑 var id = this.students?this.students[0].id+1:1 this.obj.id = id var add_obj = { ...this.obj } this.students.unshift(add_obj) }, change(id){ //需要得到要修改数据的id //通过id查找到这条数据 this.students.forEach(element => { if(element.id === id){ element.name = this.obj.name element.age = this.obj.age element.gender = this.obj.gender element.score = this.obj.score return alert("修改完成") } }); //把新的值赋值进去 }, get_score(){ if(this.obj.name.trim() === ""){ return alert("用户姓名不可以为空") } //判断是修改还是新增 if(this.obj.id){ //this.obj.id !== 0 this.change(this.obj.id)//修改 }else{ this.save() //新增 } this.obj = { id: 0, name: "", gender: "", age: "", score: "" } }, del(index){ this.students.splice(index,1) }, update(obj){ //将要修改的对象数据提交给this.obj this.obj.id = obj.id this.obj.name = obj.name this.obj.gender = obj.gender this.obj.age = obj.age this.obj.score = obj.score } } }) </script> </body> </html>
面板切换
版本1
<body> <div id="app"> <span @click="isShow = !isShow">面板1</span> <span @click="isShow = !isShow">面板2</span> <div class="panel" v-show="isShow">面板1</div> <div class="panel" v-show="isShow===false">面板2</div> </div> <script> var vue = new Vue({ el: "#app", data: { isShow: true } }) </script> </body>
bug: 切换没有对应性
<body> <div id="app"> <span @click="isShow = 1">面板1</span> <span @click="isShow = 2">面板2</span> <div class="panel" v-show="isShow === 1">面板1</div> <div class="panel" v-show="isShow === 2">面板2</div> </div> <script> var vue = new Vue({ el: "#app", data: { isShow: 1 } }) </script> </body>
动态样式
基于vue和js语法来研究一下动态处理样式的方式
动态的class
基本模式
<div id="app"> <div :class="isShow?'show':'hidden'"> 嘿嘿嘿 </div> <p @click="isShow = !isShow">切换</p> <p @click="change_color">变红</p> </div> <script> var vue = new Vue({ el: "#app", data: { isShow: true, class_arry: ['box', 'font'] }, methods: { change_color(){ this.class_arry.unshift('red') } } }) </script>
数组模式
<div id="app"> <!-- <div :class="['box', 'font', 'red']"> 基本数组结构 --> <div :class="class_arry"> 嘿嘿嘿 </div> <p @click="change_color">变红</p> </div> <script> var vue = new Vue({ el: "#app", data: { class_arry: ['box', 'font'] }, methods: { change_color(){ this.class_arry.unshift('red') } } }) </script>
对象模式
<div id="app"> <!-- <div :class="{'red': false,'box': true}"> --> <div :class="class_obj"> 嘿嘿嘿 </div> <p @click="isShow = !isShow">切换</p> <p @click="change_color">变红</p> </div> <script> var vue = new Vue({ el: "#app", data: { isShow: true, class_arry: ['box', 'font'], class_obj: {'red': true,'box': true} }, methods: { change_color(){ this.class_arry.unshift('red') } } }) </script>
动态的style
常规写法
<body> <div id="app"> <div :style="'background-color:red'"> 嘿嘿嘿 </div> </div> <script> var vue = new Vue({ el: "#app", data: { }, }) </script> </body>
对象写法
<body> <div id="app"> <!-- <div :style="{'background-color':'red'}"> --> <div :style="obj"> 嘿嘿嘿 </div> </div> <script> var vue = new Vue({ el: "#app", data: { obj:{ backgroundColor:'red' } }, }) </script> </body>
数组写法
<div id="app"> <div :style="[obj,obj1]"> 嘿嘿嘿 </div> </div> <script> var vue = new Vue({ el: "#app", data: { obj:{ backgroundColor:'red' }, obj1:{ fontSize: "24px" } }, }) </script>
生命周期
在vue执行的过程当中,会有几个共性关键的执行节点,我们称为生命周期节点。生命周期指的是vue实例从创建,挂载,运行到销毁的过程。
创建阶段(create)
创建阶段的生命周期函数是自动执行的
首先执行beforeCreate,然后执行created
beforeCreate 创建前
创建前,vue的变量还没有声明
created 创建后
创建后,vue的变量已经声明完成,通常用于项目的初始化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <div id="app"> <div> {{ message }} </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: "hello world" }, created(){ console.log(this.message,"+++++++++++++created") }, beforeCreate(){ console.log(this.message,"+++++++++++++beforeCreate"); } }) </script> </body> </html>
挂载阶段(mount)
beforeMount 挂载前
mounted 挂载后
挂载阶段的生命周期函数在创建阶段的生命周期函数执行完成自动执行,
beforeMount 还没有实现页面上变量数据的挂载
mounted 已经完成页面数据的挂载
<body> <div id="app"> <div id="msg"> {{ message }} </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: "hello world" }, beforeMount(){ console.log(document.querySelector("#msg"),"+++++++++++++beforeMount"); }, mounted(){ console.log(document.querySelector("#msg"),"+++++++++++++mounted"); }, }) </script> </body>
更新阶段(update)
beforeUpdate 更新前
updated 更新后
更新阶段的生命周期函数在挂载阶段的生命周期函数执行完成后发生数据修改自动执行,
beforeUpdate 数据修改前执行
updated 数据修改后执行
更新阶段的生命周期函数每次跟新都执行,但是挂载阶段的生命周期函数和创建阶段的生命周期函数只在第一次加载执行一次
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <div id="app"> <div id="msg"> {{ message }} <input v-model="message"> </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: "hello world" }, beforeUpdate(){ console.log(document.querySelector("#msg").innerHTML,"+++++++++++++beforeUpdate"); }, updated(){ console.log(document.querySelector("#msg").innerHTML,"+++++++++++++updated"); } }) </script> </body> </html>
销毁阶段 (destroy)
beforeDestroy 销毁前
destroyed 销毁后
触发销毁函数后执行销毁阶段的生命周期函数,通常用来进行内存回收。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <div id="app"> <div id="msg"> {{ message }} <input v-model="message"> <button @click="destroy">销毁吧</button> </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: "hello world" }, methods:{ destroy(){ this.$destroy() } }, beforeDestroy(){ console.log(this,"+++++++++++++beforeDestroy"); }, destroyed(){ console.log(this,"+++++++++++++destroyed"); } }) </script> </body> </html>
vue的生命周期一共有四种,八个,其中两种四个自动执行,剩下的条件执行。
侦听器
方法名称: watch
作用,监听某个数据的变化,从而进行一些特殊的操作
特点:不需要调用执行,触发执行即可,类似update
注意:watch本身有一定的系统开销,所以,侦听器不可以滥用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <div id="app"> <div id="msg"> {{ message }} <input v-model="message"> </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: "hello world", hhh: "hhh", }, watch :{ message(new_value,old_value){ console.log(new_value,old_value) } } }) </script> </body> </html>
深度侦听器
深度侦听器主要针对的是嵌套结构的数据修改。
<div id="app"> <div id="msg" @click="add"> {{ message }} </div> </div> <script> var vue = new Vue({ el: "#app", data: { message: { msg: { name: "王" } }, }, methods: { add(){ this.message.msg.name = "李" } }, watch :{ message:{ handler(){ console.log(arguments); }, //句柄 deep: true //启动深度侦听 } } }) </script>
计算属性
computed:作为vue当中的计算属性,主要用在页面当中的数据计算,特点:
1、调用的时候不用加括号,灵活度不够好。
2、本身具备缓存能力。
3、计算属性不可以和其他的vue变量或者功能重名。
4、计算属性的值可以读取,不可以修改
<div id="app"> <div id="msg" @click="add"> {{ add() }} {{ add() }} {{ add() }} <hr /> {{ add_1 }} {{ add_1 }} {{ add_1 }} <input v-model="add_2"> {{ add_1 }} </div> </div> <script> var vue = new Vue({ el: "#app", data: { add_2: 123 }, methods:{ add(){ console.log("add 被调用了,methods") return 999 } }, computed:{ add_1(){ console.log("add 被调用了,computed") return "abc" } } }) </script>
购物车案例
全选反选
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .container { width: 1000px; margin: 20px auto; } .form-group { margin: 15px 0; } .form-control { height: 40px; padding: 10px; width: 100%; border: 1px solid #DDD; border-radius: 4px; } tr { height: 60px; text-align: center; } .active { background-color: #EEE; } table { width: 100%; } table, th, td { border: 1px solid #CCC; /* 合并边框 */ border-collapse: collapse; } label { cursor: pointer; } input[type='checkbox'] { width: 16px; height: 16px; vertical-align: middle; } .btn { border: 0; width: 70px; line-height: 35px; text-align: center; color: #555; background: #EEE; border-radius: 4px; cursor: pointer; font-weight: 700; outline: 0; margin: 0 2px; } .btn.danger { background-color: #D9534F; color: #FFF; } .btn.success { background-color: #337AB7; color: #FFF; } .line { border-bottom: 1px solid #DDD; margin: 10px 0; } .btn-cart { width: 30px; height: 30px; background: #CCC; border: 0; outline: none; cursor: pointer; } </style> <script src="./vue.js"></script> </head> <body> <div class="container" id="app"> <h1>购物车</h1> <table cellpadding="0" cellspacing="0" width="100%"> <thead> <tr class="active"> <th width="80"> <label><input type="checkbox" v-model="checked" @change="toggleChecked"> 全选</label> </th> <th>商品</th> <th>单价</th> <th>数量</th> <th>小计</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="goods in goods_list"> <td> <input type="checkbox" v-model="goods.checked" @change="childCheck"/> </td> <td> <img style="height: 120px;" :src="goods.goodsimg" alt=""> <h5>{{ goods.goodsname }}</h5> </td> <td>¥{{ goods.price }}</td> <td> <button class="btn-cart">-</button> 1 <button class="btn-cart">+</button> </td> <td>¥12999</td> <td> <button class="btn danger">删除</button> </td> </tr> <tr> <td colspan="6" style="text-align: left;"> <b style="color:red"> 订单总金额: ¥8888 </b> </td> </tr> </tbody> </table> </div> </body> <script> var vue = new Vue({ el: "#app", data: { checked: false, goods_list: [{ id: 1, goodsname: "vivo IQ777", goodsimg: "https://img12.360buyimg.com/seckillcms/s280x280_jfs/t1/166249/4/1781/100467/5ffc0654Eb618aa21/2188e4ef3f6abe7e.jpg.webp", price: 2999, num: 1, checked: false }, { id: 2, goodsname: "小米10", goodsimg: "https://img10.360buyimg.com/seckillcms/s280x280_jfs/t1/97161/40/12127/66234/5e440190E7cd0f54e/33627b2c39a67241.jpg.webp", price: 998, num: 1, checked: false }, { id: 3, goodsname: "传世的珐琅锅", goodsimg: "https://img20.360buyimg.com/seckillcms/s280x280_jfs/t1/168750/15/16697/127861/606c22c0Eca19515e/5adfe8e452fffbff.png.webp", price: 1768, num: 1, checked: true }, { id: 4, goodsname: "蔻驰的丑包", goodsimg: "https://img12.360buyimg.com/ceco/s300x300_jfs/t1/119430/19/3585/133796/5eb12fe7E1a4ee264/392d83858bb8cd52.jpg!q70.jpg.webp", price: 17865, num: 1, checked: false } ] }, methods: { toggleChecked(){ //this.checked === false 全选 this.goods_list.forEach(element => { element.checked = this.checked }); }, childCheck(){ var ckd = true; this.goods_list.forEach(element => { if(!element.checked){ ckd = false } }); this.checked = ckd } } }) </script> </html>
数量修改
总计
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; box-sizing: border-box; } .container { width: 1000px; margin: 20px auto; } .form-group { margin: 15px 0; } .form-control { height: 40px; padding: 10px; width: 100%; border: 1px solid #DDD; border-radius: 4px; } tr { height: 60px; text-align: center; } .active { background-color: #EEE; } table { width: 100%; } table, th, td { border: 1px solid #CCC; /* 合并边框 */ border-collapse: collapse; } label { cursor: pointer; } input[type='checkbox'] { width: 16px; height: 16px; vertical-align: middle; } .btn { border: 0; width: 70px; line-height: 35px; text-align: center; color: #555; background: #EEE; border-radius: 4px; cursor: pointer; font-weight: 700; outline: 0; margin: 0 2px; } .btn.danger { background-color: #D9534F; color: #FFF; } .btn.success { background-color: #337AB7; color: #FFF; } .line { border-bottom: 1px solid #DDD; margin: 10px 0; } .btn-cart { width: 30px; height: 30px; background: #CCC; border: 0; outline: none; cursor: pointer; } </style> <script src="./vue.js"></script> </head> <body> <div class="container" id="app"> <h1>购物车</h1> <table cellpadding="0" cellspacing="0" width="100%"> <thead> <tr class="active"> <th width="80"> <label><input type="checkbox" v-model="checked" @change="toggleChecked"> 全选</label> </th> <th>商品</th> <th>单价</th> <th>数量</th> <th>小计</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(goods,index) in goods_list"> <td> <input type="checkbox" v-model="goods.checked" @change="childCheck"/> </td> <td> <img style="height: 120px;" :src="goods.goodsimg" alt=""> <h5>{{ goods.goodsname }}</h5> </td> <td>¥{{ goods.price }}</td> <td> <button class="btn-cart" @click="computed('d',index)">-</button> {{ goods.num }} <button class="btn-cart" @click="computed('u',index)">+</button> </td> <td>¥{{ goods.num*goods.price }}</td> <td> <button class="btn danger">删除</button> </td> </tr> <tr> <td colspan="6" style="text-align: left;"> <b style="color:red"> 订单总金额: ¥{{ total }} </b> </td> </tr> </tbody> </table> </div> </body> <script> var vue = new Vue({ el: "#app", data: { checked: false, goods_list: [{ id: 1, goodsname: "vivo IQ777", goodsimg: "https://img12.360buyimg.com/seckillcms/s280x280_jfs/t1/166249/4/1781/100467/5ffc0654Eb618aa21/2188e4ef3f6abe7e.jpg.webp", price: 2999, num: 1, checked: false }, { id: 2, goodsname: "小米10", goodsimg: "https://img10.360buyimg.com/seckillcms/s280x280_jfs/t1/97161/40/12127/66234/5e440190E7cd0f54e/33627b2c39a67241.jpg.webp", price: 998, num: 1, checked: false }, { id: 3, goodsname: "传世的珐琅锅", goodsimg: "https://img20.360buyimg.com/seckillcms/s280x280_jfs/t1/168750/15/16697/127861/606c22c0Eca19515e/5adfe8e452fffbff.png.webp", price: 1768, num: 1, checked: true }, { id: 4, goodsname: "蔻驰的丑包", goodsimg: "https://img12.360buyimg.com/ceco/s300x300_jfs/t1/119430/19/3585/133796/5eb12fe7E1a4ee264/392d83858bb8cd52.jpg!q70.jpg.webp", price: 17865, num: 1, checked: false } ] }, methods: { computed(type,index){ console.log(type) if(type == "u"){ this.goods_list[index].num++ }else{ if(this.goods_list[index].num>1){ this.goods_list[index].num-- } } }, toggleChecked(){ //this.checked === false 全选 this.goods_list.forEach(element => { element.checked = this.checked }); }, childCheck(){ var ckd = true; this.goods_list.forEach(element => { if(!element.checked){ ckd = false } }); this.checked = ckd } }, computed: { total(){ var price = 0; this.goods_list.forEach(element => { if(element.checked){ price += element.price*element.num } }); return price } } }) </script> </html>
删除
del(index){ this.goods_list.splice(index,1) },
百度案例优化
jsonp
跨域问题:同源问题,在浏览器当中,为了安全,不同域名或相同域名不同端口,或者相同域名和端口,不同协议,都称为不同源的请求,不同的地址之间不可以直接发送ajax或者表单请求,但是可以发生a标签,img标签,script标签的get请求。
jsonp可以利用script标签进行数据发送,所以也先天可以免疫跨域问题,但是只能完成get请求。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./vue.js"></script> <style> * { padding: 0; margin: 0; box-sizing: border-box; } li { list-style: none; } .container{ width: 600px; margin: 20px auto; } .form-control { padding: 10px; width: 100%; border: 1px solid #DDD; border-radius: 4px; vertical-align: middle; outline: none; } .options{ background: #EEE; } .options li{ line-height: 30px; padding: 0 10px; cursor: pointer; } .options li:hover{ background: cornflowerblue; color:#FFF; } </style> </head> <body> <div id="app"> <div class="container"> <input class="form-control" type="text" placeholder="请输入搜索关键词" v-model="keywords"/> <ul class="options"> <li v-for="v in search_list">{{ v }}</li> </ul> </div> </div> </body> <script> var vue = new Vue({ el: '#app', data:{ keywords: "", search_list: [] }, watch:{ keywords(value){ var url = "https://suggestion.baidu.com/su?cb=callback&wd="+value const script = document.createElement('script') if(this.keywords.trim() !== ""){ script.src = url } document.body.appendChild(script) } } }); function callback(res){ vue.search_list = res.s } </script> </html>