目录
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 = false
new 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 = false
new 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 = 99
input.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>