Vue核心
vue 的使用有两种方式:安装、使用CDN
CDN 方式: 通过script 标签引入
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
Vue 基础语法
1.创建一个Vue对象
通过new 关键字,Vue对象中有 几个比较重要的属性
-
el :模板 ,用来绑定视图,用 #视图id 占位
-
data:向视图中绑定数据
-
methods:该vue对象的方法
2.在视图中用 {{}} 可以使用 data 中的数据
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--导入vue-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<div id="divvue">
{{message}}
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {
message: "this is first vue"
}
});
</script>
</body>
</html>
-
Vue和容器 是一一对应的
-
真实开发中只有一个Vue实例
-
{{}} 中要写js表达式,并且可以读取到实例的data的任何值
-
另外一种方式指定vue实例的容器是 调用 vm.$mount('#divvue')
-
此处的data是对象式写法,此外data还可以写成函数式,必须返回值
data:function(){ return{ name:"xxx" } }
这个data函数是Vue所管理的函数,一定不要写成箭头函数,因为写成箭头函数this就不再是vue实例了
3.v-band 绑定数据
<body>
<div id="divvue">
<span v-bind:title="message">
将鼠标悬停在此,查看详细信息!
</span>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {
message: "this is first vue"
}
});
</script>
</body>
-
在span标签上用v-band绑定到它的 title属性,内容还是message
-
v-bind 可以给标签里的任意属性动态绑定值
-
v-bind: 可以简写成 : (v-bind:span ----> :span)
4.条件渲染 v-if v-else
v-if、v-else、v-else-if是一组判断必须挨在一起,不能被打断
<body>
<div id="divvue">
<h3 v-if="flag">{{message}}</h3>
<h3 v-else> Have not Message</h3>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {
message: "this is first vue",
flag: true
}
});
</script>
</body>
使用v-show 做条件渲染,v-show中可以放boolean值,也可放表达式或 对象
v-if 和 v-show的区别
v-if 对于不展示的DOM元素直接删除。v-show不展示的DOM元素不移除,而是隐藏
5.列表渲染 v-for
<body>
<div id="divvue">
<li v-for="(item,index) in student" :key="index">
{{item.name}}-----{{index}}
</li>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {
message: "this is first vue",
student:[
{name: '小明'},
{name: '小红'}
]
}
});
</script>
</body>
-
想生成多个谁就在谁身上用 v-for
-
最好在遍历的后面,写上:key,并且必须是唯一的
-
v-for 还可以遍历对象,遍历对象时,item是 对象的value,index是对象的key
v-for中的 key
vue根据模板先生成虚拟DOM,然后根据虚拟DOM 生成真实DOM,key就用于虚拟DOM
-
虚拟DOM 中 key的作用
-
key是虚拟DOM的标识,当状态中的数据发生变化时,Vue会根据新数据生成新的虚拟FOM
随后Vue 进行新虚拟DOM 和 旧虚拟DOM 的差异比较
-
-
差异比较的 规则
-
旧的虚拟DOM中找到了与新虚拟DOM相同的 key
-
如果虚拟DOM中的内容没变,直接使用之前的真实DOM
-
如果虚拟DOM 中的内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
-
-
旧的虚拟DOM 中未找到与 新虚拟DOM相同的Key,则创建新的真实DOM,渲染到页面
-
-
用 index作为key可能会引发的问题
-
若对数据进行逆序添加或删除等破坏顺序的操作,最产生没有必要的真实DOM 更新,如果结构中还包括输入类的DOM,就会产生错误的DOM更新,界面会出问题
-
-
开发中如何选择key
-
最好使用每条数据的唯一标识作为key
-
如果不存在对数据的逆序添加或删除等破坏顺序操作,用index作为key是没有问题的
-
如果不写 key,在生成虚拟DOM时,会自动将 index 作为 key
-
Vue绑定事件
在vue对象中的属性 methods,方法需要定义在里面
在视图中 用 v-on 来绑定事件,比如点击事件等等
<body>
<div id="divvue">
<button v-on:click="alertMessage">点击弹出</button>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {},
methods: {
alertMessage:function () {
alert(this.message)
}
}
});
</script>
</body>
-
使用v-on:xxx 绑定事件,简写是 @xxx
-
事件回调需要配置在methods对象中
-
methods中配置的函数,不要使用箭头函数,否则this就不是vm了
-
@click=“alertMessage” 和 @click="alertMessage($event)" 效果一致,后者可以传参
事件修饰符
-
prevent 阻止默认事件
-
stop 阻止事件冒泡
-
once 事件只触发一次
-
capture 使用事件的捕获模式
-
self 只有event.target 是当前操作的元素时才触发事件
-
passive 事件的默认行为立即执行,无需等待时间回调执行完毕
键盘事件
-
键盘事件常用的有两种 keydown和 keyup 分别是 按键 按下和弹起时去执行的事件
-
键盘事件对应的也有修饰符,在按下这些键位之后才去执行绑定的事件,常用修饰符有九个enter、delete、esc、sapce、tab、up、down、left、right,注意这些只是vue起的别名,用它原来的名字 如Enter 也是可以的
-
其中 tab 比较特殊 必须搭配 keydown 去使用,因为当你按下tab还未抬起时,焦点就切换到下一个了,当抬起时,焦点已不在原来的位置
-
使用系统修饰符 ctrl、alt、shift、meta 时,搭配 keyup使用时在按下修饰键的同时,在按下其他键,然后松开其他键,事件才触发,搭配keydown使用正常触发,在系统提示符后面可以继续追加按键,代表的就是 哪两个键一起按触发事件
<body>
<div id="root">
<input type="text" placeholder="请输入" @keyup.enter="showInput">
</div>
<script>
const vm = new Vue({
el:"#root",
methods:{
showInput(e){
console.log(e.target.value);
}
}
})
</script>
</body>
这段代码的含义就是 在输入框中输入内容按下 回车键,会在控制台输出内容
Vue 双向绑定
使用 v-model vue中的数据可以跟着视图的改变而改变
<body>
<div id="divvue">
<input type="text" v-model="message">{{message}}
<input type="radio" name="sex" value="男" v-model="sex">男
<input type="radio" name="sex" value="女" v-model="sex">女
{{sex}}
<input type="checkbox" name="hobby" value="写代码" v-model="hobby">写代码
<input type="checkbox" name="hobby" value="唱歌" v-model="hobby">唱歌
<input type="checkbox" name="hobby" value="跳舞" v-model="hobby">跳舞
{{hobby}}
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data: {
message: "",
sex:"",
hobby:[]
}
});
</script>
</body>
-
v-model 叫做双向绑定,一般都应用在表单元素上
-
v-model:value 可以简写为v-model ,因为v-model默认收集的就是value值
-
v-model.number 该修饰符保证收集到用户的表单后,数字类型的数据,收集到依旧是数字类型,不加该修饰符,就会变成字符类型
-
v-model.lazy 该修饰符是让表单项失去焦点后收集到信息,而不是立刻收集
-
v-model.trim 去掉前后的空格
Axios
作用:实现AJAX 异步通信
由于vue不包含ajax通信功能,所以我们可以使用 axios 来进行通信
<body>
<div id="divvue">
<div>{{info.name}}</div>
<div>{{info.address.country}}</div>
<a v-bind:href="info.url">点击跳转</a>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
//钩子函数 mounted,在这里面进行ajax请求获取数据
mounted(){
axios.get("../data.json").then(response=>(this.info=response.data))
},
//在data 方法中接收数据
data(){
return{
info:{
name:null,
url:null,
usePerson:0,
address:{
country:null
}
}
}
}
});
</script>
</body>
这个案例是从 json文件中获取数据,使用axios的get方法,返回的data数据赋值给 data方法的info,注意data的info 要和 json文件的格式一致
Vue计算属性
它是一个能将计算结果缓存起来的属性。
<body>
<div id="divvue">
<p>currentTime1:{{currentTime1()}}</p>
<p>currentTime2:{{currentTime2}}</p>
</div>
<script>
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data:{
message:"Compote"
},
methods:{
currentTime1: function () {
return Date.now();
}
},
computed:{
currentTime2: function () {
return Date.now();
}
}
});
</script>
</body>
在computed中定义的方法 currentTime2 就是计算属性,一旦计算就被缓存,只有当其中的数据被刷新后才会重新计算。
计算属性就是通过计算得出的属性,他也有 get和 set方法
get什么时候调用:1.初次读取该计算属性时 2.该属性所依赖的属性发生变化时
set什么时候调用:当该计算属性被修改时
计算属性可以简写,在只读取不修改的前提下,可以简写为
列表过滤排序的案例
<body>
<div id="root">
<div>人员列表</div>
<label>
<input type="text" v-model="keyword"></input>
<button @click="sortType= 2">升序</button>
<button @click="sortType= 1">降序</button>
<button @click="sortType= 0">原顺序</button>
</label>
<ul>
<li v-for="(item,index) in filPerson" :key="item.id">
{{item.name}}-{{item.age}}-{{item.sex}}
</li>
</ul>
</div>
<script>
new Vue({
el:"#root",
data:{
keyword:"",
sortType:0,
persons:[
{id:"001",name:"马冬梅",age:25,sex:"女"},
{id:"002",name:"周冬雨",age:19,sex:"女"},
{id:"003",name:"周杰伦",age:30,sex:"男"},
{id:"004",name:"温兆伦",age:21,sex:"男"}
]
},
computed:{
filPerson(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyword) !== -1
})
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType === 2 ? p1.age-p2.age : p2.age-p1.age
})
}
return arr
}
}
})
</script>
</body>
Vue 插槽 slot
插槽可以动态地替换视图
比如在一个列表中 表的标题名和 列表的内容都是从别的地方获取的
<div>
<ul>
<li>
</li>
</ul>
</div>
其中列表名div标签和 列表内容 li标签都是从数据中获取,而ul 标签不用
可以使用插槽将 div 和 li 插入
-
通过 component 创建组件 ,包括列表名和列表内容 ,div和 li 标签都用 slot标签来代替,slot的属性name,ul 不变
-
再通过component 创建两个组件,分别是 标题 div 和 列表的项 li,这里就和之前学习组件一样
<body>
<div id="divvue">
<todo>
<todo-title slot="list-title" v-bind:title="title"></todo-title>
<todo-items slot="list-items" v-for= "item in citys" v-bind:cityitem="item"></todo-items>
</todo>
</div>
<script>
Vue.component("todo",{
template: '<div>' +
'<slot name="list-title"></slot>' +
'<ul>' +
'<slot name="list-items"></slot>' +
'</ul>'
});
Vue.component("todo-title",{
props:['title'],
template: '<div>{{title}}</div>>'
});
Vue.component("todo-items",{
props:['cityitem'],
template: '<li>{{cityitem}}</li>>'
});
<!-- 创建vue对象-->
var vm = new Vue({
el: "#divvue",
data:{
title:'城市',
citys:["A济南","B青岛","C淄博"]
},
});
</script>
</body>
视图中的两个 todo-title 和 todo-items 去替换掉 第一个component组件中的 slot 标签,而这两个组件本身就是 div 和 li
数据代理
1.Object.defineProperty
这个方法是给一个对象定义属性使用的
下面是给 person 添加 age 属性
<script>
let person = {
name:"张三",
sex:"男"
}
Object.defineProperties(person,'age',{
value:3
})
</script>
defineProperty的三个参数分别是,要添加属性的对象、要添加的属性、属性模板(value是属性的值)
通过这种方式添加的属性不可被枚举,也就是不能被遍历,若想能够被遍历可以在属性模板中添加配置项
<script>
let person = {
name:"张三",
sex:"男"
}
Object.defineProperties(person,'age',{
value:3
enumerable:true //控制属性可以被枚举,默认值是false
writable:true //控制属性可以被修改
configurable: true //控制属性可以被删除
})
</script>
需求:给person的age 赋值为一个变量number,当变量改变时,age跟着变
<script>
let number = 10
let person = {
name:"张三",
sex:"男"
}
Object.defineProperty(person,'age',{
//value:3
get:function (){
return number
},
set(value){
}
})
</script>
当person的age属性被读取时,get函数就会被调用,返回值就是age的值,与之对应的是 当有人修改
person的age属性时,set函数就会被调用,参数就是修改的值
2.何为数据代理
通过一个对象代理对另一个对象中属性的操作,代码如下:obj2去代理obj
<script>
let obj={x:100}
let obj2={y:100}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
3.Vue 中的数据代理
通过vm对象来代理data对象中属性的操作,vm里面有一个_data,真正的数据代理出现在 _data的属性和vm的属性中,基本原理就是通过 Object.defineProperty 的 getter/setter 方法添加到vm上
监视属性
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">点击切换天气</button>
</div>
<script>
new Vue({
el:"#root",
data:{
isHot:true
},
methods:{
changeWeather(){
this.isHot = !this.isHot
}
},
computed:{
info(){
return this.isHot ? "炎热":"凉爽";
}
}
})
</script>
</body>
案例:点击按钮,改变天气,通过计算属性info来展示天气状况
我们想要知道 每次属性的改变,可以用vue实例的watch属性
里面就写要监视的属性 isHot 或 info,也是写成对象的形式,其中有个方法叫做handler
handler 有两个参数第一个是 属性改变后的值,第二个是属性改变前的值
watch:{
isHot:{
handler(newValue,oldValue){
console.log("isHot 发生了改变",newValue,oldValue)
}
}
}
在watch 中还有一个属性是 immediate 设置为true 表示初始化时调用handler
总结:
-
当被监视的属性发生变化时,回调函数自动调用
-
监视的属性必须存在
-
两种写法
-
在new Vue中传入watch配置
-
通过vm.$watch()
两种写法的内容一致
-
深度监视
Vue中默认不监视对象内部值的改变,也就是data中的对象里的对象
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
如果你去监视numbers,当 numbers中的a或b改变时,并不会被监视到,如果想要numbers被监视到可以使用 watch中的配置 deep: true,这样 就可以监视numbers内部值的变化了
当你不需要 immediate 和 deep这两个配置时,就可以将watch简写
watch:{
isHot(newValue,oldValue){
console.log("isHot 发生了改变",newValue,oldValue)
}
}
除了在new Vue 中配置watch,还可以通过 vm对象调用
vm.$watch(isHot,{
handler(newValue,oldValue){
xxx
}
})
这种方式的简写形式为:
vm.$watch(isHot, function(){
xxx
})
同样不能使用 immediate 和 deep这两个配置
Vue监视数据的原理
-
vue会监视data 中所有层级的数据
-
如何监视对象中的数据?
-
通过setter实现监视,且要在new Vue时就传入要监视的数据
-
对象中后追加的属性,Vue不做响应式处理
-
如果需要给后追加的属性做响应式,可用如下API
-
Vue.set(target, propertyName/index, value)
-
vm.$set(target, propertyName/index, value)
-
-
-
如何监视数组中的数据?
-
通过包裹数组更新元素的方法,本质做了两件事
-
调用原生对应的方法对数组进行更新
-
重新解析模板,进而更新界面
-
-
-
在Vue修改数组中某个元素一定要用如下方法
-
push()、pop、shift、unshift、spilce、sort、reverse
-
使用 Vue.set 或 vm.$set
-
Vue.set 或 vm.$set 不能给vm或vm的根数据对象添加属性
-
过滤器
过滤器也是定义在 Vue 对象中的配置,和computed、methods类似
通过格式化 时间戳的案例展示 过滤器的使用
<html>
<head>
<meta charset="utf-8">
<title>过滤器</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.11/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>现在是:{{time | timeFilter}}</h3>
</div>
<script>
new Vue({
el:"#root",
data:{
time:1621561377603
},
filters:{
timeFilter(){
return dayjs().format('YYYY-MM-DD HH:mm:ss')
}
}
})
</script>
</body>
</html>
-
在 模板中 的插值语句中写上要过滤的属性,后面接一个管道符(|) 后面就是 过滤器的函数
-
过滤器函数在filters 中配置
-
在上面的案例中 time 作为参数 传给了 timeFilter,timeFilter返回值 替换掉插值语法的内容
-
过滤器 除了前面的内容还可以接收其他的参数
-
过滤器还可以串联,在已有过滤器后面再加管道符 和 新的过滤器,新过滤器的接收的参数就是老过滤器的返回值
<body> <div id="root"> <h2>显示格式化后的时间</h2> <h3>现在是:{{time | timeFilter | mySlice}}</h3> </div> <script> new Vue({ el:"#root", data:{ time:1621561377603 }, filters:{ timeFilter(){ return dayjs().format('YYYY-MM-DD HH:mm:ss') }, mySlice(val){ return val.slice(0,4) } } }) </script> </body>
上面的过滤器是局部过滤器,只能由创建它的vue对象去调用,除此之外还有全局的过滤器
<script>
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
new Vue({...})
</script>
这样所有的vue都能使用它,需要注意的是:必须在new Vue之前创建它
过滤器并没有改变原本的数据,是产生新的对应的数据
内置指令
1.v-text
作用:向其所在的节点中渲染文本内容
和 插值语法效果相同,但是不如插值语法灵活,在标签内原来的内容会完全被替换掉。并且v-text不能解析标签
2.v-html
作用:向指定节点中渲染包含html结构的内容
v-html 和 v-text 的区别就是 前者能够解析html 标签
v-html 存在安全性问题,容易导致XSS攻击(Cookie泄露 ),不要在用户提交的内容上使用 v-html
3.v-clock
-
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-clock属性
-
使用 css 配合 v-clock可以解决网速慢时页面展示出 {{xxx}} 的效果
4.v-once
-
v-once所在节点在初次动态渲染后,就视为静态内容了
-
以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<h2 v-once >name的值是:{{name}}</h2>
5.v-pre
-
跳过其所在节点编译过程
-
可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
自定义指令
函数式指令
<body>
<div id="root">
<h2>num的值是:<span v-text="number"></span></h2>
<h2>放大10倍的num的值是:<span v-big="number"></span></h2>
<button @click="number++">点击number+1</button>
</div>
<script>
new Vue({
el:"#root",
data:{
number:1
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
}
}
})
</script>
</body>
此处 自定义的指令是 v-big
在模板中就是使用 v-big来展示数据,和 v-text相似
在定义该指令的时候,使用的配置对象是 directives,这里介绍用函数的方式定义
这个函数接收两个参数,第一个参数是 v-big指令所在的真实DOM 元素,第二个参数是一个对象,里面的内容是这个指令的数据,包括定义的名字、使用的名字,最重要的是 value(要填充的数据的值),也就是 number
在函数中 通过操作DOM元素,给它填充值
big函数执行的时机?
-
指令与元素成功绑定时(一上来)
-
指令所在的模板被重新解析时
对象式指令
上面的那种方式有时不能处理一些细节,比如有一个 input 输入框,给他绑定一个数值,之前可以用v-bind去实现,现在还有另外一个需求是让这个input自动获取焦点,我们可以用 自定义的指令来实现,这里我们叫他 v-fbind,如果按照 函数式去定义指令,实现不了这种效果
<body>
<div id="root">
<h2><span>num的值是:{{number}}</span></h2>
<h2>放大10倍的num的值是:<span v-big="number"></span></h2>
<button @click="number++">点击number+1</button> <br/>
<input type="text" v-fbind:value="number">
</div>
<script>
new Vue({
el:"#root",
data:{
number:1
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
},
fbind(element,binding){
element.value = binding.value
element.focus()
}
}
})
</script>
</body>
原因是 在解析模板的时候,input还没被放到 页面中,就直接获取焦点,所以没效果
这个时候就需要用 对象式的自定义指令去实现
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
}
}
}
使用对象式自定义,该对象要写三个方法
-
bind 是指令与元素成功绑定时调用
-
inserted 指令所在元素被插入页面时调用
-
update 指令所在模板被重新解析时调用
注意点
-
指令命名时如果有多个单词,就用”-“来连接,不要使用驼峰命名法,定义的时候名字就要用引号引起来
<body> <div id="root"> <input type="text" v-big-number:value="number"> </div> <script> new Vue({ el:"#root", data:{ number:1 }, directives:{ "big-number"(){ ... } } }) </script> </body>
-
在 directives 配置项中的this 指的并不是 vue对象,而是 window
-
全局 自定义指令 和 过滤器十分类似
<body> <div id="root"> <input type="text" v-big-number:value="number"> </div> <script> Vue.directive('big-number'{ ... }) new Vue({ el:"#root", data:{ number:1 }, }) </script> </body>
使用 Vue.directive 定义
生命周期
-
生命周期又名生命周期回调函数、生命周期函数、生命周期钩子
-
Vue在关键时刻帮我们调用的一些特殊名称的函数
-
生命周期函数名字不可更改,内容根据需求更改
-
生命周期函数中的this指的是vm或组件实例对象
生命周期图
常用的生命周期钩子:
-
mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
-
beforeDestroy:清除定时器、解绑自定义事件、取消订阅等收尾工作
关于销毁Vue实例
-
销毁后自定义事件会失效,但原生DOM事件仍然有效
-
一般不会在beforeDestroy操作数据,因为即便操作有效,也不会触发更新流程了