- 当我们把vue引入的时候,实际上只引入了一个对象,就是 Vue 对象
- 好比引入 jQuery 的时候,实际上只引入了'$'或者'jQuery'对象
- 删除Vue的提示信息
Vue.config.productionTip = false;
hello 示例
<head>
......
<script src="js/v2.6.10/vue.js"></script> <!--引入vue-->
</head>
<body>
<div id="container">
{{name}}
</div>
<script type="text/javascript">
var app = new Vue({
el:'#container',
data:{
name:'Jim Green'
},
})
</script>
</body>
- 想让vue工作,就必须创建一个vue实例,且要传入一个配置对象
- container容器里面的代码依然符合html规范,只不过混入了一些特殊的vue语法
- container容器里面的代码被称为'vue模板'
分析'Hello'示例
- Vue对象和容器之间的关系 只能是'一对一关系'(一个Vue实例不会关照多个容器)
<html>
<head>
<script src="js/v2.6.10/vue.js"></script>
</head>
<body>
<div class="container">
{{name}} <!--有效果-->
</div>
<div class="container">
{{name}} <!--不会生效-->
</div>
<script type="text/javascript">
var app = new Vue({
el:'.container',
data:{
name:'Jim Green'
},
})
</script>
</body>
</html>
- 区分"js表达式"和'js代码(语句)'
- 表达式: 一个表达式会产生一个值,可以放在任何需要该值的地方
- 简单讲,就是有返回值
- 例如
- a
- a + b
- demo(1)
- x === y ? 'a' : 'b'
- js代码(语句)
- if(){...}
- for(){...}
-
vue-devtools的使用: Root 指的就是管理这个容器的Vue实例(上手体验一下)
-
小结
- 真实开发中,只有一个Vue实例,并且会配合着'组件'(就是'小弟',帮助实例干活)一起使用
- {{xxx}}, xxx中要写"js表达式",且xxx可以自动读取到data中的所有属性
- 一旦 data 中的数据发生改变,那么模板中用到该数据的地方也会自动更新
模板语法
-
插值语法: {
-
功能:解析标签体内容
-
写法: {{xxx}},xxx 是js表达式,且可以直接读到data中的所有属性
-
-
指令语法: v-xxx
-
功能: 用于解析标签(标签属性/标签体内容/绑定事件)
-
举例: v-bind:href='xxx',xxx同样要写js表达式,也可以直接读到data中的所有属性
-
<body>
<div id="container">
{{name}} <br> <!--插值语法-->
<a v-bind:href="url">百度链接</a> <!--指令语法-->
<a :href="new_url" :x="hello">新浪链接</a> <!--这里可以简写(仅限于v-bind)-->
<div :msg=company.name :address=company.address></div> <!--把数据绑定在一个对象上面-->
</div>
<script type="text/javascript">
var app = new Vue({
el:'#container',
data:{
name:'Jim Green',
url:'http://www.baidu.com',
new_url:'http://www.sina.com',
hello:'world',
company:{
name:'aclas',
address:'yyyyy'
}
},
})
</script>
</body>
数据绑定
-
单向绑定
- 使用 v-bind: 数据只能从data流向页面
-
双向绑定
-
使用 v-model: data和页面数据之间,可以互相影响
-
局限性:只能应用在 表单类元素/输入类元素上面
-
-
基础demo
......
<body>
<div id="container">
<form action="" method=""> <!--单向绑定-->
用户名: <input type="text" name="" id="" :value="name" /><br>
<!--双向绑定-->
<!--<......简写(默认就是value,所以value可以不用写): v-model="email" />-->
邮箱: <input type="email" name="" id="" v-model:value="email" />
</form>
<!--错误写法:这个属性不会生效,只能应用在表单类元素上面-->
<h2 v-model:x="email">Hello</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:'#container',
data:{
name:'username',
email:'agassics@sina.com'
},
})
</script>
</body>
......
vue 实例大概可以看成由三部分组成
- "$"开头的属性,例如 $attrs,$children...: 是给'开发者'调用的
- "_"开头的属性,例如 _c,_data...: 是vue的底层代码,一般无需理会
- "Prototype": 原型,和js对象一样
vue 实例配置对象中,'el'和'data'的两种写法
- 'el'的另外一种写法:以下写法,一模一样的效果
......
<body>
<div id="container">
Title: {{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
// el:'#container',
data:{
name:'Jim Green',
},
})
// 原型里面,有$mount()这个函数
vm.$mount('#container')
</script>
</body>
- 上述示例小改一下,使用定时器,效果一开始是双括号,然后才变成data定义的值
......
<body>
<div id="container">
Title: {{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
// el:'#container',
data:{
name:'Jim Green',
},
})
setTimeout(()=>{ // 新增定时器
vm.$mount('#container')
},1000)
</script>
</body>
-
data的第二种写法(函数式): 写成函数的形式,然后返回一个配置对象
-
第一种写法也可以称为'对象式'
-
细节问题:函数一定要写成 function 的形式(this指向Vue实例)
若写成'箭头函数'形式,this指向window对象 -
用到'组件'的时候,data一定要写'函数式'(function形式)
-
<body>
<div id="container">
Title: {{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
// data:{
// name:'Jim Green'
// }
data:function(){ // 该函数必须返回一个'配置对象'
console.log(this) // Vue实例
return {
name:'Jim Green'
}
}
})
</script>
</body>
- 箭头函数this测试
......
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:()=>{
console.log(this); // this指向window对象
return {
name:'Jim Green'
}
}
})
</script>
MVVM 模型
-
M:模型(Model),对应 data 中的数据
-
V:视图(View),模板
-
VM:视图模型(ViewModel),Vue实例对象
-
简单理解: Vue实例 就是后端js逻辑和前端模板之间的桥梁,Vue实例就是'中间商'
data属性解析
-
data中的所有属性,变量vm都有
-
而 vm 身上所有的属性,以及Vue原型上的所有属性,在Vue模板中,都可以直接使用
......
<body>
<div id="container">
Title: {{name}}
Address: {{address}} // data里面的
Other1: {{$children}}
Other2: {{_c}} // vm 里面的
Other3: {{$emit}} // 原型里面的
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:function(){
return {
name:'Jim Green',
address:'Xiamen'
}
}
})
</script>
</body>
......
Object.defineProperty: 给对象增加属性
......
<script type="text/javascript">
var person = {
name:'JimGreen',
age:18
}
Object.defineProperty(person,'sex',{ // 给 person 新增 sex 属性值
value:'male'
})
</script>
......
- 返回结果
- name,age(颜色标识一致)
sex(颜色标识和上面不一样): 'sex' 属性不可被'枚举'(就是不可被'遍历')
- console.log(Object.keys(person)) 返回结果 ['name','age']
- 如何让 sex 参与'枚举'呢?配置对象添加一个参数即可
Object.defineProperty(person,'sex',{
value:'male',
enumerable:true // 添加之处
})
- console.log(Object.keys(person)) 返回结果 ['name','age','sex']
- 此时,sex 属性是无法被修改的,测试如下:
>person.sex = 'female'
>'female'
>person
>{name: 'JimGreen', age: 18, sex: 'male'} // 无法被修改
- 同样的解决方式,配置一个参数即可
Object.defineProperty(person,'sex',{
value:'male',
enumerable:true,
writable:true, // 新增
})
- 此时,sex 属性是无法被删除的,测试如下:
>delete person.sex
>false
- 同样的解决方式,配置一个参数即可
Object.defineProperty(person,'sex',{
value:'male',
enumerable:true,
writable:true,
configurable:true // 新增
})
>delete person.sex
>true
- 小结:
- enumerable:true, 是否可以被枚举,默认 false
- writable:true, 是否可以被修改,默认 false
- configurable:true, 是否可以被删除,默认 false
- 双向绑定对象属性: get/set
<body>
<div id="container">
1111
</div>
<script type="text/javascript">
var num = 18
var person = {
name:'JimGreen',
sex:'male',
}
// 把 num值 赋予 person.age 属性值
Object.defineProperty(person,'age',{
get:function(){ // 可以简写成 get(){......}
return num
}
}
})
</script>
</body>
- 现在,无论如何修改 num值, person.age值也会跟着变
- 小结: 当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
- set 方法: 当修改了person的age属性时,set函数会被调用,且会受到修改的值
<script type="text/javascript">
var num = 18
var person = {
name:'JimGreen',
sex:'male',
}
Object.defineProperty(person,'age',{
get(){
return num
},
set(value){
num=value;
}
})
</script>
// 测试
>person.age = 22
>22
>num // 跟着变
>22
数据代理
-
定义: 通过一个对象,代理 对另外一个对象的属性操作(读/写)
-
demo演示
......
<script type="text/javascript">
var num1 = {
x:100
}
var num2 = {
y:200
}
Object.defineProperty(num2,'x',{
get(){
return num1.x
},
set(value){
num1.x=value
}
})
</script>
......
- Vue实例中的'_data',就是js中,Vue的data
......
<body>
<div id="container">
{{name}}--{{age}}
</div>
<script type="text/javascript">
// 定义一个对象
var data = {
name:'JimGreen',
age:18
}
var vm = new Vue({
el:'#container',
data:data // 赋值
})
</script>
</body>
// 控制台验证
>vm._data === data
>true
- 小结:
- Vue中的数据代理:
- 通过vm对象来代理data对象中属性的操作(读/写)
- 好处: 更加方便的操作data中的数据
- 原理:
- 通过 Object.defineProperty()把data对象中所有的属性添加到vm上
- 为每一个添加到vm上的属性,都指定一个 gettter/setter
- 在 gettter/setter 内部去操作data中对应的属性
绑定事件
- 点击事件示例
......
<body>
<div id="container">
{{name}}--{{age}}
<!--绑定点击事件-->
<button type="button" v-on:click="showMsg">点我提示</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(){ // 执行事件逻辑
alert('Hello!')
}
}
})
</script>
</body>
......
-
自动传参: event 参数
- 作用: 存储本次事件的全部信息,通过它可以获取事件的各种信息
<body>
<div id="container">
{{name}}--{{age}}
<!--绑定点击事件-->
<button type="button" v-on:click="showMsg">点我提示</button>
<!--简写形式-->
<!--<button type="button" @click="showMsg">点我提示</button>-->
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(event){ // 这个是js自动帮我们传过来的
// alert('Hello!')
// 执行结果: '点我提示'
console.log(event.target.innerText)
}
}
})
</script>
</body>
-
methods 里面的方法,this指向问题
- 结论:methods里面,不要写成箭头函数的形式,因为this指向问题
......
methods:{
showMsg(event){
console.log(this) // 结果: Vue实例
}
}
......
methods:{
showMsg:(event)=>{
console.log(this) // 结果: 指向window对象
}
}
- 传参的写法
......
<div id="container">
{{name}}--{{age}}
<!--关注之处-->
<button type="button" @click="showMsg(666)">点我提示</button>
</div>
......
<script type="text/javascript">
var vm = new Vue({
......
methods:{
// 形参传进来
showMsg:(number)=>{
console.log(number) // 结果: 666
}
}
})
</script>
- 此时,有一个问题: event 参数丢失
......
methods:{
showMsg:(number,a,b,c)=>{
console.log(number,a,b,c) // 结果: 666 undefined undefined undefined
}
}
- 解决办法: 使用 Vue 提供的形参'$event'(不要去修改它的名称)
......
<!--传入 $event-->
<button type="button" @click="showMsg(666,$event)">点我提示</button>
......
methods:{
showMsg:(number,a,b,c)=>{
console.log(number,a,b,c) // 结果: 666 event对象 undefined undefined
}
}
-
小结:
-
使用'v-on:xxx' 或 '@xxx'绑定事件,其中xxx是事件名
-
@click="showMsg"和@click="showMsg($event)"效果一致,但是后者支持传参
-
事件修饰符
- prevent 修饰符引入场景: 当标签触发点击事件的时候,页面同时被带走
<body>
<div id="container">
<h4>测试</h4>
<a href="http://www.baidu.com" @click="showMsg">点我提示信息</a>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(){
alert('Hello!')
}
}
})
</script>
......
- 现在,阻止页面被带走,可以这么写
......
methods:{
showMsg(event){
event.preventDefault() // 阻止页面被带走
alert('Hello!')
}
}
......
<!--关注之处,添加修饰符-->
a href="http://www.baidu.com" @click.prevent="showMsg">点我提示信息</a>
methods:{
showMsg(event){
// event.preventDefault() 这句不用了
alert('Hello!')
}
}
- 常用修饰符
- prevent: 阻止默认事件
- stop: 阻止事件冒泡
- once: 限制事件只能触发一次
- capture: 使用事件的"捕获模式"
- self: 只有 event.target 是当前操作的元素,才触发事件
- 冒泡事件解析
<body>
<div id="container">
......
<!--容器和按钮都绑定点击事件,为了方便,绑定同一个点击事件-->
<div class="box" @click="test">
<button type="button" @click="test">测试</button>
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
......
methods:{
......
test(){
alert('hello!!!')
}
}
})
</script>
</body>
- 结果: 点击按钮以后,弹窗两次,因为事件向上冒泡了,导致容器里面的点击事件也被触发
- 解决办法
......
ethods:{
......
test(event){
event.stopPropagation() // 阻止事件冒泡
alert('hello!!!')
}
}
............
<!--使用修饰符,可以添加prevent修饰符: @click.stop.prevent="test"-->
<button type="button" @click.stop="test">测试</button>
- once: 事件只能执行一次
......
<div> <!--添加once修饰符-->
<button type="button" @click.once="test1">测试1</button>
</div>
......
methods:{
......
test1(){
alert('hhh')
}
}
- self: 只有 event.target 是当前操作的元素,才触发事件,把之前的示例小改一下
<!--绑定同一个点击事件-->
<div class="box" @click="test">
<button type="button" @click="test">测试</button>
</div>
......
test(event){
console.log(event.target) // <button type="button">测试</button> 输出两遍
}
......
- 可以看出,冒泡的时候, event.target始终指向内层的按钮
若在外层容器使用 self 修饰符,由于 event.target 不属于容器自身,所以点击事件会失效
从而也达到'阻止冒泡'的效果
......
<!--添加self修饰符,阻止冒泡-->
<div class="box" @click.self="test">
<button type="button" @click="test">测试</button>
</div>
键盘事件
- 引入demo: 给"input框"绑定键盘事件,获取用户输入的值
<body>
<div id="container">
<!--绑定keyup-->
<input type="text" @keyup="showMsg" />
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(event){
console.log(event.target.value) // 输出input框的值
},
}
})
</script>
</body>
- 把上述demo小改一下,当用户按下'回车'的时候,才显示'input框'的值
......
methods:{
showMsg(event){
// 回车键的数字编码是 13,当不是它的时候,就返回空值,满足需求
if(event.keyCode !== 13) return
console.log(event.target.value)
},
}
......
- 上述示例还可以这么写
- 把 if 判断注释掉,Vue模板里面,修改一句('.enter'): <input type="text" @keyup.enter="showMsg" />
- 还有使用数字编码(不推荐这种写法)
<input type="text" name="" id="" value="" @keydown.13="showMsg" />
- 小结:
- vue 中常用的按键别名
- 回车: enter
- 删除: delete(捕获'删除键'和'退格键')
- 退出: esc
- 空格: space
- 换行: tab
- 上: up
- 下: down
- 左: left
- 右: right
- vue 未提供别名的按键,可以使用按键原始的key值去绑定,但要注意转为"kebab-case"(短横线命名)
- 系统修饰键(用法特殊): ctrl,alt,shift,meta(window键)
- 配合keyup使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
- 配合keydown使用: 正常触发事件
- 也可以使用keyCode去指定具体的按键(不推荐,因为数字编码不是通用的...)
- Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名
- js提供查看 按键和按键码的API
......
<body>
<div id="container">
<!--绑定事件-->
<input type="text" name="" id="" value="" @keyup="showMsg" />
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(event){
console.log(event.key,event.keyCode) // Enter 13
},
}
})
</script>
</body>
......
- Caps Lock 按键 demo 演示: 只有按下这个键,才显示input框的值
......
<div id="container">
<!---注意Vue提供的这种格式->
<input type="text" name="" id="" value="" @keyup.caps-lock="showMsg" />
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
showMsg(event){
console.log(event.target.value)
},
}
})
</script>
- Tab 按键 demo 演示: 只有按下这个键,才显示input框的值
......
<!--无效果,因为按下的瞬间,已经失去焦点,keyup事件不会被触发-->
<input type="text" name="" id="" value="" @keyup.tab="showMsg" />
......
- 解决办法: 使用 keydown事件即可
......
<input type="text" name="" id="" value="" @keydown.tab="showMsg" />
......
- 自定义按键的'别名'
......
<body>
<div id="container"> <!--应用-->
<input type="text" name="" id="" value="" @keydown.huiche="showMsg" />
</div>
<script type="text/javascript">
Vue.config.keyCodes.huiche = 13; // 加这句
.......
</script>
</body>
- 自定义'快捷键'
<body>
<div id="container"> <!--ctrl+y才生效-->
<input type="text" name="" id="" value="" @keydown.ctrl.y="showMsg" />
</div>
<script type="text/javascript">
......
methods:{
showMsg(event){
console.log(event.target.value)
},
}
......
</script>
</body>
计算属性
- 引入场景: 姓名,年龄案例之插值语法
......
<body>
<div id="container">
姓名: <input type="text" name="" id="" v-model="name" /> <br>
年龄: <input type="text" name="" id="" v-model="age" /> <br>
小结: <span>{{name + '--' +age}}</span>
</div>
<script type="text/javascript">
// Vue.config.keyCodes.huiche = 13;
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{
}
})
</script>
</body>
......
- 上述示例可以小改成这样
......
<div id="container">
......
小结: <span>{{name}} -- {{age}}</span> <!--改进之处-->
</div>
- 改造上述示例,"小结"部分改成'只保留前三个字符'
<div id="container">
......
小结: <span>{{name.slice(0,3)}} -- {{age}}</span> <!--修改之处-->
</div>
......
- 虽然实现了需求,但是这种写法不够简洁,假设有更多逻辑的话,代码就更复杂
- 把上述复杂需求,使用方法来实现
......
<body>
<div id="container">
......
<!--这里一定要加括号,否则就是返回'函数对象'-->
小结: <span>{{showMsg()}}</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
methods:{ // 修改之处
showMsg(){
return this.name + '--' + this.age
}
}
})
</script>
</body>
......
- 如果嫌弃使用方法的方式,代码还是不够简洁(多加了一对括号),就可以使用'计算属性'来实现
......
<body>
<div......
<!--是'属性',加括号会报错-->
小结: <span>{{showMsg}}</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'JimGreen',
age:18
},
computed:{ // 属性,配置成对象,对象里面配置key,返回get()方法即可
showMsg:{
get(){
// 这里的this被Vue作了优化,指向'Vue实例'
return this.name + '--' + this.age
}
}
}
})
</script>
</body>
......
- get()的作用: 当有人读取了 showMsg 时,get就会被调用,且返回值就作为 showMsg 的值
- 小结: get()什么时候被调用
- 初次调用 showMsg 的时候
- 所依赖的数据发生变化,get()会再次被调用,从而实现数据的更新
- 注意事项: 为什么不写在methods?原因是 methods太过活跃
- 每调用一次 methods,里面的逻辑就执行一次
- 而 computed 不一样,有'缓存',不会这么活跃,从而节省性能开销
-
set()介绍
- 什么时候被调用: 当 showMsg 的值被修改的时候
......
<script>
var vm = new Vue({
el:'#container',
data:{
name:'Jim Green',
age:18
},
computed: {
showMsg:{
get(){
return this.name + '--' + this.age
},
set(value){ // 关注之处
console.log('set被调用了',value)
}
}
},
})
Vue.config.productionTip = false;
</script>
......执行结果:
>vm.showMsg = 'king' // 修改值,触发了set()
index.html:30 set被调用了 king
- 当我们修改 showMsg 的值时,也修改 name 和 age,从而实现页面数据实时刷新
......
computed: {
showMsg:{
get(){
......
},
set(value){ // value 就是 showMsg 的值
var arr = value.split('--')
this.name=arr[0]
this.age=arr[1]
}
}
},
- 计算属性小结
-
定义: 要用的属性不存在,要通过已有属性计算得来
-
原理: 底层借助了 Object.defineproperty 方法提供的 getter 和 setter
-
优势: 相比 methods,由于有了'缓存机制',效率更高
-
注意事项:
-
"计算属性"最终会出现在vm上,直接读取使用即可
-
如果'计算属性'要被修改,那必须写set()去实现,且set中要引起计算时依赖的数据发生变化
-
-
计算属性的简写形式
-
前提条件:只读取数据,而不修改数据
-
即只实现get,而放弃set
-
......
computed: {
// showMsg:{
// get(){
// return this.name + '--' + this.age
// },
// set(value){
// var arr = value.split('--')
// this.name=arr[0]
// this.age=arr[1]
// }
// }
showMsg(){ // 写成函数的形式即可
return this.name + '--' + this.age
}
},
监视属性
- 引入场景:天气预报案例分析
......
<body>
<div id="container">
<!--三元运算符解决问题,只不过此时是写死的-->
<h2>今天天气很{{isHot ? '炎热' : '凉爽'}}</h2>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true
},
})
Vue.config.productionTip = false;
</script>
</body>
- 把上述示例修改成用'方法'/'属性'实现
......
<div id="container">
<!--计算属性-->
<h2>今天天气很{{info}}</h2>
<!--绑定点击事件-->
<button @click="changeWeather">改变天气</button>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true
},
computed: {
info() {
// data的值一旦改变,计算属性跟着变
return this.isHot?'炎热':'寒冷'
}
},
methods: {
changeWeather() {
// 修改 data的值
this.isHot = !this.isHot
}
},
......
-
@click='methods'的简写形式
- 当代码逻辑很简单的时候,可以考虑这种写法
......
<div id="container">
<h2>今天天气很{{info}}</h2>
<!--把简单代码逻辑写在这里-->
<button @click="isHot = !isHot">改变天气</button>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true
},
computed: {
info() {
return this.isHot?'炎热':'寒冷'
}
},
// 注释掉 methods
- 简写的时候,注意以下示例
......
<!--window对象不可以省略-->
<button @click="window.alert(111)">改变天气</button>
......
var vm = new Vue({
el:'#container',
data:{
isHot:true,
// window:window 这种写法也可以
window // 简写形式
},
监视属性
- 引入场景
......
<body>
<div id="container">
<h2>今天天气很{{info}}</h2>
<button @click="isHot = !isHot">改变天气</button>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true,
},
computed: {
info() {
return this.isHot?'炎热':'寒冷'
}
},
watch:{
isHot:{ // 配置哪个data被监视
handler(){ // handler什么时候被调用?当isHot发生改变的时候
console.log('isHot被修改了!')
}
}
}
})
Vue.config.productionTip = false;
</script>
</body>
- handler(newValue,oldValue)的真正用法是这样
......
watch:{
isHot:{
// 可以获取到 新的值 以及 以前的值
handler(newValue,oldValue){
console.log('isHot被修改了!',newValue,oldValue)
}
}
}
......
- 这里还可以增加一个配置项'immediate',初始化时,让hander()调用一次
......
watch:{
isHot:{
immediate:true, // 即时 isHot 的值没有被修改,handler也会调用一次
handler(newValue,oldValue){
// oldValue 为 undefined
console.log('isHot被修改了!',newValue,oldValue)
}
}
}
- 监视属性的另外一种写法
......
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true,
},
computed: {
info() {
return this.isHot?'炎热':'寒冷'
}
},
})
// vm.$watch 传入两个参数,"被监视的属性"和'配置项'
vm.$watch('isHot',{
immediate:true,
handler(newValue,oldValue){
console.log('isHot被修改了!',newValue,oldValue)
}
})
Vue.config.productionTip = false;
</script>
- 小结
- 当被监视的属性发生变化时,回调函数(handler)自动被调用,执行函数体的逻辑
- 被监视的属性必须是存在的,才能进行监视
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
深度监视--引入场景: 修改a的值
......
<body>
<div id="container">
<h2>今天天气很{{info}}</h2>
<button @click="isHot = !isHot">改变天气</button>
<hr>
<!--注意语法-->
<h2>a的值是{{numbers.a}}</h2>
<button @click="numbers.a++">改变值</button>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
isHot:true,
numbers:{
a:1,
b:2
}
},
})
</body>
- 此时,如何监视a值,可以这么写
......
watch:{
// 监视多级结构中,某个属性的变化
'numbers.a':{ // 这里不能写成 numbers.a:{...} 会报语法错误
handler(newValue,oldValue){
console.log('a被修改了!',newValue,oldValue)
}
}
}
- 若b值也重复a值的结构,此时该如何监视numbers?要加一个配置
......
<div id="container">
<h2>a的值是{{numbers.a}}</h2>
<button @click="numbers.a++">改变值</button>
<hr>
<h2>b的值是{{numbers.b}}</h2>
<button @click="numbers.b++">改变值</button>
</div>
......
<script>
var vm = new Vue({
el:'#container',
data:{
numbers:{
a:1,
b:2
}
},
watch:{
numbers:{
deep:true, // 监视多级结构中,所有属性的变化
handler(newValue,oldValue){
console.log('numbers被修改了!',newValue,oldValue)
}
}
}
})
</script>
- 小结
- Vue中的watch默认不检测对象内部值的改变(一层可以)
- 配置 deep=true 可以监测对象内部值的改变(根据值的层级结构来决定是否配置此项)
-
watch 的简写形式
- 前提条件,当只需要 handler() 而不需要其他配置项的时候,就可以简写
......
watch:{
// isHot:{
// immediate:true,
// handler(newValue,oldValue){
// console.log('isHot被修改了!',newValue,oldValue)
// }
// },
// 简写形式
isHot(newValue,oldValue){
console.log('isHot被修改了!',newValue,oldValue)
},
}
......
- 或者可以这么写
// vm.$watch('isHot',{
// immediate:true,
// handler(newValue,oldValue){
// console.log('isHot被修改了!',newValue,oldValue)
// }
// })
vm.$watch('isHot',function(newValue,oldValue){ // 传入一个function
console.log('isHot被修改了!',newValue,oldValue)
})
计算属性 和 监视属性 的区别
- 引入示例,先把之前的例子,使用 监视属性 实现一次
......
<body>
<!--骨架-->
<div id="container">
姓: <input type="text" v-model="firstName"><br>
名: <input type="text" v-model="lastName"><br>
<span>全名: {{fullName}}</span>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{ // 绑定三个值
firstName:'Jim',
lastName:'Green',
fullName:'Jim Green'
},
watch:{ // 监视并获取新值,给 fullName 重新赋值
firstName(val){
this.fullName = val + this.lastName
},
lastName(val){
this.fullName = this.firstName + val
}
}
})
Vue.config.productionTip = false;
</script>
</body>
- 我们使用 计算属性 再实现一次
......
<body>
<div id="container">
姓: <input type="text" v-model="firstName"><br>
名: <input type="text" v-model="lastName"><br>
<span>全名: {{fullName}}</span>
</div>
<script>
var vm = new Vue({
el:'#container',
data:{
firstName:'Jim',
lastName:'Green',
},
computed: {
fullName() {
return this.firstName + this.lastName
}
},
})
Vue.config.productionTip = false;
</script>
</body>
......
- 可以看出,使用 计算属性 明显更为方便,然而,真实的情况的确如此么?
- 现在有这样的需求,'全名'那边要等1秒以后再显示,使用 监视属性 可以这么写
......
watch:{
firstName(val){
// 调用定时器,且一定要使用箭头函数,写的成普通函数是不会赋值成功的
setTimeout(()=>{
console.log(this) // Vue实例
this.fullName = val + this.lastName
},1000)
// setTimeout(function(){
// console.log(this) // 指向 window对象,显然赋值代码无法成功
// this.fullName = val + this.lastName
// },1000)
},
lastName(val){
this.fullName = this.firstName + val
}
}
- 但是,计算属性就无法实现
......
computed: { // 关注返回值,定时器的返回值是 number,显然是不满足需求的
fullName() {
console.log('fullName 被调用了')
setTimeout(()=>{
return this.firstName + this.lastName
},1000)
return 800
}
},
......
- 计算属性 和 监视属性 区别小结
- computed 能完成的功能, watch 也可以完成
- watch 能完成的功能, computed 不一定能完成,例如 watch 可以进行异步操作
- 注意事项
- 被Vue管理的函数,最好写成普通函数的形式,this指向才是Vue实例
- 不被Vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成箭头函数的形式,this指向才是Vue实例
使用 Vue 修改css样式
- 引入场景:单击文字,改变文字颜色
......
<style type="text/css">
.normal {
color: yellow;
}
.sad {
color: skyblue;
}
</style>
......
<body>
<div id="container" class="normal" @click="changeMood"> <!--给定默认样式并绑定点击事件-->
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色'
},
methods:{
changeMood(){
document.querySelector('#container').className = 'sad' // 单击改变文字颜色
},
}
})
</script>
</body>
- 上述写法,使用原生js去实现,现在,我们使用 vue 的写法实现相同的功能
......
<body> <!--动态绑定class属性,给定默认值-->
<div id="container" :class="mood" @click="changeMood">
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
mood:'normal' // 默认值
},
methods:{
changeMood(){
this.mood = 'sad' // 更改默认值
},
}
})
</script>
</body>
- 优化上述写法,每点击一次文字,文字颜色就随机生成
......
<style type="text/css">
.normal {
color: yellow;
}
.sad {
color: skyblue;
}
.red {
color: red;
}
</style>
......
<body>
<div id="container" :class="mood" @click="changeMood">
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
mood:'normal'
},
methods:{
changeMood(){
var arr = ['normal','sad','red'];
var index = Math.floor(Math.random()*3); // 随机生成索引
this.mood = arr[index]; // 赋值
},
}
})
</script>
</body>
-
小结
-
:class="mood"
-
'类名'的字符串写法,适用于'类名'不确定,需要动态指定
-
-
如果想同时应用多个样式,可以把class值写成'数组'的形式
- 适用场景: 类名不确定,类名的个数也不确定
......
<style type="text/css">
.normal {
color: yellow;
}
.sad {
background-color: skyblue;
}
.red {
border: 1px solid purple;
}
</style>
......
<body> <!--值为数组-->
<div id="container" :class="arr" @click="changeMood">
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
arr:['normal','sad','red'] // 把类名写在这边
},
methods:{
......
}
})
</script>
</body>
- 还可以把数组直接写在class值
......
<body>
<div id="container" :class="[a,b,c]" @click="changeMood">
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
a:'normal',
b:'sad',
c:'red'
},
methods:{
......
}
})
</script>
</body>
......
- 注意事项:如果class值不包裹在'[]'里面,那么此时只显示 class='normal'
<div id="container" :class="a,b,c" @click="changeMood"> <!--只有 a样式 生效-->
-
class值的最后一种写法---对象式
- 适用场景: 类名确定,类名个数也确定,自己决定用不用
......
<!--class值为一个'配置对象'-->
<div id="container" :class="classObj" @click="changeMood">
{{name}}
</div>
......
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
classObj:{
normal:true, // 只适用前两种样式,最后一种不要
sad:true,
red:false
}
},
- 使用vue,实现行内式的css样式
......
<!--动态绑定style属性-->
<div id="container" :style="styleObj" @click="changeMood">
{{name}}
</div>
......
var vm = new Vue({
el:'#container',
data:{
name:'心情颜色',
styleObj:{
fontSize:'60px' // 设置样式
}
},
-class值可以写成数组的形式
......
<!--数组成员为一个个对象-->
<div id="container" :style="[styleObj1,styleObj2]" @click="changeMood">
{{name}}
</div>
......
data:{
......
styleObj1:{
fontSize:'40px'
},
styleObj2:{
color:'orange'
}
},
- 绑定样式小结
- class样式
- 写法: class='xxx',xxx可以是字符串,对象,数组
- 字符串写法适用于: 类名不确定,要动态获取
- 对象写法适用于: 要绑定多个样式,名字不确定,个数也不确定
- 数组写法适用于: 要绑定多个样式,名字确定,个数确定,但不确定用不用
- sytle样式
- :style="{fontSize: xxx}"其中xxx是动态值
- :sytle="[a,b]",其中 a,b是样式对象
条件渲染指令(v-show和v-if)
-
v-if:项目的切换频率低,推荐使用
-
v-show: 项目的切换频率高,推荐使用
......
<div id="container" v-show="false"> <!--不会显示name-->
你好,{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'Jim Green',
}
})
</script>
- 通过调试可以发现,DOM节点还是存在的,只不过设置了display:none.
- 把上述示例换成 v-if
......
<div id="container" v-if="false"> <!--不会显示name-->
你好,{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
name:'Jim Green',
}
})
</script>
- 通过调试可以发现,没有DOM节点了,项目本身被删得干干净净...
- 小案例,依据number的值显示不同的内容
......
<body>
<div id="container">
{{number}}
<button type="button" @click="number++">改变number值</button>
<div v-if="number===1">111</div>
<div v-if="number===2">222</div>
<div v-if="number===3">333</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#container',
data:{
number:0
}
})
</script>
</body>
- 小改demo,变成 v-if,v-else-if
......
<div id="container">
{{number}}
<button type="button" @click="number++">改变number值</button>
<div v-if="number===1">111</div> <!--满足任何一个条件以后,都不会再向下判断-->
<div v-else-if="number===2">222</div>
<div v-else-if="number===3">333</div>
</div>
- 再次小改demo,变成 v-if,v-else-if,v-else
<div v-if="number===1">111</div>
<div v-else-if="number===2">222</div>
<div v-else-if="number===3">333</div>
<div v-else>无穷无尽...</div> <!--页面加载完就会显示,因为 number 不满足前面所有的条件-->
- 注意事项: v-if,v-else-if,v-else 的结构必须是连续的,若中途被打断,那么前面的判断会生效,后面的判断无效(后面白写)
<div id="container">
{{number}}
<button type="button" @click="number++">改变number值</button>
<div v-if="number===1">111</div>
<div v-else-if="number===2">222</div>
<div>......</div> <!--打断结构,后面的判断无效-->
<div v-else-if="number===3">333</div>
<div v-else>无穷无尽...</div>
</div>
-
template 元素介绍:临时元素,不会破坏原有的结构
- 注意事项:只能搭配 v-if 使用(搭配v-show不会生效)
- 因为v-show是通过display来控制标签进行渲染的,但是template 标签在vue解析后是不会显示在页面上的 ,是虚拟Dom ,所以不可以,v-if是条件渲染,只要满足v-if后的条件就可以完成渲染
......
<!--<template v-show="false">--> <!--不会生效-->
<template v-if="false">
<div>111</div>
<div>222</div>
<div>333</div>
</template>
- 注意事项: 使用v-if元素可能无法获取到,而使用v-show元素一定可以获取到