Vue
渐进式 JavaScript 框架,用来动态构建用户界面
特点:
- 遵循 MVVM 模式
- 编码简洁,体积小,运行效率高,适合 移动/PC 端开发
- 它本身只关注 UI,可以轻松引入 vue 插件或其它第三方库开发项目
- 采用组件化模式,提高代码复用率、且让代码更好维护
- 声明式编码,让编码人员无需直接操作 DOM,提高开发效率
- 使用虚拟 DOM 和 Diff 算法,尽量复用 DOM节点
借鉴 angular 的 模板 和 数据绑定 技术
借鉴 react 的 组件化 和 虚拟DOM 技术
引入 Vue
本地引入
CDN 引入
快速创建 Vue 的实例
概念性的问题:
- 想让 Vue 工作,就必须创建一个Vue实例,且要传入一个配置对象
- root 容器里的代码被称为 Vue 模板
- Vue 实例和容器是一一对应的
- 真实开发中只有一个 Vue 实例,并且会配合着组件一起使用
el: 又称挂载点,可认为是 element 的简写,创建一个 vue实例 得知道是在哪一块元素上创建 Vue实例 ,对哪一块视图进行操作
var vm = new Vue({
el: '#root',
data: {
msg: '123'
},
methods: {
test() {
}
}
})
el 和 data 的两种写法
data 与 el 的2种写法
- el 有2种写法
(1) new Vue 时候配置 el 属性
(2) 先创建 Vue 实例,随后再通过 vm.$mount(’#root’) 指定 el 的值 - data 有2种写法
(1) 对象式
(2) 函数式
如何选择: 目前哪种写法都可以,以后学习到组件时,data 必须使用函数式,否则会报错 - 一个重要的原则:
由 Vue 管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是 Vue 实例了
Vue 管理的函数举例:data(), method方法中定义的函数等
Vue 的插值语法
{{js 表达式}} 用于解析标签体内容
<div id="test">
<input type="text" v-model="msg"><br>
<!--调用函数-->
<p>Hello {{msg.toUpperCase()}} </p>
</div>
<script src="CDN 引入或者本地引入vue.js"></script>
<script>
const vm = new Vue({ // 配置对象 options
// 配置选项(option)
el: '#test', // element: 指定用vue来管理页面中的哪个标签区域
data: { // 数据(model)
msg: 'World'
}
})
</script>
理解 MVVM
M: 模型(Model) :data中的数据
V: 视图(View) :模板代码 (不是静态页面) (两个语法:指令,大括号表达式)
VM: viewModel: 视图模型 (Vue的实例)
- Dom Listeners (Dom 监听)
- Data Bindings (数据绑定)
MVVM 本质上是 MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。
模型 model 指的是后端传递的数据,视图 view 指的是所看到的页面。
视图模型 viewModel 是 mvvm 模式的核心,它是连接 view 和 model 的桥梁。它有两个方向:
将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定
将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听
这两个方向都实现的,我们称之为数据的双向绑定
- data 中所有的属性,最后都出现在了 vm 身上。
- vm 身上所有的属性 及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用
理解模板
- 动态 html 页面
- 包含了 js 代码
插值语法: {{xxx}} 用于解析标签体的内容, 可以向页面输出数据
指令语法: v- 开头的自定义标签属性
数据绑定
v-bind: 数据绑定 (指定变化的属性值)
v-bind:xxx='yy' // yy 会被作为表达式解析
// 简写
:xxx='yy'
特点: 数据只能从 data 流向页面, 是单向数据绑定
v-model: 双向的数据绑定
数据能从 data 流向页面, 还能从页面流向 data
只能应用于表单类元素 (输入类元素) 上
收集表单数据:
若:<input type="text"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="password"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model默认收集的是value值,因为此类型无法输入内容,则无法通过输入得到value值,所以要给标签手动添加value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么默认读取的的就是checked是否被勾选(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
v-model 的三个修饰符
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
v-model:value='yy'
// 简写 因为 v-model 默认收集的就是 value 值
v-model='yy'
v-text: 向所在节点渲染文本内容
和插值语法的区别: v-text 会替换节点的内容, 插值语法不会
v-html: 像节点中渲染包含 html 结构的内容
和插值语法的区别:
- v-html 会替换节点所有内容, 插值语法不会
- v-html 可以识别 html 结构
- 注意: v-html 有安全性问题, 在网站动态渲染任意 html 是非常危险的, 容易遭受 XSS 攻击, 要在可信的内容使用 v-html, 不要使用在用户提交的内容上
<body>
<div id='app'>
<h2>1. 大括号表达式</h2>
<p>{{msg}}</p> <!--textContent -->
<p>{{msg.toUpperCase()}}</p>
<p v-html="msg"></p> <!--innerHTML -->
<p v-text="msg"></p> <!--textContent -->
<p v-text="msg.toUpperCase()"></p>
<h2>2. 指令一: 强制数据绑定</h2>
<img src="imgUrl" alt="Vue"> <!--无法显示图片,没有识别成js表达式 -->
<img v-bind:src="imgUrl" alt="Vue"> <!--属性值识别成js表达式 -->
<img :src="imgUrl" alt="Vue">
<h2>3. 指令二: 绑定事件监听</h2>
<button v-on:click="test1">test1</button>
<button @click="test1">test1</button>
<button @click="test2('abc')">test2</button> <!--可以传参数 -->
<button @click="test2(msg)">test2</button>
</div>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: '<a href="http:www.baidu.com">I Will Back!</a>',
imgUrl: "https://cn.vuejs.org/images/logo.png"
},
methods: {
test1() {
alert('heheh');
},
test2(content){
alert(content);
}
}
})
</script>
</body>
Object.defineProperty()
<!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给对象添加属性
Object.defineProperty(person, 'age', {
//里面三个参数(对象,属性名,options配置对象{})
// value: 18,
// enumerable: true, //控制属性是否可以枚举(遍历),默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取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>
事件
v-on: 绑定事件监听
绑定指定事件名的回调函数
v-on:click='xxx'
v-on:keyup='xxx(参数)'
v-on:keyup.enter='xxx'
// 简写
@click='xxx'
@keyup='xxx'
@keyup.enter='xxx'
事件修饰符
- prevent:阻止默认事件
- stop:阻止事件冒泡
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
键盘事件
Vue 中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
系统修饰键(用法特殊):tab、ctrl、alt、shift、meta(win)
(1) 配合 keyup 使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2) 配合 keydown 使用: 正常触发事件
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<input type="text" placeholder="输入" @keydown.huiche="showInfo">
<!-- <input type="text" placeholder="提示输入" @keydown.enter="showInfo"> -->
<!--当有需求要同时按下两个键才能生效时
<input type="text" placeholder="提示输入" @keyup.ctrl.y="showInfo"> -->
</div>
计算属性
只要 data 中的数据发生改变, Vue 会重新解析模板
- 在 computed 属性对象中定义计算属性的方法
- 在页面中使用
{{方法名}}
来显示计算的结果
原理:底层借助了 Objcet.defineproperty
方法提供的 getter
和 setter
get 函数什么时候执行?
(1) 初次读取时会执行一次。
(2) 当依赖的数据发生改变时会被再次调用。
优势:与 methods 实现相比,内部有缓存机制, 效率更高,调试方便。
备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变
//完整写法
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]
}
}
简写 (只读不改就可以用简写)
//简写
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
监视属性
通过 vm 的 $watch() 或 watch 配置来监视指定的属性 (可以监视计算属性中的属性)
-
属性变化时, 回调函数自动调用, 在函数内部进行计算
-
监视的属性必须存在,才能进行监视
两种写法:
-
new Vue 时传入 watch 配置
watch: { isHot: { immediate: true, //初始化时让 handler 调用一下 //当 isHot 发生改变时, 调用 handler handler(newValue, oldValue){ console.log('isHot被修改了',newValue,oldValue) } } }
-
通过 vm.$watch 监视
vm.$watch('isHot',{ immediate: true, //初始化时让 handler 调用一下 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } })
深度监视
监视某个 number 的 a 属性
'numbers.a':{
deep:true,
handler(){
console.log('numbers改变了')
}
}
(1) Vue 中的 watch 默认不监测对象内部值的改变 (一层)
(2) 配置 deep:true 可以监测对象内部值改变 (多层)
备注:
(1) Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
(2) 使用 watch 时根据数据的具体结构,决定是否采用深度监视
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
一般写法
isHot:{
// immediate:true, //初始化时让 handler 调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
简写
//简写
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
计算属性和监视属性的区别
- computed 能完成的功能,watch 都可以完成
- watch 能完成的功能,computed 不一定能完成,例如: watch 可以进行异步操作
重要原则
- 所被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
- 所有不被 Vue 所管理的函数 (定时器的回调函数、ajax 的回调函数等、Promise 的回调函数), 最好写成箭头函数,这样 this 的指向才是 vm 或组件实例对象
样例代码
<body>
<div id="demo">
姓:<input type="text" placeholder="First Name" v-model="firstName"><br>
名:<input type="text" placeholder="Last Name" v-model="lastName"><br>
姓名1(单向):<input type="text" placeholder="Full Name1" v-model="fullName1"><br>
姓名2(单向):<input type="text" placeholder="Full Name2" v-model="fullName2"><br>
姓名3(双向):<input type="text" placeholder="Full Name3" v-model="fullName3"><br>
<p>{{fullName1}}</p>
<p>{{fullName1}}</p>
<p>{{fullName1}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#demo',
data: {
firstName: 'A',
lastName: 'B',
fullName2: 'A B'
},
computed: {
// 什么时候执行:初始化显示 / 相关的data属性数据发生改变
// 做什么的: 计算并返回当前属性的值
fullName1() { //计算属性中的一个方法,方法的返回值作为属性 (回调函数)
console.log('fullName1()') // 必然会掉用
return this.firstName + ' ' + this.lastName;
},
fullName3: { //不是函数是一个对象,里面有两个方法
get() {
// 回调函数 : 1. 你定义的 2. 你没有调用 3. 但他最终执行了
// 1. 什么时候调用? 2. 用来做什么
// 回调函数 当需要读取当前属性值时回调 根据相关的数据计算并返回当前属性的值
return this.firstName + ' ' + this.lastName;
},
set(value) {
// 回调函数 监视当前属性值的变化 当属性值发生改变时回调 更新相关的属性数据
const names = value.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
},
watch: { //配置监视
firstName: function(value){ // firstName 发生了变化
console.log(this); //就是vm对象
this.fullName2 = value + ' ' + this.lastName;
}
}
});
vm.$watch('lastName', function(value) {
//更新fullName2
this.fullName2 = this.firstName + ' ' + value;
})
</script>
绑定样式
1. class 样式
写法 :class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2. style 样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式</title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
.atguigu1 {
background-color: yellowgreen;
}
.atguigu2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.atguigu3 {
border-radius: 20px;
}
</style>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<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>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: '我要进大厂',
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: true,
atguigu2: false,
},
styleObj: {
fontSize: '40px',
color: 'red',
},
styleObj2: {
backgroundColor: 'orange'
},
styleArr: [
{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'gray'
}
]
},
methods: {
changeMood() {
//随机切换心情
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
//Math.random() 返回 0 ~ 1 之间的随机数,包含 0 不包含 1。
//Math.floor(x) 对 x 进行下舍入,即向下取整。
this.mood = arr[index]
}
},
})
</script>
</html>
条件渲染
v-if
v-else-if
v-else
适用于: 切换频率较低的场景
特点: 不展示的 DOM 元素直接被移除
v-show
适用于: 切换频率较高的场景。
特点: 不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉
注: v-if 的时候,元素可能无法获取到,而使用 v-show 一定可以获取到
比较 v-if 和 v-show
- 如果需要频繁切换 v-show 较好
- 当条件不成立时, v-if 的所有子节点不会解析
// v-if 和 template 配合使用
<template v-if="n === 1">
<h2>hi</h2>
<h2>hi</h2>
</template>
列表渲染
v-for
- 用于展示列表数据
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名(形参)v-for
还支持一个可选的第二个参数,即当前项的索引。- 语法:v-for="(item, index) in xxx" :key=“yyy”
- 你也可以用
of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法
key 的原理
有相同父元素的子元素必须有独特的 key, 重复的 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 是没有问题的。
3.如果不写 key,则默认为 index
列表过滤
用 computed 实现 (推荐)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 1.收集用户输入
2.拿用户输入的东西进行数据匹配
-->
<!-- 准备好一个容器-->
<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>
用 watch 实现
//将下面代码放入上方代码块中
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调用一下,因为开始filPerons: []为空数组,
// 页面上什么内容都不显示,而恰巧handler(val)获取的值为空,
// 而所有字符串第0位不仅包含自己第一个字符,也包含着一个空字符,p.name.indexOf('')为0
// 所以handler(val)函数判断成功,页面显示内容
handler(val) {
this.filPerons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
// 判断p.name中是否包含有val值,如果不等于-1,则说明包含
// filter返回一个全新的数组,原数组不变
})
}
}
}
})
列表排序
列表排序与列表过滤一般是连在一起使用的
<!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 !== 0) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
</script>
</html>
监视数据原理
更新时的问题
为什么 this.person[0] = {id:'001', name:'马老师', age:50, sex:'男'}
Vue 没有检测到改变 ?
Vue监视数据的原理:
- vue 会监视 data 中所有层次的数据。
- 如何监测对象中的数据?
通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。
(1) 对象中后追加的属性,Vue 默认不做响应式处理,即改变其值页面无变化
(2) 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
// vm.$set(添加目标,‘添加属性/索引值’,‘属性值’) - 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1) 调用原生对应的方法对数组进行更新
(2) 重新解析模板,进而更新页面 - 在 Vue 修改数组中的某个元素一定要用如下方法:
1.使用这些API: push()、pop()、shift() (删除第一个)、unshift()、splice() (删除, 替换, 插入 指定位置元素)、sort()、reverse()
2.Vue.set() 或 vm.$set()
3.如果不使用上面方法而是直接对数组赋值则 vue 无法响应,例如:
this.student.hobby[0] = “开车”,数据已被更改,但页面中无任何反应
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象(vm._data) 添加属性
对于 filter 如何让 Vue 监测? 直接把过滤后的数组直接传给原数组
array.splice(index, howmany, item1, ....., itemX)
// index 必须, 整数,指定在什么位置添加/删除项目,使用负值指定从数组末尾开始的位置
// howmany 可选, 要删除的项目数, 如果设置为 0,则不会删除任何项目
// 可选, 要添加到数组中的新项目
过滤器
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
- 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
- 使用过滤器:{{ xxx | 过滤器名 }} 或 v-bind:属性 = “xxx | 过滤器名”
备注:
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据, 是产生新的对应的数据
- 不是必须的属性,完全可以用 methods 和 computed 实现下面代码中的过滤功能
- 当全局过滤器和局部过滤器重名时,会采用局部过滤器
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>过滤器</title>
<script type="text/javascript" src="../js/vue.js"></script>
<!-- <script type="text/javascript" src="../js/dayjs.min.js"></script> -->
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>计算属性实现:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>methods实现:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>过滤器实现:{{time | timeFormater}}</h3> //将time当参数传给timeFormater
<!-- 过滤器实现(传参) -->
<h3>过滤器实现(传参):{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">{{msg}}</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局过滤器,必须在new Vue({})之前
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) {
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
// }
// }
//局部过滤器
filters: {
//如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用
//传入的参数'YYYY_MM_DD'
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el: '#root2',
data: {
msg: 'hello,atguigu!'
}
})
</script>
</html>
常用指令
v-cloak 指令
- 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性
- 使用 css 配合 v-cloak 可以解决网速慢时页面展示出模板 {{xxx}} 的问题
<!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">
new Vue({
el: '#root',
data: {
name: '尚硅谷'
}
})
</script>
</html>
v-once
- v-once 所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
<div id="root">
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
v-pre
用于vue性能优化
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2 >当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
自定义指令
需求1:定义一个 v-big 指令,和 v-text 功能类似,但会把绑定的数值放大 10 倍
需求2:定义一个 v-fbind 指令,和 v-bind 功能类似,但可以让其所绑定的 input 元素默认获取焦点
语法:
// 局部指令
new Vue({
directives: {
指令名: 配置对象
}
})
new Vue({
directives: {
指令名: 回调函数
}
})
// 全局指令
Vue.directive(指令名, 配置对象)
Vue.directive(指令名, 回调函数)
二、配置对象中常用的3个回调:
(1) bind: 指令与元素成功绑定时调用。
(2) inserted: 指令所在元素被插入页面时调用。
(3) update: 指令所在模板结构被重新解析时调用。
三、备注:
1. 指令定义时不加 v-,但使用时要加 v-
2. 指令名如果是多个单词,要使用 kebab-case 命名方式,别忘了加"", 不要用 camelCase 命名
3. 所有指令相关的 this 都是 window
<!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-number="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
//定义全局指令
/* Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
Vue.directive('big',function(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
console.log(element, binding)
// console.log('big', this)
//注意此处的this是window
element.innerText = binding.value * 10
},)
*/
new Vue({
el: '#root',
data: {
name: '尚硅谷',
n: 1
},
directives: {
//指令名如果是多个单词,别忘了加"",方法内部放的是"key",value值,大部分情况""可省略,但加了-之后引号必须带。
// 全写是: 'big-number':function(element,binding){}
/* 'big-number'(element,binding){
// console.log('big')
element.innerText = binding.value * 10
}, */
// big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
big(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
console.log(element, binding)
// console.log('big', this)
//注意此处的this是window
element.innerText = binding.value * 10
},
fbind: {
//指令与元素成功绑定时(一上来)调用
bind(element, binding) {//两个参数:当前DOM元素(input),本次绑定的所有信息
element.value = binding.value
},
//指令所在元素被插入页面时调用
inserted(element, binding) {
element.focus()
// input输入框自动获取焦点,这句代码必须放在将input放入页面的后面
},
//指令所在的模板被重新解析时调用
update(element, binding) {
element.value = binding.value
element.focus()
}
}
}
})
</script>
</html>
生命周期
- 又叫做生命周期回调函数、生命周期函数、生命周期钩子
- 是 Vue 在关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的 this 指向是 vm 或 组件实例对象, 这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())
<!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 :style="{opacity:opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
stop() {
this.$destroy()
}
},
// Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted(挂载),只是初始化的时候调用了一次,后续数据更改重新解析模板时不会再次调用
mounted() {
console.log('mounted', this)
this.timer = setInterval(() => {
// 这里箭头函数无this,自动寻找上下文this,找到了mounted()的this,这个this指向vm
console.log('setInterval')
this.opacity -= 0.01
if (this.opacity <= 0) this.opacity = 1
}, 16)
},
beforeDestroy() {
console.log('vm即将被销毁')
clearInterval(this.timer)
// 清除定时器
},
})
//通过外部的定时器实现(不推荐)
// setInterval(() => {
// vm.opacity -= 0.01
// if (vm.opacity <= 0) { vm.opacity = 1 }
// }, 16)
</script>
</html>
生命周期流程
<!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" :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">
const vm = new Vue({
el: '#root',
// template: `
// <div>
// <h2>当前的n值是:{{n}}</h2>
// <button @click="add">点我n+1</button>
// </div>
// `,
//使用template模板,容器内就不用放入内容了,不过template模板解析的时候会将外面的root容器给覆盖掉。
// 而且template模板只能有一个根元素,所以必须用div 将h2与button包裹起来,否则报错
data: {
n: 1
},
methods: {
add() {
console.log('add')
this.n++
},
bye() {
console.log('bye')
this.$destroy()
// 调用销毁函数,但之前的工作成果还在,只是以后不能管理了
}
},
watch: {
n() {
console.log('n变了')
}
},
beforeCreate() { //看图:这里是指数据代理和数据监测创建之前,不是vm
console.log('beforeCreate')
// console.log(this);
// debugger
// 此时打开控制台可以看到data中无数据,无methods方法
},
created() {
console.log('created')
// console.log(this);
// debugger
// 此时打开控制台可以看到data中有数据,有add,bye方法
},
beforeMount() {
console.log('beforeMount')
// console.log(this);
// debugger
// 元素都加载完成(未经编译),但还没有挂载上去,HTML的body结构里呈现的依然是模板
// 在这里面操作DOM白操作,最终会被虚拟dom转换成的真实dom覆盖掉
},
mounted() {
console.log('mounted')
// console.log(this);
// debugger
// 元素都加载完成(编译完成),已经挂载上去了,HTML的body结构里呈现的你想让他呈现的样子
// 在这里面操作DOM有效,但不推荐
},
beforeUpdate() {
console.log('beforeUpdate')
// console.log(this.n);
// debugger
// 更新数据时调用,数据为新的,但页面还是旧的,尚未更新
},
updated() {
console.log('updated')
// console.log(this.n);
// debugger
// 数据为新的,但页面也是新的,数据与页面保持同步
},
beforeDestroy() {
console.log('beforeDestroy')
// console.log(this.n);
// this.add()
// debugger
//销毁阶段触发
// 点击销毁vm,能打印出n,调用了add方法,但页面不再更新,即到了这个阶段,
//能够访问到数据,调用方法, 但所有对数据的修改不会再触发更新了。
// 此时vm中的data methods 指令等都处于可用状态,马上要执行销毁过程,
// 一般在此阶段:关闭定时器,取消订阅消息,解绑自定义事件等收尾操作
},
destroyed() {
//销毁阶段触发
console.log('destroyed')
},
})
// vm.$mount("#root")
</script>
</html>
常用的生命周期钩子:
1. mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等 (初始化操作)
2. beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等 (收尾工作)
关于销毁 Vue 实例
1. 销毁后借助 Vue 开发者工具看不到任何信息。
2. 销毁后自定义事件会失效,但原生 DOM 事件依然有效。
3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了
vm的一生(vm的生命周期)∶
将要创建===>调用beforeCreate函数 创建完毕===>调用created函数。
将要挂载===>调用beforeMount函数
(重要)挂载完毕===>调用mounted函数 ============>【重要的钩子】
将要更新===>调用beforeUpdate函数
更新完毕===>调用updated函数
(重要)将要销毁===>调用beforeDestroy函数 ========>【重要的钩子】
销毁完毕===>调用destroyed函数