一、初识Vue
-
要让vue工作,就必须创建一个vue实例,且要传入一个配置对象;
-
root容器里面代码还是符合html规范,只不过混入了一些特殊的vue语法
-
root容器里面的代码成为vue模板:先有容器后有vue实例
-
容器与vue实例是1对1关系,一个容器只能支持Vue实例
-
区分js表达式与js代码
js表达式:一个表达式会生成一个值,可以放在人和需要一个值的地方
{{js表达式}}->{{1+1}},{{函数方法调用}}
<!-- 准备一个容器(模板) -->
<div id="root">
<h1>你好,{{name}},{{age}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;//直至vue在启动时生产提示
new Vue({//容器与vue实例建立连接
el: '#root',//el用于指定当前vue实例为哪个容器服务,之通常为css选择器字符串
data:{//data中用于存储数据,数据共el锁只当的容器去使用,值我们先用对象
name: 'chen',
age: 18
}
});
</script>
二、模板语法
-
v-bind:属性名
由此vue会把属性名当作一个js表达式,从data对象中取数据 -
v-bind可以简写为
:
-
vue模板语法有2大类
-
插值语法:
功能: 用于解析标签体内容
写法: {{xxx}},xxx是js表达式,且可以直接读取到data中所有属性
-
指令写法:
功能:用于解析标签,包括:标签属性、标签体内容、绑定事件...
eg: v-bind:href="xxx"或者简写:href="xxx",xxx同样是js表达式,可以取到data中所有属性
备注: vue有很多指令,形式都是:v-???
-
-
具有多层级
<div id="root"> <h1>插值语法,{{name}}</h1> <h2>指令语法<a v-bind:href="url" v-bind:x="hello">博客链接</a></h2> <h2>v-bind简写 <a :href="url" :x="hello">这也是博客链接</a></h2> <h2>学校名称是{{school.name}}</h2> </div> <script> new Vue({ el: '#root', data:{ name:'陈皮', url: 'http://www.randycout.com', hello: 'v-bind可以跟任何属性', school: { name: '清华' } } }) </script>
三、el和data的两种写法
-
el的两种写法
-
new Vue的时候写el属性
new Vue({ el: '#root' })
-
先创建Vue实例,然后再通过app.$mount('#root')指定el的值
vm.$mount('#root');//el的第二种写法,这种写法更加灵活
-
-
data的两种写法
-
对象式
new Vue({ data: { name: 'chen' } })
-
函数式:创建组件时必须用函数式
new Vue({ data:function(){ return{ name: 'chen' } } })
-
四、数据绑定
Vue中有两种数据绑定方式:
1. `v-bind`(单项绑定):数据只能从vue实例的data中流向页面
- 可以简写为成 `:属性`
2. `v-model`(双向绑定):数据可以在data和页面中双向流动
- 双向绑定一般都应有在表单元素上,eg:input,select
- `v-model:value`可简写成`v-mode`,默认收集的就是元素`value`值
<!-- 容器,或者说是模板 -->
<div class="root">
<input type="text" v-bind:value="name">
<input type="text" v-model:value="name">
<!-- 简写效果 -->
<input type="text" :value="sex">
<input type="text" v-model="sex">
</div>
<script>
Vue.config.devtools = true;
//一个vue实例,接管上面的容器
new Vue({
el: '.root',
data: {
name: '陈皮',
sex: '男'
}
})
</script>
五、理解MVVM模型
MVVM含义
-
M:(模型Model) 指data中的数据
-
V:(视图View) 指容器,模板代码(html+vue语法的代码)
-
VM:(视图模板ViewModel)其实就是Vue实例,连接M与V的中间人
特别注意
- data中所有的属性,最后都出现在vm身上
- vm身上所有的属性和Vue原型上所有的属性,在Vue模板中康都可以直接使用,注意函数都不用写()
<div id="root">
<h2>你好,{{name}},我住在{{address}}</h2>
<h2>测试1{{_t}}</h2>
<h2>测试2{{$refs}}</h2>
<h2>测试3{{ $options }}</h2>
</div>
<script>
Vue.config.productionTip = false;
const vm=new Vue({
el: '#root',
data: {
name: '小陈',
address: '新余'
}
})
console.log(vm);
</script>
六、Object.defineproperty()方法回顾
<script>
let num=10;
let person={
name: '小陈',
sex: '男'
}
Object.defineProperty(person,'age',{
//value: num,
enumerable: true,//age可以枚举
//writable: true,//值可以修改
configurable: true,//控制属性是否可以删除,默认为false
get: function(){
console.log('每次查询age都会调用get函数');
return num;
},
set(value){
console.log('每次修改age都会调用set函数');
num=value;
}
});
// person.age=11;//writable默认为false,无法修改
console.log(person);
for (const key in person) {
console.log(key+"=>"+person[key]);//无法枚举出age,enumerable默认为false
}
</script>
思考:js如何在obj2中操作obj1的属性?
<script>
let obj1={name: 'chen'};
let obj2={address: '江西'};
//思考:如何在obj2中操作obj1中的属性内?
Object.defineProperty(obj2,'name',{
get(){
return obj1.name;
},
set(value){
obj1.name=value;
}
})
</script>
七、Vue中的数据代理
- vue实例中并没有data属性,vue实例是把data中的属性放到_data对象中,然后我们访问name或者address都是调用getter从_data属性中拿的,我们修改name,都是调用setter修改_data中的属性
- Vue中数据代理:通过vm对象来代理data对象中属性的操作(读写)
- 同之前Object.defineProperty()一样,把data对象中所有属性添加到vm上,为每一个添加到vm上的属性都指定一个setter和getter.在getter和setter内部操作data中对应的属性.
- vue数据代理的好处是更加方便操作data中数据,其实没有数据代理,我们访问data中的属性就是vm._data.name,也可以实现.
<div id="root">
<h3>hello,{{name}},在{{address}}</h3>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '小陈啊',
address: '江西'
}
});
console.log(vm);
</script>
八、Vue的事件
事件的基本使用
- 使用v-on:xxx 或者简写为@xxx,绑定事件,其中xxx是事件名称(事件类型)
- 事件的回调需要配置在methods对象中,最终方法会在vm对象上
- methods中配置的函数,不要用箭头函数,否则this就不是指向vm或者组件实例对象了,而是指向window
- methods中配置的函数,都是被Vue所管理的函数,this的指向都是vm或者组件实例对象
- @click="fn"和@click="fn($event)"效果是一致的,但是后者可以传参数
<div id="root">
<h3>hello,{{name}}</h3>
<button v-on:click="fn1">我是按钮1</button>
<button @click="fn2">我是按钮2</button>
<button @mouseover="fn3">我是按钮3</button>
<button class="btn4" @click="fn4(333,$event)">我是按钮4,带了参数</button>
</div>
<script>
const vm=new Vue({
el: '#root',
data: {
name: 'chen',
fn3:()=>{
alert('我是方法3,写在data对象中,会被vue数据代理');
console.log(this);//箭头函数得到的this是window
}
},
methods: {
fn1: function(){
alert('我是方法1');
console.log(this);//普通函数里面的this是Vue实例
},
fn2(){
alert('我是方法2');
},
fn4(num,event){//event是空,因为btn中没有用$event来占位置
console.log(num);
console.log(event.target.innerText);//得到按钮的值
console.log(event.target.getAttribute('class'));//得到按钮的类
}
}
});
</script>
事件修饰符
- @click.prevent:点击超链接会先出弹窗,然后执行超链接默认事件跳转,加了@click.prevent则会阻止默认事件
- @click.stop(加到儿子头上):在子盒子中加会阻止冒泡事件
- @click.once:事件只会执行一次
- @click.capture(加到父亲头上):使用事件的捕获机制,先弹出父亲事件,再子
- 修饰符可以连续写: @click.prevent.once
- 键盘事件总结
- 回车:enter
- 退出:esc
- 空格:space
- 换行: tab(结合keydown使用)
- @keyup:按键抬起时才会触发
- @keydown:按下就触发
- 上:up,下down
<div id="root">
<button @click.once="fn">我是按钮</button>
<h2><a href="http://www.randycout.com" @click.prevent.once="fn">我的博客网址</a></h2>
<div class="parent" @click.capture.once="fn1"><!--只会执行一次,并且执行捕获模式-->
我是你爸爸
<div class="son" @click.stop="fn2">
我是儿子
</div>
</div>
<input type="text" placeholder="练习键盘事件" @keyup.space="fn3">
</div>
<script>
new Vue({
el: '#root',
data: {
},
methods: {
fn(){
alert('点了超链接');
},
fn1(){
alert('爸爸盒子的弹框');
},
fn2(){
alert('儿子的弹框');
},
fn3(e){
alert(e.target.value);
}
}
})
</script>
姓名案例插值与方法的写法
<div id="root">
姓:<input type="text" v-model:value="firstName"><br><br>
名:<input type="text" v-model="lastName" ><br><br>
<!-- 1.插值写法 -->
姓名:<span>{{firstName.slice(0,3)+"--"+lastName}}</span><br><br>
姓名:<span>{{firstName}}--{{lastName}}</span><br><br>
<!-- methods写法1,作为事件的回调实现 -->
全名<button @click="fn">点击出全名</button><br><br>
<!-- methods写法2,插值语法,自己调用事件,一定要加(),不加就会展示函数体,加()是要返回值-->
全名<span>{{fn1()}}</span><br><br>
</div>
<script>
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fn(){
alert(this.firstName+'--'+this.lastName);
},
fn1(){
console.log('-----');
return this.firstName+'--'+this.lastName;
}
}
})
</script>
计算属性
- 定义:要用的属性不存在,要通过已有的属性计算得来
- 原理:底层借助Object.defineProperty()方法提供的getter和setter
- get函数什么时候执行:
- 初次读取时会执行一次
- 当依赖的数据发生变化时会再次调用
- 优势: 与methods实现相比,内部有缓存机制,效率更好,调式方便
- 备注
- 计算属性最终会出现再vm上,直接用插值语句使用即可
- 如果计算属性要被修改,那一定要写setter函数,且setter函数要引起计算式依赖的数据发生变化
<div id="root">
姓:<input type="text" v-model="firstName"><br><br><br>
名:<input type="text" v-model="lastName"><br><br><br>
姓名(完整写法):<span>{{fullName}}</span><br><br><br>
姓名(简写法):<span>{{fullName2}}</span><br><br><br>
</div>
<script>
let a=3;//a脱离了vue实例,修改了它也无法检测到,所以要通过现有的属性,脱离的不算
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed:{//计算属性,现有的属性加工处理,得到的新属性,这个属性再vm身上,但不在_data对象里面
//完整写法
fullName: {
//当有个读取fullName时就会调用get,且返回值就是fullName的值
//get什么时候被调用?1.初次读取fullName时 2.所依赖的数据发生修改时调用
//计算属性做了缓存,如果数据没有发生变化,多次调用此属性,它会从缓存中读取,期间get不会被调用
get(){//不能写成箭头函数,不然this就是window了
console.log('get函数被调用');
return this.firstName+'--'+this.lastName+a;
},
//只要修改fullName,就会调用set函数,修改后的值保存为fullName的值
set(value){//不配set表示不用修改
console.log('set函数被调用',value);
const arr=value.split('--');
this.firstName=arr[0];
this.lastName=arr[1];
}
},
//计算属性的简写(1.没有setter,只要getter方法 2.fullName2不是方法,调用时不能带() 2.function里面写set内容)
fullName2: function(){
console.log('get函数被调用');
return this.firstName+"--"+this.lastName;
}
}
});
</script>
天气案例
<div class="root">
<h2>今天天气{{weather}}</h2>
<button @click="change">切换天气</button>
<button @click="ishot=!ishot">切换天气</button>
</div>
<script>
const vm= new Vue({
el: '.root',
data: {
ishot : true
},
methods: {
change(){
this.ishot=!this.info;
}
},
computed: {
weather(){
return this.ishot ? '炎热':'凉爽';
}
},
})
</script>
监视属性watch
- 当被监视的属性发生变化后,回调函数会自动调用,进行相关操作
- 监视的属性必须存在,才能进行监测
- 监视的两种写法
- new Vue时传入watch配置
- 通过vm.$watch('dataName',{})进行监视
<div class="root">
<h2>今天天气{{weather}}</h2>
<button @click="change">切换天气</button>
</div>
<script>
const vm= new Vue({
el: '.root',
data: {
ishot: true,
},
methods: {
change(){
this.ishot=!this.ishot;
}
},
computed: {
weather(){
return this.ishot ? '炎热': '凉爽';
}
},
watch: {
ishot: {
immediate: true,//初始化时会调用1次
handler(newValue,oldValue){//当ishot变化时会调用handler
console.log(newValue+'---'+oldValue);
// return this.ishot ? '炎热':'凉爽';
}
},
// weather: {//计算属性也可以监听
// immediate: true,
// handler(newValue,oldValue){
// console.log(newValue);
// console.log(oldValue);
// }
// }
}
});
//监视的另一种写法
vm.$watch('weather',{//这里面的写法wather:{..}一模一样
immediate: true,
handler(n,o){
console.log(n+'---'+o);
},
})
</script>
深度监视
- vue中的watch默认不检测对象内部值的变化(只看最外一层)
- 配置deep:true可以监测对象内部值的变化(多层)
- vue自身可以监测对象内部值的改变,但是vue提供的watch默认不行
- 使用watch时根据数据的具体结构,决定是否采用深度监视
<div class="root">
<h2>监测数据a:{{num.a}}</h2>
<button @click="num.a++;">增加a</button>
<h2>监测数据b:{{num.b}}</h2>
<button @click="num.b++;">增加b</button>
<h2>监测数据d:{{num.c.d}}</h2>
<button @click="num.c.d++;">增加d</button>
</div>
<script>
const vm=new Vue({
el: '.root',
data: {
num: {
a:10,
b:20,
c:{
d:100
}
}
},
watch:{
'num.a':{//可以监测到a,完整写法
immediate:true,
handler(n,o){
console.log('a的值:'+n+'---'+o);
}
},
'num.b':function(n,o){//当不需要其他配置,只要handler时可以简写
console.log('b的值:'+n+'---'+o);
}
}
});
//如何监测到num的变化呢?
// vm.$watch('num',{
// deep: true,
// handler(n,o){
// console.log('num值发生变化');
// }
// });
vm.$watch('num.a',function(n,o){//简写
console.log('a值变了哦');
})
</script>
computed与watch的区别
- computed能完成的功能watch都能完成
- watch能完成的功能,computed不一定能完成,比如,watch可以进行异步操作
- 备注
- 所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或者组件实例对象
- 所有不能被vue管理的函数(定时器的回调函数,ajax的回调函数,promise的回调函数),最好写成箭头函数,这样this的指向才是vm或者组件实例
<div class="root">
姓:<input v-model="firstName"><br><br><br>
名:<input v-model="lastName"><br><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
new Vue({
el: '.root',
data: {
firstName: '陈',
lastName: '志斌',
fullName: '陈-志斌'
},
watch: {
firstName:function(newValue){
this.fullName=newValue+'-'+this.lastName;
},
lastName:{
handler(value){
setTimeout(() => {//这里一定要写成箭头函数,时间到了往外找,找到handler层,这里就是vm对象了
this.fullName=this.firstName+'-'+value;
}, 2000);
}
}
}
})
</script>
九、Vue绑定样式
class样式
- 写法 :class="xxx" xxx可以是字符串,对象和数组
style样式
- :style="{fontSize: xxx}" xxx是动态值
- :style="[a,b]",其中a,b是样式对象
<style>
.base{
width: 200px;
height: 200px;
margin: 50px;
padding: 50px;
border-color: black;
border: 2em;
background-color: aqua;
}
.one{
background-color: greenyellow;
}
.two{
border-radius: 50%;
}
.three{
font-size: 50px;
}
</style>
</head>
<body>
<!-- 绑定样式
1.class样式:
- 写法 :class="xxx" xxx可以是字符串,对象和数组
2.style样式
:style="{fontSize: xxx}" xxx是动态值
:style="[a,b]",其中a,b是样式对象
-->
<div id="root">
<!-- 字符串写法,适用于样式的类名不确定,需要动态指定 -->
<div class="base" :class="classOne">老大</div>
<!-- 数组写法,适用于要绑定的样式个数不确定,名字也不确定(三个属性都用上了) -->
<div class="base" :class="classArr">老二</div>
<!-- 对象写法,适用于样式个数确定,名字也确定,但要动态决定用不用 -->
<div class="base" :class="classObj">老三</div>
<!-- 绑定style样式,对象写法 key不能瞎写,要是css中存在的 -->
<div class="base" :style="styleObj">老四</div>
<!-- 数组写法 -->
<div class="base" :style="styleArr">老五</div>
<div class="base" :style="[{'fontSize':'90px'},{'color':'red'}]">老六</div>
</div>
<script>
new Vue({
el: '#root',
data: {
classArr:['one','two','three'],//数组写法
classOne: 'one',//字符串写法
classObj:{//对象写法
one: false,
two: false,
three: true
},
styleObj:{
backgroundColor: 'orange'
},
styleArr:[
{
fontSize: '60px'
},
{
color: 'blue'
}
]
}
})
</script>
十、条件渲染
v-if(完全把组件移除):
- v-if="表达式"
- v-else-if="表达式"
- v-else
- 适用于切换频率较低的场景,不展示的dom元素直接删除,v-if可以和v-else-if,v-else一起使用,但要求结构不能被打乱
v-show
-
写法: v-show="表达式"
-
适用于切换频率较高的场景,不展示的dom元素不会被删除,只是用display:none隐藏了
注意:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
<div id="root">
<h3>数字是{{num}}</h3>
<button @click="num++">点我加1</button><br><br><br>
<div class="box" v-show="num==3">
大盒子
</div>
<div class="box" v-if="num==1">
num=1我就显示
</div>
<div class="box" v-else-if="num==2">
num=2我就显示
</div>
<div class="box" v-else>
v-else不需要加条件
</div>
<template v-if="num<3">
<h2>num小于3时我会显示</h2>
</template>
<template v-else-if="num>=3&&num<=6">
<h2>3<=num<=6时我会显示</h2>
</template>
<template v-else>
<h2>num>6时我会显示</h2>
</template>
</div>
<script>
new Vue({
el: '#root',
data: {
num: 0
}
})
</script>
十一、列表渲染
v-for指令总结
- 语法 v-for="(item,index) in xxx" :key="yyy" ,其中in可以改为of
- 可遍历数组,对象,字符串和指定次数
<div class="root">
<ul>
<!-- 遍历数组对象 -->
<li v-for="(p,index) in persons">
{{p.name}}--{{p.age}}--{{index}}
</li>
</ul>
<ul>
<!-- 遍历对象 -->
<li v-for="(value,key) in student">
{{key}}---{{value}}
</li>
</ul>
<ul>
<!-- 遍历字符串 -->
<li v-for="(s,index) in str">
{{s}}--{{index}}
</li>
</ul>
<ul>
<!-- 遍历数字 -->
<li v-for="(num,index) in 6">
{{num}}--{{index}}
</li>
</ul>
</div>
<script>
new Vue({
el: '.root',
data: {
str: 'abcefdfd',
student:{
'sno': '001',
'sname': '小陈',
'sage': 20
},
persons:[
{
'no': '001',
'name': '张三',
'age': 18
},
{
'no': '002',
'name': '李四',
'age': 19
},
{
'no': '003',
'name': '王五',
'age': 20
}
]
}
})
</script>
列表渲染key详解(vue中key内部原理)
-
虚拟DOM中key的作用
key时虚拟Dom对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM, 随后Vue进行新虚拟DOM与旧的虚拟DOM的差异比较,比较规格如下
-
新旧虚拟DOM比较规则
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
- 若虚拟DOM中内容没变,直接使用之前的真实DOM
- 若虚拟DOM内容变了,则长生新的真实DOM,随后替换界面之前的真实DOM
- 虚拟DOM中没有找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
-
用index作为key可能引发的问题
- 若对数据进行逆序添加,逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新,效率低下
- 如果结构中还包含输入类的DOM会产生错误的DOM更新,界面会出问题
-
如何选择key
- 最好使用每条数据的唯一标识作为key,即数据主键
- 如果不存在对数据的逆序操作,则使用index作为key没有问题
- 若没有指明key,则系统默认会用index作为key
<div id="root">
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}--{{p.age}}
<input type="text">
</li>
</ul>
<button @click="add">在头部添加老刘</button>
</div>
<script>
new Vue({
el: '#root',
methods: {
add(){
const p={'no': '004','name': '老刘','age': 22};
this.persons.unshift(p);//在头部添加老刘
}
},
data: {
persons:[
{
'no' : '001',
'name': '张三',
'age': 19
},
{
'no': '002',
'name': '李四',
'age': 20
},
{
'no': '003',
'name': '王五',
'age': 21
}
]
}
})
</script>
列表排序案例
<div class="root">
计算属性查找:<input type="text" v-model="sel"><br><br><br>
侦听方法查找:<input type="text" v-model="key"><br><br><br>
<ul>
<!-- 计算属性查询到的人 -->
<li v-for="p in selPersons">
{{p.name}}--{{p.age}}
</li>
</ul>
<ul>
<!-- 侦听方法查询到的人 -->
<li v-for="p in personsList">
{{p.name}}--{{p.age}}
</li>
</ul>
<hr>
<ul>
<!-- 遍历数组对象 -->
<li v-for="(p,index) in persons">
{{p.name}}--{{p.age}}
</li>
</ul>
</div>
<script>
const vm=new Vue({
el: '.root',
data: {
sel: '',
key: '',
personsList: {},
persons:[
{'no': '001','name': '张三','age': 18 },
{'no': '002','name': '李四','age': 18 },
{'no': '003','name': '王五','age': 19 },
{'no': '004','name': '王三','age': 20 },
{'no': '005','name': '李五','age': 21 },
]
},
computed: {//用计算属性查找
selPersons(){
return this.persons.filter(p=>{
return p.name.indexOf(this.sel) !=-1;
})
}
},
watch: {
// key(val){//监听key属性(简写)
// this.personsList=this.persons.filter(function(p){
// return p.name.indexOf(val)!=-1;
// })
// }
key: {
immediate: true,//初始数值没变的时候先执行一波handler
handler(val){
this.personsList=this.persons.filter(p=>{//p是每一个对象,返回为true就筛选进数组,最后得到的是数组
return p.name.indexOf(val)!=-1;
})
}
}
}
});
</script>
十二、Vue数据监视原理
-
Vue会监视data中所有层次的数
-
如何监测对象中的数据?
-
通过setter实现监视,且要在new Vue时就传入要监测的数据
-
对象中后追加的属性,Vue默认不做响应式处理
-
如需要添加的属性做响应式,要使用如下API
Vue.set(target.propertyName/index,value) vm.$set(target.propertyName/index,value)
-
-
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue修改数组中的某个元素一定要用如下方法
- push():末尾添加一个元素
- pop():删除末尾一个元素
- unshift():在数组头部添加一个元素
- shift():删除头部一个元素
- splice():替换元素
- reverse():反转元素
- sort():数组排序
-
Vue.set()和vm.$set()不能给vm或者vm的根数据对象添加属性!!!!
不能Vue.set(vm._data,'key','value')
<div class="root">
<h3>学生姓名:{{student.name}},学生年龄:{{student.age}}</h3>
<h3>学生爱好:{{student.hobbit}}</h3>
<button @click="changeOneHobbit">修改一个爱好</button> <button @click="changeAllHobbit">修改所有爱好</button>
<h3>学生朋友:{{student.friends}}</h3>
<button @click="changeFriend1">修改朋友不展示</button> <button @click="changeFriend2">修改一个朋友展示</button>
<h3>学生班级:{{student.clazz}}</h3>
<button @click="addAdr">增加班级地址</button>
<h2>人群:{{persons}}</h2>
<button @click="changePerson1">修改人不展示</button>
<button @click="changePerson2">修改人展示</button>
<button @click="addAge">给第一个人增加人群年龄</button>
<h3>{{student.father}}</h3>
<h3>{{student.mother}}</h3>
<button @click="addParent">给学生加双亲</button>
</div>
<script>
const vm=new Vue({
el: '.root',
data: {
student: {
name: '小陈',
age: '18',
hobbit: ['看书','打游戏','敲代码'],
friends:[
{'name':'张三','age':18},
{'name':'李四','age':19}
],
clazz:{
'name':'计科3班',
'num':45,
'room': 307
}
},
persons:[
{'name':'大毛','sex':'男'},
{'name':'二毛','sex':'女'},
]
},
methods: {
changeOneHobbit(){
this.student.hobbit[0]='阅读';//页面展示不奏效,虽然数据在vm.student.hobbit中改好了
},
changeAllHobbit(){
// this.student.hobbit=['唱','跳','rap','篮球'];//页面展示奏效,因为hobbit有get和set方法
// this.student.hobbit.push('唱歌');//正确操作数组的方式,用那7个方法,尾部加唱歌
// this.student.hobbit.shift('看书');//头部删除看书
// this.student.hobbit.sort();
this.student.hobbit.unshift('摸鱼');//头部加摸鱼
this.student.hobbit.splice(1,2,'玩NBA2KOL');//splice(起始下标,数量,新增元素),splice(起始下标,数量)//删除元素
this.student.hobbit.reverse();//逆序输出
},
changeFriend1(){
this.student.friends[0]={'name':'老王','age':'28'}//数据改了,但是vue监听不到,要等下次vue解析模板时才会显示
},
changeFriend2(){
this.student.friends[0].name='老陈';//同样也监测不到,name和age都没有set和get方法
this.student.friends[0].age=38//
},
changePerson1(){
this.persons[0]={'name':'熊大','sex':'男'};//不奏效
},
changePerson2(){//坑点:如果先点了changePerson1,则数据改变后没有get和set,这会导致changePerson2也无法被vue监测到
this.persons[0].name='熊二'
this.persons[0].sex='女'
},
addAdr(){//添加响应式地址属性
this.$set(this.student.clazz,'adress','上海');
},
addAge(){
this.$set(this.persons[0],'age',50);
},
addParent(){
this.$set(this.student,'father','老王');
Vue.set(this.student,'mother','小花');//两种写法都可以
}
}
});
</script>
十三、表单数据收集
- ,则v-model收集的时value值,用户输入的就是value值
- ,则v-model收集的是value值,我们要给标签配置value值
- [ ] - 没有配置的input的value属性,则收集的就是checked(勾选 or 未勾选,是个布尔值) - 若配置了value属性 1. v-model的初始值是非数组,那么收集就是checked(布尔值) 2. v-model的初始值是数组,那么收集的就是value组成的数组
- v-model的三个修饰符
- lazy: 失去焦点再来收集数据
- number: 输入字符串转为有效数数字
- trim: 对首尾空格过滤
- @submit.prevent: 阻止表单默认跳转事件
<div class="root">
<!--@submit.prevent阻止表单默认跳转事件 -->
<form @submit.prevent>
姓名:<input type="text" v-model.trim="user.name"><br><br><br>
年龄:<input type="number" v-model.number="user.age"><br><br><br>
性别:
男<input type="radio" v-model="user.sex" value="man">
女<input type="radio" v-model="user.sex" value="woman"><br><br><br>
爱好:
唱<input type="checkbox" v-model="user.hobbit" value="sing">
跳<input type="checkbox" v-model="user.hobbit" value="drup">
rap<input type="checkbox" v-model="user.hobbit" value="rap">
篮球<input type="checkbox" v-model="user.hobbit" value="basketball"><br><br><br>
居住过的城市: <select v-model="user.city">
<option value="">请选择城市</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="sz">深圳</option>
</select><br><br><br>
其他:<textarea v-model="user.other"></textarea><br><br><br>
<button @click="showAll">提交数据</button>
</form>
</div>
<script>
const vm=new Vue({
el: '.root',
data: {
user:{
name: '',
age: '',
sex: '',
hobbit:[],
city:'',
other: ''
}
},
methods: {
showAll(){
console.log(JSON.stringify(this.user));
}
},
})
</script>
十四、指令
内置指令
- v-bind 单向绑定解析表达式,可简写为:
- v-model 双向数据绑定
- v-for
- v-on
- v-show
- v-if v-else-if v-else
- v-text : 会完全替换标签中的文本
- v-html : 能解析文本中的html标签,不安全,容易导致xxs攻击
- v-cloak : 没有值,Vue实例创建完毕后会移除此属性,配合css,保证Vue未接管容器时,不会给客户展示{{name}}未解析的内容
- v-once : 初次解析一次就视为静态内容不会在改变了
- v-pre : 跳过所在节点的模板解析过程,提高效率
<div class="root">
<!-- v-text会完全替h2中的文本 -->
<h2 v-text="name">我在敲代码</h2>
<!-- v-html能解析文本中的html标签,不安全,容易导致xxs攻击 -->
<h2 v-html="str">我是h2的文本</h2>
<!-- v-cloak没有值,Vue实例创建完毕后会移除此属性,配合css,保证Vue未接管容器时,不会给客户展示{{name}}未解析的内容 -->
<h2 v-cloak>我在{{name}}</h2>
<!-- v-once指令,初次渲染一次就视为静态内容不会在改变了 -->
<h2 v-once>初始值:{{num}}</h2>
<h2>num的值:{{num}}</h2>
<button @click="num++">num+1</button>
<!-- v-pre跳过所在节点的编译过程,提高效率 -->
<h3 v-pre>加了v-pre指令,不会解析num{{num}}</h3>
</div>
<script>
new Vue({
el: '.root',
data: {
name: '练习v-text',
str: '<h1>我是带h1标签的str</h1>',
num: 10,
}
})
</script>
十五、自定义指令
-
定义语法:
-
局部指令
new Vue({//完整写法 directives: { 指令名称: { bind(el,binding){//指令与元素成功绑定时调用(解析完毕,未放到页面) }, inserted(el,binding){//指令所在元素被插入页面时调用(解析好的模板,放到页面) }, update(el,binding){//指令所在模板被重新解析时调用(模板其他元素的改变引起页面重新解析也会被调用) } } } }) new Vue({//简单写法 directives:{ 指令名称(el,binding){//只有bind和update两个时刻 } } })
-
全局指令
Vue.directive('指令名称',function(el,binding){//简单写法 }) Vue.directive('指令名称',{//完整写法 bind(el,binding){ }, inserted(el,binding){ }, update(el,binding){ } })
-
-
备注:
- 指令是通过函数来处理逻辑,不是靠返回值,不是一个表达式.
- 指令定义时不用加- ,但是使用时要加v-
- 指令名如果是多个单词,定义用驼峰oneTwo,使用时v-one-two
<div class="root">
<h3>n的值时:{{n}}</h3>
n的10倍:<span v-big="n"></span><br><br><br>
n的100倍:<span v-biger="n"></span><br><br><br>
n的1000倍:<span v-bigest="n"></span><br><br><br>
n的10000倍并且拿到焦点:<input type="text" v-big-focus="n"><br><br><br>
<button @click="n++">点我n+1</button><br><br><br>
</div>
<script>
Vue.directive('biger',function(el,binding){//全局自定义指令写法(只有bind和update)
el.innerText=binding.value*100;
});
Vue.directive('bigest',{//完整写法
bind(el,binding){//指令与元素成功绑定时调用
el.innerText=binding.value*1000;
},
inserted(el,binding){//指令所在元素被插入页面时调用
el.innerText=binding.value*1000;
},
update(el,binding) {//指定所在模板结构被重新解析时调用
el.innerText=binding.value*1000;
},
})
const vm = new Vue({
el: '.root',
data: {
n: 1
},
directives:{//局部指令
big(element,binding){//这样简写只包括bind和update两个时刻,没有更细化的inserted时刻
// console.log(element);//<button v-big="n">点我n*10</button>,拿到元素
// console.log(binding);//绑定对象,有n的新旧值和表达式
element.innerText=binding.value*10;
},
bigFocus:{//使用是的名称: v-big-focus
bind(element,binding){//解析完模板,还没有放到真实DOM上
element.value=binding.value*10000;
},
inserted(element,binding){//解析完模板,并放到真实DOM放在页面
element.focus()
},
update(element,binding){//重新解析模板时调用
element.value=binding.value*10000;
}
}
}
});
</script>
十六、Vue生命周期
-
什么是生命周期?
是Vue实例从创建到销毁这一生中各个关键时刻给我们用的函数(vue实例自己会调用,不要程序员手动调用)
-
生命周期函数名不可更改,但里面的业务逻辑我们可以编写,共8组,4对
- beforeCreate() : 数据监测,数据代理初始化之前(有vm,没有_date属性,没有响应式setter和getter,无法访问data,methods)
- created() : 数据监测,数据代理初始化完毕后(可以访问data,methods)
- beforeMount() : Vue已经解析好了模板,并生成虚拟DOM(放入内存),但是并没有放到页面中,页面还是未经编译的DOM,此时对DOM的操作最终都不奏效
- mounted() : Vue将虚拟的DOM(内存)转为真实的DOM(vm.$el()留了一份真实DOM),并插到页面中了,页面展示的也是已经解析完毕的DOM
- beforeUpdate() : data发生变化时会触发,这个点数据是新的,但是页面是旧的,是唯一一个数据和页面不一致的时刻
- updated() : 新的虚拟DOM与旧的虚拟DOM进行比较完毕后,最终页面更新,到这个时刻,数据与页面保持一致
- beforeDestroy() : 当vm调用vm.$destroy()会触发,此时vm的data,methods和指令等等都可以用,但是页面不进行响应更新了
- destroyed()
-
生命周期图
- mounted(): 到这一时刻,Vue的初始化工作完成,此时可以在这进行发送网络请求,开启定时器,订阅消息,绑定自定义事件等
- beforeDestroy(): 销毁之前进行关闭定时器,取消订阅消息,解绑自定义事件等收尾工作.
Comments