目录
1. 内容渲染指令(v-text,{{ }},v-html)
3.3按键修饰符:(@keyup.esc,@keyup.enter)
4.1v-model的修饰符(.number,.trim,.lazy)
5.条件渲染指令(v-if,v-else,v-else,v-show,)
7.v-cloak指令(防止闪现, 与 css 配合: [v-cloak] { display: none })
vue 的两个特性
-
数据驱动视图:
-
数据的变化会驱动视图自动更新
-
好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
-
-
双向数据绑定:
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
-
js 数据的变化,会被自动渲染到页面上
-
页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中
-
注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)
ViewModel 作为 MVVM 的核心 ,是它把当前页面的 数据源 ( Model )和 页面的结构 ( View )连接在了一起。当 数据源发生变化 时,会被 ViewModel 监听到, VM 会根据最新的数据源 自动更新 页面的结构当 表单元素的值发生变化 时,也会被 VM 监听到, VM 会把变化过后最新的值 自动同步 到 Model 数据源中
3.vue的基本使用
- ①导入vue.js 的script 脚本文件
- ②在页面中声明一个将要被vue 所控制的DOM 区域
- ③创建vm 实例对象(vue 实例对象)
<body>
<!-- 希望Vue能够控制下面的div,
帮我们在把数据填充到div内部 -->
<div id="app">{{username}}</div>
<!-- 1.导入Vue的库文件,在window全局
就有这个Vue这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2.创建Vue的实例对象 -->
<script>
//创建Vue的实例对象
const vm = new Vue({
/* el属性是固定写法,表示当前vm实例
要控制页面哪个区域,接收的值是一个选择器 */
el: '#app',
// data对象就是要渲染到页面的数据
data: {
username: 'dilireba'
}
})
</script>
</body>
vue 指令
1. 内容渲染指令(v-text,{{ }},v-html)
v-text
指令的缺点:会覆盖元素内部原有的内容!
{{ }}
插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
v-html
指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!
<div id="app">
<!-- 内容渲染指令 -->
<!-- v-text指令的缺点,会覆盖元素
内部原有的内容! -->
<p v-text="username"></p>
<p v-text="gender">性别:</p>
<hr>
<!-- 解决方法:弥补v-text的缺点:插值表达式{{}} -->
<!-- {{}}插值表达式,实际开发运用最多 -->
<p>姓名:{{username}}</p>
<p>性别:{{gender}}</p>
<hr>
<!-- {{}}和v-text只能渲染纯文本内容,
不能渲染html内容 -->
<div v-text="info"></div>
<div>{{info}}</div>
<!-- 需要用v-html渲染带标签的字符串 -->
<div v-html="info"></div>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
username: 'dilireba',
gender: '女',
info: '<h4 style="color:red;font-weight:bold;">Hello Vue!</h4>'
}
})
</script>
2. 属性绑定指令(v-bind:,简写 :)
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
-
在 vue 中,可以使用
v-bind:
指令,为元素的属性动态绑定值; -
简写是英文的
:
-
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<div :title="'box' + index">这是一个 div</div>
<div id="app">
<!-- 属性绑定指令 -->
<!-- 插值表达式只能用到内容节点元素中,
不能应用到属性节点上 -->
<!--把tips值放到属性placeholder里面,需
要用的v-bind -->
<input type="text" v-bind:placeholder="tips">
<hr>
<!-- Vue规定v-bind:指令可以简写为: -->
<img :src="photo" alt="" style="width: 150px;">
<hr>
<!-- 在 vue 提供的模板渲染语法中,除了
支持绑定简单的数据值之外,还支持 Javascript 表达式的运算 -->
<div>1+2的结果是:{{1+2}}</div>
<div>{{ tips }}反转后的结果是:{{ tips.split('').reverse().join('')}}</div>
<!-- 在使用 v-bind 属性绑定期间,如果绑
定内容需要进行动态拼接,则字符串的外面应该
包裹单引号,双引号里面的内容看成js代码 -->
<div :title="'box'+index">这是一个div</div>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
tips: '请输入用户名',
photo: 'https://cn.vuejs.org/images/logo.svg',
index: 3
}
})
</script>
绑定样式
2.1. class样式写法:
-
class="xxx" xxx可以是字符串、对象、数组。
-
字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2.2. style样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
data:{
name:'迪丽热巴',
mood:'normal',
classArr:['atguigu1','atguigu2','atguigu3'],
classObj:{
atguigu1:false,
atguigu2:false,
},
styleObj:{
fontSize: '40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
},
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
},
3.事件绑定指令(v-on:,简写 @)
-
v-on:
简写是@
-
语法格式为:
<button @click="add"></button>
methods: {
add() {
// 如果在方法中要修改 data 中的数据,
//可以通过 this 访问到
this.count += 1
}
}
<div id="app">
<!-- 事件绑定指令 -->
<p>count 的值是:{{count}}</p>
<!-- v-on语法,v-on:事件名称="事件处理函数名称" -->
<!-- 在绑定事件处理函数的时候可以使用()进行传递参数 -->
<!-- v-on:指令可以被简写为@ -->
<button v-on:click="add(1)">+1</button>
<button @click="sub">-1</button>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 0,
},
/*methods的作用,就是定义事件的处理函数 */
methods: {
/* add: function() {
console.log('ok');
} */
/* es6语法简写 */
add(n) {
console.log(vm === this);
/* vm.count += 1 */
this.count += n
},
sub() {
console.log('no');
this.count -= 1
}
}
})
</script>
3.1事件对象$event
$event
的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。例如:
<button @click="add(3, $event)"></button>
methods: {
add(n, e) {
// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
this.count += 1
}
}
<div id="app">
<p>count的值是:{{count}}</p>
<!-- 如果count是偶数,则 按钮背景变成红色,否则取消背景颜色 -->
<!-- vue提供了内置变量,名字叫做$event,它就是原生DOM的事件对象e -->
<button @click="add($event,1)">+N</button>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
count: 0,
},
methods: {
add(e, n) {
this.count += n
console.log(e);
//判断this.count的值是否为偶数
if (this.count % 2 === 0) {
//偶数
e.target.style.backgroundColor = 'red'
} else {
//奇数
e.target.style.backgroundColor = ''
}
}
}
})
</script>
3.2事件修饰符:
-
.prevent
<a @click.prevent="xxx">链接</a>
-
.stop
<button @click.stop="xxx">按钮</button>
<div id="app">
<!-- 事件修饰符,绑定在事件绑定后面,
如event.preventDefault()阻止事件默认行为
或event.stopPropagation()阻止事件冒泡,简化成.prevent和.stop -->
<a href="http://www.baidu.com" @click.prevent="show">跳转到百度页面</a>
<hr>
<div style="height: 200px;background-color: orange;padding-left: 100px;line-height: 200px;" @click="divHandler">
<button @click.stop="btnHandler">按钮</button>
</div>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
},
methods: {
show() {
/* e.preventDefault(); */
console.log('点击了a链接');
},
btnHandler() {
console.log('btnHandler');
},
divHandler() {
console.log('divHandler');
},
}
})
</script>
3.3按键修饰符:(@keyup.esc,@keyup.enter)
<!-- 按键修饰符 -->
<div id="app">
<input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {},
methods: {
clearInput(e) {
console.log('触发了clearInput方法');
e.target.value = ''
},
commitAjax() {
console.log('触发了commitAjax方法');
}
},
})
</script>
4.双向绑定指令(v-model)
<!-- 双向绑定指令v-model指令,用在表单元素input输入框,textare,select下拉菜单 -->
<div id="app">
<p>用户的名字是:{{username}}</p>
<input type="text" v-model="username">
<hr>
<input type="text" :value="username">
<hr>
<select v-model="city">
<option value="">请选择城市</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">广州</option>
</select>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'dilireba',
city: '2'
}
})
</script>
4.1v-model的修饰符(.number,.trim,.lazy)
<!-- v-model的修饰符 -->
<div id="app">
<!-- 1.v-model的修饰符将用户输入的字符串转为数值类型
.number -->
<input type="text" v-model.number="n1">+<input type="text" v-model.number="n2">=<span>{{n1+n2}}</span>
<hr>
<!--2. 修饰符.trim去除两端空格 -->
<input type="text" v-model.trim="username">
<button @click="showName">获取用户名</button>
<hr>
<!-- 3.修饰符.lazy 在‘change’时而非“input”时更新 -->
<input type="text" v-model.lazy="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'dilireba',
n1: 1,
n2: 2,
},
methods: {
showName() {
console.log(`用户名是:"${this.username}"`)
}
}
})
</script>
5.条件渲染指令(v-if,v-else,v-else,v-show,)
- v-if
- v-show
5.1.v-if 和v-show 的区别
- v-if 指令会动态地创建或移除DOM 元素,从而控制元素在页面上的显示与隐藏;
- v-show 指令会动态为元素添加或移除style="display: none;" 样式,从而控制元素的显示与隐藏;
- 如果需要非常频繁地切换,则使用v-show 较好
- 如果在运行时条件很少改变,则使用v-if 较好
<!-- 条件渲染指令用来辅助开发者按需控制DOM 的显示与隐藏,v-if和v-show -->
<div id="app">
<!-- `v-if` 的原理是:每次动态创建或移除元素,实现元素的显示和隐藏.
应用场景:如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好。-->
<p v-if="flag">这是被v-if控制的元素</p>
<!--v-show 的原理是:动态为元素添加或移除 display: none 样式,来实现元素的显示和隐藏
应用场景:如果要频繁的切换元素的显示状态,用 v-show 性能会更好 -->
<p v-show="flag">这是被v-show控制的元素</p>
<hr>
<div v-if="type==='A'">优秀</div>
<div v-else-if="type==='B'">良好</div>
<div v-else-if="type==='C'">一般</div>
<div v-else>差</div>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
/* 如果flag为true,则显示被控制的元素,若为false则隐藏被控制的元素,默认为true */
flag: true,
type: 'A'
}
})
</script>
6.列表(循环)渲染指令(v-for)
- items 是待循环的数组
- item 是被循环的每一项
- 当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的DOM 元素,从而提升渲染的性能。但这种默认的性能优化策略,会导致有状态的列表无法被正确更新。
- 为了给vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲染的性能。此时,需要为每项提供一个唯一的key 属性:
6.1key 的注意事项
- ①key 的值只能是字符串或数字类型
- ②key 的值必须具有唯一性(即:key 的值不能重复)
- ③建议把数据项id 属性的值作为key 的值(因为id 属性的值具有唯一性)
- ④使用index 的值当作key 的值没有任何意义(因为index 的值不具有唯一性)
- ⑤建议使用v-for 指令时一定要指定key 的值(既提升性能、又防止列表状态紊乱)
<!-- 列表渲染指令v-for -->
<div id="app">
<table class="table table-bordered table-hover table-striped ">
<thead>
<th>索引</th>
<th>ID</th>
<th>姓名</th>
</thead>
<tbody>
<!-- 建议:只要用到了v-for指令,那么一定要绑定一个:key属性 -->
<!-- 而且,尽量把id作为key值 -->
<!--官方对key的值类型,有要求,字符串或数字类型 -->
<!-- key的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index}}</td>
<td>{{item.id }}</td>
<td>{{item.name}}</td>
</tr>
</tbody>
</table>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: '迪丽热巴'
}, {
id: 2,
name: '李四'
}, {
id: 3,
name: '张三'
}]
}
})
</script>
6.2 label的for属性
for的值等于checkbox的id属性进行绑定,可以实现点击标签label也能改变checkbox状态
<input type="checkbox" id="cb1">
<label for="cb1">男</label>
<hr>
<input type="checkbox" id="cb2">
<label for="cb2">女</label>
6.3key的内部原理
面试题:react、vue中的key有什么作用?(key的内部原理)
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
- ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
4. 开发中如何选择key?:
- 1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示, 使用index作为key是没有问题的。
7.v-cloak指令(防止闪现, 与 css 配合: [v-cloak] { display: none })
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
<style>
[v-cloak]{
display:none;
}
</style>
<!-- 引入Vue -->
</head>
<body>
<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">
new Vue({
el:'#root',
data:{
name:'迪丽热巴'
}
})
</script>
8.v-once指令(初次动态渲染后不再改变)
- 1.v-once所在节点在初次动态渲染后,就视为静态内容了。
- 2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<div id="root">
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
9.v-pre指令(被该指令用的vue不会去解析它)
- 1.跳过其所在节点的编译过程。
- 2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<div id="root">
<h2 >Vue其实很简单</h2>
<h2 v-pre>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
列表案例
<title>品牌列表案例</title>
<link rel="stylesheet" href="./lib/bootstrap.css">
<link rel="stylesheet" href="./css/brandlist.css">
</head>
<body>
<div id="app">
<!-- 卡片区域 -->
<div class="card">
<div class="card-header">
添加品牌
</div>
<div class="card-body">
<!-- 添加品牌的表单区域 -->
<!-- form 表单元素有 submit 事件 -->
<form @submit.prevent="add">
<div class="form-row align-items-center">
<div class="col-auto">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">品牌名称</div>
</div>
<input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="brand">
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2">添加</button>
</div>
</div>
</form>
</div>
</div>
<!-- 表格区域 -->
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">品牌名称</th>
<th scope="col">状态</th>
<th scope="col">创建时间</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<div class="custom-control custom-switch">
<!-- 使用 v-model 实现双向数据绑定 -->
<input type="checkbox" class="custom-control-input" :id="'cb' + item.id" v-model="item.status">
<!-- 使用 v-if 结合 v-else 实现按需渲染 -->
<label class="custom-control-label" :for="'cb' + item.id" v-if="item.status">已启用</label>
<label class="custom-control-label" :for="'cb' + item.id" v-else>已禁用</label>
</div>
</td>
<td>{{ item.time }}</td>
<td>
<a href="javascript:;" @click="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
// 用户输入的品牌名称
brand: '',
// nextId 是下一个,可用的 id
nextId: 4,
// 品牌的列表数据
list: [{
id: 1,
name: '宝马',
status: true,
time: new Date()
}, {
id: 2,
name: '奔驰',
status: false,
time: new Date()
}, {
id: 3,
name: '奥迪',
status: true,
time: new Date()
}, ],
},
methods: {
// 点击链接,删除对应的品牌信息
remove(id) {
this.list = this.list.filter(item => item.id !== id)
},
// 阻止表单的默认提交行为之后,触发 add 方法
add() {
// 如果判断到 brand 的值为空字符串,则 return 出去
if (this.brand === '') return alert('必须填写品牌名称!')
// 如果没有被 return 出去,应该执行添加的逻辑
// 1. 先把要添加的品牌对象,整理出来
const obj = {
id: this.nextId,
name: this.brand,
status: true,
time: new Date()
}
// 2. 往 this.list 数组中 push 步骤 1 中得到的对象
this.list.push(obj)
// 3. 清空 this.brand;让 this.nextId 自增 +1
this.brand = ''
this.nextId++
}
},
})
</script>
</body>
</html>
Vue中的数据代理
1.Object.defineProperty方法
/* 双向绑定原理数据劫持 v-model */
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,
get函数(getter)就会被调用,
且返回值就是age的值
get(){
console.log('有人读取age属性了')
return number
},
//当有人修改person的age属性时,
set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
// console.log(Object.keys(person))
console.log(person)
2.数据代理:通过一个对象代理对另一个对象中属性的操作
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
3.Vue的数据代理
- Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
- Vue中数据代理的好处:
更加方便的操作data中的数据
- 基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
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修改数组中的某个元素一定要用如下方法:
- 1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- 2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
/* 修改对象 */
addSex(){
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:'jack',age:70})
},
updateFirstFriendName(){
this.student.friends[0].name = '张三'
},
/* 修改数组 */
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
// this.student.hobby.splice(0,1,'开车')
// Vue.set(this.student.hobby,0,'开车')
this.$set(this.student.hobby,0,'开车')
},
removeSmoke(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '抽烟'
})
}
}
})
</script>