一. 什么是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. 原理:
![](https://i-blog.csdnimg.cn/blog_migrate/c09194aeab2189970c864a833d859ca4.png)
三. 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增删改查操作。
如图原理:
![](https://i-blog.csdnimg.cn/blog_migrate/e9af7984847392551bcd82c4f461ef91.png)
常见错误: 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内容,替换页面中组件标签所在的位置
![](https://i-blog.csdnimg.cn/blog_migrate/059a949126b472ba973b74dc24a18628.png)
2). 自动调用data函数,自动创建新模型对象副本给当前新的组件副本
![](https://i-blog.csdnimg.cn/blog_migrate/0ecd6126bb6847cbc9c42e20b5c6b020.png)
3). 自动为当前组件副本所在的区域创建一个缩微的new Vue()小快递员对象,监视这个组件小区域的功能和变化。
![](https://i-blog.csdnimg.cn/blog_migrate/55d3c91867f4791f1400db4254fe5697.png)
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. 示例: 使用组件化开发实现待办事项列表的界面组件划分和整合
示意图:
![](https://i-blog.csdnimg.cn/blog_migrate/92f5c10fcdc3547643eb30036d178005.png)
所有创建的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. 示例: 使用父给子传参,实现待办事项列表的添加、删除和列表功能
![](https://i-blog.csdnimg.cn/blog_migrate/e54cf196a97c31dbbde4c295a1d2d271.png)
实现+添加功能
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()配合,才能完成。
创建好后的目录如图:
![](https://i-blog.csdnimg.cn/blog_migrate/6e0806716d623acfd4e242312cd514ba.png)
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). 如果部分页面需要页头,部分页面不需要页头:
应该只在那些个别需要页头的页面组件顶部使用页头标签
示意图:
![](https://i-blog.csdnimg.cn/blog_migrate/1bd2cb69f2a110c3140f1980ee8c2121.png)
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中的同名形参变量中。
这是整体的示意图
![](https://i-blog.csdnimg.cn/blog_migrate/3bf37a980c22c81d5a6eb2ab53c1d85d.png)
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()对象