【Vue】Vue全家桶(一)Vue基础
【Vue】Vue全家桶(一)Vue基础
文章目录
1 Vue概述
1.1 vue简介
Vue
(读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架
。
渐进式
: Vue 可以自底向上逐层应用
。在简单应用只需一个轻量小巧的核心库,而在复杂应用就渐进的引入各式各样的的vue插件。
Vue借鉴angular
的模板和数据绑定技术以及react
的组件化和虚拟DOM技术。
1.2 vue特点
- 遵循
MVVM
模式 - 编码简洁,体积小,运行效率高,适合移动/PC 端开发
- 它本身只关注视图层,所有的 DOM 操作都由 Vue 来处理,可以轻松引入 vue 插件或其它第三方库开发项目
- 采用
组件化
模式,提高代码复用率、且让代码更好维护 声明式编程
,让编码人员无需直接操作DOM,提高开发效率- 使用
虚拟DOM
和Diff算法
,尽量复用DOM节点
注
:
命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
1.3 Vue 扩展插件
- vue-cli:vue 脚手架
- vue-resource(axios):ajax 请求
- vue-router:路由
- vuex:状态管理(它是 vue 的插件但是没有用 vue-xxx 的命名规则)
- vue-lazyload:图片懒加载
- vue-scroller:页面滑动相关
- mint-ui:基于 vue 的 UI 组件库(移动端)
- element-ui:基于 vue 的 UI 组件库(PC 端)
2 Vue的基本使用
2.1 传统开发模式对比
//原生JS
<div id="msg"></div>
<script type="text/javascript">
var msg = 'Hello World'
var div = document.querySelector('#msg');
div.innerHTML = msg
</script>
//jQuery
<div id="msg"></div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
var msg = 'Hello World';
$('#msg').html(msg);
</script>
2.2 引入Vue.js的方法
1.直接<script>
引入,在Vue.js官网上下载,放在自己正在开发项目的js文件夹里
<script src="js/vue.js"></script>
2.使用CDN引入
对于制作原型或学习,你可以这样使用最新版本:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏。
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
2.3 Vue.js案例分析
Vue的基本使用步骤
:
1、需要提供标签用于填充数据
2、引入Vue.js库文件
3、创建Vue实例,并配置对象
4、把vue提供的数据填充到标签里面
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<div id="app">
<!-- 插值表达式{{}} -->
<div>{{num}}</div>
<!-- 事件绑定v-on -->
<div><button v-on:click="handle">点击</button></div>
</div>
<!-- //引入vue -->
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
//创建vue实例 vm (ViewModel)
var vm = new Vue({
el: '#app',
data: {
num: 0,
},
methods: {
// ES6 的对象字面量方法简写允许我们省略对象方法之后的冒号及function关键字
// handle:function(){
// this.num++;
// }
handle() {
this.num++;
},
},
});
</script>
</body>
</html>
2.3.1 实例参数el、data、methods的写法
el
:指定当前Vue实例为哪个标签服务(值可以是CSS选择器或者DOM元素),el有2种写法:
(1) new Vue时候配置el属性。
const vm = new Vue({
el:'#root', //第一种写法
data:{
msg:' '
}
})
(2) 先创建Vue实例,随后再通过vm.$mount(’#root’)指定el的值。
除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来
const vm = new Vue({
data:{
msg:' '
}
})
vm.$mount('#root') //第二种写法 */
data
:用于存储数据(值是一个对象或函数),数据供el所指定的标签使用,data有2种写法:
(1) 对象式
data:{
msg:' '
}
(2) 函数式
data(){
//console.log(this)//此处的this是Vue实例对象
return{
msg:' '
}
}
如何选择:目前data的哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
methods
: 该属性用于在Vue对象中定义方法。
- 事件的回调需要配置在methods对象中,最终会在vm上;
- methods中配置的函数,不要用箭头函数!否则this就不是vm
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
两个重要的小原则
:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,又因为箭头函数没有this,需要往外层作用域寻找父级,这样this的指向还是是vm或组件实例对象。
2.3.2 Vue代码运行原理分析
概述编译过程
:Vue语法经过Vue框架编译成原生JS语法,才能够被浏览器执行
2.3.3 前端渲染
前端渲染
:把数据填充到HTML标签中
前端渲染方式
:
- 原生js拼接字符串
- 使用前端模板引擎
- 使用vue特有的模板语法(推荐)
app标签里的代码被称为vue模板
2.3.4 Vue基本使用总结
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- app标签里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
- app标签里的代码被称为【Vue模板】;
- Vue实例和标签是一一对应的; 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- {{xxx}}中的xxx要写js表达式`,且xxx可以自动读取到data中的所有属性;
- 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
2.3.5 js表达式 和 js语句的区别
js表达式
:是由运算元和运算符(可选)构成,功能是执行计算,并返回一个值。比如:
- (1). a
- (2). a+b
- (3). demo(1)
- (4). x === y ? ‘a’ : ‘b’
js代码(语句)
:语句可以理解为一个行为,循环语句和判断语句就是典型的语句。一个程序有很多个语句组成,比如:
- (1). if(){}
- (2). for(){}
3 Vue 模板语法
Vue模板语法
包括两大类:
1.插值语法
:双大括号表达式{{ }}
(“Mustache”语法)【一个】
2.指令语法
:指令(以v-开头的自定义标签属性)【很多】
3.1 插值语法{{}}
- 功能:用于解析标签体内容(如
<div>{{xxx}}</div>
起始标签和结束标签之间的内容{{xxx}}就是标签体) - 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性,且可以直接读取到data中的所有属性,也可以调用对象的方法和计算属性
- 里面写js表达式(有返回值的js代码,而不是js语句)
<div id="app">
<p> {{ msg }} </p>
</div>
<script>
var vm = new Vue({
el: ‘#app’,
data: {
msg: 'hello vue.js'
}
})
</script>
3.2 指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,href为参数,xxx同样要写js表达式,且可以直接读取到data中的所有属性
备注:Vue中有很多的指令,且形式都是:v-???
3.3 属性绑定v-bind
属性绑定v-bind
,数据只能从 data 流向页面
功能:指定变化的属性值
语法:
v-bind:href ="xxx" 或简写为 :href ="xxx" //xx会作为表达式解析执行
3.3.1 class样式绑定
:class='xxx' // xxx可以是字符串、对象、数组。
- 对象语法
<div :class="{ active:isActive,error:isError }"></div>
- 数组语法
<div :class="[activeClass, errorClass]"></div>
样式绑定相关语法细节
:
1、对象绑定和数组绑定可以结合使用
2、class绑定的值可以简化操作
3、默认的class会保留
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type='text/css'>
.active {
border: 1px solid red;
width: 100px;
height: 100px;
}
.error {
background-color: pink;
}
</style>
</head>
<body>
<div id="app">
<div :class="{active:isActive,error:isError}">测试样式</div>
<!-- <div :class='[activeClass, errorClass]'>测试样式</div> -->
<button @click='handle'>切换</button>
</div>
</body>
<script type='text/javascript' src='js/vue.js'></script>
<script type='text/javascript'>
const app = new Vue({
el: "#app",
data: {
isActive: true,
isError: true,
//activeClass: 'active',
//errorClass: 'error'
},
methods: {
handle() {
//控制isActive在true和false之间切换
this.isActive = !this.isActive
this.isError = !this.isError
//this.activeClass = '';
//this.errorClass = '';
}
}
})
</script>
</html>
3.3.2 style样式绑定
- 对象语法
<div :style="{ color: activeColor, fontSize: fontSize }"></div>
- 数组语法
<div :style="[baseStyles, overridingStyles]"></div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 盒子1 -->
<div :style='{border: borderStyle, width: widthStyle, height: heightStyle}'></div>
<!-- 盒子2 -->
<div :style='objStyles'></div>
<!-- 盒子3 -->
<div :style='[objStyles, overrideStyles]'></div>
<button @click='handle'>切换</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
borderStyle: '1px solid blue',
widthStyle: '100px',
heightStyle: '200px',
objStyles: {
border: '1px solid green',
width: '200px',
height: '100px'
},
overrideStyles: {
border: '5px solid orange',
backgroundColor: 'blue'
}
},
methods: {
handle() {
this.heightStyle = '100px';
this.objStyles.width = '100px';
}
}
});
</script>
</body>
</html>
3.4 双向数据绑定v-model
Vue中双向数据绑定使用v-model
指令来实现标签内容的绑定,它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。如表单元素和数据的双向绑定。
语法
: v-model=“xxx”
特点
:数据不仅能从 data 流向视图,还能从视图流向 data
备注
:
- v-model只能用于表单类元素(Input单行文本、textarea多行文本
、select 下拉多选、 radio 单选框、checkbox多选框),其余类型可用v-bind - v-model指令的原始写法为.v-model:value ,因为v-model默认收集的就是value值。
- v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value 属性 和 input 事件;
- checkbox 和 radio 使用 checked 属性 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div>
<input type="text" v-model='msg'>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/* 双向数据绑定
1、从页面(用户)到数据
2、从数据到页面 */
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello Vue'
}
});
</script>
</body>
</html>
3.5 双向数据绑定原理
1.什么是单向数据绑定和双向数据绑定?
单向数据绑定(v-bind)
:数据只能从 data 流向页面
双向数据绑定(v-model)
:数据不仅能从data流向页面,还可以从页面流向data。
2.双向数据绑定原理
双向数据绑定原理
:vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
双向数据绑定主要是指:数据变化更新视图,视图变化更新数据.
- 输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
- Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。
其中,View 变化更新 Data ,可以通过事件监听的方式来实现,比如input标签监听 ‘input’ 事件就可以实现了,所以主要讨论如何根据 Data 变化更新 View。
根据 Data 变化更新 View实现过程:
首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听数据的所有属性。如果属性发生变化,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下4个步骤,实现数据的双向绑定:
1、实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
2、实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;
3、实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;
4、实现一个指令解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。
3.6 事件的基本使用
3.6.1 事件绑定v-on
v-on
指令用法:用于绑定HTML事件,如鼠标事件、键盘事件
写法
:v-on:click=‘xxx’ 其中xxx是事件名;简写形式
:@click=‘xxx’功能
:绑定指定事件名的回调函数- 事件的回调需要配置在methods对象中,最终会在vm上;
- methods中配置的函数,不要用箭头函数!否则this就不是vm
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
- 事件函数的调用方式: @click="demo”和@click="demo($event)”效果一致,但后者可以传参
<!-- 1.如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 -->
<button @click='handle1'>点击1</button>
<!-- 2.如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event -->
<button @click='handle2(123, 456, $event)'>点击
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{num}}</div>
<div>
<!-- 4种都可以实现-->
<button v-on:click='num++'>点击</button>
<button @click='num++'>点击1</button>
<button @click='handle'>点击2</button>
<button @click='handle()'>点击3</button>//和上面效果一致,但可传参
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
handle () {
// 这里的this是Vue的实例对象
console.log(this === vm)
// 在函数中 想要使用data里面的数据 一定要加this
this.num++;
}
}
});
</script>
</body>
</html>
3.6.2 事件修饰符
修饰符 (modifier)
是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定,例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()
- stop:阻止冒泡
<a @click.stop="handle">跳转</a>
js语法实现
event.stopPropagation();
- prevent:阻止默认行为,下面两种都可以
<a @click.prevent="handle">跳转</a>
js语法实现
event.preventDefault();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{num}}</div>
<div @click='handle0'>
<button @click.stop='handle1'>点击1</button>
</div>
<div>
<a href="http://www.baidu.com" @click.prevent='handle2'>百度</a>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
handle0: function () {
this.num++;
},
handle1: function (event) {
//event.stopPropagation()
},
handle2: function (event) {
//event.preventDefault();
}
}
});
</script>
</body>
</html>
- once:事件只触发一次
- capture:使用事件的捕获模式;
- self:只有event.target是当前操作的元素时才触发事件;
- passive:滚动事件的默认行为 (即滚动行为) 将会立即触发
注:修饰符可以连续写,使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
未加事件修饰符:
加事件修饰符:
3.6.3 按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符
。
Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
系统修饰键(用法特殊):ctrl、alt、shift、meta
(1). 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。比如ctrl+s 然后释放s
(2). 配合keydown使用:正常触发事件。
也可以使用keyCode去指定具体的按键(不推荐),但注意要转为kebab-case(短横线命名)
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
实例
- enter 回车键
//keyup 按键松开触发
<input @keyup.enter='submit'>
- delete删除键
<input @keyup.delete='handle'>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<form action="">
<div>
用户名: <input type="text" @keyup.delete='clearContent' v-model='uname'>
</div>
<div>
密码:<input type="text" @keyup.enter='handleSubmit' v-model='pwd'>
</div>
<div>
<input type="button" @click='handleSubmit' value="提交">
</div>
</form>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
uname: '',
pwd: '',
age: 0
},
methods: {
clearContent(){
// 按delete键的时候,清空用户名
this.uname = '';
},
handleSubmit(){
console.log(this.uname,this.pwd)
}
}
});
</script>
</body>
</html>
- 自定义按键修饰符全局
规则
:自定义按键修饰符名字是自定义的,但是对应的值必须是按键对应event.keyCode值
Vue.config.keyCodes.f1 = 65
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type='text/javascript' src='js/vue.js'></script>
</head>
<body>
<div id="app">
<input type="text" @keyup.f1='handle' v-model='msg'>
</div>
<script type='text/javascript'>
//a的ACSII值为65
Vue.config.keyCodes.f1 = 65
const app = new Vue({
el: "#app",
data: {
msg: ''
},
methods: {
handle: function (event) {
console.log(event.keyCode);
}
}
})
</script>
</body>
</html>
3.7 MVVM设计思想
MVVM
是Model-View-ViewModel的简写,是一种基于前端开发的架构模式。它本质上就是MVC 的改进版。Model代表数据,View代表视图,ViewModel是View和Model的桥梁,ViewModel会通过Data Binding(数据绑定)将model的改变渲染到视图中,会通过DOM Listener(DOM监听)监听view的变化更新数据。
Model(数据)
:data中的数据
数据可能是我们data中的固定数据,更多的是来自我们服务器,从网络上请求下来的数据。View(视图)
:vue模板
在我们前端开发中,通常就是DOM层。主要的作用是给用户展示各种信息。ViewModel(视图模型)
:Vue实例
视图模型层是View和Model沟通的桥梁。一方面它通过Data Binding(数据绑定),将Model的改变实时的反应到View中,另一方面它通过DOM Listener(DOM监听),当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。这两个方向都实现的,我们称之为数据的双向绑定。
注意
:
- data中所有的属性,最后都出现在了vm身上。
- vm身上所有的属性及Vue原型上所有属性,在Vue模板中都可以直接使用(谨记!!!)。如下图中
$options
可以通过<div>{{$options}}</div>
在模板中获得
MVC
是Model-View- Controller的简写,即数据-视图-控制器
实现: 在Controller里面把数据赋值给视图
缺点:不适合小型项目的开发,视图与控制器间的连接过于紧密,妨碍了他们的独立重用
3.8 Objcet.defineProperty()和Vue数据代理
3.8.1 Objcet.defineProperty()
Object.defineProperty()
方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
通过Objcet.defineproperty()添加的参数不参与枚举(遍历)、不可修改、不可删除,除非设置enumerable:true(控制属性是否可以枚举)、writable:true(控制属性是否可以被修改)、configurable:true(控制属性是否可以被删除)
语法:
Object.defineProperty(obj, prop, descriptor)
- obj 需要定义属性的对象。
- prop 需被定义或修改的属性名。
- descriptor 需被定义或修改的属性的描述符。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
</head>
<body>
<script>
let number = 18;
let person = {
name: '张三',
sex: '男',
};
Object.defineProperty(person, 'age', {
//value:18,
//enumerable:true //控制属性是否可以枚举,默认值为false
//writable:true //控制属性是否可以被修改,默认值为false
//configurable:true //控制属性是否可以被删除,默认值为false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get: function () {
console.log('有人读取age属性了');
return number;
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value) {
console.log('有人修改了age属性,且值是', value);
number = value;
},
});
console.log(person);
</script>
</body>
</html>
3.8.2 Vue数据代理
数据代理
:通过一个对象代理对另一个对象中属性的操作(读/写)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
</head>
<body>
<script>
let obj = { x: 100 };
let obj2 = { y: 200 };
//为obj2添加x属性
Object.defineProperty(obj2, 'x', {
get() {
//obj2代理了obj中x的数据
return obj.x;
},
set(value) {
obj.x = value;
},
});
</script>
</body>
</html>
Vue数据代理
:通过vm对象来代理data对象(以data对象为例)中属性的操作
Vue中数据代理的好处
:更加方便的操作data中的数据
基本原理
:通过Object.defineproperty()把data对象中所有属性都添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter,在getter/setter内部去操作(读/写)data中对应的属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
name: '尚硅谷',
address: '科技园',
},
});
</script>
</body>
</html>
3.9 条件渲染v-if、v-show
作用
:控制元素是否在页面中显示
v-if、v-else、v-else-if
写法
:
- v-if=“表达式”
- v-else-if=“表达式”
- v-else=“表达式”
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else>
Not A/B/C
</div>
适用
:切换频率较低的场景。
特点
:不展示的DOM元素直接被移除。
注意
:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”。
若想切换多个元素,可以把一个 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-show
写法
:v-show=“表达式”
适用
:切换频率较高的场景。
特点
:不展示的DOM元素未被移除,仅仅是使用样式display:none隐藏掉
v-if和v-show的区别
v-if
指令是直接销毁和重建DOM达到让元素显示和隐藏的效果
v-show
指令是通过修改元素的display属性让其显示或者隐藏
- v-if只有在为true的时候才会显示数据,执行if,否则,执行else。v-show当条件为false时,将元素的display属性设置为none
- 当需要在显示与隐藏之间切换很频繁时,使用v-show.当只有一次切换时,通过使用v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div v-if='score>=90'>优秀</div>
<div v-else-if='score<90&&score>=80'>良好</div>
<div v-else-if='score<80&&score>60'>一般</div>
<div v-else>比较差</div>
<div v-show='flag'>测试v-show</div>
<button v-on:click='handle'>点击</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*v-show的原理:控制元素样式是否显示 display:none*/
var vm = new Vue({
el: '#app',
data: {
score: 10,
flag: false
},
methods: {
handle: function(){
this.flag = !this.flag;
}
}
});
</script>
</body>
</html>
3.10 列表渲染v-for
3.10.1 基本列表
v-for作用
:用于遍历展示列表数据
语法
:v-for=“(item, index) in xxx” :key=“item.id” (:key 保证key是唯一的即可,:为v-bind:的简写)
key的作用
:帮助vue区分不同的元素,可以提高vue的渲染效率,确保key的唯一性,,尽可能在使用 v-for 时提供 key ,提高渲染效率
可遍历
:数组、对象、字符串(用的很少)、指定次数(用的很少)
- 数组: (item, index),item每一项的数据,index索引
- 对象: (value, key,index) , value属性值,key属性名,index索引
- 字符串:(char, index) , char字符串中的每一个字符,index每一个字符的索引
- 指定次数:(number, index) ,number字符串中的每一个字符,index每一个字符的索引
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<h2>1.遍历数组</h2>
<li v-for="(item,index) in persons">
{{item.name}}--{{item.age}}--{{index}}
</li>
<h2>2.遍历对象</h2>
<li v-for="(value,key) in car">{{key}}--{{value}}</li>
<h2>3.遍历字符串</h2>
<li v-for="(char, index) in str">{{char}}--{{index}}</li>
<h2>4.遍历指定次数</h2>
<li v-for="(number, index) in 5">{{number}}--{{index}}</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
],
car: {
name: '奥迪',
price: '30万',
},
str: 'hello',
},
methods: {},
});
</script>
</body>
</html>
注意
:类似于 v-if,你也可以利用带有 v-for 的 来循环渲染一段包含多个元素的内容。比如:
<ul>
<template v-for="item in items"f>
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
3.10.2 虚拟DOM、Diff算法、Key
原始JS实现功能
:数据 => 真实DOM
Vue实现功能
:数据 => 虚拟DOM(内存中的数据)=> 真实DOM
虚拟DOM
可以理解为虚拟节点,是一个用来描述真实DOM结构的js对象。
- 优点:减少了DOM操作,减少了回流与重绘,保证性能的下限,比正常的DOM性能更好
- 缺点:首次渲染DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。
Diff算法
是用于比较新旧虚拟节点之间差异的一种算法,每个虚拟节点都有一个唯一标识key,通过对比新旧节点的key来判断节点是否改变,将两个节点不同的地方存储在patch对象中,最后利用patch记录的消息局部更新DOM。
Key
主要用在虚拟Dom算法中,每个虚拟节点都有一个唯一标识Key,通过对比新旧节点的key来判断节点是否改变,可以大大提高渲染效率。
-
对比规则:
(1)旧节点中找到了与新节点相同的key:
①若新节点中内容没变(和旧节点一样), 直接复用之前的旧节点生成真实DOM
②若新节点中内容变了(和旧节点不一样), 则生成新的真实DOM,随后替换掉页面中之前旧节点生成的真实DOM
(2) 旧节点中未找到与新节点相同的key,则创建新的真实DOM,随后渲染到到页面。 -
用index作为key可能会引发的问题:
(1).若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ,这时界面效果没问题, 但渲染效率低。
(2).如果结构中还包含输入类的DOM,会产生错误DOM更新,使界面有问题 -
开发中如何选择key:
(1).最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
(2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
由下图可知,第一次有三项数据渲染为真实DOM,第二次渲染中的数据比第一次多了一项,此时Vue使用Diff算法按照规则在虚拟DOM中依次对比每一项数据,发现前三项数据key和内容都相同,则复用第一次的真实DOM,最后一项数据没有则需渲染新数据为真实DOM,提高了渲染效率。
3.10.3 列表过滤与排序
列表过滤
:可以使用watch也可以使用计算属性,使用计算属性更加简单方便一点(见4.3 和4.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">
<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
//用watch实现
//#region
/* 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:{
//初始化自动调用handler,输入框为空,handler的val为空字符串
immediate:true,
//'abcd'.indexOf('') =0 一个字符串indexOf空字符串结果为0,filPerons就获取到了persons所有值
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
}) */
//#endregion
//用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>
列表排序
<body>
<div id="test">
<input type="text" v-model="searchName">
<ul>
<li v-for="(p, index) in filterPersons" :key="index">
{{index}}---{{p.name}}---{{p.age}}
</li>
</ul>
<button @click="setOrderType(1)">年龄升序</button>
<button @click="setOrderType(2)">年龄降序</button>
<button @click="setOrderType(0)">还原顺序</button>
</div>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#test',
data: {
searchName: '',
orderType: 0, //0代表原本, 1代表升序, 2代表降序
persons: [
{name: 'Tom', age: 10},
{name: 'Jack', age: 16},
{name: 'Rose', age: 12},
{name: 'Aka', age: 18}
]
},
computed: {
filterPersons(){
// 1. 取出相关数据
const {searchName, persons, orderType} = this; // 解构赋值
let fPersons;
// 2. 对persons进行过滤
fPersons = persons.filter(p => p.name.indexOf(searchName) !== -1);
// 3. 排序
if(orderType !== 0){
fPersons.sort(function(p1, p2){ // 返回负数p1在前,返回正数p2在前
if(orderType === 2){
return p2.age - p1.age; // 降序
}else{
return p1.age - p2.age; // 升序
}
})
}
return fPersons;
}
},
methods: {
setOrderType(orderType){
this.orderType = orderType;
}
}
})
//数组排序
//sort排序允许接受一个参数(函数),这个函数接受2个形参a、b(数组中的数字),并且通过冒泡的方式比较
// let arr =[2,3,4,6,65,7]
// arr.sort((a,b)=>{
// //return a-b//升序
// return b-a//降序
// })
</script>
</body>
3.10.4 Vue监测数据的原理
Vue监测数据
:vue会监视data中所有层次的数据。
如何监测对象中的数据
?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
//向响应式对象中添加一个property,并确保这个新property同样是响应式的,且触发视图更新。
//target目标对象(target不能是vue实例或实例中的data第一层(跟)数据对象),propertyName属性名,value属性值
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
变更数组的方法
Vue将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。变更方法包括:push()、pop()、shift()、unshift()、splice()、sort()、reverse()。这些方法会变更原数组
变更方法详细介绍:
- push / pop: 末尾添加、删除,改变原数组, 返回添加之后新数组的长度或删除的这个值
- unshift / shift: 头部添加、删除,改变原数组,返回添加之后新数组的长度或删除的这个值
- sort/ reverse: 排序、反转,改变原数组
- splice(start开始的位置, number删除/更改的个数, 替换的值): 一般用于删除或更改数组中的元素,返回删除或更改元素组成的数组,改变原数组
替换数组的方法
非变更方法
:不会变更原始数组,而总是返回一个新数组。非变更方法包括: filter()、concat() 和 slice()。当使用非变更方法时,用一个含有相同元素的数组去替换原来的数组是非常高效的操作,例如:
//h !== '抽烟'的元素组成一个新数组
this.student.hobby = this.student.hobby.filter((h) => {
return h !== '抽烟';
});
非变更方法详细介绍:
- filter(item => true(满足条件为true))返回的是满足条件的一个新数
- concat: 连接数组,不影响原数组, 浅拷贝
- slice(start开始的索引, end结束的索引): 返回截断后的新数组,不改变原数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button> <br />
<button @click="addSex">添加性别属性,默认值:男</button> <br />
<button @click=" student.sex='女' ">修改性别为女</button> <br />
<button @click="addFriend">在列表首位添加一个朋友</button> <br />
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button><br />
<button @click="addHobby">添加一个爱好</button> <br />
<button @click="updateFirstHobby">修改第一个爱好为:开车</button> <br />
<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3>性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">{{h}}</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends " :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['抽烟', '喝酒', '烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 },
],
},
},
methods: {
addSex() {
//Vue.set(this.student, 'sex', '男');
this.$set(this.student, 'sex', '男');
},
addFriend() {
this.student.friends.unshift({ name: 'tom', age: 34 });
},
updateFirstFriendName() {
this.student.friends[0].name = '张三';
},
addHobby() {
this.student.hobby.push('打游戏');
},
updateFirstHobby() {
//this.student.hobby.splice(0, 1, '开车');
this.$set(this.student.hobby, 0, '开车');
},
removeSmoke() {
//arr.filter(item => true(满足条件为true));//返回的是满足条件的一个新数组
this.student.hobby = this.student.hobby.filter((h) => {
return h !== '抽烟';
});
},
},
});
</script>
</body>
</html>
3.11 其他指令语法
3.11.1 v-cloak指令
插值表达式存在的问题:“闪动”
如何解决该问题:使用v-cloak
指令
ref
: 为某个元素注册一个唯一标识, vue对象通过$refs属性访问这个元素对象
v-cloak指令的用法
:
- 提供样式
[v-cloak]{
display: none;
}
- 在插值表达式所在的标签中添加v-cloak指令
背后的原理
:先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后再显示最终的结果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app">
<div v-cloak>{{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
msg: 'Hello Vue'
}
});
</script>
</body>
</html>
3.11.2 数据绑定指令语法v-text、v-html、v-pre
如何理解响应式?
html5中的响应式(尺寸数据的变化导致屏幕样式的变化)
数据的响应式(数据的变化导致页面内容的变化)
数据绑定
:将数据填充到标签中,默认是响应式的,即data中属性发生改变,插值内容也会随之变化。
指令语法
-
v-text
更新元素的 textContent
语法:v-text=‘msg’
作用:向其所在的节点中渲染文本内容。
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。 -
v-html
输出真正的 HTML
语法: `v-html=‘msg’
作用:向指定节点中渲染包含html结构的内容。
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
与插值语法的区别:
(1). v-html会替换掉节点中所有的内容,{{xxx}}则不会。
(2). v-html可以识别html结构。
严重注意:v-html有安全性问题!!!!
(1). 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2). 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
v-pre指令:
v-pre
填充原始信息 跳过其所在节点的编译过程。
显示原始信息,跳过分析编译过程,可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译。
语法:{{msg}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div v-text='msg'></div>
<div v-html='msg1'></div>
<div v-pre>{{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello Vue',
msg1: '<h1>HTML</h1>'
}
});
</script>
</body>
</html>
3.11.3 v-once指令
v-once指令
只编译一次,显示内容之后不再具有响应式功能v-once的应用场景
:以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。语法
:
<h2 v-once>{{msg}}</h2>
4 Vue常用特性
4.1 表单操作
input中的type类型
- text单行文本
- textarea多行文本
- select 下拉多选
- radio 单选框
- checkbox多选框
收集表单数据:
- 若
<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值。 - 若
<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值 - 若
<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
v-model的三个修饰符:
number
:表单输入的字符串转化为数值trim
:去掉开始和结尾的空格lazy
:失去焦点再收集数据
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<form @submit.prevent="demo">
账号:
<input type="text" v-model.trim="userInfo.account" />
<br />
密码:
<input type="password" v-model="userInfo.password" />
<br />
性别: 男
<input type="radio" name="sex" v-model="userInfo.sex" value="male" />
女
<input type="radio" name="sex" v-model="userInfo.sex" value="female" />
<br />
爱好: 学习
<input type="checkbox" v-model="userInfo.hobby" value="study" />
打游戏
<input type="checkbox" v-model="userInfo.hobby" value="game" />
<br />
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="wuhan">武汉</option>
</select>
<br />
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea>
<br />
<input type="checkbox" v-model="userInfo.agree" />
阅读并接受
<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
sex: 'female',
hobby: [],
city: 'beijing',
other: '',
agree: '',
},
},
methods: {
demo() {
console.log(JSON.stringify(this.userInfo));
},
},
});
</script>
</html>
4.2 自定义指令
自定义指令
:内置指令不满足需求,需要自己定义指令使用
需求1
:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2
:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
定义语法
:
(1) 局部指令
//写法一:
new Vue({
directives:{指令名:回调函数}
})
//写法二:
new Vue({
directives{指令名:配置对象}
})
例子
directives: {
//写法一:回调函数写法
//el,指令所绑定的元素. binding,一个对象,包含以下属性:name:指令名,不包括 v- 前缀。value:指令的绑定值 arg:传给指令的参数,
big(el, binding) {
el.innerText = binding.value * 10;
},
//写法二:配置对象写法 在不使用inserted钩子函数时可以使用方法一简写
big: {
//指令与元素成功绑定时(一上来)
bind(el, binding) {
el.innerText = binding.value * 10;
},
//指令所在元素被插入页面时
inserted(el, binding) {},
//指令所在的模板被重新解析时
update(el, binding) {
el.innerText = binding.value * 10;
},
},
},
(2) 全局指令
//方法一
Vue.directive(指令名,配置对象)
//方法二
Vue.directive(指令名,回调函数)
配置对象中常用的3个钩子函数
:
- bind:指令与元素成功绑定时调用。
- inserted:指令所在元素被插入页面时调用
- update:指令所在模板结构被重新解析时调用。
备注
:
指令定义时不加v-,但使用时要加v-;
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
<!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>{{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.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}) */
new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:1
},
directives:{
//big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
big(el,binding){
console.log('big',this) //注意此处的this是window
el.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>
</html>
4.3 计算属性computed
计算属性computed
:所需要的属性不存在,要通过已有属性(data中的属性或外部传入的prop)计算得来
使用
:在computed属性对象中定义计算属性的方法,在页面中使用{{方法名}}来显示计算的结果
原理
:底层借助了Objcet.defineproperty()方法提供的getter和setter。
get有什么作用?
当读取computed中的方法时,get就会被调用,且返回值就作为该方法的值
get函数什么时候执行?
(1) 初次读取时会执行一次。
(2) 当依赖的数据发生改变时会被再次调用。
优势
:与methods实现相比,computed内部有缓存机制(复用),效率更高,调试方便,方法methods不缓存。
备注
:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
计算属性完整写法
<div>{{fullName}}</div>
computed: {
//我们提供的函数fullName将用作vm.fullName的getter函数上,计算属性默认只有 getter
fullName: {
get(){//get可以省略
//当fullName所依赖的firstNameh和lastName发生改变时会被再次调用
return this.firstName + '-' + this.lastName;
},
set(value){
//当fullName被修改时调用set
console.log(value)
}
}
}
常用简写(只考虑读取,不考虑修改的情况下)
<div>{{fullName}}</div>
computed: {
fullName() {
return this.firstName + '-' + this.lastName;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="app">
<div> 名:<input type="text" v-model="firstName" /> </div>
<div> 姓:<input type="text" v-model="lastName" /> </div>
<div>{{fullName}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
firstName: 'Jim',
lastName: 'Green',
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
},
},
});
</script>
</body>
</html>
4.4 监听属性watch
监听属性watch
:监听具体数据变化,当数据属性变化时, 回调函数handler自动调用, 在函数内部进行计算
写法
:
(1)在vue实例vm中传入watch配置来监视指定的属性
(2)通过vm对象的$watch()
应用场景
:数据变化时执行异步或开销较大(比较耗时)的操作
注意
:监听的属性必须在vm中存在,才能进行监听
监听属性watch的写法
var vm = new Vue({
el: '#app',
data: {
firstName: 'Jim',
lastName: 'Green',
fullName: 'Jin Green',
},
//侦听器监听输入信息的变化
watch: {
//xxx为vm实例中存在且被监听的属性
// xxx: {
// immediate: false, //初始化时让handler调用一下
// //handler(固定函数) 什么时候调用?当函数中的数据发生改变时
// handler() {
// ....
// },
// },
//简写 (当只需要handler属性时)
firstName(val) {
this.fullName = val + ' ' + this.lastName;
},
lastName(val) {
this.fullName = this.firstName + ' ' + val;
},
},
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span>名:</span>
<span>
<input type="text" v-model='firstName'>
</span>
</div>
<div>
<span>姓:</span>
<span>
<input type="text" v-model='lastName'>
</span>
</div>
<div>{{fullName}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
firstName: 'Jim',
lastName: 'Green',
fullName: 'Jin Green'
},
watch: {
firstName(val) {
this.fullName = val + ' ' + this.lastName;
},
lastName(val) {
this.fullName = this.firstName + ' ' + val;
},
}
});
</script>
</body>
</html>
深度监听
:
(1) Vue中的watch默认不监测对象内部值的改变,只检测第一层。
(2) 配置deep:true可以监测对象内部值改变,可以检测多层。
备注
:
(1). Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
(2). 使用watch时根据数据的具体结构,决定是否采用深度监视。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a++</button>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
numbers: {
a: 1,
b: 2,
},
},
watch: {
numbers: {
//深度监听
deep: true,
handler() {
console.log('numbers改变了');
},
},
},
});
</script>
</body>
</html>
4.5 computed与watch、methods的区别
computed
:计算属性,依赖其他属性,当其他属性改变的时候,下一次获取computed值时也会改变,computed的值会有缓存
watch
:监听属性,监听具体数据变化,当数据属性变化时, 回调函数handler自动调用, 在函数内部进行计算
methods
: 该属性用于在Vue对象中定义方法。
computed与watch区别
:
- computed能完成的功能,watch都可以完成。watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
- 当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
- 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
计算属性computed在大多数情况下更合适,但当需要在数据变化时执行异步或开销较大的操作时,使用watch更适用。
computed与methods区别
:
计算属性是基于它们的依赖进行缓存,如果多次使用时,计算属性只会调用一次,性能上计算属性明显比methods好,如果依赖改变则重新缓存,而方法不缓存
4.6 过滤器filter
定义
:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法
:
全局过滤器
:
Vue.filter(filterName, function(value[,arg1,arg2,...]){
// 进行一定的数据处理
return newValue
})
局部过滤器
:
new Vue{
filters:{
filterName(value){
return newValue
}
}
}
//使用方法
<div>{{myData | filterName}}</div>
<div>{{myData | filterName(arg)}}</div>
备注
:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据, 是产生新的对应的数据
示例
<body>
<div id="demo">
<h2>显示格式化的日期时间</h2>
<p>{{date}}</p>
<p>完整版:{{date | dateString}}</p>
<p>年月日:{{date | dateString('YYYY-MM-DD')}}</p>
<p>时分秒:{{date | dateString('HH:mm:ss')}}</p>
</div>
<script src="../js/vue.js"></script>
//引入时间格式化插件
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.js"></script>
<script>
Vue.filter('dateString', function(value, format='YYYY-MM-DD HH:mm:ss'){
return moment(value).format(format);
});
new Vue({
el: '#demo',
data: {
date: new Date()
}
})
</script>
</body>
使用dayjs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>过滤器</title>
<script type="text/javascript" src="../js/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.11.0/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在是:{{time | timeFormater}}</h3>
<!-- 过滤器实现(传参) -->
<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">尚硅谷</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局过滤器
Vue.filter('mySlice', function (value) {
return value.slice(0, 4);
});
new Vue({
el: '#root',
data: {
time: 1621561377603, //时间戳
msg: '你好',
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss');
},
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss');
},
},
//局部过滤器
filters: {
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
// console.log('@',value)
return dayjs(value).format(str);
},
},
});
new Vue({
el: '#root2',
data: {
msg: 'hello!',
},
});
</script>
</html>
4.7 Vue生命周期
生命周期
:事物从诞生到消亡的整个过程
vue生命周期
:Vue 实例从创建到销毁的过程,vue生命周期钩子
:在达到某一阶段时去触发的函数
生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的,生命周期函数中的this指向是vm 或 组件实例对象。
它可以总共分为8个阶段
:创建前/后, 挂载前/后,更新前/后,销毁前/销毁后
create阶段:vue实例被创建
mount阶段: vue实例被挂载到真实DOM节点
update阶段:当vue实例里面的data数据变化时,触发组件重新渲染
destroy阶段:vue实例被销毁
1、beforeCreate(创建前)
表示实例完全被创建出来之前,vue 实例的挂载元素$el和数据对象 data 都为 undefined,还未初始化。
2、created(创建后)
数据对象 data 已存在,可以调用 methods 中的方法,操作 data 中的数据,但 dom 未生成,$el 未存在 。
3、beforeMount(挂载前)
vue 实例的 $el 和 data 都已初始化,挂载之前为虚拟的 dom节点,模板已经在内存中编辑完成了,但是尚未把模板渲染到页面中。data.message 未替换。
4、mounted(挂载后)
vue 实例挂载完成,data.message 成功渲染。内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,DOM 渲染在 mounted 中就已经完成了。
5、beforeUpdate(更新前)
当 data 变化时,会触发beforeUpdate方法 。data 数据尚未和最新的数据保持同步。
6、updated(更新后)
当 data 变化时,会触发 updated 方法。页面和 data 数据已经保持同步了。
7、beforeDestory(销毁前)
组件销毁之前调用 ,在这一步,实例仍然完全可用。
8、destoryed(销毁后)
组件销毁之后调用,对 data 的改变不会再触发周期函数,vue 实例已解除事件监听和 dom绑定,但 dom 结构依然存在。
常用的生命周期方法
:
mounted():初始化操作,发送ajax请求, 启动定时器、绑定自定义事件、订阅消息等异步任务
beforeDestroy(): 做收尾工作, 清除定时器、解绑自定义事件、取消订阅消息等
关于销毁Vue实例
:
销毁后借助Vue开发者工具看不到任何信息
销毁后自定义事件会失效,但原生DOM事件依然有效
一般不会在beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程了。
vue生命周期在真实场景下的业务应用
created:vue实例被创建
mounted: 挂载元素,获取dom节点
nextTick: 针对单一事件更新数据后立即操作dom
updated: 任何数据的更新,做统一的业务逻辑处理
watch: 监听具体数据变化,并做相应的处理
更多推荐
所有评论(0)