1、el挂载点和data
el选项命中的元素及其内部的后代元素,可以使用其他的选择器,但是建议使用id选择器
data中可以写复杂类型的数据,渲染复杂类型数据时,遵守js的语法就可以
<body>
<div id="app">
{{message}}
<span>{{message}}</span>
</div>
<div class="app2">
{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",//挂载点el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
data:{
message:"hello vue"//data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。
}
});
var app2=new Vue({
el:".app2",
data:{
message:"hello vue2"
}
})
</script>
</body>
<body>
<div id="app">
{{message}}
<h2>
{{stus.name[1]}}
</h2>
<h3>
{{stus.name[2]}}
</h3>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
message:"hello data",
stus:{
name:["mike","amy","jack"],
num:111111111
},
city:["guangzhou","beijing","shanghai"]
}
})
</script>
</body>
1.2 容器和实例是一对一的
创建vue实例接管容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
{{message}} {{message2}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el:"#app",//挂载点
data:{
message:"hello vue"
}
});
new Vue({
el:"#app",//挂载点
data:{
message2:"hello vue222"
}
});
</script>
</body>
</html>
容器和实例是一一对应的
初识Vue:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为【Vue模板】;
4.Vue实例和容器是一一对应的;
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
注意区分:js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? ‘a’ : ‘b’
2.js代码(语句)
(1). if(){}
(2). for(){}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="demo">
<h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
</div>
<script type="text/javascript" >
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//创建Vue实例
new Vue({
el:'#demo', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。
name:'atguigu',
address:'北京'
}
})
</script>
</body>
</html>
1.3 模板语法
Vue模板语法有2大类:
1. 插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2. 指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性。把xxx当js表达式执行
备注:Vue中有很多的指令,且形式都是:v-???
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>模板语法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}</h3>
<hr/>
<h1>指令语法</h1>
<a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1</a>
<a :href="school.url" x="hello">点我去{{school.name}}学习2</a>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'jack',
school:{
name:'',
url:'http://www.baidu.com',
}
}
})
</script>
</html>
而x还是固定写死的值
1.4 单向数据绑定
- 语法:
v-bind:href ="xxx" 或简写为 :href
把"xxx"当表达式在data中读取href
特点:数据只能从 data 流向页
<!-- 普通写法 -->
<input type="text" v-bind:value="name"><br/>
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name"><br/>
1.5 双向数据绑定
- 语法:
v-mode:value="xxx" 或简写为 v-model="xxx"
- 特点:数据不仅能从 data 流向页面,还能从页面流向 data
<!-- 普通写法 -->
<input type="text" v-model:value="name"><br/> -->
<!-- 简写 -->
双向数据绑定:<input type="text" v-model="name"><br/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据绑定</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'Jack'
}
})
</script>
</html>
第二行改变会影响第一行,因为是双向绑定,但是第一行修改不会影响第二行
如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上
<h2 v-model:x="name">你好啊</h2>
1.6 el与data的两种写法
- el有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(’#root’)指定el的值。mount意思有挂载
//第一种写法
new Vue({
el:'#root',
data:{
name:'Jack'
}
})
//第二种写法 */
const v = new Vue({
data:{
name:'Jack'
}
})
v.$mount('#root')
- data有2种写法
(1).对象式
(2).函数式
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。 - 一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。而是指向window
new Vue({
el:'#root',
//data的第一种写法:对象式
data:{
name:'Jack'
}
})
new Vue({
el:'#root',
//data的第二种写法:函数式
data:function(){
console.log('@@@',this)
//此处的this是Vue实例对象
return{
name:'Jack'
}
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>el与data的两种写法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:function(){
console.log('@@@',this)
return{
name:'Z'
}
}
})
</script>
</html>
data函数不能写成箭头函数
1.7 MVVM
vue参考了MVVM
MVVM模型
- M:模型(Model) :data中的数据
- V:视图(View) :模板代码
- VM:视图模型(ViewModel):Vue实例
观察发现:
- data中所有的属性,最后都出现在了vm身上。
- vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>理解MVVM</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<!-- <h1>测试一下1:{{1+1}}</h1>
<h1>测试一下2:{{$options}}</h1>
<h1>测试一下3:{{$emit}}</h1>
<h1>测试一下4:{{_c}}</h1> -->
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:'北大',
address:'北京',
}
})
console.log(vm)
</script>
</html>
vm身上有adress和name
1.8 数据代理
1.8.1 Object.defineproperty
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
- 语法
Object.defineProperty(obj, prop, descriptor)
-
参数
obj 要定义属性的对象。
prop 要定义或修改的属性的名称或 Symbol 。
descriptor 要定义或修改的属性描述符。- enumerable:true, //控制属性是否可以枚举,默认值是false
- writable:true, //控制属性是否可以被修改,默认值是false
- configurable:true //控制属性是否可以被删除,默认值是false
返回值 被传递给函数的对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>回顾Object.defineproperty方法</title>
</head>
<body>
<script type="text/javascript" >
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true,
// writable:true,
// configurable:true
//当有人读取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)
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>回顾Object.defineproperty方法</title>
</head>
<body>
<script type="text/javascript" >
let number = 18
let person = {
name:'张三',
sex:'男',
age:18
}
console.log(Object.keys(person));
for(key in person)
{
console.log(person[key]);
}
console.log(person)
</script>
</body>
</html>
默认不可迭代,不可修改,不可删除
1.8.1 数据代理
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>何为数据代理</title>
</head>
<body>
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
</html>
1.8.3 vue中数据代理
- 1.Vue中的数据代理:
通过vm来代理data对象中属性的操作(读/写) - 2.Vue中数据代理的好处:
更加方便的操作data中的数据 ,不然要写成_data.name
- 3.基本原理:
通过Object.defineProperty()
把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter
。
在getter/setter
内部去操作(读/写)data中对应的属性。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue中的数据代理</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>名称:{{name}}</h2>
<h2>地址:{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'school',
address:'广州'
}
})
console.log(vm);
</script>
</html>
2、内置指令
2.1、v-text
设置标签的文本值
3. 若使用v-text
指令,将会把标签内的文字全部替换
4. 或者用差值表达式进行部分替换
5. 在v-text
指令内进行字符串拼接,内部支持写表达式
<body>
<div id="app">
<h2 v-text="message">1111111</h2>
<h2 v-text="name">11111111</h2>
<h2>{{message}}11111111</h2><!--只有这个的111才显示-->
<h2 v-text="message+'11111111'"></h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
message:"v-text",
name:"mike"
}
})
</script>
</body>
2.3、v-html
设置标签的innerHTML
v-html
指令作用式设置元素的innerHTML
,内容中有html结构会被解析成标签v-text
指令不论是什么内容,只会解析成文本- 解析文本用
v-text
,解析html结构使用v-html
<body>
<div id="app">
<p v-html="baidu"></p>
<p v-text="baidu"></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
baidu:"<a href='https://www.baidu.com/'>百度<a>"
}
})
</script>
</body>
2. 4、v-on
为元素绑定事件
- 绑定的方法定义在
methods
属性中,最终会在vm上; - 方法内部通过
this
关键字可以访问修改定义在data中的数据 - 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
- methods中配置的函数,不要用箭头函数!否则this就不是vm了;
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
- @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参;
<body>
<div id="app">
<button v-on:click="sayHi">按钮</button>
<button @click="sayHi">按钮</button>
<button @dblclick="addReally">really</button>
<p> {{feeling}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
feeling:"happy"
},
methods:{
addReally:function(){
this.feeling+="really!"
},
sayHi:function(){
alert("你好呀");
}
}
})
</script>
</body>
2. 4.1 传递参数
- 事件绑定的方法写成函数调用的形式,可以自定义传入参数
- 定义方法时需要定义形参来接受传入的实参
- 事件后面跟上
.修饰符
可以对事件进行修饰,eg:.enter
可以限制触发的按键为回车键 - 事件修饰符有多种
<div id="app">
<input type="button" @click="doit(p1,p2)"/>
<input type="text" @keyup.enter="sayHi">
</div>
<script>
var app=new Vue({
el:"#app",
methods:{
doit:function(p1,p2){
},
sayHi:function(){
}
}
})
</script>
2. 4.2 事件修饰符
Vue中的事件修饰符:
- 1.prevent:阻止默认事件(常用);
- 2.stop:阻止事件冒泡(常用);
- 3.once:事件只触发一次(常用);
- 4.capture:使用事件的捕获模式;
- 5.self:只有event.target是当前操作的元素时才触发事件;
- 6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
a标签默认会跳转,阻止以后不会跳转
<!-- 阻止默认事件(常用) -->
<a href="https://www.bilibili.com/" @click.prevent="showInfo">点我提示信息</a>
事件只触发一次(常用)
<button @click.once="showInfo">点我提示信息</button>
2. 5、计数器
<body>
<div id="app">
<button @click="sub">-</button>
<span>{{ num }}</span>
<button @click="add">+</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
num:1
},
methods:{
add:function(){
if(this.num<=9)
{
this.num++;
}
else{
alert("数量已经达到上限啦!");
}
},
sub:function(){
if(this.num>=1)
{
this.num--;
}
else{
alert("数量已经达到下限啦!");
}
}
}
})
</script>
</body>
2. 6、v-show
根据表达式的真假,切换元素的显示和隐藏
<div id="app">
<img src="#" alt="" v-show="true">
<img src="#" alt="" v-show="isShow">
<img src="#" alt="age>=18">
</div>
var app=new Vue({
el:"#app",
data:{
isShow:false,
age:16
}
})
- v-show原理式修改元素的display,实现显示隐藏
- 指令后面的值,最后会被解析成布尔值
- 数据改变后,元素的显示状态会同步更新
<body>
<div id="app">
<img src="/img/yellow.png" v-show="isShow">
<button @click="doit">点击</button>
<img src="/img/yellow.png" v-show="num==5">
<button @click="sub">-</button>
<span>{{num}}</span>
<button @click="add">+</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
isShow:true,
num:0
},
methods:{
doit:function(){
this.isShow=!this.isShow;
},
add:function(){
this.num++;
},
sub:function(){
this.num--;
}
}
})
</script>
</body>
2. 7、v-if
根据表达式的真假,切换元素的显示和隐藏(操作dom元素)
- 表达式的值为true,元素存在在dom中,为false,在dom树中移除
-频繁的切换使用v-show,消耗比较小,反之使用v-if
<body>
<!--if是直接移除样式,show是改变display-->
<div id="app">
<p v-if="isShow">今天是星期六</p>
<button @click="clickIt">按钮</button>
<p v-if="num>=20">明天是星期天</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
isShow:true,
num:2
},
methods:{
clickIt:function(){
this.isShow=!this.isShow;
}
}
})
</script>
</body>
2. 8、v-bind
设置元素的属性(比如src,title,class)
- 完整写法是
v-bind:属性名
- 简写的华可以省略
v-bind
只保留:属性名
- 需要动态的增删class建议使用对象的方式
<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">
<title>Document</title>
<style>
.active{
border: 10px solid red;
}
</style>
</head>
<body>
<div id="app">
<img v-bind:src="imgSrc" alt=""
:class="isActive?'active':''"
@click="fan">
<br><br><br>
<img :src="imgSrc" alt="" :title="imgT+'!!'"
@click="fan" :class="{active:isActive}">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
imgSrc:"/img/yellow.png",
imgT:"鸭子",
isActive:false
},
methods:{
fan:function(){
this.isActive=!this.isActive;
}
}
})
</script>
</body>
2. 8.2、绑定样式
class 绑定
- :class=‘xxx’
- 表达式是字符串: ‘classA’
- 表达式是对象: {classA:isA, classB: isB}
- 表达式是数组: [‘classA’, ‘classB’]
style 绑定
- :style="{ color: activeColor, fontSize: fontSize + ‘px’ }"
- 其中 activeColor/fontSize 是 data 属性
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div> <br/><br/>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div> <br/><br/>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
2. 9、图片切换
<body>
<div id="app">
<img :src="imgSrc[num]" alt="">
<br><br><br>
<button v-show="num!=0" @click="sub">上一页</button>
<button v-show="num!=3" @click="add">下一页</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
imgSrc:["/img/1.jpg","/img/2.jpg","/img/3.jpg","/img/4.jpg"],
num:0
},
methods:{
add:function(){
this.num++;
},
sub:function(){
this.num--;
}
}
})
</script>
</body>
2. 10、v-for
2.10.1、根据数据生成列表结构
- 数组经常和v-for结合使用
- v-for="(item, index) in xxx" :key=“yyy”
- item和index可以结合其他指令一起使用
- 数组长度的更新会同步到页面上,是响应式的
<body>
<div id="app">
<button @click="add">增加数据</button>
<button @click="sub">移除数据</button>
<ul>
<li v-for="(item,index) in arr">
{{item}}{{index+1}}
</li>
</ul>
<h2 v-for="(it,index) in vegetable" :title="it.name">
{{it.name}}
</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
arr:["北京","广州","上海","深圳"],
vegetable:[
{name:"potato"},
{name:"tomato"},
{name:"cabage"}
]
},
methods:{
add:function(){
this.vegetable.push({name:"peach"});
},
sub:function(){
this.vegetable.shift();//移除最左边的
}
}
})
</script>
</body>
2.10.2、key的内部原理
面试题: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,随后渲染到到页面。 - 用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
(2)如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。 - 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,用index作为key是没有问题的。
2.10.3、列表过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<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
})
}
}
})
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<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
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:'男'}
],
filPerons:[]
},
watch:{
keyWord:{
immediate:true,
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
</html>
2. 10.4、列表排序
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<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>
</html>
2.11、v-model
设置和获取表单元素的值(双向数据绑定)
- 绑定的数据会和表单元素的值相互关联
- v-model的三个修饰符:
(1)lazy:失去焦点再收集数据
(2)number:输入字符串转为有效的数字
(3)trim:输入首尾空格过滤v-model.trim="userInfo.account"
<body>
<div id="app">
<button @click="setV">修改</button>
<input type="text"v-model="message" @keyup.enter="add">
<h2>{{message}}</h2><!--双向数据绑定-->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
message:"冲冲冲"
},
methods:{
add:function(){
alert(this.message);
},
setV:function(){
this.message="不会的都可以学";
alert(this.message);
}
}
})
</script>
</body>
2.11、v-cloak
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
</head>
<script type="text/javascript"src="http://localhost:8080/resource/5s/vue.js"></script>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
</body>
<script type="text/javascript">
console.log(1)
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
这时会有js阻塞,5s后控制台才输出1
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
</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">
console.log(1)
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
会有没解析的模板出现在页面,5秒后才渲染
在没设置样式前,网页还是会呈现为接管的代码,一旦vue接管了代码,v-cloak就会被删除,结合样式,可以让未接管的代码不显示,可以在网速过慢的时候,页面显示未解析的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<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">
console.log(1)
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
2.12、v-once
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-once指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
2.12、v-pre
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pre指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2 >当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
会跳过其所在节点的编译过程,显示未解析的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pre指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2 v-pre>当前的n值是:{{n}}</h2>
<button @click="n++" v-pre>点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
3、自定义指令
一、定义语法:
(1).局部指令:
new Vue({
directives:{指令名:配置对象}或directives{指令名:回调函数}
})
(2).全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
三、备注:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>{{name}}</h2>
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:'learn',
n:1
},
directives:{
big(element,binding){
console.log(element,binding);
}
}
})
</script>
</html>
element是真实的dom,binding.value是1,一开始n等于1。由于v-big绑定的是“n”,所以expression显示为“n”
<span v-big="n">
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:'learn',
n:1
},
directives:{
big(element,binding){
element.innerText=binding.value*10
}
}
})
</script>
big函数何时调用
自定义指令写成函数式时只有在下面两种方式下被调用
- 指令与元素成功绑定时(以上来)
- 指令所在模板被重新解析,像上面的如果只更改name属性的值,big函数还是会被调用。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>{{name}}</h2>
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
<button @click="n++">点我n+1</button>
<hr/>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:'learn',
n:1
},
directives:{
big(element,binding){
element.innerText=binding.value*10
},
fbind(element, binding){
element.value=binding.value;
element.focus();
}
}
})
</script>
</html>
页面初始化第一次的时候并没有获取焦点
因为input只是和find绑定,还没渲染到页面,所以element.focus()无效,第二次第三次点击按钮,n值变化,指令所在模板被重新解析,find函数被重新调用
写成对象式
//指令与元素成功绑定时(一上来)
bind(element,binding){
},
//指令所在元素被插入页面时
inserted(element,binding){
},
//指令所在的模板被重新解析时
update(element,binding){
}
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:'learn',
n:1
},
directives:{
big(element,binding){
element.innerText=binding.value*10
},
fbind:{
bind(element,binding){
element.value = binding.value
},
inserted(element,binding){
element.focus()
},
update(element,binding){
element.value = binding.value
}
}
}
})
</script>
完整写法
/* 'big-number'(element,binding){
console.log('big',this) //注意此处的this是window
// console.log('big')
element.innerText = binding.value * 10
}, */
- 如果用破折号连接就要用上面的写法,如果写v-bigNumber不会生效
- 指令里的this指向的是window
<h2>放大10倍后的n值是:<span v-bigNumber="n"></span> </h2>
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase驼峰命名法命名。
定义全局指令
//定义全局指令
/* Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}) */
3、axios基本使用
- axios必须先导入才可以使用
- 使用get或者post方法即可发送对应的请求
- then方法中的回调函数会在请求成功或者失败时触发
- 通过回调函数的形参可以获取响应内容或者错误内容
<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">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.0.7/vue.cjs.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
</head>
<body>
<input type="button" value="get请求">
<input type="button" value="post请求">
<script>
const input=document.querySelectorAll('input');
input[0].onclick=function(){
axios.get("https://autumnfish.cn/api/joke/list?num=3")
.then(response=>{
console.log(response.data);
},reason=>{
console.log(reason);
});
}
input[1].onclick=function(){
axios.post("https://autumnfish.cn/api/user/reg",
{
username:'盐焗鹌鹑蛋'
}
).then(response=>{
console.log(response);
},reason=>{
cosnole.log(reason);
});
}
</script>
</body>
配合vue使用
- axios回调函数中this的指向已经发生了改变,无法访问到data中的数据
- 把this保存起来,回调函数中直接使用保存的this即可
<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">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="button" value="获取笑话" @click="getJoke">
<p>{{joke}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
joke:"笑话"
},
methods:{
getJoke:function(){
var _this=this;
console.log(this.joke);
axios.get("https://autumnfish.cn/api/joke")
.then(function(response){
console.log(response);
console.log(response.data);
_this.joke=response.data;
},function(reason){
console.log(reason);
});//methods里最好不要用箭头函数,this指向问题
}
}
})
</script>
</body>
4、 计算属性
- 1.定义:要用的属性不存在,要通过已有属性计算得来。
- 2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- 3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。 - 4.优势:与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便。 - 5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
测试:<input type="text" v-model="x"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
x:'你好'
},
methods: {
demo(){
}
},
computed:{
fullName:{
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
</html>
- get有什么作用?
当有人读取fullName时,get就会被调用,且返回值就作为fullName的值 - get什么时候调用?
1.初次读取fullName时。
2.所依赖的数据发生变化时。 - set什么时候调用?
1.当fullName被修改时。
4.1 简写
只读不改就用简写
完整写法
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
//完整写法
fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
简写
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
</script>
5、监视属性
5.1、监视属性watch:
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
})
</script>
</html>
(2).通过vm.$watch监视
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
})
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
</script>
</html>
5.2、深度监视
(1).Vue中的watch默认不监测对象内部值的改变(一层)。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_深度监视</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
<button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1,
}
},
watch:{
//监视多级结构中某个属性的变化
'numbers.a':{
handler(){
console.log('a被改变了')
}
}
}
})
</script>
</html>
(2).配置deep:true可以监测对象内部值改变(多层)。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_深度监视</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
<button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1,
}
},
watch:{
//监视多级结构中所有属性的变化
numbers:{
//deep:true,
handler(){
console.log('numbers改变了')
}
}
}
})
</script>
</html>
如果不加deep属性,改变a和b也不会有影响,因为监测的是对象的地址,对象的地址并没有改变
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以,除非加deep属性!
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
5.3、简写
只需要handle的时候
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性_简写</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
//简写
/* isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
} */
}
})
//简写,不可以写箭头函数
/* vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}) */
</script>
</html>
5.4、computed和watch之间的区别
- 1.computed能完成的功能,watch都可以完成。
- 2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。watch没有返回值可以用定时器。
- 3.两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_watch实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
fullName:'张-三'
},
watch:{
firstName(newName){
setTimeout(()=>{
console.log(this)
this.fullName = newName + '-' + this.lastName
},1000);
},
lastName(newName){
this.fullName = this.firstName + '-' + newName
}
}
})
</script>
</html>
5.5、监视数据原理
Vue监视数据的原理:
- vue会监视data中所有层次的数据。
- 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value) - 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。 - 在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!
6、生命周期
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>引出生命周期</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 v-if="a">你好啊</h2>
<h2 :style="{opacity}">欢迎学习Vue</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
a:false,
opacity:1
},
methods: {
},
//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted(){
console.log('mounted',this)
setInterval(() => {
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
})
//通过外部的定时器实现(不推荐)
/* setInterval(() => {
vm.opacity -= 0.01
if(vm.opacity <= 0) vm.opacity = 1
},16) */
</script>
</html>
Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root" :x="n">
<h2 v-text="n"></h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="add">点我n+1</button>
<button @click="bye">点我销毁vm</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
// template:`
// <div>
// <h2>当前的n值是:{{n}}</h2>
// <button @click="add">点我n+1</button>
// </div>
// `,//只能一个根节点
data:{
n:1
},
methods: {
add(){
console.log('add')
this.n++
},
bye(){
console.log('bye')
this.$destroy()
}
},
watch:{
n(){
console.log('n变了')
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
},
})
</script>
</html>
点击销毁以后按按钮,依然有效,vue通过dom绑定方法,即使销毁了,方法依旧还绑定着,可以继续使用
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
7、模块与组件
模块
- 理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件
- 为什么: js 文件很多很复杂
- 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率
组件
- 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image……)
- 为什么: 一个界面的功能很复杂
- 作用: 复用编码, 简化项目编码, 提高运行效率
模块化
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。
组件化
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,
7.1、非单文件组件
一个文件中包含n个组件
1.模板编写没有提示
- 没有构建过程, 无法将 ES6 转换成 ES5
- 不支持组件的 CSS
- 真正开发中几乎不用
Vue中使用组件的三大步骤:
7.1.1、定义组件(创建组件)
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
- 1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
- 2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。 备注:使用template可以配置组件结构。
7.1.2、注册组件
- 1.局部注册:靠new Vue的时候传入components选项
- 2.全局注册:靠Vue.component(‘组件名’,组件)
7.1.3、使用组件(写组件标签)
<school></school>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<hello></hello>
<hr>
<h1>{{msg}}</h1>
<hr>
<!-- 第三步:编写组件标签 -->
<school></school>
<hr>
<!-- 第三步:编写组件标签 -->
<student></student>
</div>
<div id="root2">
<hello></hello>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:创建school组件
const school = Vue.extend({
template:`
<div class="demo">
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
`,
//必须使用函数式,使用对象式组件复用的时候数据会同步改变
data(){
return {
schoolName:'北大',
address:'北京'
}
},
methods: {
showName(){
alert(this.schoolName)
}
},
})
//第一步:创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
studentName:'张三',
age:18
}
}
})
//第一步:创建hello组件
const hello = Vue.extend({
template:`
<div>
<h2>你好啊!{{name}}</h2>
</div>
`,
data(){
return {
name:'Tom'
}
}
})
//第二步:全局注册组件
Vue.component('hello',hello)
//创建vm
new Vue({
el:'#root',
data:{
msg:'你好啊!'
},
//第二步:注册组件(局部注册)
components:{
school,
student
}
})
new Vue({
el:'#root2',
})
</script>
</html>
7.1.3、注意点
1.关于组件名:
一个单词组成:
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
多个单词组成:
- 第一种写法(kebab-case命名):my-school
- 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
-组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。 - 可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
7.1.4、组件嵌套
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>组件的嵌套</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
const student = Vue.extend({
name:'student',
template:`
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'张三',
age:18
}
}
})
//定义school组件
const school = Vue.extend({
name:'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
data(){
return {
name:'北大',
address:'北京'
}
},
//注册组件(局部)
components:{
student
}
})
//定义hello组件
const hello = Vue.extend({
template:`<h1>{{msg}}</h1>`,
data(){
return {
msg:'Hello!'
}
}
})
//定义app组件,包含hello和shool组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
//创建vm
new Vue({
template:'<app></app>',
el:'#root',
//注册组件(局部)
components:{app}
})
</script>
</html>
7.1.5、VueComponent
- 上面的 school组件本质是一个名为
VueComponent
的构造函数,且不是程序员定义的,是Vue.extend生成的。 - 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:
new VueComponent(options)
。 - 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
- 关于this指向:
(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue(options)
配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。 - VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
7.1.6、原型链
//定义一个构造函数
function Demo(){
this.a = 1
this.b = 2
}
//创建一个Demo的实例对象
const d = new Demo()
console.log(Demo.prototype) //显示原型属性
console.log(d.__proto__) //隐式原型属性
console.log(Demo.prototype === d.__proto__)
程序员通过显示原型属性操作原型对象,追加一个x属性,值为99
Demo.prototype.x = 99
console.log('@',d) */
7.1.6、内置关系
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
实例的隐式原型属性,永远指向自己缔造者的原型对象
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
7.2、单文件组件
一个简单的school组件
<template>
<div class="demo">
<h2>
名称:{{schoolName}}
</h2>
<h2>
地址:{{address}}
</h2>
<button @click="showName">点击我显示学校名称</button>
</div>
</template>
<script>
export default{
name:'School',
data(){
return{
schoolName:"北大",
address:"北京"
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
}
</script>
<style>
.demo{
background-color:pink;
}
</style>
vscode安装了vetur后,快速生成vue模板
<v 然后回车
7.2.1、App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
//引入组件
import School from './School.vue'
import Student from './Student.vue'
export default {
name:"App2",
components:{
School,
Student
}
}
</script>
<style>
</style>
main.js和App.vue配对