vue框架介绍
一. 什么是VUE:
1. 什么是:
(1). 第三方开发的: 下载
(2). 基于MVVM设计模式的: (后续介绍...)
(3). 渐进式的: 学了多少,就可以暂时使用多少,比较容易和其它技术混搭。——但是,依然强烈不建议将VUE框架和旧的技术混搭。
(4). 纯前端: 只靠浏览器就可以运行,不需要nodejs相关的知识。
(5). js框架: 可以自动化的帮助人们完成很多重复性的劳动的半成品项目
2. 为什么: 因为旧的技术中,虽然有简化,但是简化并不彻底,没有从根本上减少开发步骤,依然包含大量重复的代码!而使用框架之后,可以极大的减少开发步骤!几乎不存在重复代码!
3. 示例: 使用jq实现点击按钮修改数量
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/jquery-1.11.3.js"></script> 9 </head> 10 <body> 11 <button id="btnMinus">-</button> 12 <span>0</span> 13 <button id="btnAdd">+</button> 14 <script> 15 //先实现+ 16 //DOM 4步 17 //1. 查找触发事件的元素 18 $("#btnAdd") 19 //2. 绑定事件处理函数 20 .click(function(){ 21 //3. 查找要修改的 22 var $span=$("span"); 23 //4. 修改元素 24 //4.1 先取出span中现在的数量,转为整数 25 var n=parseInt($span.html()); 26 //4.2 将n+1 27 n++; 28 //4.3 将新的n再放回去: 29 $span.html(n); 30 }); 31 //再实现- 32 //DOM 4步 33 //1. 查找触发事件的元素 34 $("#btnMinus") 35 //2. 绑定事件处理函数 36 .click(function(){ 37 //3. 查找要修改的 38 var $span=$("span"); 39 //4. 修改元素 40 //4.1 先取出span中现在的数量,转为整数 41 var n=parseInt($span.html()); 42 //4.2 如果n>0,才能n-1 43 if(n>0){ n-- } 44 //4.3 将新的n再放回去: 45 $span.html(n); 46 }); 47 </script> 48 </body> 49 </html>
4. 何时: 程序或项目中绝大多数以数据操作(增删改查)为主的项目都可用vue框架来完成
比如: 美团,饿了么,淘宝,新闻,12306,京东,猫眼,滴滴等软件
二. 如何使用:
1. 官网: cn.vuejs.org或https://cn.vuejs.org/v2/guide/可以下载
2. 下载: 2种:
(1). 像jq一样,将Vue.js文件下载到项目本地,在网页中用<script>引入
a. 简单,适合初学者入门之用!
b. 问题: 现代的前端项目规模非常庞大,但是不同的人组织不同的项目,所用的文件夹和文件结构、名称都千差万别!不利于流行和协作开发!
(2). 脚手架方式:
a. 什么是脚手架: 已经包含核心功能的标准化的半成品项目结构
b. 好处: 任何人创建出的项目结构和文件夹名几乎完全一样!极其便于分工协作开发和技术的流行
c. 问题: 文件夹结构比较复杂,且技术非常综合,所以不适合初学者入门之用!
d. 许多公司中都是用脚手架开发的!
3. 示例: 第一个vue程序:
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 8 <title>Document</title> 9 <!--先引入vue.js--> 10 <script src="js/vue.js"></script> 11 </head> 12 13 <body> 14 <!--VUE 3步--> 15 <!--1. 先做界面 16 1.1 统一的要求: 界面中所有元素,都必须包裹在一个唯一的父元素下,习惯上<div id="app"></div> 17 1.2 找到界面中将来可能随程序自动变化的位置,用专门的语法:{{变量名}}来标记/占位 18 本例中, span元素的内容,有可能随程序自动变化,所以用{{n}}来标记 19 1.3 找到界面中将来可能触发事件的元素,用专门的语法: @事件名="事件处理函数名" 来标记 20 本例中: 两个按钮都能点击,一个执行的减法操作,一个执行的是加法操作,所以: --> 21 <div id="app"> 22 <button @click="minus">-</button> 23 <span>{{n}}</span> 24 <button @click="add">+</button> 25 </div> 26 <script> 27 //2. 常见一个new Vue()对象,来监控div所包含的区域。vue中,必须用el属性,告诉new Vue()要监控的区域是哪里: (el其实是element的缩写) 28 new Vue({ 29 // id选择器 30 el:"#app", 31 //3. 定义模型对象,来保存界面中所需的所有变量和事件处理函数 32 //什么是模型对象: 就是专门替界面保存变量和事件处理函数的特殊的对象 33 //3.1 先创建一个data:{}来保存界面中所需的所有变量和初始值 34 //本例中: 因为界面中只需要一个变量n,且开局时数量时0,所以data中只需要一个变量n就够了 35 data:{ 36 n:0 37 }, 38 //3.2 再创建一个methods:{}来保存界面中所需的所有事件处理函数 39 //本例中: 因为界面中需要两个事件处理函数minus()和add(),所以 40 //强调: 41 //3.2.1 methods中的事件处理函数中,如果要操作data中的变量,必须加this. 42 //3.2.2 methods中的事件处理函数中,根本不用考虑如何从界面取值,也不用考虑如何将新值放回界面,只需要专门考虑如何把data中的变量值修改正确即可! 43 //new Vue()会自动保持界面中变量n和data中变量n同步: 44 //开局时,data中n是几,new Vue()就把n的值送到页面上,对应位置显示给人看 45 //当methods中修改了n的值,new Vue()会自动把n的新值自动更新到界面中n所在的位置给人看 46 methods:{ 47 //复习对象方法的简写 48 // minus:function(){ ... } 49 minus(){ 50 if(this.n>0){ 51 this.n-- 52 } 53 }, 54 add(){ 55 this.n++ 56 } 57 } 58 }) 59 </script> 60 </body> 61 62 </html>
4. 原理:

三. MVVM:
1. 旧的前端代码划分为3部分:
(1). HTML: 定义网页的内容和结构
(2). CSS: 定义网页的样式
(3). JS: 为网页添加交互行为
2. 问题: 因为HTML和css功能太弱了!几乎生活不能自理!HTML和CSS连程序基本的变量,函数都不支持!所以,哪怕是很小的为需改,也必须依靠js来完成。所以,导致js中存在大量重复的步骤。
3. 解决: MVVM设计模式: 对前端代码的重新划分,分三部分:
(1). 界面(View): HTML+CSS,增强版的:
a. 支持变量,可以实现程序中变化,界面内容自动跟着变
b. 还支持更多程序中的自动化功能: 分支、循环、函数...
(2). 模型对象(Model): 专门替界面保存所有所需的变量和函数的特殊对象
界面上需要什么,模型对象中就保存什么!
(3). 视图模型(ViewModel): 专门自动保持模型对象中的数据与界面中的显示同步的特殊的对象。比如: new Vue()
4. vue是如何实现数据绑定的: vue的绑定原理
(1). new Vue()对象将data对象引入,然后自动:
a. 隐藏原data中的实际变量
b. 在new Vue()下data外部,自动为每个data中的变量定义访问器属性。而且,还会自动在每个访问器属性的set()函数中安插一个通知函数。
c. 结果:
1). 程序或项目中我们使用的任何变量,都不再是原版的data中的变量,其实都是访问器属性。
2). 只要我们试图修改data中的变量时,其实都是修改访问器属性,都会自动触发访问器属性的set方法。此时,就会向外部发出通知,哪个变量的值被改变了!
(2). new Vue()对象将methods对象引入,然后:
a. 打散methods对象,原methods对象中的方法,直接隶属于new Vue()对象了。并且和自动创建的访问器属性平级了
b. 结果: 在methods中的方法中只要想操作data中的变量,都必须加this.
(3). new Vue()对象会按照el属性的指引,找到自己要监控的区域。然后扫描区域中所有元素:
a. new Vue()会只扫描出可能发生变化的元素,集中保存在new Vue()内部的一棵虚拟DOM树上。
b. 每当new Vue()中一个变量被修改时,都会触发变量的set(),都会自动向虚拟DOM树发通知
c. 虚拟DOM树接到通知后,扫描虚拟DOM树中所以元素,找到受本次变量修改影响的元素
d. 利用早就提前封装好的DOM增删改查操作,只修改界面中受影响的个别元素。
(4). 总结: VUE的绑定原理=访问器属性+虚拟DOM树
5. 虚拟DOM树的好处: 4个
(1). 小: 只保存可能发生变化的少量元素
(2). 快: 因为保存的元素少,所以遍历查找快
(3). 效率高: 因为只修改受影响的元素。
(4). 避免重复编码: 已经提前封装好了DOM增删改查操作。
如图原理:

常见错误: Error compiling template : 模板编译错误
通常因为HTML中所写的HTML标签或绑定语法不符合vue的要求了!
进一步确定出错的位置:
沿控制台左侧向下找,跳过大片的HTML代码,找到一个很短的"-",这里标记的就是出错的位置!
四. 绑定语法: 学名: 插值语法 Interpolation
1. 什么是: 专门在HTML中标记变量的特殊的语法
2. 何时: 程序或项目中,只要页面中某个元素的内容可能随程序自动变化时,都要用{{变量}}标记
3. 如何:
html地盘 | js地盘 | html地盘
<元素>xxxx{{ 变量或表达式 }}xxx</元素>
注意:{{}}与模板字符串一样只能加入有返回值的表达式或变量
4. 原理:
(1). new Vue()扫描到{{}}后,就会先计算{{}}中变量或表达式的值,然后将计算后的值,填写在{{}}的位置
(2). 程序或项目中在new Vue()只要修改了该变量,new Vue()都会自动重新将变量或表达式的新值填写回html中{{}}的位置.
5. 强调:
(1). 在HTML中{{}}中使用变量前不用加this.
(2). {{}}外是HTML地盘,只能写HTML语法
{{}}内是JS地盘,可以写合法的js表达式。
6. {{}}中: 和模板字符串的${}完全一样
(1). 可以放: 一切有返回值的js变量或表达式
比如: 变量、算术计算、三目、函数调用、创建对象、访问数组
(2). 不可以放: 分支,循环,以及没有返回值的js表达式。
6. 示例: 测试{{}}中可以写那些js表达式:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/vue.js"></script> 9 </head> 10 <body> 11 <div id="app"> 12 <h3>欢迎{{uname}}</h3> 13 <h3>性别:{{sex==1?"男":"女"}}</h3> 14 <h3>小计:¥{{(price*count).toFixed(2)}}</h3> 15 <h3>下单时间:{{new Date(orderTime).toLocaleString()}}</h3> 16 <h3>今天星期{{arr[day]}}</h3> 17 </div> 18 <script> 19 new Vue({ 20 el:"#app", 21 data:{ 22 uname:"dingding", 23 sex:1, 24 price:12.5, 25 count:5, 26 orderTime:1614158191101, 27 arr:["日","一","二","三","四","五","六"], 28 // 0 1 2 3 4 5 6 29 day:new Date().getDay()//0 1 2 3 4 5 6 30 } 31 }) 32 </script> 33 </body> 34 </html>
五. 指令(directive):
第一种
1. 问题: {{}}绝对不是万能!{{}}只适合于元素的内容随程序发生变化时才能用。
2. 解决: 程序或项目中,除了元素的内容之外,其余的属性或样式也可能随程序发生改变时,就要用指令来代替{{}}绑定变量或表达式。
3. 包括: 13种:
第二种
4. v-bind:
(1). 什么是: 专门用于标记可能发生变化的属性的特殊指令
(2). 何时: 程序或项目中,只要一个元素的属性值随程序自动变化,都应该用v-bind。
" "代替了{{ }}的作用
HTML地盘 | JS地盘 | HTML地盘
(3). 如何: <元素 v-bind:属性名="变量或js表达式">
(4). 原理:
a. 每当new Vue()扫描到v-bind时,就会先计算""中的变量值或js表达式值,然后将计算后的结果当做这个属性的属性值
b. 每当这个属性依赖的变量被改变时,new Vue()会重新计算""中变量值或js表达式值,自动将新值作为这个属性的属性值
(5). 简写: 其实,程序或项目中"v-bind:",都可省略为":"
" "代替了{{ }}的作用
HTML地盘 | JS地盘 | HTML地盘
<元素 :属性名="变量或js表达式">
(6). 总结: 程序或项目中,只要一个元素的属性值可能随程序自动变化,只要用:绑定属性值即可!
(7). 示例: 根据PM25的数值不同,页面中显示不同的表情(每张png存着不同表情)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/vue.js"></script> 9 </head> 10 <body> 11 <div id="app"> 12 <!--因为img的src属性,有可能随程序自动变化--> 13 <img :src="pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'"> 14 //用一个新标签接受一下变化的src地址 15 <h3>{{pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'}}</h3> 16 </div> 17 <script> 18 var vm=new Vue({ 19 el:"#app", 20 data:{ 21 //程序中只保存pm2.5的数值,不保存图片路径 22 pm25:60 23 } 24 }) 25 //运行后,F12打开控制台,console,输入vm.pm25=数值,观察页面上图片的变化. 26 </script> 27 </body> 28 </html>
第三种
v-show:
(1). 什么是: 专门控制一个元素显示隐藏的指令
(2). 何时: 程序中,只要想控制一个元素的显示隐藏,都用v-show
(3). 如何: <元素 v-show="变量或表达式">
判断条件
(4). 原理: 每次new Vue()扫描页面时,只要扫描到v-show指令,都会先计算""中的变量值或表达式的值
a. 如果""中的变量值或表达式的值为true,则当前元素正常显示
b. 如果""中的变量值或表达式的值为false,则new Vue()自动给当前元素添加display:none属性!当前元素隐藏!
(5). 示例: 用vue实现对话框效果:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 #pop{ 10 position:fixed; 11 top:50%; 12 left:50%; 13 width:300px; 14 height:100px; 15 margin-left:-150px; 16 margin-top:-50px; 17 background-color:lightBlue 18 } 19 #pop>a{ 20 float:right; 21 margin-right:20px; 22 text-decoration:none; 23 } 24 </style> 25 </head> 26 <body> 27 <!--VUE 3步 28 1. 做界面: 29 1.1 唯一父元素 30 1.2 找可能发生变化的元素 31 本例中:div#pop的显示隐藏状态随程序自动改变!所以,应该用v-show标记!定义变量show来表示对话框显示或隐藏 32 1.3 找触发事件的元素 33 本例中: 34 1.3.1 单击button元素可以显示对话框 35 1.3.2 单击a元素可以隐藏对话框--> 36 <div id="app"> 37 <button @click="open">click me</button> 38 <div id="pop" v-show="show"> 39 <a href="javascript:;" @click="close">×</a> 40 </div> 41 </div> 42 <script> 43 //2. 创建new Vue()对象,监控id为app的区域 44 new Vue({ 45 el:"#app", 46 //3. 创建模型对象: 47 //3.1 创建data对象 48 //本例中: 因为界面中只需要一个show变量,所以data中包含一个show变量 49 data:{ 50 show:false //开局,对话框默认隐藏 51 }, 52 //3.2 创建methods对象 53 //本例中: 因为界面中需要两个事件处理函数open和close,所以methods中就包含2个事件处理函数 54 methods:{ 55 open(){ 56 this.show=true; 57 }, 58 close(){ 59 this.show=false; 60 } 61 } 62 }) 63 </script> 64 </body> 65 </html>
第四种
v-if, v-else:
(1). 什么是: 专门在两个元素之中二选一显示的特殊指令
(2). 何时: 程序中只要在两个元素之中二选一显示时,都用v-if和v-else
(3). 如何: 判断条件
<元素1 v-if="变量或表达式">
<元素2 v-else>
(4). 原理: 每当new Vue()扫描到v-if指令时,都会先计算if后""中的变量或表达式的值。
a. 如果if后""中变量或表达式的值为true,则保留v-if所在的元素,删除v-else所在的元素
b. 如果if后""中变量或表达式的值为false,则删除v-if所在的元素,保留v-else所在的元素。
(5). 与v-show的差别:
a. v-show: display:none方式隐藏元素
b. v-if: 删除元素方式隐藏元素
(6). 强调:
a. v-if和v-else所在的两个元素,必须紧挨着写!中间不能插入其他元素
b. v-else和程序中的else一样,之后不用写任何条件。
(7). 问题: 为什么删除了的元素,还能再回到页面上?还能来回切换?
因为: 所有带有指令的元素,其实都被保存在内存中虚拟DOM树中,不会被删除。被删除的只是真实DOM树上的元素对象。每次new Vue()都会根据变量值不同,重新指派虚拟DOM树中的元素对象,重新出现在页面上。
——只要虚拟DOM树中有这个元素,这个元素就随时可以回到页面
(8). 示例: 切换登录状态:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <!--VUE 3步 11 1. 做界面 12 1.1 唯一父元素包裹 13 1.2 找可能发生变化的元素 14 本例中: 两个div二选一显示,所以,应该用v-if和v-else标记,并定义变量isLogin状态来记录用户是否登录 15 1.3 找触发事件的元素 16 本例中: 17 1.3.1 点登录,状态改为已登录 18 1.3.2 点注销,状态改为已注销--> 19 <div id="app"> 20 <!--已登录时显示--> 21 <div v-if="isLogin"> 22 <h3>Welcome dingding <a href="javascript:;" @click="logout">注销</a></h3> 23 </div> 24 <!--未登录时显示--> 25 <div v-else> 26 <a href="javascript:;" @click="login">登录</a> | 27 <a href="javascript:;">注册</a> 28 </div> 29 30 </div> 31 <script> 32 //2. 创建new Vue() 33 new Vue({ 34 el:"#app", 35 //3. 创建模型对象 36 //3.1 创建data对象 37 //本例中,因为界面上只需要一个isLogin变量 38 data:{ 39 isLogin:false //开局,默认未登录 40 }, 41 //3.2 创建methods对象 42 //本例中,因为界面上需要2个事件处理函数logout()和login() 43 methods:{ 44 logout(){ 45 this.isLogin=false; 46 }, 47 login(){ 48 this.isLogin=true; 49 } 50 } 51 }) 52 </script> 53 </body> 54 </html>
第五种
v-else-if:
(1). 什么是: 专门控制多个元素选其一显示的特殊指令
(2). 何时: 程序中只要在多元素中选其一显示,都用v-else-if
(3). 如何:
<元素1 v-if="条件1">
<元素2 v-else-if="条件2">
<元素n v-else-if="条件n">
<元素 v-else>
(4). 原理: 每当new Vue()扫描到v-if指令时,都会先计算if后""中的变量或表达式的值。
a. 如果if后""中变量或表达式的值为true,则保留v-if所在的元素,删除其余v-else-if和v-else所在的元素。
b. 如果if后""中变量或表达式的值为false,new Vue()会继续计算后续v-else-if中的条件表达式。如果某个v-else-if中的条件为true,则只保留这个v-else-if所在的元素,删除其余v-if、v-else-if、v-else所在的元素。
c. 除非所有条件都不满足要求,则删除所有v-if、v-else-if所在元素,只保留v-else所在元素。
(5). 强调: v-if、v-else-if、v-else几个元素必须连续写,中间不能插入其他元素。
(6). 示例: 根据pm25的数值,显示不同的表情
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 //每张图片保存着不同的表情 12 <img v-if="pm25<100" src="img/1.png" alt=""> 13 <img v-else-if="pm25<200" src="img/2.png" alt=""> 14 <img v-else-if="pm25<300" src="img/3.png" alt=""> 15 <img v-else src="img/4.png" alt=""> 16 </div> 17 <script> 18 var vm=new Vue({ 19 el:"#app", 20 data:{ 21 pm25:360 22 } 23 }) 24 </script> 25 </body> 26 </html>
第六种
v-for:
(1). 什么是: 专门遍历一个数组,反复生成多个相同结构的元素的特殊指令
(2). 何时: 程序中,只要反复生成多个相同结构的元素时,都用v-for。
(3). 如何:
<要重复生成的元素 v-for="(元素值, 下标) of 数组">
(4). 原理: 每当new Vue()扫描到v-for时,都会先遍历of后的数组中每个元素,每遍历一个元素,就取出每个元素值和下标位置,保存到of前的变量中。每遍历一个元素,v-for同时都会创建当前HTML元素的副本。在v-for所在的当前元素中,及其子元素中,元素值和下标可用于各种指令和绑定语法!
(5). 强调:
a. v-for一定要放在要反复生成的元素上!不能放在要反复生成的元素的父元素上。
b. 按标准来说,()必须加!有时,去掉()虽然正确。但是将来公司中都会启用代码质量检查工具!代码质量检查工具认为!不加()是不规范的,会报错!
c. 其实, of也可以换成in。但是,of和in的效果完全一样!没有差别!
(6). 坑: VUE中,使用下标修改数组中的元素值,界面上是不会自动跟着变的!
原因: VUE框架只监控自定义下标的属性名和方法名,无法监控数字下标!
解决: 程序中在vue中只要修改数组中的元素值,都用splice函数使用替换的方式来修改。
this.数组.splice(i,1,新元素值) //复习第一阶段数组函数
删除i位置的一个值
再在i位置插入一个值
(7). 问题: 即使只修改了数组中一个元素,v-for也会傻乎乎的重建整个界面中的列表。——完全没必要,且效率极低!
(8). 解决: 程序中,只要使用v-for,几乎都要绑定一个特殊属性":key",且绑定给:key的属性值变量,值应该是唯一的!
<要重复生成的元素 v-for="(元素值, 下标) of 数组" :key="下标">
(9). 好处:
a. 程序中每个v-for反复生成的HTML元素副本上都带有一个隐藏的且值唯一的key属性。
b. 从此v-for如果只更新一个元素值,就可通过key属性值找到要修改的一个HTML元素,只更新一个HTML元素即可,不用重建整个列表——效率高
(10). v-for因为统一了js中的for of 和for in,所以v-for不但可以遍历索引数组,还能变量对象属性!
<要重复生成的元素 v-for="(属性值, 属性名) of 对象" :key="属性名">
(11). 示例: 使用v-for遍历数组和对象,反复生成多个页面元素
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <ul> 12 <li v-for="(元素值, i) of arr" :key="i"> 13 {{i+1}} - {{元素值}} 14 </li> 15 </ul> 16 <hr/> 17 <ul> 18 <li v-for="(属性值, 属性名) of lilei" :key="属性名"> 19 {{属性名}} - {{属性值}} 20 </li> 21 </ul> 22 </div> 23 <script> 24 var vm=new Vue({ 25 el:"#app", 26 data:{ 27 arr:["欢欢","平平","安安"], 28 lilei:{ 29 sname:"Li Lei", 30 sage:11, 31 class:"初一2班" 32 } 33 } 34 }) 35 //比较: 36 //vm.arr[1]="小月月",能否修改数组,能否更新界面 37 //vm.arr.splice(1,1,"小月月"),能否修改数组,能否更新界面 38 </script> 39 </body> 40 </html>
(12). v-for还会数数!
<要重复生成的元素 v-for="(i) of 整数" :key="i">
v-for自动从1开始连续数数,每数一个数,就自动创建当前元素的一个副本。直到数到of后的整数为止!
(13). 示例: 根据总页数,生成分页按钮
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 ul{ 10 list-style: none; 11 } 12 ul>li{ 13 float:left; 14 width:40px; 15 height:40px; 16 text-align:center; 17 line-height:40px; 18 border:1px solid #555; 19 border-radius: 5px; 20 margin-left:5px; 21 } 22 ul>li:hover{ 23 cursor:pointer; 24 background-color:lightskyblue; 25 color:#fff; 26 border:0; 27 } 28 </style> 29 </head> 30 <body> 31 <div id="app"> 32 <ul> 33 <li v-for="i of pageCount" :key="i">{{i}}</li> 34 </ul> 35 </div> 36 <script> 37 var vm=new Vue({ 38 el:"#app", 39 data:{ 40 pageCount:6 //共6页 41 } 42 }) 43 </script> 44 </body> 45 </html>
第七种
v-on
(1). 什么是: 专门给元素绑定事件处理函数的特殊指令
(2). 何时: 程序中只要绑定事件,都用v-on
(3). 如何: 2步:
a. <元素 v-on:事件名="事件处理函数()">
b. methods:{
事件处理函数(){
... ...
}
}
(4). 简写:
a. 程序中可用@代替v-on:
<元素 @事件名="事件处理函数()">
b. 如果事件处理函数不需要传参数值,则()可省略!
<元素 @事件名="事件处理函数">
(5). 其实: 事件处理函数可以传实参值:
a. <元素 @事件名="事件处理函数(自定义实参值,...)">
b. methods:{
事件处理函数(形参变量, ...){
... ...
}
}
c. 示例: 点谁,谁喊疼:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 img{ width:250px; } 10 </style> 11 </head> 12 <body> 13 <!--点谁,谁喊疼--> 14 <div id="app"> 15 <img src="img/tupain1.jpg" @click="say('tu1')"> 16 <img src="img/tupain2.jpg" @click="say('tu2')"> 17 </div> 18 <script> 19 new Vue({ 20 el:"#app", 21 methods:{ 22 say(name){ 23 alert(`${name} 疼!`) 24 } 25 } 26 }) 27 </script> 28 </body> 29 </html>
(6). 其实: vue中也可以获得事件对象:
a. <元素 @事件名="事件处理函数"> //一定不要加()
b. methods:{
事件处理函数(e){
//e就是DOM中的e
//事件发生时,会自动接住DOM中的事件对象
//后续可以: 阻止冒泡、利用冒泡、阻止默认行为、获得鼠标坐标、获得键盘按键号...
}
}
c. 示例: 点哪个部位,喊哪个部位疼:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 img{ width:250px; } 10 </style> 11 </head> 12 <body> 13 <!--点哪个部位,就喊哪个部位疼!--> 14 <div id="app"> 15 <img src="img/tupain1.jpg" @click="say"> 16 <img src="img/tupian2.jpg" @click="say"> 17 </div> 18 <script> 19 new Vue({ 20 el:"#app", 21 methods:{ 22 say(e){ 23 if(e.offsetY<150){ 24 alert(`头 疼!`); 25 }else if(e.offsetY<250){ 26 alert(`胸 疼!`); 27 }else{ 28 alert(`肚子疼!`); 29 } 30 } 31 } 32 }) 33 </script> 34 </body> 35 </html>
(7). 问题: 如何既获得事件对象,又能传自定义实参值?
a. 错误的解决1:
<元素 @事件名="事件处理函数(自定义实参值, ...)"
methods:{ DOM的event
×
事件处理函数( e ){
}
}
b. 错误的解决2:
<元素 @事件名="事件处理函数(自定义实参值)"
methods:{
事件处理函数( 形参, e ){ //Vue只能自动给第一个形参传event
}
}
c. 正确的解决: 必须借助于一个新的Vue的关键字: $event
1). 什么是: 专门在vue中自动获得DOM中事件对象的关键字
2). 何时: 程序中,只要既想获得事件对象,又想传自定义实参值时,都用$event
3). 如何:
$event就是DOM的事件对象e
<元素 @事件名="事件处理函数($event , 自定义实参值)"
methods:{
事件处理函数(e , 形参){
}
}
4). 说明: $event与自定义实参值的顺序可以交换,但是,必须保证methods中的形参变量顺序与HTML中元素上实参值的顺序一致就行!
5). 示例: 点谁的哪个部位,就喊谁的哪个部位疼!
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 img{ width:250px; } 10 </style> 11 </head> 12 <body> 13 <!--点谁的哪个部位,就喊谁的哪个部位疼!--> 14 <div id="app"> 15 <img src="img/tupian1.jpg" @click="say($event,'tu1')"> 16 <img src="img/tupian2.jpg" @click="say($event, 'tu2')"> 17 </div> 18 <script> 19 new Vue({ 20 el:"#app", 21 methods:{ 22 say(e, name){ 23 if(e.offsetY<150){ 24 alert(`${name} 的 头 疼!`); 25 }else if(e.offsetY<250){ 26 alert(`${name} 的 胸 疼!`); 27 }else{ 28 alert(`${name} 的 肚子疼!`); 29 } 30 } 31 } 32 }) 33 </script> 34 </body> 35 </html>
第七种
v-html:
(1). 问题: {{}}如果绑定的变量值中包含内嵌的HTML标签或特殊符号,则{{}}不会交给浏览器解析,而是原样显示!正常人看不懂的!
(2). 解决: 程序中,只要要绑定的变量值中,包含内嵌的HTML标签或特殊符号,需要先让浏览器解析,再给人看时,都用v-html指令,代替{{}}
(3). 如何: <元素 v-html="变量"></元素>
(4). 强调: v-html会先解析变量值为可以给人看的内容,然后,将解析后的结果完整替换<元素></元素>的内容!所以,绑定时,在元素开始标签和结束标签之间什么内容也不要加!即使加了,也会被覆盖的!
(5). 示例: 使用v-html绑定HTML内容:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h3>{{msg}}</h3> 12 <h3 v-html="msg"></h3> 13 </div> 14 <script> 15 new Vue({ 16 el:"#app", 17 data:{ 18 msg:`来自<a href="#"><<博客园>></a>的消息` 19 } 20 }) 21 </script> 22 </body> 23 </html>
第八种
v-cloak
防止用户短暂看到{{}}:
(1). 问题: 当网速慢时,js可能延迟下载和运行,用户就有可能提前看到HTML中的{{}}绑定语法!
(2). 解决: 2种:
a. v-cloak: (隐身斗篷)
1). 什么是: 专门在new Vue()加载完之前,暂时隐藏元素的特殊指令
2). 如何: 2步
i.
必须先在css中,手工添加一个属性选择器:
//选中所有带有v-cloak属性的元素,开局默认隐藏
[v-cloak]{ display:none } //复习第二阶段属性选择器
ii. <要隐藏的元素上 v-cloak>
3). 原理:
i. 开局: 因为new Vue()加载有延迟,所有v-cloak暂时发挥作用,让元素隐藏。
ii. 但是,当new Vue()加载完成后,会自动扫描页面中所有v-cloak,自动移除所有v-cloak。结果,所有v-cloak隐藏的元素就恢复显示了!
4). 示例: 使用v-cloak防止用户短暂看到{{}}
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 [v-cloak]{ 10 display:none 11 } 12 </style> 13 </head> 14 <body> 15 <div id="app"> 16 <h3 v-cloak>Welcome {{uname}}</h3> 17 </div> 18 <script> 19 setTimeout(function(){ 20 new Vue({ 21 el:"#app", 22 data:{ 23 uname:"dingding" 24 } 25 }) 26 },2000) 27 </script> 28 </body> 29 </html>
第九种
与v-cloak作用类似
v-text:
1). 什么是: 专门代替{{}}来绑定元素内容的特殊指令
2). 如何:
| js地盘 |
<要隐藏内容的元素 v-text="变量或表达式"></要隐藏的元素>
3). 原理:
i. 开局: 因为没有用{{}}绑定元素内容,所以,用户看不到任何内容的
ii. new Vue()加载完之后,new Vue()扫描到v-text时,会先计算""中变量或表达式的内容,用变量或表达式的内容完整代替开始标签到结束标签之间的内容。
4). 强调: 因为v-text也是完整覆盖元素的内容。所以,开局在元素内容中放置一些初始的内容,则后续都会被覆盖!无法显示!所以,如果v-text想用部分写死的内容和变量值拼接形成要显示的值时,应该在v-text后""中用模板字符串``来拼接字符串。
5). 示例: 使用v-text防止用户短暂看到{{}}
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <!-- | js地盘 |--> 12 <h3 v-text="`Welcome ${uname}`"></h3> 13 </div> 14 <script> 15 setTimeout(function(){ 16 new Vue({ 17 el:"#app", 18 data:{ 19 uname:"dingding" 20 } 21 }) 22 },2000) 23 </script> 24 </body> 25 </html>
v-text 与v-cloak. 比较:
a. v-text:
1). 优点:只需要js就可解决问题
2). 缺点: v-text只能控制一个元素的内容,如果要控制的元素特别多,则代码很繁琐
b. v-cloak:
1). 优点: 如果多个元素都需要暂时隐藏,我们只要将v-cloak放在父元素上即可!连父元素带子元素一起隐藏了
2). 缺点: 必须要手工添加css代码配合才能完成!
第十种
. v-once:
(1). 什么是: 专门标记一个元素只在首次加载时绑定一次的特殊指令
(2). 何时: 程序中,只要一个元素只想在首次加载时绑定一次,程序中,即使变量值发生变化,也不改变界面时,就用v-once来标记.
(3). 如何: <元素 v-once>
(4). 原理: 其实带有v-once的元素,只在首次加载时,绑定一次,然后并没有加入到虚拟DOM树中!结果: 程序中,即使变量变化,也无法通知到这个元素更新界面!
(5). 优化: 程序中,只要我们发现一个元素的内容,只在首次加载时,绑定一次,之后几乎不会改变时!都应该用v-once标记!减少虚拟DOM树中的元素个数,加快虚拟DOM树的查找和修改速度!
(6). 示例: 使用v-once标记上线时间,只在首次加载时绑定一次
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h3 v-once>上线时间: {{time}}</h3> 12 <h3>当前系统时间: {{time}}</h3> 13 </div> 14 <script> 15 var vm=new Vue({ 16 el:"#app", 17 data:{ 18 time:new Date().toLocaleString() 19 } 20 }); 21 //用定时器每隔一秒更新一次time的事件 22 setInterval(function(){ 23 vm.time=new Date().toLocaleString(); 24 },1000); 25 </script> 26 </body> 27 </html>
第十一种
v-pre:
(1). 什么是: 专门阻止vue编译内容中的{{}}的特殊指令
(2). 何时: 程序中万一元素内容中包括{{}},但是又不想给vue编译,就用v-pre
(3). 如何: <元素 v-pre>
(4). 示例: 阻止内容中的{{}}被vue编译:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h1 v-pre>vue框架采用{{自定义变量名}}方式标记页面中可能发生变化的位置</h1> 12 </div> 13 <script> 14 new Vue({ 15 el:"#app" 16 }) 17 </script> 18 </body> 19 </html>
上面11种都是单向绑定接下来介绍第十二种双向绑定
第十二种
v-model
<input type='text' v-model='变量'>
1. 问题: 使用:绑定文本框的value属性,无法自动获得用户输入的新值
2. 原理: 其实vue中的绑定分2种:
(1). 单向绑定:
a. 只能将程序中的变量值自动更新到界面上显示。(从M->V)
b. 无法将界面上用户的修改自动更新回程序中的变量中保存(从V->M)
(2). 双向绑定:
a. 既能将程序中的变量值自动更新到界面上显示。(从M->V)
b. 又能将界面上用户的修改自动更新回程序中的变量中保存(从V->M)
3. 解决: 程序中,只要我们想获得用户在界面上输入的或选择的新值,都要用双向绑定才行!
4. 总结: 因为只有表单元素,用户才能修改,所以,将来只要绑定表单元素时,几乎都用双向绑定!
5. 如何: <表单元素 v-model="变量">
6. 示例: 使用v-model自动获得表单元素的值
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <!--VUE 3步 11 1. 做界面 12 1.1 唯一父元素包裹 13 1.2 找可能发生改变的位置 14 本例中: 文本框input的内容会发生改变。因为文本框的内容保存在input的value属性上,所以,如果属性值发生改变,应该用:标记,并且定义变量str,准备接住用户输入的新搜索关键词 15 1.3 找触发事件的元素 16 本例中: 点按钮触发搜索事件--> 17 <div id="app"> 18 <!--错误!单向绑定--> 19 <!-- <input :value="str"> --> 20 <!--正确!双向绑定--> 21 <input v-model="str"> 22 <button @click="search">百度一下</button> 23 </div> 24 <script> 25 //2. 创建new Vue()对象, 监视id为app的区域 26 var vm=new Vue({ 27 el:"#app", 28 //3. 创建模型对象: 29 //3.1 创建data对象 30 //本例中: 界面中只需要一个变量str 31 data:{ 32 str:"" //开局,用户没输入关键词时,内容为空 33 }, 34 //3.2 创建methods对象 35 //本例中: 界面中需要一个事件处理函数 36 methods:{ 37 search(){ 38 console.log(`查找 ${this.str} 相关的内容...`); 39 } 40 } 41 }) 42 </script> 43 </body> 44 </html>
7. 原理:
(1). 凡是标有v-model的表单元素,new Vue()对象,都会自动为这个表单元素绑定input或change事件。
(2). 只要用户在表单元素中修改了值,都会触发input或change事件。
(3). new Vue()早就在input或change事件处理函数中,内置了修改指定变量的代码!就可实现只要用户一修改,就可将新值,自动更新回程序中指定变量上保存。
8. 示例: 使用单向绑定和事件,模拟实现双向绑定的效果:(与v-model还是有一点小区别 当@input存在时利用watch监控到的变量打印出来会含最初的字母而v-model不会)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <!-- <input v-model="str"> --> 12 <!-- 自动翻译为↓--> 13 <input :value="str" @input="update"> 14 <button @click="search">百度一下</button> 15 </div> 16 <script> 17 //2. 创建new Vue()对象, 监视id为app的区域 18 var vm=new Vue({ 19 el:"#app", 20 data:{ 21 str:"" 22 }, 23 methods:{ 24 search(){ 25 console.log(`搜索 ${this.str} 相关的内容...`) 26 }, 27 //v-model自动添加update函数 28 //复习DOM第四天下午的事件 29 update(e){ //e获得事件对象 30 //e.target获得当前正在触发修改事件的input元素 31 //e.target.value获得当前input元素的新值 32 this.str=e.target.value; 33 } 34 } 35 }) 36 </script> 37 </body> 38 </html>
9. 如何实现按回车、一边输入一边也能搜索!
(1). 按回车搜索,其实就是绑定@keyup事件
(2). 问题: 只希望按回车键13号时,才执行搜索操作,按别的键时,什么也不过!
(3). 解决:
a. DOM中: 先获得事件对象e,然后if(e.keyCode===13){ ... }
b. VUE中: 用事件修饰符来代替if判断,限制触发事件的条件
1). 什么是事件修饰符: 对DOM中一系列事件操作的简写:
2). 何时: 程序中,只要在vue中,想修改事件的行为,或限制事件的行为时,都用事件修饰符。
3). 比如: 如果限制只有按下回车键再抬起时,才能触发事件:
<元素 @keyup.13="事件处理函数">
等效于: if(e.keyCode===13){ ... }
4). 了解:
@事件名.stop="事件处理函数"
等效于: e.stopPropagation() //停止冒泡
@事件名.prevent="事件处理函数"
等效于: e.preventDefault() //阻止默认行为
(4). 实现一边输入一边搜索:
a. vue中提供了一个对变量的监视机制: watch
b. vue中的watch机制可以做到,只要变量值一被修改,就会立刻自动触发一个提前自定义好的回调函数.
c. 何时: 程序中,只要希望,变量值一变,立刻执行一项自定义操作时,都可用watch机制!
d. 如何:
new Vue({
el:"#app",
data:{
变量名: 值
},
methods:{
事件处理函数(){ ... }
},
watch:{ //new Vue()中,专门保存所有监视变量的函数的区域
//如果想监视一个变量
变量名(){ //程序中,无论以何种手段,修改了该变量的值,都会自动触发该变量对应的监视函数!
执行自定义操作!
}
}
})
(5). 示例: 实现按回车、一边输入一边搜索
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <!--只有当按下13号回车键时才能执行搜索操作--> 12 <input v-model="str" @keyup.13="search"> 13 <button @click="search">百度一下</button> 14 </div> 15 <script> 16 //2. 创建new Vue()对象, 监视id为app的区域 17 var vm=new Vue({ 18 el:"#app", 19 data:{ 20 str:"" 21 }, 22 methods:{ 23 search(){ 24 console.log(`搜索 ${this.str} 相关的内容...`) 25 } 26 }, 27 watch:{ 28 //希望变量str的值一旦修改,立刻自动执行搜索 29 str(){ 30 //调用搜索函数 31 this.search(); 32 } 33 } 34 }) 35 </script> 36 </body> 37 </html>
<input type='radio' v-model='变量'> 单选按钮
(1). 单选按钮的特点:
<input type="radio" value="1" name="sex">男
<input type="radio" value="0" name="sex">女
a. 单选按钮的value是写死的!等待用户来选择的!
b. 当用户选择单选按钮时,改变的不是value属性,而是checked属性状态。
(2). 如何:
<input type="radio" v-model="变量名" value="1" name="sex">男
<input type="radio" v-model="变量名" value="0" name="sex">女
(3). 原理:
a. 当首次加载时,new Vue()先获得变量的值,然后用变量的值与radio的value值做比较。哪个radio的value值与变量值相同!哪个radio就被自动选中!其余value值与变量值不相同的radio,就不会被选中。
(4). 示例: 选择性别:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 性别: <input type="radio" v-model="sex" name="sex" value="1">男 12 <input type="radio" v-model="sex" name="sex" value="0">女 13 <br/> 14 <h3>您选择的性别是:{{sex}}</h3> 15 </div> 16 <script> 17 new Vue({ 18 el:"#app", 19 data:{ 20 sex:1 21 } 22 }) 23 </script> 24 </body> 25 </html>
下拉列表
<selecte v-model='变量'>
<option></option>
<option></option>
</selecte>
(1). select元素的特征:
<select value="3">
<option value="1">文本1</option>
<option value="2">文本2</option>
<option value="3">文本3</option>
</select>
a. value值是写死在select下每个option上的!等待用户来选择的!
b. 当用户选择切换选择时,改变的不是option的value属性,该的是select的value。DOM中规定,选中的option的value值,会自动成为select元素的value值。
c. 因为选择option,改的是整个select的value值,所以v-model应该放在select元素上
(2). 如何:
<select v-model="变量" value="3">
<option value="1">文本1</option>
<option value="2">文本2</option>
<option
value="3">文本3</option>
</select>
(3). 原理:
a. 首次加载时, v-model会用变量的初始值与每个option的value值做比较,哪个value值等于变量值,则哪个option被选中。其余value值与变量值不相等的option,不会被选中
b. 当用户切换选中项后,v-model会自动将当前选中项的value,也就是整个select的新value,自动更新回程序中的变量中保存。
(4). 示例: 选城市,显示城市图片:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <!--VUE 3步 11 1. 做界面 12 1.1 唯一父元素 13 1.2 找可能发生变化的位置: 14 本例中: 15 1.2.1 select元素由用户主动手动选择改变,所以应该用双向绑定v-model来自动获得用户新选择的值,并定义变量src保存新选择的图片路径值 16 1.2.2 img元素的src属性会随程序自动变化,所以应该用单向绑定,又因为要改变的src是属性,所以用:绑定--> 17 <div id="app"> 18 请选择城市: <select v-model="src" id="sel"> 19 //每个value表示不同图片 20 <option value="imgs/bj.jpg">北京</option> 21 <option value="imgs/sh.jpg">上海</option> 22 <option value="imgs/hz.jpg">杭州</option> 23 </select><br/> 24 <br/> 25 <br/> 26 <img style="width:300px" :src="src"> 27 </div> 28 <script> 29 //用DOM测试,当选中某个option后,select的value值会有什么变化 30 // var sel=document.getElementById("sel"); 31 //当切换选中项时,自动触发 32 // sel.onchange=function(){ 33 // console.log(`select.value=${sel.value}`) 34 // } 35 //上下连段代码,不能同时保留,运行时,注释一个,再运行另一个。 36 37 //2. 创建new Vue()对象 38 new Vue({ 39 el:"#app", 40 //3. 创建模型对象 41 //3.1 因为界面中只需要一个变量src,所以 42 data:{ 43 src:"imgs/bj.jpg" //开局,默认显示北京图片 44 }, 45 //3.2 因为没有事件处理函数,所以,不用methods 46 }) 47 </script> 48 </body> 49 </html>
复选框单用:<input type="checkbox">
(1). 复选框单用:
<input type="checkbox">同意
a. 用户选中与不选中复选框,修改的不是value值,而是checked属性状态。
(2). 如何:
<input type="checkbox" v-model="变量">
(3). 强调: 变量的值最好是bool类型
(4). 原理:
a. 首次加载时,v-model会判断变量的中为true还是false。如果变量值为true,则当前checkbox选中,否则如果变量值为false,则当前checkbox不选中
b. 当用户切换checkbox的选中状态后,v-model会将新的选中状态(checked属性值)自动更新回程序中的变量中保存。
(5). 示例: 点同意,启用元素,不同意,禁用元素
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <!--VUE 3步 11 1. 做界面 12 1.1 唯一父元素 13 1.2 找可能发生变化的位置: 14 1.2.1 checkbox的选中状态会被用户主动改变,所以,应该用双向绑定,并定义变量isAgree表示是否同意 15 1.2.2 其余3个表单元素的disabled属性会随程序而自动变化。所以,应该用单向绑定。而且,因为其余三个表单元素的disabled状态,直接和checkbox的选中状态,也就是isAgree变量的值有关。 16 规则: checkbox选中, isAgree=true,表示用户同意 17 其余表单元素的disabled应该=false,表示启用checkbox未选中,isAgree=false,表示用不同意 18 其余表单元素的disabled应该=true,表示禁用 19 总结: 其余三个表单的disabled属性值与isAgree的变量值刚好相反!--> 20 <div id="app"> 21 <br/> 22 用户名:<input :disabled="!isAgree"><br/> 23 <br/> 24 密码:<input :disabled="!isAgree" type="password"><br/> 25 <br/> 26 <input type="checkbox" v-model="isAgree">同意<br/> 27 <br/> 28 <button :disabled="!isAgree" >注册</button> 29 </div> 30 <script> 31 //2. 创建new Vue()对象 32 new Vue({ 33 el:"#app", 34 //3. 创建模型对象 35 //3.1 本例中: 因为界面上四个元素的改变都参照同一个变量isAgree,所以 36 data:{ 37 isAgree:false //开局,默认用户不同意 38 } 39 }) 40 </script> 41 </body> 42 </html>
六样式绑定
1. 绑定内联样式
(1). 何时: 如果只修改个别css属性值时,优先修改内联样式
(2). 解决方案1:
a. 将style属性看成一个普通的字符串属性来绑定
b. 如何:
<元素 :style="变量">
data:{
变量: "css属性1: 值1; css属性2:值2"
}
c. 问题: 多个css属性值都挤在一个字符串中,极其不便于修改某一个css属性值
(3). 解决方案2:
a. 将style属性值看做一个匿名对象语法来绑定:
b. 如何:
| JS地盘 JS对象语法 对象的属性:属性值 |
<元素 :style="{ css属性值: 变量1, css属性值2: 变量2 }"
//如果css属性名刚好与变量名相同,可只写一个名字
<元素 :style="{ css属性值, css属性值2 }"
既当css属性名
又当变量名
data:{
变量1: 值1,
变量2: 值2
}
c. 强调: 长度、大小、位置、距离相关的属性,必须带单位!
d. 原理:
1). new Vue()将data中的变量中保存的css属性值,更新到界面中对象语法内
2). 将对象语法编译为内联样式字符串,赋值给元素的style属性
e. 好处: 2个
1). 可以使用ES6的对象属性名变量名简写
2). 可以独立修改某一个css属性
f. 示例: 修改飞机的位置
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 </style> 10 </head> 11 <body> 12 <div id="app"> 13 <!—其中img为飞机图片--> 14 <!--做法1--> 15 <!-- <img style="position:fixed;" :style="imgStyle" src="imgs/p3.png" > --> 16 <!--做法2--> 17 <!-- <img style="position:fixed;" :style="{ left: left, top:top }" src="imgs/p3.png" > --> 18 <img style="position:fixed;" :style="{ left, top }" src="imgs/p3.png" > 19 20 <!-- <img style="position:fixed" src="imgs/p5.png" > --> 21 </div> 22 <script> 23 var vm=new Vue({ 24 el:"#app", 25 data:{ 26 //不好做法1: 27 // imgStyle:`left:200px; top:100px;` 28 //不好做法2: 29 left:"200px", 30 top:"100px" 31 } 32 }); 33 //在f12 console控制台: 34 //vm.left="300px" 让飞机水平向右飞100px 35 //vm.top="50px" 让飞机水平向上飞50px 36 </script> 37 </body> 38 </html>
g. 缺点: 如果多个元素都需要修改内联样式,则变量会很多,变量名极容易发生冲突!
(3)推荐解决方案
. 用有名称的对象来绑定
b. 如何:
<元素1 :style="变量名1">
<元素2 :style="变量名2">
//"css属性:值; css属性:值"
data:{
变量名1:{
css属性: 值,
css属性: 值
},
变量名2:{
css属性: 值,
css属性: 值
}
}
c. 原理:
1). new Vue()先将对象翻译为内联样式字符串:
"css属性:值; css属性:值;..."
2). 然后再将内联样式字符串替换到元素上style位置
d. 好处: 极大的减少了变量的个数,且避免了属性名与属性名之间命名冲突!
(4). 其实: 不带:的style和带:的style可以并存:
a. 结果: new Vue()先编译带:的style为内联样式字符串,再和不带:的style字符串拼接形成最后完整的style字符串
b. 程序或项目中,可能随程序动态变化的css属性,都放在带:的style中
c. 程序或项目中,固定不变的css属性,都放在不带:的style中
(5). 示例: 使用有名称的对象绑定飞机位置,并用键盘操作飞机位置
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 </style> 10 </head> 11 <body> 12 <div id="app"> 13 //src保存着不同的飞机图片 14 <img style="position:fixed;" :style="img1Style" src="imgs/p3.png" > 15 <img style="position:fixed" :style="img2Style" src="imgs/p5.png" > 16 </div> 17 <script> 18 var vm=new Vue({ 19 el:"#app", 20 data:{ 21 img1Style:{ 22 left:"200px", 23 bottom:"100px" 24 }, 25 img2Style:{ 26 left:"500px", 27 bottom:"100px" 28 } 29 } 30 }); 31 32 window.onkeydown=function(e){ 33 //希望按方向键操作第一架红色飞机移动 34 console.log(e.keyCode); 35 //左37 上38 右39 下40 36 if(e.keyCode==37){ 37 //先获得第一架飞机的left值,将left值-20 38 var left=parseInt(vm.img1Style.left) 39 left-=20; 40 vm.img1Style.left=`${left}px`; 41 }else if(e.keyCode==39){ 42 var left=parseInt(vm.img1Style.left) 43 left+=20; 44 vm.img1Style.left=`${left}px`; 45 }else if(e.keyCode==38){ 46 //先获得第一架飞机的bottom值,将bottom值+20 47 var bottom=parseInt(vm.img1Style.bottom) 48 bottom+=20; 49 vm.img1Style.bottom=`${bottom}px`; 50 }else if(e.keyCode=40){ 51 var bottom=parseInt(vm.img1Style.bottom) 52 bottom-=20; 53 vm.img1Style.bottom=`${bottom}px`; 54 } 55 //可以实现另一架飞机的移动 56 //左65 上87 右68 下83 57 } 58 59 </script> 60 </body> 61 </html>
2. 绑定class
(1). 何时: 如果批量修改一个元素的多个css属性值时,就绑定class
(2). 不好的绑定:
a. 将class属性看做一个普通的字符串属性来绑定
b. 如何:
<元素 :class="变量名">
data:{
变量名:"class1 class2
... "
}
c. 问题: 极其不便于只修改其中某一个class
(3). 不好的绑定2:
a. 使用匿名的对象来绑定class属性:
b. 如何:
<元素 :class="{ class名1:变量1 , class名2: 变量2 }"
data:{
变量1: true或false, //开关
变量2: true或false //开关
}
c. 原理: new Vue()在扫描class时,会先识别变量值。
1). 如果变量值为true,则该class就会出现在最终元素的class属性中
2). 如果变量值为false,则该class不会出现在最终元素的class属性中
d. 示例: 只验证手机号格式是否正确:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 .success{ 10 background-color:lightGreen; 11 color:green; 12 border:green 1px solid ; 13 } 14 .fail{ 15 background-color:pink; 16 color:red; 17 border:red 1px solid; 18 } 19 </style> 20 </head> 21 <body> 22 <!--VUE 3步 23 1. 做界面 24 1.1 唯一父元素 25 1.2 找可能变化的位置: 26 1.2.1 文本框的内容由客户主动输入而改变,所以用双向绑定,并定义变量phone接住用户输入的手机号 27 1.2.2 span元素的内容随验证是否通过而改变,所以用{{}}来绑定,并定义变量msg,来保存提示信息 28 1.2.3 span元素的class随程序动态改变,所以用:来绑定。因为有两个class来回切换,所以定义两个变量名,分别控制两个class具体应用哪一个 29 1.3 找可能触发事件的位置 30 本例中: 因为一边输入一边执行验证,所以用watch就可实现--> 31 <div id="app"> 32 手机号:<input type="text" v-model="phone"> 33 <!-- <span :class="{ success:success, fail:fail }">{{msg}}</span><br> --> 34 <span :class="{ success, fail }">{{msg}}</span> 35 </div> 36 <script> 37 //2. 创建new Vue()对象 38 new Vue({ 39 el:"#app", 40 //3. 创建模型对象: 41 //3.1 因为页面中一共需要4个变量 42 data:{ 43 phone:"",//开局,手机号默认为空 44 success:false,//开局,默认不应用任何class 45 fail:false,//开局,默认不应用任何class 46 msg:"" //开局,没验证,不需要提示信息 47 }, 48 //3.2 因为一边输入一边执行验证,所以 49 //本例中: 因为只有phone变量是用户主动修改的 50 watch:{ 51 phone(){ 52 //1. 定义正则表达式描述手机号的规则 53 var reg=/^1[3-9]\d{9}$/; 54 //2. 用正则表达式验证phone变量的值 55 var result=reg.test(this.phone); 56 //3. 如果验证通过 57 if(result==true){ 58 //3.1 先修改msg的提示信息为格式正确 59 this.msg="手机号格式正确" 60 //3.2 再修改success和fail的变量值,控制启用success样式类,禁用fail样式类 61 this.success=true; 62 this.fail=false; 63 }else{//4. 否则如果验证不通过 64 //4.1 先修改msg的提示信息为格式不正确 65 this.msg="手机号格式不正确" 66 //4.2 再修改success和fail的变量值,控制禁用success的样式类,启用fail样式类 67 this.success=false; 68 this.fail=true; 69 } 70 } 71 } 72 }) 73 </script> 74 </body> 75 </html>
e. 问题: 如果多个元素都需要控制样式,则变量名就会很多,极容易造成冲突!
(4). 好的绑定:
a. 用有名称的对象来绑定
b. 如何:
<元素1 :class="变量1">
<元素2 :class="变量2">
data:{
变量1:{
class1: true或false,
class2: true或false
},
变量2:{
class1: true或false,
class2: true或false
},
}
c. 原理:
1). new Vue()先扫描变量中的对象中的class名后的bool值,将对象格式翻译为class字符串。
a. 哪个class名对应的值为true,则该class会出现在最终的class字符串中
b. 哪个class名对应的值为false,则该class不会出现在最终的class字符串中
2). 最后,将翻译后的class字符串,替换:class的位置
d. 优点: 即使多个元素都要动态绑定相同的class名,也不会冲突
e. 示例: 验证手机号和密码的格式是否正确:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <style> 9 .success{ 10 background-color:lightGreen; 11 color:green; 12 border:green 1px solid ; 13 } 14 .fail{ 15 background-color:pink; 16 color:red; 17 border:red 1px solid; 18 } 19 </style> 20 </head> 21 <body> 22 <div id="app"> 23 手机号:<input type="text" v-model="phone"> 24 <span :class="phone_class">{{msg_phone}}</span><br> 25 密码:<input type="password" v-model="pwd"> 26 <span :class="pwd_class">{{msg_pwd}}</span> 27 </div> 28 <script> 29 //2. 创建new Vue()对象 30 new Vue({ 31 el:"#app", 32 data:{ 33 phone:"", 34 pwd:"", 35 phone_class:{ 36 success:false, 37 fail:false 38 }, 39 pwd_class:{ 40 success:false, 41 fail:false 42 }, 43 msg_phone:"", 44 msg_pwd:"" 45 }, 46 watch:{ 47 phone(){ 48 var reg=/^1[3-9]\d{9}$/; 49 var result=reg.test(this.phone); 50 if(result==true){ 51 this.msg_phone="手机号格式正确" 52 this.phone_class={ 53 success:true, 54 fail:false 55 }; 56 }else{ 57 this.msg_phone="手机号格式不正确" 58 this.phone_class={ 59 success:false, 60 fail:true 61 }; 62 } 63 }, 64 pwd(){ 65 var reg=/^\d{6}$/; 66 var result=reg.test(this.pwd); 67 if(result==true){ 68 this.msg_pwd="密码格式正确" 69 this.pwd_class={ 70 success:true, 71 fail:false 72 }; 73 }else{ 74 this.msg_pwd="密码格式不正确" 75 this.pwd_class={ 76 success:false, 77 fail:true 78 }; 79 } 80 } 81 } 82 }) 83 </script> 84 </body> 85 </html>
(5). 其实: 不带:的class和带:的class可以并存
a. 固定不变的class,可以放在不带:的class属性中
b. 动态改变的class,可以放在带:的class属性中
c. 最后编译时,两部分class是合并后,共同发挥作用的。
七自定义指令
1. 问题: 如果在new Vue()之前为DOM元素执行一些初始化操作时,会被后续的new Vue()清除掉!无法发挥作用!比如: 自动获得焦点.
2. 解决: 程序或项目中,只要想在页面加载时,就为元素自动执行一些初始化的设置操作时,都要用自定义指令!
3. 如何: 2步
(1). 先创建子定义指令:
//先创建一个指令对象
//再将指令对象加入到内存中Vue类型中保存起来备用
Vue.directive("指令名",{
//回调函数: 当new Vue()扫描到带有这个指令的元素时自动执行
//同时,会将当前带有这个指令的DOM元素自动传给回调函数第一个形参变量——信任!
inserted( 当前DOM元素 ){
//对当前DOM元素执行原生的DOM操作
}
})
(2). 使用指令: 指令其实就是一个自定义的HTML属性而已
<元素 v-指令名>
(3). 原理: 当new Vue()扫描到一个带有自定义指令的元素时,会回到内存中Vue类型中查找是否有同名的指令。一旦找到同名的指令,就会自动调用指令对象中的inserted函数,并自动将当前DOM元素对象传给inserted函数的第一个形参!在inserted函数内,对当前DOM元素执行原生的DOM初始化操作。
(4). 好处: 不会被new Vue()冲掉!可以保留下来!
(5). 强调:
a. 定义指令对象时,指令名一定不要带v-前缀。但是,在html中使用该指令时,又必须加v-前缀!
b. 如果指令名中包含多个英文单词,必须用-来分隔多个单词,所有字母必须小写!
原因: 因为HTML语言很蠢!HTML无法区分大写字母和小写字母!所以,在HTML中使用的标签名、属性名,就不应该使用任何形式的大写字母,一律都要用小写字母!
4. 示例: 让文本框在开局时自动获得焦点:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <!--希望在页面加载时,input文本框自动获得焦点--> 12 <input id="txt" v-my-focus><button>搜索</button> 13 </div> 14 <script> 15 //DOM中: 和后边的vue冲突! 16 // var txt=document.getElementById("txt"); 17 // txt.focus();//让当前表单元素自动获得焦点——减少用户的操作步骤,提高用户体验 18 19 //先定义自定义指令: 20 Vue.directive("my-focus",{ 21 inserted( 当前DOM元素 ){ 22 当前DOM元素.focus(); 23 } 24 }) 25 26 new Vue({ 27 el:"#app" 28 }) 29 </script> 30 </body> 31 </html>
八计算属性
1. 问题: 页面上有些值需要经过复杂的计算才能获得,而{{}}不支持循环、分支等复杂程序结构!
2. 解决: 程序或项目中,只要一个值需要经过复杂的计算才能获得时,都要用计算属性
3. 什么是: 不实际保存属性值,获取属性值时,都要经过其它属性值计算获得。
4. 如何: 2步:
(1). 在new Vue()中定义计算属性:
new Vue({
el:"#app",
data:{ ... },
methods:{ ... },
watch:{ ... },
computed:{ //专门保存所有计算属性的区域
//虽然称为"属性",但是实际上是一个函数
属性名(){
复杂的计算过程
return 计算结果!
}
}
})
(2). 在界面中绑定语法中使用计算属性:
<元素>{{ 属性名 }}</元素>
//千万不要加()!
5. 原理:
(1). new Vue()在绑定语法中如果发现"变量",会先去data中找是否有同名变量。如果没找到,再进入computed()中找有没有同名的计算属性。
(2). 如果在computed中找到了同名的计算属性,就自动执行计算属性的计算过程。并将计算结果return,返回到界面(如果应用了的话),代替计算属性名出现的位置。
6. 计算属性与methods中函数的差别:
(1). methods中的函数,调用时必须加(),且调用几次,就会反复执行几次。methods的执行结果不会被缓存
(2). computed中的计算属性,使用时,不用加(),且无论使用多少次,只在第一次使用时计算一次。然后计算结果就被new Vue()缓存起来!之后每次使用时,不必重复计算,直接从缓存中取数据即可!
(3). 总结:
a. 程序或项目中如果一个函数,更侧重于执行一项操作任务,而不太关心返回的结果数据时,就用methods中的函数。
b. 程序或项目中如果更关心一个函数的计算结果时,并且可能反复使用计算结果时,都用计算属性computed。
7. 示例: 计算购物车总价:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h3>总价: ¥{{ total.toFixed(2) }}</h3> 12 <ul> 13 <li v-for="(p,i) of cart" :key="i"> 14 {{p.pid}} | {{p.pname}} | ¥{{p.price.toFixed(2)}} | {{p.count}} | 小计: ¥{{(p.price*p.count).toFixed(2)}} 15 </li> 16 </ul> 17 <h3>总价: ¥{{ total.toFixed(2) }}</h3> 18 </div> 19 <script> 20 new Vue({ 21 el:"#app", 22 data:{ 23 cart:[ 24 {pid:1, pname:"华为", price:5588, count:2}, 25 {pid:2, pname:"苹果", price:9588, count:1}, 26 {pid:3, pname:"小米", price:3588, count:3} 27 ] 28 }, 29 methods:{ 30 31 }, 32 computed:{ 33 //计算购物车中所有商品的总价 34 total(){ 35 console.log(`计算了一次购物车的总价...`) 36 var result=0; 37 //遍历购物车中每个商品对象 38 for(var p of this.cart){ 39 // 用每个商品对象的单价*数量,获得小计 40 // 再将小计累加到结果变量result中 41 result+=p.price*p.count; 42 } 43 //返回计算结果 44 return result; 45 } 46 } 47 }) 48 </script> 49 </body> 50 </html>
九过滤器
1. 问题: 程序中有些数据不能直接个人看,而是必须先加工才能个人看
2. 解决: 程序中,只要一个数据必须先经过加工才能给人看时,都要用过滤器
3. 什么是: 对一个变量的原始值,先加工,再显示的一个特殊的函数
4. 如何: 2步:
(1). 创建过滤器函数:
//先创建一个过滤器函数
//再将过滤器函数保存到Vue类型中备用
// 变量的原始值
↓ 自动传给
Vue.filter("过滤器名", function(形参){
//经过转化后
← return 可以给人看的新值
})
(2). 在界面中使用过滤器:
<元素>{{ 变量 | 过滤器 }}</元素>
连接
5. 原理:
(1). new Vue()扫描到|,会先将变量的原始值,自动传给过滤器的形参,并自动调用过滤器函数
(2). 然后将过滤器返回的新值,代替当前变量所在位置.
6. 强调: 因为将来过滤器名是在HTML中{{}}内js地盘使用的,所以如果名称中包含多个英文单词,必须用驼峰命名!
7. 示例: 过滤性别:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <!-- | JS地盘 | --> 12 <h3>性别: {{sex | sexFilter }}</h3> 13 </div> 14 <script> 15 Vue.filter("sexFilter",function(oldValue){ 16 return oldValue==1?"男":"女" 17 }) 18 19 new Vue({ 20 el:"#app", 21 data:{ 22 sex:1 23 } 24 }) 25 </script> 26 </body> 27 </html>
8. 其实过滤器也可以添加自定义参数:
(1). 何时: 希望过滤器根据不同情况,过滤出不同的结果,就可以用参数来控制
(2). 如何: 2步:
a. 定义过滤器时: 预留
Vue.filter("过滤器名", function(旧值, 自定义形参, ...){
根据不同的旧值和形参值
return 不同的新值
})
b. 使用过滤器时:
<元素>{{变量 | 过滤器名( 实参值1, ... ) }}</元素>
(3) 强调: 使用过滤器时,因为过滤器的第一个形参是给变量的原始值预留的位置,所以我们传入的第一个实参值,会自动传给第二个形参变量。
(4). 示例: 过滤不同语言的性别:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h3>性别: {{sex | sexFilter }}</h3> 12 <h3>性别: {{sex | sexFilter("en") }}</h3> 13 <h3>性别: {{sex | sexFilter("cn") }}</h3> 14 </div> 15 <script> 16 Vue.filter("sexFilter",function(oldValue,语言){ 17 if(语言=="en"){ 18 return oldValue==1?"Male":"Female" 19 }else{ 20 return oldValue==1?"男":"女" 21 } 22 }) 23 24 new Vue({ 25 el:"#app", 26 data:{ 27 sex:1 28 } 29 }) 30 </script> 31 </body> 32 </html>
9. 过滤器连用:
(1). <元素>{{变量 | 过滤器1 | 过滤器2 | ... }}</元素>
(2). 强调:
a. 下一个过滤器在定义时,必须充分考虑各种传入值的情况。
1). 如果过滤器直接接原始变量使用时,下一个过滤器就会接到原始值,
2). 如果过滤器前是前一个过滤器,不是原始值,则接到前一个过滤器处理后的中间产物。
b. 如果希望最后可以输出每个过滤器过滤结果的拼接的产物,则最后一个过滤器就不能自私的只返回自己的值!应该将自己的新值和之前的旧值拼接起来再返回!
(3). 示例: 为过滤出的性别再加图标:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <h3>性别: {{sex | sexIcon}}</h3> 12 <h3>性别: {{sex | sexFilter | sexIcon}}</h3> 13 <h3>性别: {{sex | sexFilter("en") | sexIcon}}</h3> 14 <h3>性别: {{sex | sexFilter("cn") | sexIcon}}</h3> 15 </div> 16 <script> 17 Vue.filter("sexFilter",function(oldValue,语言){ 18 if(语言=="en"){ 19 return oldValue==1?"Male":"Female" 20 }else{ 21 return oldValue==1?"男":"女" 22 } 23 }) 24 // 1,0,男,女,Male,Female 25 Vue.filter("sexIcon",function(oldValue){ 26 if(oldValue==1){ 27 return "♂" 28 }else if(oldValue==0){ 29 return "♀" 30 }else if(oldValue=="男"||oldValue=="Male"){ 31 return oldValue+"♂" 32 }else{ 33 return oldValue+"♀" 34 } 35 }) 36 37 new Vue({ 38 el:"#app", 39 data:{ 40 sex:0 41 } 42 }) 43 </script> 44 </body> 45 </html>
十 axios
1. 什么是:
(1). 第三方开发的: 下载
(2). 基于Promise的: 前一项任务().then(下一项任务)
open(参数值) function 下一项任务(形参){ ... }
(3). 专门发送http或ajax请求的函数库: 用途
2. 何时: 程序或项目中只要在vue中发送ajax请求,都用axios
3. 如何:
(0). 先引入axios.js文件
(1). 先配置所有服务器端接口的基础路径部分
axios.defaults.baseURL="服务器端接口地址的公共基础路径部分"
(2). 发送get请求:
axios.get(
"/相对接口地址名即可",
{ //如果不需要传参数,这部分可以省略
params: { 参数名: 值, ... : ... , ... }
}
).then(result=>{ //回调函数,只在成功收到响应结果后自动执行
//result接住的不是响应结果
//result.data才是服务器端的响应结果
})
(3). 发送post请求:
axios.post(
"/相对接口地址名即可",
"变量名1=值1&&变量名2=值2&..." //查询字符串的格式
).then(result=>{
//result.data才是服务器端返回的结果
})
4. 示例: 使用axios向新浪云服务器发送请求:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/axios.min.js"></script> 9 </head> 10 <body> 11 <script> 12 //将axios添加vue对象大家族中 调用时 加this即可(如this.axios.get(.....)) 13 //Vue.prototype.axios=axios; 14 15 //先配置基础路径 16 axios.defaults.baseURL="http://xzserver.applinzi.com" 17 //向新浪云服务器请求学子商城首页6个商品 18 axios.get( 19 "/index" 20 // ).then(function(result){ 21 ).then(result=>{ 22 console.log(result.data); 23 }); 24 //向新浪云服务器请求学子商城5号商品的详细信息 25 axios.get( 26 "/details", 27 { 28 params:{ lid:5 } 29 } 30 // ).then(function(result){ 31 ).then(result=>{ 32 console.log(result.data); 33 }); 34 //向新浪云服务器发送登录验证 35 axios.post( 36 "/users/signin", 37 "uname=dingding&&upwd=123456" 38 ).then(result=>{ 39 console.log(result.data); 40 }) 41 </script> 42 </body> 43 </html>
十一 生命周期
1. 问题: 在DOM和jquery中,都能在页面加载完成后自动发送请求,自动执行事件绑定等页面初始化任务。在DOM中,可以将页面初始化代码写在window.onload或DOMContentLoaded中。但是,因为vue的初始化操作和DOM的初始化有冲突!VUE会把DOM的初始化操作给清除掉!那么,在VUE中,如果想在vue加载完之后,自动执行一项任务,应该把代码写在哪儿?
2. 解决: 要将代码写在VUE的生命周期过程中!
3. 什么是Vue的生命周期: 一个new Vue()对象,创建和加载过程中所经历的所有阶段。
4. Vue对象的生命周期共有: 4个阶段:
*****必经阶段*****
(1). 创建(create)阶段:
a. 创建模型对象(data,methods...)
b. 暂时还没有虚拟DOM树
(2). 挂载(mount)阶段:
a. 已经有了data和methods对象...
b. 再创建虚拟DOM树,并首次渲染页面内容。
*****不是必经阶段*****
(3). 更新(update)阶段: 当在new Vue()中以任何方式修改了变量值,并影响了页面上的显示时,才触发更新阶段
(4). 销毁(destroy)阶段: 只有当new Vue()对象主动调用了$destroy()函数,销毁自己时,才自动触发销毁阶段。(如 var vm = new Vue(){} vm.$destroy() 会将快递员开除)
5. 其实,vue生命周期四个阶段,每个阶段前后都提供了一个回调函数: 8个
beforeCreate(){ ... } —— 钩子函数/回调函数
(1). 创建(create)阶段:
created(){ —— 钩子函数/回调函数
... 虽然可以发送axios请求,因为此时已经有data对象了,axios请求回来的数据,就可以妥妥的保存到data对象中的变量中。
... 但是不保险,因为暂时没有虚拟DOM树。如果执行一些DOM的初始化操作,就被后续的new Vue()对象的挂载给清除掉!
}
beforeMount(){ ... } —— 钩子函数/回调函数
(2). 挂载(mount)阶段:
mounted(){ —— 钩子函数/回调函数
... 既有data对象,可以保存axios请求回来的数据
... 又有虚拟DOM树,再执行一些DOM初始化操作,不会被清除掉
总结: 程序中,new Vue()中只要执行页面初始化操作(发送ajax请求,DOM操作)都要在mounted()函数中执行,才最稳妥。
}
beforeUpdate(){ ... } —— 钩子函数/回调函数
(3). 更新(update)阶段:
updated(){ ... } —— 钩子函数/回调函数
beforeDestroy() —— 钩子函数/回调函数
(4). 销毁(destroy)阶段:
destroyed()
6. 其实,将来需要哪个函数,就只写哪个函数即可。暂时不用的大部分钩子函数/回调函数都可省略!
7. 示例: 展示生命周期8个钩子函数,并在挂载阶段后自动发送axiox请求
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <script src="js/axios.min.js"></script> 9 </head> 10 <body> 11 <div id="app"> 12 <h3>Welcome {{uname}}</h3> 13 <ul> 14 <li v-for="(p,i) of arr" :key="i"> 15 {{p.pid}} | {{p.details}} | ¥{{p.price.toFixed(2)}} 16 </li> 17 </ul> 18 </div> 19 <script> 20 var vm=new Vue({ 21 el:"#app", 22 data:{ 23 uname:"dingding",//不是异步!没有延迟! 24 arr:[] //将来用axios请求回来商品对象,放入这个数组中保存 25 }, 26 /*必经阶段*/ 27 beforeCreate(){ 28 console.log(`创建阶段前自动触发...`) 29 }, 30 created(){ //模型对象创建后 31 console.log(`创建阶段后自动触发...`) 32 }, 33 beforeMount(){ 34 console.log(`挂载阶段前自动触发...`) 35 }, 36 mounted(){ 37 console.log(`挂载阶段后自动触发...`); 38 axios.get( //异步 39 "http://xzserver.applinzi.com/index" 40 ) 41 //延迟 42 .then(result=>{ 43 console.log(result.data); 44 this.arr=result.data;//会触发更新阶段 45 }) 46 }, 47 /*不是必经阶段*/ 48 beforeUpdate(){ 49 console.log(`更新前自动触发...`) 50 }, 51 updated(){ 52 console.log(`更新后自动触发...`) 53 }, 54 beforeDestroy(){ 55 console.log(`销毁前自动触发...`) 56 }, 57 destroyed(){ 58 console.log(`销毁后自动触发...`) 59 } 60 }); 61 //触发更新阶段: 62 //vm.uname="丁丁" 63 //触发销毁阶段: 64 //vm.$destroy() 65 </script> 66 </body> 67 </html>
十二 组件
1. 何时: 只要发现页面上一个功能可能被多次反复使用,都要先将功能封装为组件,再反复使用组件。——重用!
2. 什么是组件: 拥有专属的HTML+CSS+JS+数据的独立的可重用的页面功能区域
3. vue中如何创建组件:
//先创建一个组件对象
//再将组件对象保存到内存中Vue类型中备用
Vue.component("组件名", {
//VUE中每个组件其实都是一个功能完善的缩微的小vue对象
//new Vue()对象有什么,每个小的组件对象就应该包含什么!
el:"#app", 因为将来组件不确定出现在页面哪个位置,出现几次,所以无法执行查找操作,所以不需要el属性
//取而代之的是template HTML片段模板
template:`<唯一父元素>
组件的HTML片段
</唯一父元素>`, //将来每使用一次组件,就会自动将模板的HTML片段复制一份到页面中指定位置。组件被反复使用了多少次,HTML模板就被反复复制多少遍。
data:{ ... },因为每个组件的副本都要有专属的数据副本,所以只创建一个data对象肯定不行!
data(){
return {
//界面所需的变量
}
}, //将来在界面中每使用一次组件,都会自动调用一次data()函数,返回一个新创建的模型对象(老母鸡下蛋),为每个组件副本都创建一个独立的专属的模型对象。保证组件对象之间数据互不干扰!
/*****以下和new Vue()完全一致*****/
methods:{ ... },
watch:{ ... },
computed:{ ... },
八个生命周期钩子函数(){ ...}
...
})
4. 如何在页面中反复使用组件:
(1). 组件在用法上其实就是一个可重用的自定义的HTML标签而已!
<组件名></组件名>
(2). 原理:
a. new Vue()扫描到一个不认识的标签时,会回内存中Vue类型中查找是否有同名的组件。
b. 如果找到了同名的组件: 3步
1). 复制组件对象中的HTML片段template内容,替换页面中组件标签所在的位置

2). 自动调用data函数,自动创建新模型对象副本给当前新的组件副本

3). 自动为当前组件副本所在的区域创建一个缩微的new Vue()小快递员对象,监视这个组件小区域的功能和变化。

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <ul> 12 <li><my-counter></my-counter></li> 13 <li><my-counter></my-counter></li> 14 <li><my-counter></my-counter></li> 15 </ul> 16 </div> 17 <script> 18 //做一个组件和做new Vue()完全一样:3步 19 //和前三天所讲的内容完全一致 20 Vue.component("my-counter",{ 21 //1. 做界面 22 //1.1 唯一父元素 23 //1.2 查找可能发生变化的位置 24 //1.3 触发事件的元素 25 template:`<span> 26 <button @click="minus">-</button> 27 <span>{{n}}</span> 28 <button @click="add">+</button> 29 </span>`, 30 //2. 创建模型对象 31 //2.1 创建data 32 data(){ 33 return { 34 n:0 35 } 36 }, 37 //2.2 创建methods 38 methods:{ 39 minus(){ 40 if(this.n>0){ this.n-- } 41 }, 42 add(){ 43 this.n++; 44 } 45 } 46 }) 47 48 new Vue({ 49 el:"#app" 50 }) 51 </script> 52 </body> 53 </html>
十三 组件化开发
1. 什么是组件化开发: 拿到一个页面后,先将页面拆分成多个组件,分头编写,最后再整合成一个页面显示给用户看。
2. 为什么:
1). 便于大项目的分工协作。——提高开发效率
2). 松耦合。——不让一个人的错误,影响大家
3. 何时: 程序中,几乎所有项目和框架的开发,都采用组件化开发。
4. 如何:
(1). 拿到一个页面之后,先将页面划分区域:
a. 按从上到下
b. 按从左向右
c. 按由内向外
d. 按是否重用
(2). 每个独立的功能区域都创建一个独立的组件文件,在文件中创建组件对象,保存这个区域的HTML代码片段
(3). 在唯一完整的HTML页面中,引入所有组件文件
(4). 在网页和组件的各个位置标记组件标签占位。
5. 结果: 运行时,new Vue()会逐级扫描各级组件中的所有内容,只要发现自定义组件,都会用对应的组件对象的template,代替页面或组件中组件标签所在的位置。最后,无论拆分多少组件,都会回到一个页面中共同显示!
6. 问题: 用Vue.component()创建的组件,可在页面任何位置使用,没有任何限制!——不是我们想要的!如何把一个子组件限制在其父组件内才能使用?
7. 原理: 其实,程序中vue中的组件分为三大类:
(1). 根组件: root,其实就是new Vue()对象
将来一个应用程序,只可能有一个完整的HTML页面。
根组件只出现在项目中唯一完整的HTML页面中
整个项目也就只有一个new Vue()
(2). 全局组件:
a. 什么是: 可以在网页任何位置随意使用的组件,不受任何限制!
b. 如何: 凡使用Vue.component()创建的组件都是全局组件!
(3). 子组件:
a. 什么是: 被限制在,只能在其父元素范围内使用的组件
b. 何时: 程序中,只要希望一个组件,只能在其父元素内使用时,不能超出父元素范围使用时,都用子组件。
8. 如何创建子组件:
(1). 不能用Vue.component()创建子组件,只能先将子组件对象,创建为一个普通的js对象。但是,对象的内容必须符合vue的组件成员的要求:
var 组件对象名={
template:`...`,
data(){ return { ... } },
methods:{ ... },
... ...
}
(2). 将子组件对象,加入并限制在父组件范围内:
a. 还是要先在完整的HTML页面中引入所有组件.js
b. 去子组件所在的父组件内,使用components:{}成员限制子组件的使用范围
components:{ 子组件对象名, ... }
凡是components{}内的子组件,只能在当前父组件范围内使用。超出当前父组件范围使用,会报错。
c.
强调: 在完整的HTML页面中引入组件文件时,必须保证子组件.js文件必须在父组件.js文件之前引入才行!
(3). 在父组件的template中,使用<子组件></子组件>引入子组件的内容.
a. 虽然子组件对象名在js中是驼峰命名,但是为了保证在html中使用子组件时不出错,vue的components会自动将驼峰命名的子组件名,翻译为-分隔的子组件名,在HTML template中使用。
9. 示例: 使用组件化开发实现待办事项列表的界面组件划分和整合
示意图:

所有创建的js文件都是集中保存在一文件中
components/TodoAdd.js
1 var todoAdd={ 2 template:`<div> 3 <input> 4 <button>+</button> 5 </div>` 6 }
components/TodoItem.js
1 var todoItem={ 2 template:`<span> 3 1 - 吃饭 <a href="javascript:;">×</a> 4 </span>` 5 }
components/TodoList.js
1 var todoList={ 2 template:`<ul> 3 <li> 4 <todo-item></todo-item> 5 </li> 6 <li> 7 <todo-item></todo-item> 8 </li> 9 <li> 10 <todo-item></todo-item> 11 </li> 12 </ul>`, 13 components:{ 14 todoItem 15 } 16 }
components/Todo.js
1 Vue.component("todo",{ 2 template:`<div> 3 <h3>待办事项列表</h3> 4 <!--所以,界面中写的子组件标签名,必须还是用-分隔!--> 5 <todo-add></todo-add> 6 <todo-list></todo-list> 7 </div>`, 8 components:{ 9 //组件对象名,因为js中不能所以写-,所以都是驼峰命名 10 //但是,驼峰命名会被vue自动翻译为-分隔! 11 todoAdd, todoList 12 } 13 })
/index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/vue.js"></script> 9 <script src="components/TodoAdd.js"> 10 //var todoAdd={ ... } 11 </script> 12 <script src="components/TodoItem.js"> 13 //var todoItem={ ... } 14 </script> 15 <script src="components/TodoList.js"> 16 //var todoList={ 17 //... 18 //components:{ todoItem } 19 //} 20 </script> 21 <script src="components/Todo.js"> 22 //Vue.component("todo",{ 23 //... 24 //components:{ todoAdd, todoList } 25 //}) 26 </script> 27 28 29 </head> 30 31 <body> 32 <div id="app"> 33 <todo></todo> 34 <!--todoItem已经被限制在了todoList中才能使用 35 所以,以下代码会报错!--> 36 <!-- <todo-item></todo-item> --> 37 </div> 38 <script> 39 new Vue({ 40 el: "#app" 41 }) 42 </script> 43 </body> 44 45 </html>
10. 组件间传值:
(1). 问题: Vue中,即使父组件中的成员,子组件也无权直接使用!
(2). 为什么: vue中的组件的数据成员都是专属的!
(3). 解决: Vue中组件间传参主要有三种方式:
a. 父给子: 2步:
1). 父组件向子组件中放一个数据: 父组件的template中:
<子组件标签 :自定义属性="父组件变量"></子组件标签>
相当于: 父组件给子组件身上添加了一个自定义的属性,其中保存住了父组件的一个成员变量。
2). 子组件中从父组件放数据的位置取出父组件给的数据:
子组件对象中: {
... ,
props:[ "自定义属性" , ... , ... ]
//props专门收集子组件身上所有自定义属性值的特殊集合。
//子组件对象内,props中的自定义属性名和data中的变量名用法完全一样:
//i. 都可用于HTML中的绑定语法或指令中
//ii. 都在js程序中使用,但是必须加this.
}
b. 子给父: (后期博客更新介绍)
c. 兄弟间: (后期博客更新介绍)
11. 示例: 使用父给子传参,实现待办事项列表的添加、删除和列表功能

实现+添加功能
components/TodoAdd.js
1 var todoAdd={ 2 props:["tasks"], 3 template:`<div> 4 <input v-model="task" @keyup.13="add"> 5 <button @click="add">+</button> 6 </div>`, 7 data(){ 8 return { 9 task:"" 10 } 11 }, 12 methods:{ 13 add(){ 14 this.tasks.push(this.task); 15 this.task=""; //清空文本框 16 } 17 } 18 }
实现×删除功能
/components/TodoItem.js
1 var todoItem={ 2 props:[ "t", "i", "tasks" ], 3 template:`<span> 4 {{i+1}} - {{t}} <a href="javascript:;" @click="del">×</a> 5 </span>`, 6 methods:{ 7 del(){ 8 this.tasks.splice(this.i, 1); 9 } 10 } 11 }
components/TodoList.js
1 var todoList={ 2 props:["tasks"], 3 template:`<ul> 4 <li v-for="(t,i) of tasks" :key="i"> 5 <todo-item :t="t" :i="i" :tasks="tasks"></todo-item> 6 </li> 7 </ul>`, 8 components:{ 9 todoItem 10 } 11 }
给最初的父元素添加 data数据
/ components/Todo.js
1 Vue.component("todo",{ 2 template:`<div> 3 <h3>待办事项列表</h3> 4 <!--所以,界面中写的子组件标签名,必须还是用-分隔!--> 5 <todo-add :tasks="tasks"></todo-add> 6 <todo-list :tasks="tasks"></todo-list> 7 </div>`, 8 data(){ 9 return { 10 tasks:[ "吃饭","睡觉","打豆豆" ] 11 } 12 }, 13 components:{ 14 //组件对象名,因为js中不能所以写-,所以都是驼峰命名 15 //但是,驼峰命名会被vue自动翻译为-分隔! 16 todoAdd, todoList 17 } 18 })
实现效果的html
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <script src="js/vue.js"></script> 9 <script src="components/TodoAdd.js"> 10 //var todoAdd={ ... } 11 </script> 12 <script src="components/TodoItem.js"> 13 //var todoItem={ ... } 14 </script> 15 <script src="components/TodoList.js"> 16 //var todoList={ 17 //... 18 //components:{ todoItem } 19 //} 20 </script> 21 <script src="components/Todo.js"> 22 //Vue.component("todo",{ 23 //... 24 //components:{ todoAdd, todoList } 25 //}) 26 </script> 27 28 29 </head> 30 31 <body> 32 <div id="app"> 33 <todo></todo> 34 <!--todoItem已经被限制在了todoList中才能使用 35 所以,以下代码会报错!--> 36 <!-- <todo-item></todo-item> --> 37 </div> 38 <script> 39 new Vue({ 40 el: "#app" 41 }) 42 </script> 43 </body> 44 45 </html>
十四 . SPA: Single Page Application (单页面应用)
1. 什么是: 整个应用程序只有一个唯一完成的HTML页面。其它所谓的"页面",都是组件而已。所谓的切换"页面",其实是切换不同的组件显示而已。
2. 单页面和多页面的比较:
|
|
多页面应用 |
单页面应用 |
|
请求次数 |
每次切换页面,都要重新发送请求,重新下载页面,请求次数多! |
因为在首次加载时,已经提前将所有组件都下载到客户端本地了。所谓的切换页面,只不过是在客户端切换不同的组件显示而已,无需重复向服务器发送请求。请求次数少! |
|
公共资源使用 |
每次请求新的页面,都要重复向服务器发送请求,请求公共资源的文件,比如bootstrap和jq等。请求次数更多! |
因为公共资源的引入,写在了唯一完整的HTML页面上。只会在首次下载所有页面时,请求一次公共资源。程序中再切换页面时,因为整个页面并未修改,只是更新局部的组件内容,所以无需重复请求公共资源。请求次数更少! |
|
页面更新效率 |
每次更新页面,都是删除旧页面,再下载重建新页面,所以旧的DOM树每次更新页面都要丢弃,再重建整棵DOM树。效率低。 |
整个应用程序只有一个唯一完成的HTML页面,且只在首次加载。所以,整棵DOM只会在首次创建。之后,切换页面时,只是更新旧DOM树中局部节点内容,无需重建整棵DOM树。效率高! |
|
页面过渡动画 |
几乎不可能实现页面过渡动画。因为页面过渡动画,要让人看到过渡效果,必须有一段时间,同时显示两个页面的部分内容。多页面应用不可能多个页面同时存在。 |
有可能实现页面过渡动画。因为单页面应用,所有页面组件在开始时,就都下载到了客户端,同时保存在客户端。自然有可能前后两个组件同时显示在页面上。可以实现页面过渡动画效果。 |
3. 何时: 程序中几乎所有项目都用单页面应用开发!
4. 组件化开发和单页面应用:
(1). 单页面应用,用来组织一个应用程序中整个项目多个"页面"的关系。
(2). 在开发每个页面时或每个组件时,会用到组件化开发。
5. 问题: 首屏加载时:
(1). 多页面应用,首屏只需要下载首页内容即可,无需下载其他页面内容。——下载量小,加载快
(2). 单页面应用,默认首屏加载,就要把所以的页面组件都下载下来。——下载量大,下载极慢
6.SPA如何使用:
1. 如何:
(1). 先创建一个唯一完整的HTML页面:
a. 包含最基本的支持vue框架的页面结构
(2). 为将来每个"页面"创建的独立的文件
a. 习惯上,所有"页面"组件的文件,都集中保存在名为views的文件夹
b. 每个"页面"文件中,其实定义的都是一个普通的子组件。
c. 将来这些"页面"组件,想加载到唯一完整的HTML页面中显示,必须提前引入到唯一完整的HTML页面中。
(3). 创建路由字典和路由器对象:
a. 在唯一完整的HTML页面顶部引入: vue-router.js
1). vue官方出的
2). 为了可以用new VueRouter()创建路由器对象,必须先引入vue-rouer.js
b. 在专门的router文件夹中,创建index.js文件,其中保存路由字典和路由器对象
1). 先创建路由字典:
i. 什么是: 专门保存相对路径与组件对象之间对应关系的数组
ii. 如何:
var routes=[
//当用户访问/相对路径时,请路由器对象自动查找指定的"页面"组件,自动加载到HTML文件中,显示给用户看
{ path:"/相对路径", component: "页面"组件对象名 }, { ... },
...
]
iii. 特殊路径:
{ path:"/", component: 首页组件对象}
2). 再创建路由器对象:
i. 什么是: 专门根据路由字典,自动监控地址栏变化,自动查找当前路径对应的组件对象,自动将组件对象替换到页面中指定区域显示的程序。
ii. 如何: var router=new VueRouter({ routes })
c. 在唯一完整的HTML页面中, <div id="app">内部,用专门的标签<router-view></router-view>为将来的页面组件指明停放的位置。
d. 在唯一完整的HTML页面中,引入router/index.js,并将router对象加入到new Vue()中:
<script src="router/index.js">
new Vue({
el:"#app",
router
})
为什么: 因为<router-view>归new Vue()管,所以,路由器router要想修改new Vue()管理的<router-view>区域的内容,必须和new Vue()配合,才能完成。
创建好后的目录如图:

2. 结果:
(1). 只要地址栏变化,就会被router对象监听到
(2). router对象自动获得地址栏中当前相对路径,用相对路径去路由字典routes中查找对应的组件对象
(3). router就会指定把找到的组件对象,通过new Vue()的配合,自动替换到唯一完整的HTML页面中<router-view>的位置。
(4). 用户就看到了新加载的页面内容
3. 404页面: 404页面也是一个普通的页面组件,所以:
(1). 应该也在views文件夹中创建404页面文件,其中保存404页面组件的内容
(2). 也要将404页面组件文件引入到唯一完整的HTML页面中
强调: 因为404页面将来也是要配置路径,也是要归路由器和路由字典管的,所以404页面和其它普通页面一起,一定要在router/index.js路由器对象之前引入!
(3). 在路由字典中添加专门的路径指向404页面组件对象:
{path:"*", component: 404页面组件对象}
*一定要放在其他正确路径之后。
*的意思是,匹配除了之前正确路径之外,其余所有错误的路径都加载NotFount组件。
4. 页头:
(1). 页头虽然不是完整的页面,但是,也是一个可反复使用的组件。
(2). 如何:
a. 在专门的文件夹components中,保存那些不足以成为整个页面组件的局部组件或子组件,比如:页头文件
b. 只要在唯一完整的HTML页面中,引入页头组件文件即可,没有顺序要求!
c. 在唯一完整的HTML页面中,将引入的页头组件对象,用Vue.component()函数变为全局组件
一定要在new Vue()之前!
d. 在指定位置,使用页头组件标签来加载页头
1). 如果所有页面组件都需要加载页头
应该在唯一完整的HTML页面中<router-view>上方添加<my-header></my-header>标签。
结果: 将来其它页面组件内容只会替换<router-view>部分,不会替换my-header部分。所以,将来所有页面组件都会和页头组合显示。
2). 如果部分页面需要页头,部分页面不需要页头:
应该只在那些个别需要页头的页面组件顶部使用页头标签
示意图:

5. 路由跳转:
(1). 在HTML中写死跳转:
a. 不能用a元素。
b. 强烈建议所有调换都用<router-link>代替<a>
c. 如何: <router-link to="/相对路径">文本</router-link>
d. 其实: <router-link>也会自动被翻译为<a>
<a href="#/相对路径">文本</a>
(2). 在程序中动态跳转不同的链接地址:
a. this.$router.push("/相对路径")
(3). 其实路由跳转过程中,可传递参数值: 3步
a. 先改造路由字典中的字典项,让某个路径允许携带参数并定义参数名
{ path:"/相对路径/:参数名", component: 组件对象名, props:true }
1). /:参数名,表示允许携带一个参数,但是参数名必须是指定的参数名
2). props:true,意为,让地址栏中传递的参数值自动掉进下个页面组件的props中,自动成为props中的变量。
3). 强调: 凡是指定了可以携带参数的路径,程序中,如果跳转时,没有携带参数值,则不允许再进入该页面!
b. 上个页面跳转时: 无论以<router-link>方式,还是在js中以this.$router.push()方式,只要在相对路径后携带一个参数值即可
/相对路径/参数值
强调:
1). 不用加:
2). 不用写参数名,参数值也可自动保存到下个页面的指定参数变量名中。
c. 下个页面中: props:[ "参数名" ]
因为在路由字典项中,我们添加了props:true,则地址栏中的参数值,会自动掉进props中的同名形参变量中。
这是整体的示意图

6.接下来是SPA每部分的代码
SPA/views/Index.js
1 var Index={ 2 template:`<div> 3 <my-header></my-header> 4 <h3 style="color:red">这里是首页</h3> 5 <button @click="goto(5)">查看5号商品详情</button><br/> 6 <button @click="goto(10)">查看10号商品详情</button> 7 </div>`, 8 methods:{ 9 goto(lid){ 10 this.$router.push(`/details/${lid}`) 11 } 12 } 13 }
SPA/views/Details.js
1 var Details={ 2 props:["lid"], 3 template:`<div> 4 <my-header></my-header> 5 <h3 style="color:green">这里是详情页</h3> 6 <h4 style="color:green">显示{{lid}}号商品的详细信息...</h4> 7 <button @click="back">返回首页</button> 8 </div>`, 9 methods:{ 10 back(){ 11 this.$router.push("/") 12 } 13 } 14 }
SPA/views/NotFound.js
1 var NotFound={ 2 template:`<div> 3 <h3>404: 你好像迷路了←_←</h3> 4 </div>` 5 }
SPA/router/index.js
1 //2件事: 2 //1. 先创建路由字典: 3 var routes=[ 4 // 不是文件名 5 //是组件对象名 6 {path:"/", component:Index}, 7 {path:"/details/:lid", component:Details, props:true}, 8 {path:"*", component:NotFound} 9 ]; 10 //2. 再创建路由器对象: 11 var router=new VueRouter({ routes });
SPA/components/MyHeader.js
1 var MyHeader={ 2 template:`<div> 3 <h3 style="color:blue">这里是页头</h3> 4 <ul> 5 <li><router-link to="/">首页</router-link></li> 6 <li><router-link to="/details">详情页(因为不带参数,所以不能跳转了)</router-link></li> 7 </ul> 8 <hr> 9 </div>` 10 }
SPA/home.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="js/vue.js"></script> 8 <script src="views/Index.js"> 9 //var Index={ ... } 10 </script> 11 <script src="views/Details.js"> 12 //var Details={ ... } 13 </script> 14 <script src="views/NotFount.js"> 15 //var NotFound={ ... } 16 </script> 17 <script src="js/vue-router.js"> 18 //vue官方出的 19 //为了可以用new VueRouter()创建路由器对象 20 </script> 21 <script src="router/index.js"> 22 //var routes=[ ... ], 23 //var router=new VueRouter({ routes }) 24 </script> 25 <script src="components/MyHeader.js"> 26 //var MyHeader={ ... } 27 //暂时只是一个普通的组件对象,不是全局组件 28 </script> 29 </head> 30 <body> 31 <div id="app"> 32 <!--希望所有页面都有页头--> 33 <!-- <my-header></my-header> --> 34 <router-view></router-view> 35 </div> 36 <script> 37 //将普通的MyHeader组件对象,变成一个随处可用的全局组件。 38 Vue.component("my-header",MyHeader); 39 40 new Vue({ 41 el:"#app", 42 router 43 }) 44 </script> 45 </body> 46 </html>
上面所有内容其实都是为了脚手架打基础 接下来介绍vue脚手架(见下篇博客!)
总结: this 8种: 判断this,
一定不要看定义在哪儿!只看调用时!
1. obj.fun() this->obj
2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数 this->window
3. new Fun() this->new正在创建的新对象
4. 类型名.prototype.共有方法=function(){ ... } this->将来谁调用指谁,同第一种情况
5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象
6. 箭头函数中的this->箭头函数外部作用域中的this
7. jQuery.fn.自定义函数=function(){ ... } this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)
8. new Vue()中methods中的函数中的this->当前new Vue()对象

浙公网安备 33010602011771号