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>
View Code

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>
View Code

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>
View Code

五. 指令(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>
View Code

 第三种

 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>
View Code

第四种

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>
View Code

第五种

 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>
View Code

第六种

 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>
View Code

         (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>
View Code

第七种

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>
View Code

(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>
View Code

(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>
View Code

第七种

 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="#">&lt;&lt;博客园&gt;&gt;</a>的消息`
19       }
20     })
21   </script>
22 </body>
23 </html>
View Code

第八种

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>
View Code

第九种

与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>
View Code

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>
View Code

第十一种

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>
View Code

上面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>
View Code

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>
View Code

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>
View Code

 <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>
View Code

下拉列表

<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>
View Code

复选框单用:<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>
View Code

六样式绑定

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>
View Code

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>
View Code

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>
View Code

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>
View Code

(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>
View Code

八计算属性

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>
View Code

九过滤器

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>
View Code

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>
View Code

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>
View Code

 十 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>
View Code

十一 生命周期

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>
View Code

十二 组件

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>
View Code

 

 十三 组件化开发

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 }
View Code

components/TodoItem.js

1 var todoItem={
2   template:`<span>
3     1 - 吃饭 <a href="javascript:;">×</a>
4   </span>`
5 }
View Code

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 }
View Code

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 })
View Code

/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>
View Code

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 }
View Code

实现×删除功能

/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 }
View Code

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 }
View Code

给最初的父元素添加 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 })
View Code

实现效果的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>
View Code

十四 . 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 }
View Code

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 }
View Code

SPA/views/NotFound.js

1 var NotFound={
2   template:`<div>
3     <h3>404: 你好像迷路了←_←</h3>
4   </div>`
5 }
View Code

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 });
View Code

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 }
View Code

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>
View Code

上面所有内容其实都是为了脚手架打基础 接下来介绍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()对象

posted @ 2021-03-02 19:48  ComeIntoBud  阅读(1343)  评论(0编辑  收藏  举报