文章目录
- 1. Vue简介
- 2. Vue官网使用指南
- 3. 初识Vue
- 3.1 搭建Vue开发环境
- 3.2 HelloWorld案例
- 3.3 el与data的两种写法
- 3.4 MVVM模型
- 3.5 模板语法
- 4. 数据绑定
- 4.1 v-bind单向数据绑定
- 4.2 v-model双向数据绑定
- 5. 事件处理
- 5.1 v-on绑定事件
- 5.2 事件修饰符
- 5.3 键盘事件
- 6. 计算属性
- 6.1 姓名案例
- 6.2 computed计算属性
- 7. 监视属性
- 7.1 天气案例
- 7.2 watch监视属性
- 8. 绑定样式
- 8.1 绑定class样式
- 8.2 绑定style样式
- 9. 条件渲染
- 9.1 v-show
- 9.2 v-if、v-else与v-else-if
- 10. 列表渲染
- 10.1 v-for渲染列表数据
- 10.2 key的作用与原理
- 10.3 列表过滤
- 10.4 列表排序
- 11. 收集表单数据
- 12. 内置指令
- 12.1 v-text 解析普通文本
- 12.2 v-html 解析html标签
- 12.3 v-cloak 隐藏模板
- 12.4 v-once 仅渲染一次
- 12.5 v-pre 跳过元素编译
- 13. 自定义指令
- 13.1 函数式和对象式
- 13.2 局部指令和全局指令
- 14. Vue的生命周期
- 14.1 生命周期概念
- 14.2 生命周期演示
- 15. Vue的组件化编程
- 15.1 模块与组件
- 15.2 非单文件组件
- 15.2.1 组件的基本使用
- 15.2.2 组件的注意事项
- 15.2.3 组件的嵌套
- 15.2.4 VueComponent
- 15.2.5 一个重要的内置关系
- 15.3 单文件组件
- 15.3.1 单文件组件的组成
- 15.3.2 单文件组件的示例
1. Vue简介
Vue的概念:Vue是一套用于构建用户界面的渐进式JavaScript框架。
渐进式:表示Vue可以自底向上逐层的应用,从简单应用逐渐递进到复杂应用。
Vue的特点:
- 采用组件化模式,提高代码复用率,且让代码更好维护。
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
- 使用虚拟DOM和Diff算法,尽量复用DOM节点。
学习Vue之前要掌握的js基础知识:
- ES6语法规范
- ES6模块化
- 包管理器
- 原型和原型链
- 数组常用方法
- axios
- promise
2. Vue官网使用指南
Vue官网:
- Vue3文档:https://cn.vuejs.org/
- Vue2文档:https://v2.cn.vuejs.org/
以Vue2文档为例,对该文档顶部的导航栏做详细的介绍。
1.学习:其中最重要的是教程和API,贯穿vue学习的始终。
- 教程:入门vue的教程
- API:vue的属性、方法和指令等
- 风格指南:官方推荐的vue代码风格指南
- 示例:官方展示的一些vue案例
- Cookbook:展示一些vue案例,教你如何优化vue代码
2.生态系统:其中工具和插件很重要,用于搭建Vue工程。
3.资源列表:其中Awesome Vue、浏览和Vue相关的包,这两个包含了官方整理的一些优秀的vue周边库。
3. 初识Vue
3.1 搭建Vue开发环境
1.首先需要下载Vue
进入Vue官网的导航列表:学习 -> 教程 -> 安装,也可点击如下地址直接跳转:https://v2.cn.vuejs.org/v2/guide/installation.html
有开发和生产两种版本,这里我们使用开发版本,其中包含了完整的警告和调试模式,适用于学习阶段。
- 开发环境版本:安装包名称为
vue.js
,包含了有帮助的命令行警告,体积较大。 - 生产环境版本:安装包名称为
vue.min.js
,优化了尺寸和速度,体积较小。
2.新建一个js文件夹,将下载好的vue.js
放入js文件夹中,然后使用 <script>
标签引入 vue.js
,就可以使用Vue了
目录结构如下:
index.html
内容如下:
<!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>Document</title><!-- 引入Vue --><script src="../js/vue.js"></script></head><body></body>
</html>
打开页面后,再打开控制台,发现有两个提示:
3.消除开发环境的提示
添加vue开发者工具,消除第一个提示,步骤如下:打开Chrome扩展程序 -> 打开开发者模式 ->拖入vue_dev_tools.crx
文件到浏览器并松开,点击添加程序即可
script
标签中写入以下vue配置,消除第二个提示:
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
3.2 HelloWorld案例
<!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>Document</title><!-- 引入Vue --><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器 --><div id="app"><h1>{{message}}</h1></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示// 创建Vue实例new Vue({// 配置el: "#app", // el用于指定当前Vue实例为哪个容器服务data: { // data中用于存储数据,数据供el所指定的容器去使用message: "Hello World!"}});</script></body>
</html>
代码解释:
1.el
表示元素(element),值通常为css选择器,此处#app
为id选择器,对应<div id="app">
,此时当前vue实例挂载到了div
容器上,为该容器服务。
2.data
表示数据,数据供挂载的容器去使用,而容器中使用了模板语法{{message}}
,引用了vue实例中的data数据。
3.vue实例中的message
属性值Hello World!
替换了{{message}}
,因此<h1>{{message}}</h1>
最终解析成了<h1>Hello World!</h1>
运行结果如下:
注:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
2.容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3.容器里的代码被称为Vue模板
4.Vue实例和容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新
3.3 el与data的两种写法
1.el
与data
的第一种写法是对象式写法,就是上述HelloWorld案例中的写法,即通过配置对象属性和属性值。
// 创建Vue实例
new Vue({// 配置el: "#app", // 将Vue实例挂载到 id=app的容器上data: { // data中用于存储数据,数据供挂载的容器去使用message: "Hello World!"}
});
2.el
与data
的第二种写法是函数式写法
// 创建Vue实例
const vm = new Vue({// data的函数式写法,组件中必须使用该写法data() {return {message: "Hello World!"};}
});vm.$mount("#app"); // 等价于 el: "#app"
注意:vue实例中的函数不能使用箭头函数的写法,原本函数中的
this
指向的是vue实例对象,使用箭头函数后,函数中的this
指向的就是window
对象。
3.4 MVVM模型
Vue采用了MVVM架构模型,其对应关系如下:
M
:模型(Model) ,对应 data 中的数据V
:视图(View) ,对应模板VM
:视图模型(ViewModel) , 对应 Vue 实例对象
3.5 模板语法
模板语法:Vue实例挂载的容器里中的代码被称为模板,模板语法也就是这个容器中代码的语法规范,模板语法分为两大类(插值语法和指令语法)。
插值语法:
- 功能:用于解析标签体内容
- 写法:
{{xxx}}
,xxx是js表达式,且可以直接读取到data中的所有属性
指令语法:
- 功能:用于解析标签(包括标签属性、标签体内容、绑定事件等)
- 写法:
v-xxx
,如v-bind
、v-model
、v-on
等
<!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>Document</title><!-- 引入Vue --><script src="../js/vue.js"></script></head><body><div id="app"><!-- 插值语法,{{js表达式}} --><h3>{{message.toUpperCase()}}</h3><!-- 指令语法,v-xxx --><a v-bind:href="url">百度链接</a></div><script>new Vue({el: "#app",data: {message: "hello world",url: "https://www.baidu.com/"}});</script></body>
</html>
运行结果:
4. 数据绑定
Vue中有两种数据绑定的方式:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
4.1 v-bind单向数据绑定
v-bind
:用于单向数据绑定,该指令也可以简写为 :
。
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 普通写法 -->单向数据绑定:<input type="text" v-bind:value="name" /> <br /><!-- 简写 -->单向数据绑定:<input type="text" :value="name" /></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {name: "hello"}});</script></body>
</html>
验证数据能从data流向页面:打开控制台,输入 vm.name="hello123"
并回车,可以发现页面中的数据也显示成了hello123
验证页面的数据无法流向data:在文本框中输入hello1234
,随后在控制台输入vm.name
,得到的结果依然是hello123
,说明vue实例中的data数据未发生改变
4.2 v-model双向数据绑定
v-model
:用于双向数据绑定,双向绑定只能应用在表单类元素(输入类元素)上,如:input、select等
注:
v-model:value
可以简写为v-model
,因为v-model默认收集的就是value值。
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 普通写法 -->双向数据绑定:<input type="text" v-model:value="name" /> <br /><!-- 简写 -->双向数据绑定:<input type="text" v-model="name" /></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {name: "hello"}});</script></body>
</html>
验证数据能从data流向页面:打开控制台,输入 vm.name="hello123"
并回车,可以发现页面中的数据也显示成了hello123
验证页面的数据也能流向data:在文本框中输入hello1234
,随后在控制台输入vm.name
,得到的结果是hello1234
,说明vue实例中的data数据随页面数据的改变而改变
5. 事件处理
5.1 v-on绑定事件
事件的基本使用:
- 使用
v-on:xxx="yyy"
指令用于绑定事件,其中v-on:
可以简写为@
,xxx
是事件名,yyy
是回调函数名(也可以是简单的语句,如isShow=!isShow
) methods
对象用于配置vm
的方法,事件的回调函数需要配置在methods
对象中methods
中配置的函数,this
指向的是vm
或组件实例对象,且配置的函数不能使用箭头函数,否则this
指向的是window
对象@click="demo"
和@click="demo($event)"
效果一致,但后者可以传参
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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 普通写法 --><button v-on:click="showInfo">按钮1</button><!-- 简写 --><button @click="showInfo">按钮2</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",// 配置方法methods: {// 事件回调函数showInfo() {alert("Hello World!");}}});</script></body>
</html>
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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><button @click="showInfo">按钮1</button><!-- $event为占位符,表示此时第一个参数为事件对象 --><button @click="showInfo1($event,'Jack')">按钮2</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",// 配置方法methods: {// 不传参时,函数的第一个参数代表事件对象showInfo(e) {console.log(e.target); // 触发事件的对象console.log(e.type); // 事件的类型console.log(this); // this指向vm},// 传参时,根据$event占位符的位置确定哪个参数代表事件对象showInfo1(e, name) {console.log(e.target);console.log(e.type);console.log(`Hello ${name}`);}}});</script></body>
</html>
运行结果:
5.2 事件修饰符
Vue中的事件修饰符:
prevent
:阻止默认事件(常用)stop
:阻止事件冒泡(常用)once
:事件只触发一次(常用)capture
:使用事件的捕获模式self
:只有event.target是当前操作的元素时才触发事件passive
:事件的默认行为立即执行,无需等待事件回调执行完毕
注:修饰符可以连续写,如
@click.prevent.stop="showInfo"
1.prevent
:阻止事件的默认行为(常用)
同js事件对象中的
e.preventDefault()
方法,用于阻止事件的默认行为
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 阻止链接跳转 --><a href="https://www.baidu.com/" @click.prevent="showInfo">百度链接</a><!-- 阻止表单提交 --><form action="https://www.baidu.com/"><input type="submit" value="提交" @click.prevent="showInfo" /></form></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo() {console.log("已阻止事件的默认行为");}}});</script></body>
</html>
运行结果:点击百度链接和表单提交按钮,都不发生页面跳转,因为跳转的默认行为被阻止了
2.stop
:阻止事件冒泡(常用)
同js事件对象中的
e.stopPropagation()
方法,用于阻止事件冒泡
<!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>Document</title><script src="../js/vue.js"></script><style>.father {position: relative;width: 200px;height: 200px;background-color: #666;margin: 30px;}.son {position: absolute;width: 100px;height: 100px;background-color: orange;margin: auto;top: 0;left: 0;right: 0;bottom: 0;}</style></head><body><div id="app" class="father" @click="showInfo1"><!-- 阻止事件冒泡 --><div class="son" @click.stop="showInfo2"></div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo1() {alert("father");},showInfo2() {alert("son");}}});</script></body>
</html>
运行结果:点击子元素 ,只弹出警示框“son”,没有后续弹出“father”
(释义:点击子元素son以后,阻止冒泡,因此没有冒泡到父元素,父元素的事件行为不执行)
3.once
:事件只触发一次(常用)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 此按钮只在第一次点击时,触发事件 --><button @click.once="showInfo">点击按钮</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo() {alert("Hello World!");}}});</script></body>
</html>
运行结果:第一次点击按钮时,弹出警示框,后续再点击无法弹出
4.capture
:使用事件的捕获模式
注:js中
addEventListener(type, listener, useCapture)
:useCapture
为false
或者忽略时,事件处于冒泡阶段;为true
时,事件处于捕获阶段
<!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>Document</title><script src="../js/vue.js"></script><style>.father {position: relative;width: 200px;height: 200px;background-color: #666;margin: 30px;}.son {position: absolute;width: 100px;height: 100px;background-color: orange;margin: auto;top: 0;left: 0;right: 0;bottom: 0;}</style></head><body><!-- 绑定事件到父元素father,为捕获阶段 --><div id="app" class="father" @click.capture="showInfo1"><div class="son" @click="showInfo2"></div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo1() {alert("father");},showInfo2() {alert("son");}}});</script></body>
</html>
运行结果:点击橘色的子元素div,先弹出警示框 “father”,后弹出警示框 “son”
(释义:捕获从上往下传递,点击子元素son以后,会先找到父元素并执行它的事件,再往下找到子元素后,执行子元素的事件)
5.self
:只有event.target
是当前操作的元素时才触发事件
注:通过这种方式,也能阻止事件冒泡
<!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>Document</title><script src="../js/vue.js"></script><style>.father {position: relative;width: 200px;height: 200px;background-color: #666;margin: 30px;}.son {position: absolute;width: 100px;height: 100px;background-color: orange;margin: auto;top: 0;left: 0;right: 0;bottom: 0;}</style></head><body><!-- 只有当e.target是当前操作的元素时才触发事件,即只有点击该父元素才触发父元素的事件 --><div id="app" class="father" @click.self="showInfo1"><!-- <div id="app" class="father" @click="showInfo1"> --><div class="son" @click="showInfo2"></div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo1() {alert("father");},showInfo2() {alert("son");}}});</script></body>
</html>
运行结果:只有点击灰色的父元素div时,才弹出警示框 “father”
5.3 键盘事件
Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
注:
1.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名,如caps-lock)
2.可以使用keyCode去指定具体的按键(不推荐),如e.keyCode===13
代表回车键
3.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名,如Vue.config.keyCodes.huiche=13
,此时别名huiche
代表回车键
4.系统修饰键(用法特殊):ctrl、alt、shift、meta,
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- 配合keydown使用:正常触发事件。
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 按下回车键松开时触发事件 --><input type="text" placeholder="输入完成后请按回车" @keyup.enter="showInfo" /></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",methods: {showInfo(e) {alert(e.target.value);}}});</script></body>
</html>
运行结果:输入“123456”,再按下回车后松开
6. 计算属性
6.1 姓名案例
要求:一个input框输入“姓”,另一个input框输入“名”,生成“姓-名”
解题方法有三种:
- 使用插值语法
{{}}
实现 - 使用
methods
方法实现 - 使用
computed
计算属性实现(推荐)
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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">姓:<input type="text" v-model="firstName" /> <br />名:<input type="text" v-model="lastName" /> <br />全名:<span>{{firstName}}-{{lastName}}</span></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {firstName: "张",lastName: "三"}});</script></body>
</html>
运行结果如下:在两个输入框中分别输入“姓”和“名”,得到全名为“姓-名”
2.使用methods
方法实现姓名案例,通过调用方法,将方法的返回值渲染到模板中
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">姓:<input type="text" v-model="firstName" /> <br />名:<input type="text" v-model="lastName" /> <br />全名:<span>{{fullName()}}</span></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {firstName: "张",lastName: "三"},methods: {fullName() {return this.firstName + "-" + this.lastName;}}});</script></body>
</html>
3.使用computed
计算属性实现,通过计算原有的属性,得到新的属性,并将其渲染到模板中
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">姓:<input type="text" v-model="firstName" /> <br />名:<input type="text" v-model="lastName" /> <br />全名:<span>{{fullName}}</span></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {firstName: "张",lastName: "三"},computed: {fullName: {get() {return this.firstName + "-" + this.lastName;}}}});</script></body>
</html>
6.2 computed计算属性
计算属性:
- 定义:要用的属性不存在,要通过已有属性计算得来,计算属性最终会出现在
vm
上,直接读取使用即可 - 原理:底层借助了
Objcet.defineproperty
方法提供的getter
和setter
- 优势:与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便 - 计算属性中的
get
和set
函数:- 使用计算属性时,初次读取会执行一次
get
函数,当依赖的数据发生改变时也会再次调用get
函数 - 修改计算属性时,会调用
set
函数
- 使用计算属性时,初次读取会执行一次
- 当计算属性中,只有
get
函数时,可以简写计算属性
1.计算属性中的get
和set
函数的使用
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">姓:<input type="text" v-model="firstName" /> <br />名:<input type="text" v-model="lastName" /> <br />全名:<span>{{fullName}}</span></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {firstName: "张",lastName: "三"},computed: {fullName: {// 使用fullName时,返回全名get() {console.log("get被调用了");return this.firstName + "-" + this.lastName;},// 修改fullName时,修改firstName和lastName属性set(value) {console.log("set被调用了");arr = value.split("-");this.firstName = arr[0];this.lastName = arr[1];}}}});</script></body>
</html>
运行结果:
由于模板中使用了fullName属性,先调用了一次get,然后在输入修改名后,fullName依赖的lastName属性发生了改变,再次调用了get;
控制台中输入vm.fullName='张-三丰'
,fullName属性被修改,调用了set,又因为set函数修改了fullName所依赖的firstName 和lastName,所以再次触发调用了get
2.简写计算属性
当计算属性中,只有get
函数时,代码可以进行如下简写:
// 普通写法
computed: {fullName: {get() {return this.firstName + "-" + this.lastName;}}
}
// 简写
computed: {fullName() {return this.firstName + "-" + this.lastName;}
}
7. 监视属性
7.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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h1>今天天气很{{info}}</h1><button @click="changeWeather">点击切换天气</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {isHot: true // 默认天气为炎热},computed: {info() {return this.isHot ? "炎热" : "凉爽";}},methods: {changeWeather() {this.isHot = !this.isHot; // 每次调用方法时,isHot取反}}});</script></body>
</html>
注:由于methods中的
changeWeather
方法逻辑简单,因此也可以将methods去掉,并把@click="changeWeather"
改为@click="isHot = !isHot"
,这样就简化了代码。
运行结果:点击按钮,天气在炎热和凉爽之间切换
7.2 watch监视属性
监视属性watch
:
- 监视的属性必须存在,才能进行监视
- 当被监视的属性变化时,
handler
回调函数自动调用 - 监视属性有两种写法,一是通过
watch
属性配置,二是通过vm.$watch()
方法 - 深度监视属性:
- 当被监视的属性有多层结构时,默认不监测对象内部值的改变(一层)
- 通过配置
deep:true
可以监测对象内部值改变(多层)
- 当监视属性中,只有
handler
函数时,可以监视计算属性
1.监视属性的基本用法(通过watch
属性配置)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h1>今天天气很{{info}}</h1><button @click="isHot = !isHot">点击切换天气</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {isHot: true // 默认天气为炎热},computed: {info() {return this.isHot ? "炎热" : "凉爽";}},methods: {changeWeather() {this.isHot = !this.isHot; // 每次调用方法时,isHot取反}},watch: {// 当isHot属性发生变化时,会调用handler函数isHot: {immediate: true, //初始化时会调用一下handler函数handler(newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);}}}});</script></body>
</html>
点击按钮,运行结果如下:
2.可以通过vm.$watch()
方法,进行属性监视
// watch属性配置的写法
watch: {isHot: {immediate: true, handler(newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);}}
}
// vm.$watch()的写法
vm.$watch("isHot", {immediate: true, handler(newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);}
});
3.deep:true
用于深度监视属性
- 深度监视单个属性:监视的属性写为
"numbers.a"
,表示监视numbers
属性中的a
属性 - 深度监视所有属性: 监视的属性写为
numbers
,再配置deep:true
,表示监视numbers
属性中的所有属性
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><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; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {// numbers属性有多层结构numbers: {a: 1,b: 1,c: {d: {e: 100}}}},watch: {//监视多级结构中某个属性的变化"numbers.a": {handler() {console.log("a发生了改变");}},//监视多级结构中所有属性的变化numbers: {deep: true,handler() {console.log("numbers发生了改变");}}}});</script></body>
</html>
分别点击上下两个按钮,使a、b值增加,运行结果如下:
4.简写监视属性
当监视属性中,只有handler
函数时,代码可以进行如下简写:
// 普通写法
watch: {isHot: {handler(newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);}}
}
// watch属性配置简写
watch: {isHot(newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);}
}// vm.$watch()方法简写
vm.$watch("isHot", function (newValue, oldValue) {console.log(`isHot被修改,原来的值是${oldValue},现在是${newValue}`);
});
5.计算属性与监视属性的区别
computed
和watch
之间的区别:
computed
能完成的功能,watch
都可以完成watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作computed
的写法更加的简洁,因此能用computed
完成的功能,不要用watch
完成
注:Vue中有关函数的两个重要的小原则:
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
8. 绑定样式
8.1 绑定class样式
绑定class
样式有三种方式,分别适用于不同的场景:写法为:class="xxx"
- 字符串写法:适用于样式的类名不确定,需要动态指定
- 对象写法:适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用
- 数组写法:适用于要绑定的样式个数不确定、名字也不确定
1.绑定class
样式,字符串写法:适用于样式的类名不确定,需要动态指定
<!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>Document</title><script src="../js/vue.js"></script><style>/* basic为固定样式,happy和sad为需要动态绑定的样式 */.basic {width: 200px;height: 100px;border: 1px solid black;}/* 快乐情绪 */.happy {border: 4px solid red;background-color: orangered;background: linear-gradient(30deg, yellow, pink, orange, yellow);}/* 悲伤情绪 */.sad {border: 4px dashed rgb(2, 197, 2);background-color: gray;}</style></head><body><div id="app"><!-- 字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood"></div><br /><button @click="changeMood">点击切换情绪</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {mood: "happy" // 属性值为字符串},methods: {// 改变情绪changeMood() {if (this.mood == "happy") {this.mood = "sad";} else {this.mood = "happy";}}}});</script></body>
</html>
运行结果:点击按钮,盒子样式发生改变(盒子的样式是动态的)
2.绑定class
样式,对象写法:适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用
动态切换单个 class
样式:
<!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>Document</title><script src="../js/vue.js"></script><style>.basic {width: 200px;height: 100px;border: 1px solid black;}.a1 {background-color: yellowgreen;}</style></head><body><div id="app"><!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="{a1:isActive}">归海一刀</div><button @click="isActive=!isActive">添加/删除样式</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {isActive: true}});</script></body>
</html>
运行结果:点击按钮,选择添加或者删除样式.
动态切换多个 class
样式:
<!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>Document</title><script src="../js/vue.js"></script><style>.basic {width: 200px;height: 100px;border: 1px solid black;}.a1 {background-color: yellowgreen;}.a2 {font-size: 30px;text-shadow: 2px 2px 10px red;}.a3 {border-radius: 20px;}</style></head><body><div id="app"><!-- 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="classObj">归海一刀</div><button @click="classObj.a1=!classObj.a1">添加/删除样式a1</button><button @click="classObj.a2=!classObj.a2">添加/删除样式a2</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {classObj: {a1: false,a2: false}}});</script></body>
</html>
运行结果:点击按钮,选择添加或者删除样式
3.绑定class
样式,数组写法:适用于要绑定的样式个数不确定、名字也不确定
<!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>Document</title><script src="../js/vue.js"></script><style>.basic {width: 200px;height: 100px;border: 1px solid black;}.a1 {background-color: yellowgreen;}.a2 {font-size: 30px;text-shadow: 2px 2px 10px red;}.a3 {border-radius: 20px;}</style></head><body><div id="app"><!-- 数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --><div class="basic" :class="classArr">归海一刀</div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {classArr: ["a1", "a2"] // 属性值为数组}});</script></body>
</html>
运行结果:可以通过对数组进行操作,从而对样式进行添加和删除
8.2 绑定style样式
绑定style
样式有两种方式:
- 对象写法:
:style="{fontSize: xxx}"
其中xxx是动态值 - 数组写法:
:style="[a,b]"
其中a、b是样式对象
1.绑定style
样式,对象写法
注:在
style
样式绑定中,CSS中的样式名要改为小驼峰写法,如:font-size
要改为fontSize
<!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>Document</title><script src="../js/vue.js"></script><style>.basic {width: 200px;height: 100px;border: 1px solid black;margin-bottom: 20px;}</style></head><body><div id="app"><!-- 绑定style样式,对象写法 --><div class="basic" :style="{fontSize: `${fsize}px`}">归海一刀</div><div class="basic" :style="styleObj">归海一刀</div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {fsize: 30,styleObj: {// 小驼峰写法fontSize: "30px",color: "red",backgroundColor: "orange"}}});</script></body>
</html>
运行结果:
2.绑定style
样式,数组写法
<!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>Document</title><script src="../js/vue.js"></script><style>.basic {width: 200px;height: 100px;border: 1px solid black;margin-bottom: 20px;}</style></head><body><div id="app"><!-- 绑定style样式,数组写法 --><div class="basic" :style="styleArr">归海一刀</div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {styleArr: [{fontSize: "30px",color: "red"},{backgroundColor: "orange"}]}});</script></body>
</html>
运行结果:
9. 条件渲染
9.1 v-show
v-show
:
- 写法:
v-show="表达式"
- 适用于:切换频率较高的场景
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(设置为
display:none
)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h1 v-show="isActive">Hello World</h1><button @click="isActive=!isActive">显示/隐藏元素</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {isActive: true}});</script></body>
</html>
运行结果:点击按钮之后,元素被隐藏,再查看DOM结构,发现元素样式上多了个style="display: none;"
9.2 v-if、v-else与v-else-if
v-if
:
- 写法:
v-if="表达式"
、v-else="表达式"
、v-else-if="表达式"
- 适用于:切换频率较低的场景
- 特点:不展示的DOM元素直接被移除
1.使用v-if
会把DOM元素直接被移除
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h1 v-if="isActive">Hello World</h1><button @click="isActive=!isActive">显示/隐藏元素</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {isActive: true}});</script></body>
</html>
运行结果:点击按钮之后,元素被隐藏,再查看DOM结构,发现元素直接被移除了
2.v-if
、v-else
与v-else-if
配合使用
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h2>当前n的值是{{n}}</h2><button @click="n++">点击n+1</button><h3 v-if="n===1">Angular</h3><h3 v-else-if="n===2">React</h3><h3 v-else-if="n===3">Vue</h3><h3 v-else>不是前端三大框架</h3></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {n: 0}});</script></body>
</html>
运行结果:点击按钮增加n的值,当n达到对应值时,分别展示对应的元素
注:
v-if
可以和v-else-if
、v-else
一起使用,但要求结构不能被“打断”
打断的结构示例如下:
<h3 v-if="n===1">Angular</h3>
<h3 v-else-if="n===2">React</h3>
<h3>打断结构</h3>
<h3 v-else-if="n===3">Vue</h3>
<h3 v-else>不是前端三大框架</h3>
3.v-if
与template
的配合使用:当几个v-if
的条件一样时,可以配合template
,将多个条件简写为一个
注:使用template标签,不会影响结构,而且只能和v-if配合使用,不能和v-show配合
<!-- 简写前 -->
<h3 v-if="n===1">Angular</h3>
<h3 v-if="n===1">React</h3>
<h3 v-if="n===1">Vue</h3>
<!-- 简写后 -->
<template v-if="n===1"><h3>Angular</h3><h3>React</h3><h3>Vue</h3>
</template>
10. 列表渲染
10.1 v-for渲染列表数据
v-for
指令:用于展示列表数据
语法:v-for="(item, index) in xxx" :key="yyy"
xxx
为可遍历对象,包括数组、对象、字符串等item
为可遍历对象的每一项数据,index
为每一项数据的索引值key
属性为每项数据的唯一标识
1.使用v-for
遍历数组(常用)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 遍历数组 --><ul><!-- p为数组成员,index为数组下标 --><li v-for="(p,index) in persons" :key="index">{{index}}:{{p.name}}-{{p.age}}</li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {// id为每项数据的唯一标识persons: [{ id: "001", name: "张三", age: 18 },{ id: "002", name: "李四", age: 19 },{ id: "003", name: "王五", age: 20 }]}});</script></body>
</html>
注:由于数组下标
index
和id
都是唯一标识,而key
只要作为唯一标识就可以,因此以上代码中的:key="index"
也可以改为:key="p.id"
,
运行结果:
2.使用v-for
遍历对象(常用)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 遍历对象 --><ul><!-- v为对象的属性值(value),k为对象的属性名(key) --><li v-for="(v,k) in car" :key="k">{{k}}-{{v}}</li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {// 对象是一个键值对,属性名就是键(key),可以作为唯一标识car: {name: "奥迪A6L",price: "70万",color: "黑色"}}});</script></body>
</html>
运行结果:
3.使用v-for
遍历字符串(用的少)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 遍历字符串 --><ul><!-- item为每个字符,index为字符串下标 --><li v-for="(item,index) in str" :key="index">{{index}}-{{item}}</li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {str: "Hello"}});</script></body>
</html>
运行结果:
4.使用v-for
遍历指定的次数(用的少)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 遍历指定的次数 --><ul><!-- 遍历5次 --><li v-for="(number,index) in 5" :key="index">{{number}}-{{str}}</li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {str: "Hello World"}});</script></body>
</html>
运行结果:
10.2 key的作用与原理
1.key
作为数据的唯一标志,选择原则遵循如下:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
2.用index
作为key
可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 遍历数组 --><button @click.once="add">添加一个老刘</button><ul><!-- p为数组成员,index为数组下标 --><li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}} <input type="text" /></li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {// id为每项数据的唯一标识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);}}});</script></body>
</html>
运行结果如下:在输入框中输入内容,然后再点击按钮,发现输入框中的数据显示有问题
当把以上代码的:key="index"
改为:key="p.id"
,数据则显示正常了
原理如下:
- 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
- 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:若虚拟DOM中内容没变, 直接使用之前的真实DOM;若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面
10.3 列表过滤
列表过滤:对列表的数据进行筛选,筛选后的数据再渲染到页面中。
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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">人员列表:<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>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",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属性,当keyWord属性值发生变化时,调用handler函数进行模糊搜索keyWord: {immediate: true, // 初始化时调用handler,空字符串会匹配所有数据,首次展示为完整数据handler(val) {this.filPerons = this.persons.filter(p => {return p.name.includes(val); // 判断输入框中的值是否出现在姓名中});}}}});</script></body>
</html>
运行结果:
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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">人员列表:<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>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",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依赖于keyWord属性,当keyWord发生改变时,会重新计算属性filPerons() {return this.persons.filter(p => {return p.name.includes(this.keyWord);});}}});</script></body>
</html>
注:使用
computed
还是watch
的原则
1.computed 的写法更加的简洁(推荐使用),能用 computed 完成的功能,不要用 watch 完成
2.computed 无法完成的功能才用 watch 实现,例如:watch 可以进行异步操作
10.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>Document</title><script src="../js/vue.js"></script></head><body><div id="app">人员列表:<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="index">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",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依赖于keyWord和sortType属性,依赖的这两个属性任何一个发生变化,都会引起重新计算属性filPerons() {// 对列表数据进行筛选const arr = this.persons.filter(p => {return p.name.includes(this.keyWord);});// 判断一下是否需要排序if (this.sortType) {arr.sort((p1, p2) => {return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;});}return arr;}}});</script></body>
</html>
运行结果:在输入框中输入关键词,可以对数据进行筛选,再点击按钮可以对筛选后的数据再进行排序
11. 收集表单数据
v-model
用于收集表单数据,且在收集表单数据时,根据表单控件类型的不同,默认收集的值也不同。
v-model
需要注意的点如下:
- 若表单控件为文本框或密码框,即
<input type="text"/>
或<input type="password"/>
,则v-model收集的是value值,用户输入的就是value值。 - 若表单控件为单选框,即
<input type="radio"/>
,则v-model收集的是value值,需要给标签配置value值。 - 若表单控件为复选框,即
<input type="checkbox"/>
,则有以下两种情况:- 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 配置了input的value属性,当v-model的初始值是非数组时,那么收集的就是checked;是数组时,那么收集的的就是value组成的数组
注:v-model的三个修饰符
1.lazy
:失去焦点时,再收集数据
2.number
:输入字符串转为有效的数字
3.trim
:清除文本框首尾空格
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 阻止表单默认跳转行为 --><form @submit.prevent="demo">账号:<input type="text" v-model.trim="userInfo.account" /> <br /><br />密码:<input type="password" v-model="userInfo.password" /> <br /><br />年龄:<input type="number" v-model.number="userInfo.age" /> <br /><br />性别: 男<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" /> 吃饭<inputtype="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.baidu.com">《用户协议》</a><button>提交</button></form></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {userInfo: {account: "",password: "",age: 18,sex: "female",hobby: [],city: "beijing",other: "",agree: ""}},methods: {demo() {console.log(JSON.stringify(this.userInfo));}}});</script></body>
</html>
运行结果如下:输入内容,勾选单选和复选框等控件,再点击提交按钮,可以看到控制台打印了表单提交的信息。
12. 内置指令
目前已经学过的内置指令有:v-bind
、v-model
、v-on
、v-show
、v-if
、v-else
、v-for
。
12.1 v-text 解析普通文本
v-text
指令:用于解析普通文本,向其所在的节点中渲染文本内容。
v-text
与插值语法的区别:v-text
会替换掉节点中的内容,而{{xxx}}
则不会,因此{{xxx}}
更加灵活,用的更多。
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 插值语法相对v-text更加灵活 --><div>Hello, {{name}}</div><!-- v-text会替换掉节点中的内容 --><div v-text="name">Hello</div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {name: "Jack"}});</script></body>
</html>
运行结果:
12.2 v-html 解析html标签
v-html
指令:用于解析html标签,向其所在的节点中渲染包含html结构的内容。
v-html
与插值语法的区别:v-html
会替换掉节点中的内容,而{{xxx}}
则不会。
注:
v-html
有安全性问题
1.在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
2.一定要在可信的内容上使用v-html,永不要用在用户提交的内容上(如表单的输入框等)
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- v-text只能解析普通文本,无法解析html标签 --><div v-text="str"></div><!-- v-html可以解析html标签 --><div v-html="str"></div></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {str: `<h3>Hello World</h3>`}});</script></body>
</html>
运行结果:
12.3 v-cloak 隐藏模板
v-cloak
指令:
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- 该指令可以配合css属性选择器,解决网速慢时页面展示出{{xxx}}的问题
<!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>Document</title><script src="../js/vue.js"></script><style>/* 使用该属性的元素被隐藏 */[v-cloak] {display: none;}</style></head><body><div id="app"><!-- 初始时,v-cloak属性存在,但由于属性选择器的作用,会将h2标签隐藏;等到vue接管容器后,会移除掉h2上的v-cloak属性,此时会显示h2标签--><h2 v-cloak>{{name}}</h2></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {name: "Jack"}});</script></body>
</html>
12.4 v-once 仅渲染一次
v-once
指令:
- v-once所在节点在初次动态渲染后,就视为静态内容
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h2 v-once>初始化的n值是:{{n}}</h2><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {n: 1}});</script></body>
</html>
运行结果:点击按钮,只有下面的n值发生变化,而上面的n值不变化
12.5 v-pre 跳过元素编译
v-pre
指令:
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><!-- 该标签未用到指令语法和插值语法,可以使用v-pre加快编译 --><h2 v-pre>Vue其实很简单</h2><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示const vm = new Vue({el: "#app",data: {n: 1}});</script></body>
</html>
13. 自定义指令
13.1 函数式和对象式
自定义指令有两种定义方式:
- 函数式定义
- 对象式定义
1.函数式定义
例:定义一个 v-big
指令,和 v-text
功能类似,但会把绑定的数值放大10倍
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h2>当前的n值是:<span v-text="n"></span></h2><h2>放大10倍后的n值是:<span v-big="n"></span></h2><button @click="n++">点我n+1</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示new Vue({el: "#app",data: {n: 1,},// 自定义指令directives: {// element表示真实的dom元素, binding表示绑定的对象big(element, binding) {element.innerText = binding.value * 10;},},});</script></body>
</html>
big函数被调用的条件:
1.指令与元素成功绑定时(一上来)
2.指令所在的模板被重新解析时
运行结果:点击按钮,上面的n值+1,下面的n值+1后乘以10倍
2.对象式定义
配置对象中常用的三个回调:
bind
:指令与元素成功绑定时调用inserted
:指令所在元素被插入页面时调用update
:指令所在模板结构被重新解析时调用
对象式定义与函数式定义的区别:函数式定义写法等于对象式定义中的
bind
+update
回调函数,而对象式定义还有inserted
回调函数,可以实现函数式定义无法实现的细节操控。
例:定义一个 v-fbind
指令,和 v-bind
功能类似,但可以让其所绑定的input元素默认获取焦点
<!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>Document</title><script src="../js/vue.js"></script></head><body><div id="app"><h2>当前的n值是:<span v-text="n"></span></h2><button @click="n++">点我n+1</button><br /><input type="text" v-fbind:value="n" /></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示new Vue({el: "#app",data: {n: 1,},// 自定义指令directives: {fbind: {//指令与元素成功绑定时(一上来)bind(element, binding) {element.value = binding.value;},//指令所在元素被插入页面时inserted(element, binding) {element.focus();},//指令所在的模板被重新解析时update(element, binding) {element.value = binding.value;},},},});</script></body>
</html>
运行结果:刷新页面后,文本框默认获取焦点,点击按钮后失去焦点
13.2 局部指令和全局指令
自定义指令分为两类:
- 局部指令
- 全局指令
1.局部指令写法
// 函数式定义new Vue({ directives:{指令名:回调函数} }) // 对象式定义 new Vue({ directives:{指令名:配置对象} })
new Vue({// 局部自定义指令directives: {// 函数式定义big(element, binding) {element.innerText = binding.value * 10;},// 对象式定义fbind: {bind(element, binding) {element.value = binding.value;},inserted(element, binding) {element.focus();},update(element, binding) {element.value = binding.value;},},},
});
2.全局指令写法
// 函数式定义
Vue.directive(指令名, 回调函数)
// 对象式定义
Vue.directive(指令名, 配置对象)
// 函数式定义Vue.directive("big", function (element, binding) {element.value = binding.value;});// 对象式定义Vue.directive("fbind", {bind(element, binding) {element.value = binding.value;},inserted(element, binding) {element.focus();},update(element, binding) {element.value = binding.value;},});
自定义指令注意事项:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
new Vue({directives: {"big-number"(element, binding) {element.innerText = binding.value * 10;},},
});
14. Vue的生命周期
14.1 生命周期概念
Vue的生命周期:又称生命周期回调函数、生命周期函数、生命周期钩子,是Vue在关键时刻帮我们调用的一些特殊名称的函数。
生命周期函数中的this指向是vm 或 组件实例对象
vue的生命周期分为以下四个流程:每个流程对应两个函数,其中最重要的是mounted
和 beforeDestroy
。
- 创建流程:
beforeCreate
:此时无法通过vm访问到data中的数据和methods中的方法。created
:此时可以通过vm访问到data中的数据和methods中配置的方法。
- 挂载流程:
beforeMount
:此时页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作,最终都不奏效。mounted
:此时页面呈现的是经过Vue编译的DOM,对DOM的操作均有效(尽可能避免)。至此初始化过程结束,一般在此阶段:开启定时器、发送网络请求、订阅消息以及绑定自定义事件等初始化操作。
- 更新流程:
beforeUpdate
:此时数据是新的,但页面是旧的,即页面尚未和数据保持同步。updated
:此时数据是新的,页面也是新的,即页面尚未和数据保持同步。
- 销毁流程
beforeDestroy
:此时vm中所有的data、methods和指令等等,都处于可用状态,马上要执行销毁过程。一般在此阶段:关闭定时器、取消订阅消息以及解绑自定义事件等收尾操作。destroyed
:此时vm已完成销毁。
Vue的生命周期流程图:
14.2 生命周期演示
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>Document</title><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="app"><h2>当前的n值是:{{n}}</h2><button @click="add">点我n+1</button><button @click="bye">点我销毁vm</button></div><script>Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示new Vue({el: "#app",data: {n: 1,},methods: {add() {this.n++;},bye() {this.$destroy();},},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></body>
</html>
2.生命周期的常用应用场景
常用的生命周期钩子:
mounted
: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等(初始化操作)beforeDestroy
: 清除定时器、解绑自定义事件、取消订阅消息等(收尾操作)
关于销毁Vue实例:
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
<!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>Document</title><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="app"><h2 :style="{opacity}">欢迎学习Vue</h2><button @click="opacity = 1">透明度设置为1</button><button @click="stop">点我停止变换</button></div><script>Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。new Vue({el: "#app",data: {opacity: 1,},methods: {stop() {this.$destroy(); // 销毁vm},},//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mountedmounted() {// 初始化操作:启动定时器console.log("mounted", this);this.timer = setInterval(() => {console.log("setInterval");this.opacity -= 0.01;if (this.opacity <= 0) this.opacity = 1;}, 16);},beforeDestroy() {// 收尾操作:清除定时器clearInterval(this.timer);},});</script></body>
</html>
运行结果:
未点击按钮停止变换前,点击设置透明度有效;点击按钮停止变换后,再点击设置透明度则无效,因为vm被销毁了。
15. Vue的组件化编程
15.1 模块与组件
1.模块与模块化:
- 模块:一个模块就是一个js文件,向外提供特定功能的 js 程序,作用是可以复用 js、简化 js 的编写、提高 js 运行效率。
- 模块化:当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。
2.组件与组件化:
- 组件:用来实现局部(特定)功能效果的代码集合(html/css/js/image……),作用是复用编码、简化项目编码、提高运行效率。
- 组件化:当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用。
传统方式编写应用:
组件化方式编写应用:
15.2 非单文件组件
组件的分类:
- 非单文件组件:模板编写没有提示, 没有构建过程,无法将 ES6 转换,不支持组件的 CSS,真正开发中几乎不用。
- 单文件组件:单文件组件为
.vue
文件,由<template>
模板页面、<script>
JS模块对象和<style>
样式组成。
虽然非单文件组件在开发中几乎不用,但是要想学会单文件组件(
.vue
) ,必须先从非单文件组件开始入手。
15.2.1 组件的基本使用
Vue中使用组件的三大步骤:
- 创建组件
- 注册组件
- 使用组件
1.创建组件:
- 使用
Vue.extend(options)
创建组件 Vue.extend(options)
和new Vue(options)
时传入的options
几乎一样,但有区别如下:el
不能写,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器data
必须写成函数,为了避免组件被复用时,数据存在引用关系
注:使用template可以配置组件结构
2.注册组件
- 局部注册:靠
new Vue
的时候传入components
配置项 - 全局注册:靠
Vue.component('组件名',组件)
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>Document</title><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="app"><!-- 第三步:使用student组件 --><student></student></div><script>Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。// 第一步:创建student组件const student = Vue.extend({template: `<div><h2>学生姓名:{{name}}</h2><h2>学生年龄:{{age}}</h2><button @click="showName">点我提示姓名</button> </div>`,// 组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器data() {return {name: "张三",age: 18,};},methods: {showName() {alert(this.name);},},});// 第二步:注册student组件(局部注册)new Vue({el: "#app",components: {student,},});</script></body>
</html>
以上局部注册也可改为全局注册:
// 第二步:注册student组件
Vue.component("student", student);new Vue({el: "#app",
});
15.2.2 组件的注意事项
1.组件名的取名:
- 组件名由一个单词组成:
- 第一种写法(首字母小写):
school
- 第二种写法(首字母大写):
School
(推荐)
- 第一种写法(首字母小写):
- 组件名由多个单词组成:
- 第一种写法(kebab-case命名):
my-school
- 第二种写法(CamelCase命名):
MySchool
(推荐,且该写法需要Vue脚手架支持)
- 第一种写法(kebab-case命名):
2.组件标签的两种写法
- 双标签:
<school></school>
- 单标签:
school/>
备注:不用使用脚手架时,
<school/>
会导致后续组件不能渲染
3.Vue.extend()
可以简写:const school = Vue.extend(options)
,可简写为 const school = options
<!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>Document</title><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="app"><!-- 第三步:使用student组件 --><Student /></div><script>Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。// 第一步:创建student组件,简写省略了Vue.extend()const Student = {template: `<div><h2>学生姓名:{{name}}</h2><h2>学生年龄:{{age}}</h2><button @click="showName">点我提示姓名</button> </div>`,// 组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器data() {return {name: "张三",age: 18,};},methods: {showName() {alert(this.name);},},};// 第二步:注册student组件new Vue({el: "#app",components: {Student,},});</script></body>
</html>
15.2.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>Document</title><script src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"></div><script>Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。//定义student组件const student = Vue.extend({name: "student",template: `<div><h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div>`,data() {return {name: "张三",age: 18,};},});//定义school组件const school = Vue.extend({name: "school",template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <student></student></div>`,data() {return {name: "北京大学",address: "北京",};},//注册组件(局部),school中嵌套studentcomponents: {student,},});//定义hello组件const hello = Vue.extend({template: `<h1>{{msg}}</h1>`,data() {return {msg: "欢迎来到北京!",};},});//创建vmnew Vue({template: `<div><hello></hello> <school></school> </div>`,el: "#root",//注册组件(局部)components: {hello,school,},});</script></body>
</html>
组件之间结构如下:
15.2.4 VueComponent
关于VueComponent:
1.school
组件本质是一个名为 VueComponent
的构造函数,且不是程序员定义的,是 Vue.extend
生成的
2.我们只需要编写 <school/>
或 <school></school>
,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行了 new VueComponent(options)
3.特别注意,每次调用 Vue.extend
,返回的都是一个全新的 VueComponent
4.关于this指向:
- 组件配置中的this指向:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是
VueComponent
实例对象 - new Vue(options)配置中的this指向:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是
Vue
实例对象
15.2.5 一个重要的内置关系
一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
,这个关系可以让VueComponent组件实例对象访问到 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>Document</title><script src="../js/vue.js"></script></head><body><div id="root"></div><script>Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。Vue.prototype.x = 99;//定义school组件const school = Vue.extend({name: "school",template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showX">点我输出x</button></div>`,data() {return {name: "北京大学",address: "北京",};},methods: {showX() {alert(this.x);},},});new Vue({template: `<school></school>`,el: "#root",components: { school },});</script></body>
</html>
15.3 单文件组件
15.3.1 单文件组件的组成
单文件组件:即一个 .vue
文件,由以下三部分组成
<template>
: 模板页面<script>
:JS模块对象<style>
: 样式
1.模板页面
<template>
页面模板
</template>
2.JS模块对象
<script>export default {data() {return {};},methods: {},computed: {},components: {},};
</script>
3.样式
<style>
样式定义
</style>
15.3.2 单文件组件的示例
本节仅展示代码和文件之间的关联逻辑,此处的代码无法直接运行,需要搭建脚手架环境以后才能运行。
示例:有以下文件结构,作为单文件组件的实际开发使用
文件之间关系如下:
index.html
:主页面main.js
:程序入口App.vue
:汇总所有组件,此处包括School和Student组件School.vue
:学校组件Student.vue
:学生组件
index.html
:
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Document</title></head><body><!-- 准备一个容器 --><div id="root"></div><script type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="./main.js"></script></body>
</html>
main.js
:
import App from './App.vue'new Vue({el:'#root',template:`<App></App>`,components:{App},
})
App.vue
:
<template><div><School></School><Student></Student></div>
</template><script>
//引入组件
import School from "./School.vue";
import Student from "./Student.vue";export default {name: "App",components: {School,Student,},
};
</script>
School.vue
:
<template><div class="demo"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button> </div>
</template><script>export default {name:'School',data(){return {name:'北京大学',address:'北京'}},methods: {showName(){alert(this.name)}},}
</script><style>.demo{background-color: orange;}
</style>
Student.vue
:
<template><div><h2>学生姓名:{{name}}</h2><h2>学生年龄:{{age}}</h2></div>
</template><script>export default {name:'Student',data(){return {name:'张三',age:18}}}
</script>