目录
1. 内置指令
1.1 v-text 指令
1.2 v-html 指令
1.3 v-on基础
1.3.1 计数器案例
1.4 v-show 条件渲染
1.5 v-if 条件渲染
1.6 v-bind
1.6.1 图片切换案例
1.7 v-for 列表渲染
1.7.1 key原理(面试常问)
1.7.2 列表过滤
1.7.3 列表排序
1.8 v-on补充
1.9 v-model 双向绑定数据
1.9.1 基础用法
1.9.2 收集表单
1.10 v-cloak 指令
1.11 v-once 指令
1.12 v-pre 指令
2. 自定义指令
2.1 函数式
2.2 对象式
2.3 总结
3. 过滤器
1. 内置指令
1.1 v-text 指令
- 设置文本属性,替换掉的全部内容
- 如果需要替换部分内容用{{}}插值表达式
- 内部支持写表达式
- 同样的插值语法{{}}也可以实现,一般常用插值语法。它比较灵活
<body><div id="app"><h2 v-text="msg"></h2><h3>{{name}} </h3></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {msg: 'hello',name: 18,}})</script>
</body>
效果:
1.2 v-html 指令
- 设置标签innerHTML,里面可以写html
- 内容中有html的结构会别解析为标签
- v-text 指令无论内容是什么,只能解析文本
- 严重注意:html 有安全性问题,在网站动态渲染任意html是很危险的,容易导致XSS攻击
- 一定要在可信的内容上使用v-html,永不要在用户提交的内容上
- 补充知识:cookie本质是字符串,不允许跨浏览器读取
<body><div id="app"><p v-html="content"></p></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {content: "<a href='#'>百度</a>"}})</script>
</body>
效果
1.3 v-on基础
为元素绑定事件
- vue事件名:click 点击、dbclick 双击、monseenter鼠标移动、
- 绑定方法在methods属性中
- 方法内部可以有this关键字访问定义在data的数据
语法: <input type="text" value="xxx" v-on:事件名="方法">
<input type="text" value="xxx" @事件名="方法">(常用)
<body><div id="app"><!-- <input type="text" value="xxx" v-on:click="getId"> --><input type="text" value="xxx" @click="getId"></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {getId: function () {alert('12546')}}})</script>
</body>
1.3.1 计数器案例
案例:点击+- 按钮可以计数,且有限制数字在1-10内
<!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"><script type="text/javascript" src="./js/vue.js"></script><title>计数器</title>
</head>
<style>* {padding: 0;margin: 0;}#app {width: 300px;height: 50px;margin: 100px auto;}button {width: 100px;height: 50px;border-style: none;}span {display: inline-block;text-align: center;width: 90px;height: 50px;}
</style><body><div id="app"><button @click="sub">-</button><span>{{num }} </span><button @click="add">+</button></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {num: 8,},methods: {sub: function () {if (this.num >= 2) {this.num--} else {alert('不能在减啦')}},add: function () {if (this.num < 10) {this.num++} else {alert('次数已经上限啦')}},}})</script>
</body></html>
效果
1.4 v-show 条件渲染
- 根据元素的真假,切换元素的显示和隐藏
- 原理:修改元素的display,实现显示和隐藏
- 指令后面的内容最后都会解析为布尔值,为true显示,为false 隐藏元素
- 数据改变,元素对应的显示状态会同步更新
-
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
-
使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
案例:点击切换 可以显示和隐藏文字
<body><div id="app"><p @click="change">点击切换</p><h2 v-show="isShow">今天好开心 </h2></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {isShow: false},methods: {change: function () {this.isShow = !this.isShow;},},})</script>
</body>
效果
1.5 v-if 条件渲染
根据表达式的真假切换元素的显示和隐藏(操作dom元素)
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
- 本质是操控dom元素来显示切换状态的,移除或者添加
- 当表达式为false,元素存在于dom数中,为false,从dom熟中移除
- 频繁切换用v-show,相反用v-if,v-show对性能的消耗比较小
<body><div id="app"><button @click="changeShow">切换显示</button><p v-if="isShow">黑马程序员</p></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {isShow: false,},methods: {changeShow: function () {this.isShow = !this.isShow;}},})</script>
</body>
效果:
1.6 v-bind
- 设置元素的属性(src、title、class)
- 需要动态的增删class建议使用对象的方式
语法:v-bind:属性名=表达式 简写:属性名=表达式(bing可以简写称冒号:)
<!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"><script type="text/javascript" src="./js/vue.js"></script><title>v-bind</title><style>.active {border: 2px solid red;}</style>
</head><body><div id="app"><img v-bind:src="imgSrc"><p>--------------------</p><!-- +''是字符串拼接 --><img v-bind:title="imgTitle+'~'" src="./images/bo.jpg" :class="{active:isActivity}" @click="changeisActivity"></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {imgSrc: './images/bo.jpg',imgTitle: 'kitty',isActivity: false,},methods: {changeisActivity: function () {this.isActivity = !this.isActivity;}},})</script>
</body></html>
效果
1.6.1 图片切换案例
案例:点击两边的按钮可以切换图片,当点到最开始的图片时左边按钮会隐藏,右边按钮同理
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"><script type="text/javascript" src="./js/vue.js"></script><link rel="stylesheet" href="./css/index.css"><title>点击切换图片</title>
</head><body><div id="demo"><h2>唯美壁纸</h2><img class='pic' :src="imgSrc[index]"><!-- 左箭头 --><a href="javascript:void(0)" v-if="index!=0" @click="prev" class="prev"><img src="./images/prev.png"></a><!-- 右箭头 --><a href="javascript:void(0)" v-show="index!=imgSrc.length-1" @click="next" class="next"><img src="./images/next.png"></a></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#demo',data: {imgSrc: ["./images/00.jpg","./images/01.jpg","./images/02.jpg","./images/03.jpg","./images/04.jpg","./images/05.jpg",],index: 2,},methods: {prev: function () {this.index--;console.log(111);},next: function () {this.index++;}},})</script>
</body></html>
css文件
* {padding: 0;margin: 0;
}#demo {text-align: center;padding-top: 50px;background-color: rgb(239, 210, 223);
}.pic {position: relative;/* width: 500px; */height: 300px;text-align: center;margin: 100px auto;
}.prev,
.next {position: absolute;top: 50%;
}.prev {left: 100px;
}.next {right: 100px;
}
效果
1.7 v-for 列表渲染
- 根据数据生成列表结构
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
- 语法:v-for="(item, index) in xxx" :key="yyy"
- item,index可以结合其他指令一起使用
- 数组长度的更新会同步到页面上(响应式开发)
<body><div id="app"><button @click="add">添加食物</button><button @click="del">删除食物</button><ul><!-- 数组 --><li v-for="(item,index) in city" :key="index">{{index}}城市:{{item}} </li></ul><!-- 对象 --><h3 v-for="item in foods"> {{item.name}} </h3></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {city: ["北京", "上海"],foods: [{ name: 'cheery' },{ name: 'apple' }]},methods: {add: function () {this.foods.push({ name: 'orange' })},del: function () {this.foods.shift()},},})</script>
</body>
效果
<!-- 遍历指定次数 --><h2>测试遍历指定次数(用得少)</h2><ul><li v-for="(number,index) of 5" :key="index">{{index}}-{{number}}</li></ul>
1.7.1 key原理(面试常问)
- 如果用index作为key的原理,不要用index作为key!
- 如果没有写key,默认将index作为key,所以一定要写key!
- 一般key写item.id
面试题:react、vue中的key有什么作用?(key的内部原理)
- 虚拟DOM中key的作用: key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
-
对比规则:
- (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- (2).旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
-
若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
-
如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
4. 开发中如何选择key:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
1.7.2 列表过滤
从列表中筛选人员(如查找同姓的人员)
<body><!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><ul><li v-for="(p,index) of filPerons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = false//用computed实现new Vue({el:'#root',data:{keyWord:'',persons:[{id:'001',name:'马冬梅',age:19,sex:'女'},{id:'002',name:'周冬雨',age:20,sex:'女'},{id:'003',name:'周杰伦',age:21,sex:'男'},{id:'004',name:'温兆伦',age:22,sex:'男'}]},computed:{filPerons(){return this.persons.filter((p)=>{return p.name.indexOf(this.keyWord) !== -1})}}})
- indexof()方法判断是否包含字符串,如果是-1,则不包含,包含返回的是索引号,空字符串也是返回1
- filter()不改变原数据
1.7.3 列表排序
<body><!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><button @click="sortType = 2">年龄升序</button><button @click="sortType = 1">年龄降序</button><button @click="sortType = 0">原顺序</button><ul><li v-for="(p,index) of filPerons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}<input type="text"></li></ul></div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{keyWord:'',sortType:0, //0原顺序 1降序 2升序persons:[{id:'001',name:'马冬梅',age:30,sex:'女'},{id:'002',name:'周冬雨',age:31,sex:'女'},{id:'003',name:'周杰伦',age:18,sex:'男'},{id:'004',name:'温兆伦',age:19,sex:'男'}]},computed:{filPerons(){const arr = this.persons.filter((p)=>{return p.name.indexOf(this.keyWord) !== -1})//判断一下是否需要排序if(this.sortType){arr.sort((p1,p2)=>{return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age})}return arr}}}) </script>
vue监测数据代理,数据监测,原理都是用getter和setter
1.7.4 vue数据监视总结
- splice(序号,长度,内容 )
- 有getter和setter的值就是响应式的
- 增加一个响应式属性:在methods加vue.set(this,'属性','属性值')or this.$set(this.'属性','属性值’)
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据
- 通过setter实现监视,且要在new Vue时就传入要监测的数据。
- (1).对象中后追加的属性,Vue默认不做响应式处理
- (2).如需给后添加的属性做响应式,请使用如下API:
- Vue.set(target,propertyName/index,value) 或
- vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据
- 通过包裹数组更新元素的方法实现,本质就是做了两件事:
- (1).调用原生对应的方法对数组进行更新。
- (2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据(vm_data)对象 添加属性!!!
1.8 v-on补充
- 传入自定义参数,事件修饰符
- 事件绑定的方法写成函数调用的形式,可以传递自定义参数
- 定义方法需要定义形参来接收传入的实参
- 事件.修饰符 对事件进行限制
<body><div id="app"><!-- 无参自定义函数 --><input type="text" @keyup.enter="Hi()"><!-- 参数自定义函数 --><button @click="doIt(1111)">ok</button></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {},methods: {Hi: function () {alert('是否要确认')},doIt: function (t) {console.log(t);},},})</script>
</body>
效果:输入回车后会弹出警示框,按下ok按钮在控制台输出111
1.9 v-model 双向绑定数据
1.9.1 基础用法
- 获取和设置表单元素的值(双向数据绑定)
- 绑定的数据会和表单元素的值相关联
- 绑定的数据一一对应表单元素
<body><div id="app"><input type="text" v-model="value"></div><script type="text/javascript">Vue.config.productionTip = false; //阻止vue启动时生产提示var app = new Vue({el: '#app',data: {value: 'lisa'},})</script>
</body>
效果:
1.9.2 收集表单
-
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
-
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
-
若:<input type="checkbox"/>:
-
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值),2.配置input的value属性:(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)(2)v-model的初始值是数组,那么收集的的就是value组成的数组
-
- 备注:v-model的三个修饰符:
-
lazy:失去焦点再收集数据
-
number:输入字符串转为有效的数字
-
trim:输入首尾空格过滤
<body><div id="root"><form @submit.prevent="demo">账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>密码:<input type="password" v-model="userInfo.password"> <br/><br/>年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>性别:男<input type="radio" name="sex" v-model="userInfo.sex" value="male">女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>爱好:学习<input type="checkbox" v-model="userInfo.hobby" value="study">打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"><br/><br/>所属校区<select v-model="userInfo.city"><option value="">请选择校区</option><option value="beijing">北京</option><option value="shanghai">上海</option><option value="shenzhen">深圳</option><option value="wuhan">武汉</option></select><br/><br/>其他信息:<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/><input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a><button>提交</button></form></div></body><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{userInfo:{account:'',password:'',age:18,sex:'female',hobby:[],city:'beijing',other:'',agree:''}},methods: {demo(){console.log(JSON.stringify(this.userInfo))}}})</script>
1.10 v-cloak 指令
- 本质是一个属性,vue实例创建完毕并接管容器,就会删掉v-cloak属性
- 解决网速慢时,页面展示出{{xxx}}的问题:v-cloak配合css使用
<div id="root"><h2 v-cloak>{{name}}</h2></div><script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script></body><script type="text/javascript">console.log(1)Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'}})</script>
1.11 v-once 指令
- v-once指令所在节点在初次动态渲染后,就视为静态内容了
- 以后数据改变不会引起v-once所在结构的更新,可以优化性能
<div id="root"><h2 v-once>初始化的n值是:{{n}}</h2><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button></div>
1.12 v-pre 指令
- 跳过所在节点的编译过程,就是vue不再解析了
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点、会加快编译
2. 自定义指令
- 定义指令不要v-xxx,直接写xxx,
- element 是真实dom
- 语法:在newvue里面写directive { 1.对象()/2.函数()}
- 对象式写法难,可以处理细节问题;函数式写法简单,不能处理细节问题
2.1 函数式
函数写法function(element,绑定元素){},
- 当元素绑定时会被调用
- 指令所在的模板发生改变时,函数会被调用
2.2 对象式
- 写法复杂,可以实现细节问题
- 对象里可以写很多函数
- 以下都是钩子函数
- bind():与函数绑定时被调用
- inserted():指令所在元素被插入页面时被调用,拿到父元素,获取焦点用
- updated():指令所在元素被重新解析时被调用
<head><meta charset="UTF-8" /><title>Document</title><style>.demo{background-color: orange;}</style></head><body><button id="btn">点我创建一个输入框</button><script type="text/javascript" >const btn = document.getElementById('btn')btn.onclick = ()=>{const input = document.createElement('input')input.className = 'demo'input.value = 99input.onclick = ()=>{alert(1)}document.body.appendChild(input)input.focus()// input.parentElement.style.backgroundColor = 'skyblue'console.log(input.parentElement)}</script></body>
2.3 总结
局部指令:
new Vue({ directives:{指令名:配置对象/回调函数}
})
全局指令:
Vue.directives:(指令名,配置对象/回调函数)
配置对象中常用的3个回调:
- bind:指令与元素成功绑定时调用。
- inserted:指令所在元素被插入页面时调用
- update:指令所在模板结构被重新解析时调用。
注意:
- 指令定义时不加v-,但使用时要加v-;
- 指令名如果是多个单词,要使用kebab-case命名方式(用-连接),不要用驼峰命名法
3. 过滤器
- 定义:对显示的数据进行特定格式化后显示(用于简单的逻辑处理)
语法:
- 注册:Vue.filter(name,callback) 或 new Vue(filters:{ })
- 使用:{{xxx| 过滤器名字 }} (常用)或 v-bind :属性=“xxx | 过滤器名”
备注:
- 过滤器可以接收额外参数、多个过滤器可以串联
- 没有改变原来得数据,是产生对应的数据
<div id="root"><h2>显示格式化后的时间</h2><!-- 计算属性实现 --><h3>现在是:{{fmtTime}}</h3><!-- methods实现 --><h3>现在是:{{getFmtTime()}}</h3><!-- 过滤器实现 --><h3>现在是:{{time | timeFormater}}</h3><!-- 过滤器实现(传参) --><h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3><h3 :x="msg | mySlice">尚硅谷</h3></div><div id="root2"><h2>{{msg | mySlice}}</h2></div></body><script type="text/javascript">Vue.config.productionTip = false//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,4)})new Vue({el:'#root',data:{time:1621561377603, //时间戳msg:'你好,尚硅谷'},computed: {fmtTime(){return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')}},methods: {getFmtTime(){return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')}},//局部过滤器filters:{timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){// console.log('@',value)return dayjs(value).format(str)}}})new Vue({el:'#root2',data:{msg:'hello,atguigu!'}})</script>