12.VUE笔记

Vue.js是一个基于MVVM设计模式的轻量级、渐进式前端框架,专为简化数据操作和构建复杂的Web应用而生。本文详细介绍了Vue的核心特性,包括MVVM模式、数据绑定、虚拟DOM、指令系统如v-if、v-for、v-bind和v-model,以及组件化开发等。通过实例展示了如何使用Vue进行开发,包括如何创建Vue实例、使用访问器属性、虚拟DOM的优势,以及如何利用插槽进行内容分发。此外,还探讨了Vue的生命周期、状态管理和TypeScript支持,帮助开发者深入理解Vue的全面功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 什么是VUE:

1. 什么是VUE:

  (1). 第三方开发的: 必须下载才能用

  (2). 基于MVVM设计模式的: ?

  (3). 渐进式的: 可以逐步在项目中使用vue技术,可以和其它技术混搭。

  (4). 纯前端: 单靠浏览器就可以运行,不需要任何后端技术。

  (5). js框架: 可以自动化的完成大部分重复的劳动,提高开发效率。

2. 为什么: 简单!避免重复劳动

3. 何时: 今后,只要以数据操作(增删改查)为主的项目,都可以由vue框架开发

  比如: 饿了么,每天,大众点评,淘宝,京东,今日头条,微博,网易云音乐,小红书,知乎,自如,链家,携程,去哪儿...

  不是以数据操作为主: 大型游戏,微信/qq,抖音,爱奇艺

二. 如何使用:

1. 官网: cn.vuejs.org

2. 下载: 2种: 

  (1). 只下载一个vue.js文件,引入网页使用: 4天

  a. 版本: 旧项目都用2.x做,新项目都用3.x

    开发版: (未压缩版)有完备的注释、代码格式,以及见名知意的变量名。还包含极其友好的错误提示。

     可读性好,适合学习和开发之用

     体积大,不适合生产环境快速下载运行

    生产版: (压缩版)去掉了所有数值和代码格式,极简化了变量名。去掉了友好的错误提示。

     体积小,适合生产环境快速下载运行

     可读性差,不适合学习和开发之用。

  b. 问题: 随着前端项目的规模越来越大,文件数量越来越多,如果还是零散的随意的管理大量的文件——没有标准,极其不便于大项目的写作。

  c. 因为单纯,所以适合初学者快速上手学习某一项技能!

  (2). 使用vue脚手架工具: 后4天

  a. 什么是: 一套已经包含核心功能的标准的半成品项目文件和文件夹结构。

  b. 为什么: 标准!

  c. 何时: 今后,企业中开发,都是基于脚手架基础上开发的。

  d. 优点: 便于大项目的标准化和分工协作。

  e. 但是: 在脚手架中开发,每做一个功能,都需要综合用到很多知识。如果基础不扎实,进入脚手架,寸步难行!

3. 第一个vue程序: 

1_first_jq.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/jquery-1.11.3.js"></script>

</head>

<body>

  <button id="btnMinus">-</button>

  <span>0</span>

  <button id="btnAdd">+</button>

  <script>

    //先实现+

    //DOM 4步

    //1. 查找触发事件的元素

    $("#btnAdd")

    //2. 绑定事件处理函数

    .click(function(){

      //3. 查找要修改的

      var $span=$("span");

      //4. 修改元素

      //4.1 先取出span中现在的数量,转为整数

      var n=parseInt($span.html());

      //4.2 将n+1

      n++;

      //4.3 将新的n再放回去: 

      $span.html(n);

    })

    //再实现-

    //DOM 4步

    //1. 查找触发事件的元素

    $('#btnMinus')

    //2. 绑定事件处理函数

    .click(function(){

      //3. 查找要修改的

      var $span=$("span");

      //4. 修改元素

      //4.1 先取出span中现在的数量,转为整数

      var n=parseInt($span.html())

      //4.2 如果n>0,才能n-1

      if(n>0){ n-- }

      //4.3 将新的n再放回去:

      $span.html(n); 

    })

  </script>

</body>

</html>

运行结果: 

2_first_vue.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <!--先引入vue.js-->

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3-->

  <!--1. 先做界面

  1.1 统一的要求界面中所有元素,都必须包裹在一个唯一的父元素下,习惯上<div id="app"></div>

  1.2 找到界面中将来可能随程序自动变化的位置,用专门的语法:{{变量名}}来标记/占位

  本例中:span的内容随程序自动变化

    <span>{{n}}</span>

  1.3 找到界面中将来可能触发事件的元素,用专门的语法: @事件名="事件处理函数名来标记

  本例中两个按钮会触发事件

    +按钮, @click="add"

    -按钮, @click="minus"-->

  <div id="app">

    <button @click="minus">-</button>

    <span>{{n}}</span>

    <button @click="add">+</button>

  </div>

  <script>

    //2. 创建一个new Vue()对象,来监控div所包含的区域。

    var vm=new Vue({

      //vue对象中,必须用el属性,告诉new Vue()要监控的区域是哪里: (el其实是element的缩写)

      el:"#app",//id选择器

      //3. 定义模型对象,来保存界面中所需的所有变量和事件处理函数

      //什么是模型对象就是专门替界面保存变量和事件处理函数的特殊的对象

      //3.1 先创建一个data:{}来保存界面中所需的所有变量和初始值

      //本例中因为界面上需要一个变量n,所以,data中只有一个变量n即可

      data:{

        n:0

      },

      //3.2 再创建一个methods:{}来保存界面中所需的所有事件处理函数

      //本例中界面上一共需要2个事件处理函数

      methods:{

        add(){this.n++},

        minus(){

          if(this.n>0){

            this.n--

          }

        }

      }

      //强调:

      //3.2.1 methods中的事件处理函数中,如果要操作data中的变量,必须加this.

      //3.2.2 methods中的事件处理函数中,根本不用考虑如何从界面取值,也不用考虑如何将新值放回界面,只需要专门考虑如何把data中的变量值修改正确即可!

      

      //new Vue()会自动保持界面中变量ndata中变量n同步

      //开局时,datan是几,new Vue()就把n的值送到页面上,对应位置显示给人看

      //methods中修改了n的值,new Vue()会自动把n的新值自动更新到界面中n所在的位置给人看

    });

    console.log(vm);

  </script>

</body>

</html>

运行结果:

三. MVVM:

1. 旧的前端代码分为三大部分:

  (1). HTML:专门定义网页的内容和结构

  (2). CSS: 专门为网页添加样式

  (3). js: 专门操作网页中的内容,为页面添加交互行为

2. 问题:

  (1). HTML和CSS功能太弱了!连程序最基本的因素(变量、判断、循环)都不支持!——生活不能自理

  (2). 导致,哪怕很小的修改,都要麻烦JS来操作。导致,js中存在大量重复和冗余的工作。

3. 解决: MVVM设计模式:

  (1). 什么是: 对前端三大代码的重新划分:

  (2). 三部分:

  a. 界面(View):

    1). 包含以前的HTML+CSS

    2). 增强版: 让HTML也支持变量、判断、循环——HTML初步生活可以自理!

  b. 模型对象(Model):

    1). 什么是: 专门保存页面中所需的变量和函数的特殊对象

    2). 包含2个:

    i. data:{}  专门保存界面中所需的所有变量

    ii. methods:{} 专门保存界面中所需的所有函数

    3). 问题: 模型对象中的变量和函数,不会自己长腿跑到页面上

  c. 视图模型(ViewModel)

1). 什么是: 专门负责将模型对象中的变量和函数,自动运送到界面中指定位置的特殊对象。——快递小哥

    2). 作用: 可自动将程序中的变量和函数运送到界面中所需的位置。并且,还能自动保持界面显示与程序中的数据同步。

4. vue框架如何实现MVVM设计模式: vue的绑定原理

  (1). 访问器属性
  a. new Vue()将data:{}引入到new Vue()中时,先将data对象及其内部的内容全部隐姓埋名。

  b. new Vue()会自动为data中每个变量创建访问器属性,监视对每个变量的修改操作。访问器属性不再隶属于data对象,而是直接隶属于new Vue()

  c. 将来,只要在程序中修改变量,都会自动调用访问器属性的set()函数。

  d. set()函数中提前安插了一个通知函数()。可通知外部,哪个变量值发生了变化。

  e. methods中所有的函数,进入new Vue()后,methods对象就被打散,原methods中所有函数直接隶属于new Vue()对象了。

  f. 所以,methods中的函数和data中的变量,最终会平级保存,都直接隶属于new Vue()。所以,methods中的函数,想操作data中的变量,必须加this。

  (2). 虚拟DOM

  a. 什么是: 专门保存界面中所有可能发生变化的元素的简化版DOM树。

  b. 何时创建:

    1). new Vue()的第二大步,在创建完data和methods之后,根据el属性值的选择器所指的元素,去扫描界面中指定区域的元素。

    2). 一边扫描真实DOM树,一边就创建虚拟DOM树,只保存可能发生变化的元素。

  c. 将来

1). 只要在程序中修改了变量值,就会自动触发访问器属性的set(),就会自动执行set()中的通知函数。通知函数()通知虚拟DOM树,哪个变量发生了变化。

    2). 虚拟DOM树扫描自己内部保存的所有可能发生变化的元素,只找出受本次变量修改影响的元素。

    3). 可以用提前封装好的DOM操作,将变量的新值,自动修改回页面中显示。

  d. 虚拟DOM树的优点: 4大优点:

    1). : 只保存可能变化的个别元素

    2). : 因为保存的元素少,所以每次遍历查找受影响的元素时,比遍历原始DOM树,快的多!

    3). 修改效率高: 因为每次只修改受影响的个别元素。不受影响的元素是不会改变的!

    4). 避免重复: 因为虚拟DOM树中已经封装了DOM的增删改查+事件绑定操作。所以,不用我们再手工查找、手工修改。都是自动完成的!

四. 绑定语法: 学名: 插值语法 Interpolation

1. 什么是: 在界面中标记可能发生变化的元素内容的特殊语法

2. 何时: 今后,只要发现一个元素的内容可能随程序自动改变时,都要用绑定语法来标记

3. 如何: <元素>xxxx{{自定义变量名}}xxx</元素>

4. {{}}可以和其它写死的文本内容混搭

5. {{}}中: 原理和模板字符串中的${}完全一样的!

  (1). 可以放: 一切有返回值的合法的js变量或表达式

    比如: 变量,三目,算术计算,访问数组元素,创建对象,调用函数

  (2). 不能放: 分支循环以及没有返回值的js表达式

6. 示例: 使用{{}}显示隐藏不同数据: 

3_{{}}.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3>Welcome {{uname}}</h3>

    <h3>性别: {{sex==1?"男":"女"}}</h3>

    <h3>小计:¥{{(price*count).toFixed(2)}}</h3>

    <h3>下单时间: {{new Date(orderTime).toLocaleString()}}</h3>

    <h3>今天星期{{arr[day]}}</h3>

  </div>

  <script>

    new Vue({

      el:"#app",

      data:{

        uname:"dingding",

        sex:1,

        price:12.5,

        count:5,

        orderTime:1614158191101,

        arr:["日","一","二","三","四","五","六"],

        //    0    1    2    3    4   5    6

        day:new Date().getDay()

      }

    })

  </script>

</body>

</html>

运行结果: 

五. 指令(directive): 

1. 什么是: 专门给HTML元素添加新功能的特殊HTML属性

2. 包括: 13种

  (1). v-bind:

  a. 问题: 如果元素的属性值可能随程序自动变化,则不能用{{}}绑定。

  b. 解决: 今后,只要元素的属性值有可能随程序自动变化,都可以用v-bind指令代替{{}}绑定属性值

c. 如何: <元素  v-bind:属性名="js变量或表达式">

d. 简写: <元素  :属性名="js变量或表达式">

e. 原理: new Vue()在扫描页面时,只要发现:开头的属性,都会自动计算=右边的js变量或表达式的值。然后,将变量或表达式的值作为当前属性的属性值

f. 强调: 加了:,就不要再加{{}}了!加了:,=右边的""就起到了{{}}的作用!""中也可以写一切有返回值的js变量和表达式。

g. 示例: 根据pm2.5数值,显示不同表情: 

4_v-bind.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>根据程序中PM25的数值显示不同的表情</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <!--哪里可能随程序自动改变?-->

    <img :src="

      pm25<100?'img/1.png':

      pm25<200?'img/2.png':

      pm25<300?'img/3.png':

               'img/4.png'

    ">

    <h3>{{

      pm25<100?'img/1.png':

      pm25<200?'img/2.png':

      pm25<300?'img/3.png':

               'img/4.png'

    }}</h3>

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        //程序中只保存pm2.5的数值,不保存图片路径

        pm25:360

      }

    })

    //运行后,F12打开控制台,console,输入vm.pm25=数值,观察页面上图片的变化.

  </script>

</body>

</html>

运行结果: 

  (2). v-show:

  a. 什么是: 专门控制一个元素显示隐藏的特殊指令

  b. 何时: 只要用程序控制一个元素的显示隐藏时,都用v-show

  c. 如何: 

    <元素  v-show="bool类型的变量或判断条件">
  d. 原理: 只要new Vue()扫描到v-show,就会先计算=右边的判断条件的值: 

    1). 如果=右边的判断条件值为true,则v-show什么也不做,当前元素默认显示

    2). 如果=右边的判断条件值为false,则v-show自动被翻译为style="display:none",当前元素就隐藏

  e. 示例: 打开和关闭对话框: 

  5_v-show.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    #pop{

      position:fixed;

      top:50%;

      left:50%;

      width:300px;

      height:100px;

      margin-left:-150px;

      margin-top:-50px;

      background-color:lightBlue

    }

    #pop>a{

      float:right;

      margin-right:20px;

      text-decoration:none;

    }

  </style>

</head>

<body>

  <!--VUE 3步

  1. 做界面: 又3步

  1.1 唯一父元素

  1.2 找可能发生变化的元素

  本例中:div id="pop"的显示隐藏状态来回切换。所以,应该用v-show

  1.3 找触发事件的元素

  本例中: 两个元素触发事件

    点按钮,让对话框显示

    点×,让对话框隐藏-->

  <div id="app">

    <button @click="show">click me</button>

    <div v-show="visible" id="pop">

      <a @click="hide" href="javascript:;">×</a>

    </div>

  </div>

  <script>

    //2. 创建new Vue()对象,监控id为app的区域

    new Vue({

      el:"#app",

      //3. 创建模型对象: 

      //3.1 创建data对象

      //本例中: 因为界面上只需要一个visible变量,表示对话框是否显示

      data:{

        visible: false //默认对话框不显示

      },

      //3.2 创建methods对象

      //本例中: 因为界面中需要两个函数,所以

      methods:{

        show(){//让对话框显示

          this.visible=true;

        },

        hide(){//让对话框隐藏

          this.visible=false;

        }

      }

    })

  </script>

</body>

</html>

运行结果:

  (3). v-if, v-else:

  a. 什么是: 专门控制两个元素二选一显示的指令

  b. 何时: 今后,只要控制两个元素二选一显示,都用v-if和v-else

  c. 如何:

   <元素1  v-if="条件1">
   <元素2  v-else>

  d. 原理: 每当new Vue()扫描到v-if时,先计算=右边条件变量或表达式的值。

    1). 如果v-if等号右边的条件为true,则new Vue()会保留v-if所在元素。删除v-else所在元素

    2). 如果v-if等号右边的条件为false,则new Vue()会先删除v-if所在的元素,保留v-else所在的元素。

  e. 强调: 

    1). v-if和v-else两个元素之间必须紧挨着写,不能插入其他元素。

    2). v-else后不要写=xxx (和程序中else后一样要求)

  f. v-show和v-if的差别

    1). v-show,用display:none来隐藏元素

    2). v-if和v-else是通过添加删除元素方式来控制元素显示隐藏。

  g. 示例: 切换用户登录状态: 

  6_v-if_v-else.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素包裹

  1.2 找可能发生变化的元素

  本例中: 两个div二选一显示,所以用v-if和v-else

  1.3 找触发事件的元素

  本例中: 登录和注销两个a元素可以触发事件-->

  <div id="app">

    <!--已登录时显示-->

    <div v-if="isLogin">

      <h3>Welcome dingding <a href="javascript:;" @click="logout">注销</a></h3>

    </div>

    <!--未登录时显示-->

    <div v-else>

      <a href="javascript:;" @click="login">登录</a> | 

      <a href="javascript:;">注册</a>

    </div>

    

  </div>

  <script>

    //2. 创建new Vue()

    new Vue({

      el:"#app",

      //3. 创建模型对象

      //3.1 创建data对象

      //本例中:因为界面中只需要1个变量isLogin来标记用户是否登录

      data:{

        isLogin:false //开局,用户默认未登录

      },

      //3.2 创建methods对象

      //本例中:因为界面需要两个事件处理函数

      methods:{

        login(){//将用户登录状态改为已登录

          this.isLogin=true;

        },

        logout(){//将用户登录状态改为未登录

          this.isLogin=false;

        }

      }

    })

  </script>

</body>

</html>

运行结果:

  (4). v-else-if

  a. 什么是: 专门和v-if和v-else一起控制多个元素多选一显示隐藏

  b. 何时: 今后,只要控制多个元素多选一显示隐藏时,都用v-else-if
  c. 如何: 

    <元素1  v-if="条件1">
    <元素2  v-else-if="条件2">

    <元素3  v-else-if="条件3">
    ... ...

    <元素n  v-else>

  d. 原理: 当new Vue()扫描到v-if时,先计算v-if后的条件: 

  1). 如果v-if条件为true,则保留v-if所在元素,删除其余v-else-if、v-else元素

   2). 如果v-if条件为false,则先删除v-if所在元素,然后,继续计算每个v-else-if后的条件。如果任意一个v-else-if的条件为true,则只保留着一个v-else-if所在元素,删除其余v-if、v-else-if和v-else元素

   3). 如果所有v-if和v-else-if的条件都不满足,则只保留v-else,删除其余v-if和v-else-if的元素

   4). 强调: v-if和v-else-if和v-else必须紧挨着写,中间不能插入其他元素。

  e. 示例: 使用v-else-if实现根据pm2.5数值不同显示不同的表情

  day01剩余/7_v-else-if.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <img v-if="pm25<100" src="img/1.png" alt="">

    <img v-else-if="pm25<200" src="img/2.png" alt="">

    <img v-else-if="pm25<300" src="img/3.png" alt="">

    <img v-else src="img/4.png" alt="">

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        pm25:360

      }

    })

  </script>

</body>

</html>

运行结果:

  (5). v-for:

  a. 什么是: 专门在网页中根据一个数组或对象的成员,连续生成多个结构相同,内容不同的一组html元素的特殊指令

  b. 何时: 今后,只要连续生成一组结构相同,但是内容不同的HTML元素时,都用v-for批量生成

  c. 如何: 

<要反复生成的元素  v-for="(元素值, 下标)  of  数组或对象">

  d. 强调: vue中v-for统一了js中的for in和for of的功能。vue中的v-for既可以遍历索引数组,又可以遍历对象

  e. 原理: 每当new Vue()扫描到v-for时

   1). 先遍历数组或对象中每个成员

   2). 每遍历一个成员: 

    i. 取出当前成员的属性名和属性值,将属性值交给of前的()中第一个变量,将属性名/下标交给of前的()中第二各变量

    ii. 同时,还会自动创建当前v-for所在元素的一个新副本。数组或对象包含几个成员,v-for就会反复创建几个HTML元素副本。

   3). of前的两个变量,虽然没有在new Vue()的data中的定义,但是,却可以在v-for所在元素及其子元素范围内用于绑定语法和指令!

  f. vue2的问题: 都是由访问器属性的问题引起的。

  1). 在vue2中使用下标修改数组中的元素值,无法自动更新页面。——new Vue()不会对数字下标的元素添加访问器属性。所以,所有数字下标的元素都不受监控!

       有办法解决: 尽量用数组家的API代替下标修改数组元素

       比如: vm.arr[1]="涛涛" //错误!

应改为: vm.arr.splice(1,1,"涛涛") //替换

  //删除1位置的1个元素

    //在1位置插入一个新元素涛涛

//复习一阶段数组函数

  2). 只有开局时就有的成员才受监控,后来新加入的成员也不受监控!

      因为: new VUE()只有在首次加载对象时,才给对象中每个属性自动创建访问器属性。但是,后来强行添加的新成员,就无法获得访问器属性了!

  g. 解决: vue3中,以上两个问题都得到了完美解决(未完待续...)

  h. v-for的问题: 即使只修改了数组或对象中一个成员的值,v-for默认也会删除所有元素副本,重新遍历,重新创建HTML元素副本。——效率极低!

    原因: v-for生成的多个HTML元素副本,除了内容不同之外,元素本身没有任何差别,所以v-for每次只能删除所有HTML副本,再重建整个列表。

  i. 解决: vue规定,今后,只要使用v-for都必须同时绑定一个专门的属性:  :key="不重复的值"

    比如: <li  v-for="(元素值, i)  of  arr"  :key="i">

  g. 结果: 从此,v-for反复生成的每个HTML元素上,都有一个唯一的key="值"。v-for就可以通过key属性值来鉴别每个HTML元素副本不同。修改时,只要修改某一个key的元素即可!不用重建整个列表。

  k. v-for为什么必须加:key: 3句话: 

    为每个元素添加唯一标识

    避免重建整个列表

    提高修改的效率

  l. 示例: 根据数组或对象的内容反复生成多个元素

  day01剩余/8_v-for.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3>遍历数组中每个元素</h3>

    <ul>

      <li v-for="(元素值, i) of arr">{{i+1}} - {{元素值}}</li>

    </ul>

    <h3>遍历对象中每个属性</h3>

    <ul>

      <li v-for="(属性值, 属性名) of lilei">{{属性名}} - {{属性值}}</li>

    </ul>

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        arr:["亮亮","楠楠","东东"],

        lilei:{

          sname:"Li Lei",

          sage:11,

          class:"初一2班"

        }

      }

    })

    //比较: 

    //手工在F12 控制台中测试: 

    //vm.arr[1]="小月月",能否修改数组,能否更新界面?

    //vm.arr.splice(1,1,"小月月"),能否修改数组,能否更新界面?

    //vm.lilei.sage=12  页面跟着变

    //vm.lilei.money=100  页面不变

  </script>

</body>

</html>

运行结果:

  m. v-for还会数数: 

  1). 何时: 只要希望反复生成指定数量的相同结构元素副本时

   2). 如何: <元素  v-for="i of n" :key="i">
  3). 原理: v-for默认从1开始数数,数到n结束。每数一个数,就自动创建当前元素的一个副本

   4). 示例: 根据页数生成分页按钮: 

  day01剩余/9_v-for_pages.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    ul{

      list-stylenone;

    }

    ul>li{

      float:left;

      width:40px;

      height:40px;

      text-align:center;

      line-height:40px;

      border:1px solid #555;

      border-radius5px;

      margin-left:5px;

    }

    ul>li:hover{

      cursor:pointer;

      background-color:lightskyblue;

      color:#fff;

      border:0;

    }

  </style>

</head>

<body>

  <div id="app">

    <ul>

      <li>1</li>

      <li>2</li>

      <li>3</li>

      <li>4</li>

      <li>5</li>

      <li>6</li>

    </ul>

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        pageCount:6 //共6页

      }

    })

  </script>

</body>

</html>

运行结果:

常见错误: Error compiling template : 模板编译错误

  通常因为HTML中所写的HTML标签或绑定语法不符合vue的要求了

  进一步确定出错的位置: 

沿控制台左侧向下找,跳过大片的HTML代码,找到一个很短的"-",这里标记的就是出错的位置!

总结:    this        8种:

一定不要看定义在哪儿,只看在哪里调用,如何调用

1. obj.fun()    this->obj

2. new  Fun()    this->new正在创建的子对象

3. 类型名.prototype.共有方法=function(){    ...    }

    this->将来调用这个共有方法的.前的子对象

4. fun() 和 (function(){    })()  和回调函数中的this->window

5. 访问器属性中的this,指访问器属性所在的当前对象。

6. DOM和jQuery事件处理函数中的this->当前正在触发事件的这个元素对象

7. jQuery.prototype.自定义函数=function(){

    … this指将来调用这个自定义函数的点前的一个jq子对象 …

    }

8. VUE中的this都指当前vue对象或当前组件对象


总结:

1. MVVM: 界面View+模型Model+视图模型ViewModel

2. Vue绑定原理: 访问器属性+虚拟DOM树

变量被修改时: 访问器属性发出通知,虚拟DOM树扫描并仅更新受影响的元素

3. 虚拟DOM树优点:

(1). : 只包含可能变化的元素。

(2). 遍历查找快

(3). 修改效率高: 只修改受影响的元素。

(4). 避免重复编码: 已封装DOM增删改查代码

4. Vue功能3步:

(1). 先创建增强版的界面:

a. 整个界面必须包含在一个唯一的父元素下:

  通常是<div id="app">
b. 可能变化的元素内容用{{自定义变量名}}标记

c. 触发事件的元素用@click="自定义处理函数名"标记

(2). 再创建new Vue()对象,其中el:指向new Vue()要监控的页面区域

(3). 在new Vue()对象内定义模型对象data和methods

a.界面所需的所有变量都放在data中

b.界面所需的所有事件处理函数都放在methods中

5. 总结: 绑定语法+13种指令

(1). 如果元素的内容可能变化: {{}}

(2). 如果元素的属性值可能变化: :

(3). 控制一个元素显示隐藏v-show

(4). 二个元素二选一显示v-if  v-else

(5). 控制多个元素多选一显示隐藏: v-if  v-else-if  v-else

(6). 反复生成多个相同结构不同内容的元素时: v-for  :key

一. 指令: 

1. v-on:

  (1). 什么是: 专门为元素绑定事件的指令

  (2). 何时: 今后,只要在vue中要给元素绑定事件处理函数,都用v-on

  (3). 如何: <元素  v-on:事件名="事件处理函数(实参值,...)"

  (4). 简写: 

  a. 所有v-on,都可简写@: 

    <元素  @事件名="事件处理函数(实参值,...)"

  b. 如果事件处理函数不需要传实参值,则可以省略()

    <元素  @事件名="事件处理函数"

  (5). 传参: vue中事件处理函数,可以传实参值: 

    <元素  @事件名="事件处理函数(实参值1, 实参值2,...)"
    methods:{
      事件处理函数(形参1, 形参2, ...){
       
      }
    }

  (6). 获得事件对象: vue中也可以像DOM一样获得事件对象

  a. 回顾: DOM中: 当事件发生时,浏览器总是将事件对象event,默认作为事件处理函数的第一个实参值传入函数。所以,我们只要定义一个形参变量e,等着接就可以了!

    //事件发生时

    //    浏览器自动创建event对象

    //                    ↓

    元素.on事件名=function(e){ e->event }

    或

    元素.addEventListener("事件名", function(e){ e->event })

  b. 在vue中也可以这样获得事件对象e:

    <元素  @事件名="事件处理函数" //一定不要加()

    methods:{
      事件处理函数(e){ e->event }
    }

  c. 问题: 想获得事件对象,就无法传实参;传实参就不能获得事件对象

  d. 解决: 既想穿实参值,又想获得事件对象,就要借助于vue中一个关键字:$event

    1). 什么是: 专门在vue中提前获得事件对象的一个关键字。

    2). 如何: 

     //当事件发生时

     //     浏览器自动创建事件对象event

     //                              ↓

     <元素  @事件名="事件处理函数($event, 其它实参, ...)"

     methods:{
       事件处理函数(e, 形参,...){
         e->event //结果是一样的。
       }
     }

    3). 原理: 在事件发生时,$event关键字会提前获得事件对象event。再由vue框架代为转交给事件处理函数对应的形参变量

    4). 强调: 只要使用$event关键字获得事件对象,则参数顺序无所谓,只要形参和实参可以对应的上即可!

  (7). 示例: 点谁,谁喊哪个部位疼: 

  5_@.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    imgwidth:250px; } 

  </style>

</head>

<body>

  <!--点谁的哪个部位,就喊谁的哪个部位疼!-->

  <div id="app">

    <img src="img/liang.jpg" @click="say($event, 'liang')">

    <img src="img/tao.jpg" @click="say($event, 'tao')">

  </div>

  <script>

    new Vue({

      el:"#app",

      methods:{

        say(e, name){

          if(e.offsetY<110){

            console.log(`${name} 头疼`);

          }else if(e.offsetY<220){

            console.log(`${name} 胸疼`)

          }else{

            console.log(`${name} 肚子疼`)

          }

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

  6_@_e.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    imgwidth:250px; } 

  </style>

</head>

<body>

  <!--点哪个部位,就喊哪个部位疼!-->

  <div id="app">

    <img src="img/liang.jpg" @click="say">

    <img src="img/tao.jpg" @click="say">

  </div>

  <script>

    new Vue({

      el:"#app",

      methods:{

        say(e){

          if(e.offsetY<110){

            console.log(`头疼!`)

          }else if(e.offsetY<220){

            console.log(`胸疼!`)

          }else{

            console.log(`肚子疼`)

          }

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

  7_@_$event.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    imgwidth:250px; } 

  </style>

</head>

<body>

  <!--点谁的哪个部位,就喊谁的哪个部位疼!-->

  <div id="app">

    <img src="img/liang.jpg" @click="say($event, 'liang')">

    <img src="img/tao.jpg" @click="say($event, 'tao')">

  </div>

  <script>

    new Vue({

      el:"#app",

      methods:{

        say(e, name){

          if(e.offsetY<110){

            console.log(`${name} 头疼`);

          }else if(e.offsetY<220){

            console.log(`${name} 胸疼`)

          }else{

            console.log(`${name} 肚子疼`)

          }

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

2. v-html:

  (1). 什么是: 专门绑定一段HTML代码片段到页面上的特殊指令。

  (2). 何时: 今后,只要要要绑定的内容是一段包含HTML内容的代码片段时,都用用v-html

  (3). 问题: 使用{{}}绑定HTML代码片段,则{{}}很懒,不会将代码片段交给浏览器解析,而是原样显示到页面。——不能直接给人看

  (4). 原理: 其实, vue中{{}}底层相当于DOM中的textContent

  (5). 解决: vue中,可用v-html代替{{}}。因为v-html指令底层相当于DOM中的innerHTML。会先将HTML片段交给浏览器解析,然后将解析后的可以给人看的结果显示到页面

                         | jd的地盘   |

  (6). 如何: <元素  v-html="变量或表达式"></元素>

  (7). 另一个差别: 

  a. {{}}可以和其它写死的部分文本随意拼接。

  b. 但是,v-html中不能将写死的部分字符串和变化的变量轻易拼接。比如使用js中的模板字符串才能拼接!因为""中是js的底盘,所以必须符合js的语法规定才行。

  (8). 示例: 绑定HTML片段到页面中显示: 

  8_v-html.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3>{{msg}}</h3>

    <h3>消息内容: {{msg}}</h3>

    <h3 v-html="msg"></h3>

    <h3 v-html="`消息内容: ${msg}`"></h3>

  </div>

  <script>

    new Vue({

      el:"#app",

      data:{

        msg:`来自<a href="#">&lt;&lt;新华社&gt;&gt;</a>的消息`

      }

    })

  </script>

</body>

</html>

运行结果: 

来自<a href="#">&lt;&lt;新华社&gt;&gt;</a>的消息

消息内容: 来自<a href="#">&lt;&lt;新华社&gt;&gt;</a>的消息

来自<<新华社>>的消息

消息内容: 来自<<新华社>>的消息

3. 防止用户短暂看到{{}}

  (1). 问题: 当网速慢时,new Vue()有可能延迟加载,用户就有可能短暂看到页面上的{{}}语法

  (2). 解决: 2种: 

  a. v-cloak: 幕布/斗篷

    1). 什么是: 专门在new Vue()加载之前,暂时隐藏部分元素的特殊指令

    2). 何时: 今后只要希望在new Vue()加载完之前,暂时隐藏部分元素,避免用户短暂看到{{}},都可以用v-cloak。

    3). 如何: 2步: 

    i. 先在css中用属性选择器,选择所有带有v-cloak属性的元素,使用display:none,手工隐藏这些元素

    ii. 然后,才<要暂时隐藏的元素  v-cloak>

    4). 原理: 

    i. 在new Vue()加载完之前,v-cloak和css中的属性选择器[v-cloak]联合发挥作用,隐藏部分元素

    ii. 当new Vue()加载完之后,会自动查找页面中所有v-cloak的元素,自动删除所有v-cloak属性。结果,原来被v-cloak隐藏的元素,现在都显示出来了。

  b. v-text:

    1). 什么是: 专门代替{{}}绑定元素内容的特殊指令

    2). 何时: 今后,只要不想让用户短暂看到{{}},都可用v-text代替{{}}

    3). 如何: 

                          |  js的地盘   |

    <要隐藏的元素  v-text="变量或表达式"></要隐藏的元素>

    4). 强调: 如果v-text的内容需要部分写死的文本和变化的内容拼接而成,则必须用模板字符串:`xxx${变量}xxx`

    5). 原理: v-text和{{}}一样,底层相当于DOM中的v-text。v-html底层相当于DOM中的innerHTML。

  (3). 示例: 使用v-cloak或v-text防止用户短暂看到{{}}

  9_v-cloak.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    [v-cloak]{

      display:none

    }

  </style>

</head>

<body>

  <div id="app">

    <h3 v-cloak>Welcome {{uname}}</h3>

  </div>

  <script>

    setTimeout(function(){

      new Vue({

        el:"#app",

        data:{

          uname:"dingding"

        }

      })

    },2000);

  </script>

</body>

</html>

运行结果: 

Welcome dingding

  10_v-text.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3 v-text="`Welcome ${uname}`"></h3>

  </div>

  <script>

    setTimeout(function(){

      new Vue({

        el:"#app",

        data:{

          uname:"dingding"

        }

      })

    },2000)

  </script>

</body>

</html>

运行结果: 

Welcome dingding

4. v-once:

  (1). 什么是: 专门控制一个元素只在首次加载时绑定一次。之后,即使变量值改变,也不会自动更新页面。

  (2). 何时: 如果发现一个元素的内容,只会在首次加载时绑定一次。之后几乎不会改变时,都用v-once标记

  (3). 如何: <元素  v-once>{{...}}</元素>

  (4). 原理: 凡是标有v-once的元素,new Vue()只在首次记载时,动态更新元素的内容。但是不会将v-once的元素加入到虚拟DOM树中。所以,将来就算变量值发生变化,也无法通知到这个元素。
  (5). 优化: 凡是标有v-once的元素,都不会加入虚拟DOM树中。所以,无形中,又减少虚拟DOM树中的元素的个数!使虚拟DOM树遍历更快,效率更高!所以,今后,只要发现一个值只在首次页面加载时动态变化,之后几乎不变时,都应该用v-once。

  (6). 示例: 使用v-once标记元素只在首次绑定时更新一次,之后不再反复更新

  11_v-once.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3 v-once>上线时间: {{time}}</h3>

    <h3>当前系统时间: {{time}}</h3>

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        time:new Date().toLocaleString()

      }

    });

    setInterval(function(){

      vm.time=new Date().toLocaleString();

    },1000);

  </script>

</body>

</html>

运行结果: 

5. v-pre:

  (1). 什么是: 专门阻止vue编译内容中的{{}}的特殊指令

  (2). 何时: 极少数情况下,正文中包含了不像被vue编译的{{}}时,采用v-pre保护。

  (3). 如何: <元素  v-pre>xxx{{xxx}}xx</元素>

  (4). 示例: 使用v-pre防止vue编译内容中的{{}}

  12_v-pre.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h1 v-pre>vue框架采用{{自定义变量名}}方式标记页面中可能发生变化的位置</h1>

  </div>

  <script>

    new Vue({

      el:"#app"

    })

  </script>

</body>

</html>

运行结果:

vue框架采用{{自定义变量名}}方式标记页面中可能发生变化的位置

二. 双向绑定: 

1. 问题: 当用户主动在文本框中输入内容后,如果使用:value="str"方式绑定,则用户输入的内容无法自动回到程序中的变量中保存: 

2. 原因: 其实之前所学12种指令+{{}}都是单向绑定: 

  (1). 只能将程序中的变量值,自动同步到页面上显示

   上的去——M->V

  (2). 不能自动将界面中的用户主动做的修改,自动同步回程序中变量里保存

   下不来——不能V->M

3. 解决: 今后只要希望在程序中自动获得页面中用户主动做的修改时,都要用双向绑定v-model

4. 双向绑定: 

  (1). 既能将程序中的变量自动同步到页面上显示

    上的去——M->V

  (2). 又能将页面上用户主动修改的新值自动更新回程序中的变量保存.

    下的来——V->M

5. 如何: <表单元素  v-model="变量">

6. 示例: 使用双向绑定实现点按钮,获得文本框中用户输入的内容: 

13_v-model.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素包裹

  1.2 找可能发生改变的位置

  本例中: 文本框的内容(value属性值)会被改变,但是是由用户主动输入而改变

  1.3 找触发事件的元素

  本例中: 点按钮触发事件-->

  <div id="app">

    <input v-model="str">

    <button @click="search">百度一下</button>

  </div>

  <script>

    //2. 创建new Vue()对象, 监视id为app的区域

    var vm=new Vue({

      el:"#app",

      //3. 创建模型对象: 

      //3.1 创建data对象

      //本例中: 因为界面中需要一个变量str保存文本框的内容,所以

      data:{

        str:""

      },

      //3.2 创建methods对象

      //本例中: 因为界面中需要一个事件处理函数search,所以

      methods:{

        search(){

          console.log(`搜索 ${this.str} 相关的内容...`)

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

7. 原理: 双向绑定无非就是在单向绑定基础上,能自动为元素添加onchangeoninput事件处理函数。并能在事件处理函数中,自动将新值更新到data中的变量中。

8. 示例: 使用:value+@input事件模拟实现双向绑定: 

14_v-model2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素包裹

  1.2 找可能发生改变的位置

  本例中: 文本框的内容(value属性值)会被改变,但是是由用户主动输入而改变

  1.3 找触发事件的元素

  本例中: 点按钮触发事件-->

  <div id="app">

    <input :value="str" @input="doit">

    <button @click="search">百度一下</button>

  </div>

  <script>

    //2. 创建new Vue()对象, 监视id为app的区域

    var vm=new Vue({

      el:"#app",

      //3. 创建模型对象: 

      //3.1 创建data对象

      //本例中: 因为界面中需要一个变量str保存文本框的内容,所以

      data:{

        str:""

      },

      //3.2 创建methods对象

      //本例中: 因为界面中需要一个事件处理函数search,所以

      methods:{

        doit(e){

          //      当前文本款的内容

          //   赋值给

          //当前vue中的

          //str变量

          this.str=e.target.value;

        },

        search(){

          console.log(`搜索 ${this.str} 相关的内容...`)

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

9. 监视函数:

  (1). 什么是: 专门用于监视一个变量的变化,并在变量值发生变化时自动执行的一个函数.

  (2). 何时: 今后,只要希望一个变量的值一改变,我们就能自动执行一项操作时,都可用监视函数

  (3). 如何: 

  new Vue({
    data:{
      变量名: 值
    },

    //监视

    watch:{

 变量名(){
//只要上面的同名变量值一发生改变watch中的同名监视函数就会自动执行
      }

    }

  })

10. 事件修饰符: 

  (1). 什么是: 简化版的对事件的约束

  (2). 何时: 今后想改变事件的默认行为或约束触发事件的条件时,都可以用事件修饰符

  (3). 包括: 

  a. 限制用户按的键盘号: 

    1). DOM中: 

     事件处理函数(e){
       if(e.keyCode==13){
         ... ...
       }
     }

    2). vue中: <元素  @事件名.13="事件处理函数"

  b. 停止冒泡: 

    1). DOM中: 

     事件处理函数(e){
       e.stopPropagation();

       ... ...
     }

    2). vue中: <元素  @事件名.stop="处理函数"

  c. 阻止默认行为: 

    1). DOM中: 

     事件处理函数(e){
       e.preventDefault()

       ... ...
     }

    2). vue中: <元素  @事件.prevent="事件处理函数">

  (4). 其实多个事-修饰符可以连用

    <元素 @事件.stop.prevent="事件处理函数">

    等效于: 

    事件处理函数(e){
      e.stopPropagation()
      e.preventDefault()

    }

11. 示例: 实现按回车搜索和一边输入一边搜索: 

15_v-model3.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素包裹

  1.2 找可能发生改变的位置

  本例中: 文本框的内容(value属性值)会被改变,但是是由用户主动输入而改变

  1.3 找触发事件的元素

  本例中: 点按钮触发事件-->

  <div id="app">

    <input v-model="str" @keyup.13="search">

    <button @click="search">百度一下</button>

  </div>

  <script>

    //2. 创建new Vue()对象, 监视id为app的区域

    var vm=new Vue({

      el:"#app",

      //3. 创建模型对象: 

      //3.1 创建data对象

      //本例中: 因为界面中需要一个变量str保存文本框的内容,所以

      data:{

        str:""

      },

      //3.2 创建methods对象

      //本例中: 因为界面中需要一个事件处理函数search,所以

      methods:{

        search(){

          console.log(`搜索 ${this.str} 相关的内容...`)

        },

        // search2(e){

        //   if(e.keyCode==13){

        //     this.search();

        //   }

        // }

      },

      //本例中:因为希望只要用户输入的内容一改变,就立刻自动执行搜索操作。所以,应该监视data中的str变量

      watch:{

        str(){//只要str变量值一改变,就立刻执行搜索操作

          console.log(`str变量值被改变了!`);

          this.search();

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

   

12. 双向绑定在不同表单元素中的原理: 

  (1). 文本框<input type="text">和文本域<textarea>: 

  a. 当首次加载时,v-model将程序中变量的值更新到页面上的文本框中显示

  b. 当用户主动在文本框中输入了内容时,v-model自动将用户输入的内容更新回程序中变量中保存。

  (2). 单选按钮<input type="radio">多选一时: 

  <input  type="radio"  name="sex"  value="1"  v-model="sex">男
  <input  type="radio"  name="sex"  value="0" v-model="sex">女

  a. 现象: 

    1). 两个或多个input为一组

    2). value值是定死的,是备选的!

  b. 如何: 每个<input type="radio">备选项上都要添加一个v-model="sex"。

  c. 原理: 

    1). 首次加载页面时,v-model读取程序中的变量值,用变量值自动与每个radio写死的value值做比较。哪个radio的写死的value值刚好等于变量值,则当前radio自动选中。否则如果radio的写死的value值与变量值不相等,则radio不选中。

    2). 当用户切换选中项时,v-mode只会自动将选中的一个radio身上写死的value值更新到程序中变量里保存。如果未选中的radio身上的value值是不需要放回程序中的。

  d. 示例: 获取选择的性别: 

  16_v-model_radio.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    性别: 

    <input type="radio"name="sex" value="1" v-model="sex">

    <input type="radio" name="sex" value="0" v-model="sex">

    <br/>

    <h3>您选择的性别是:{{sex}}</h3>

  </div>

  <script>

    new Vue({

      el:"#app",

      data:{

        sex:1

      }

    })

  </script>

</body>

</html>

运行结果: 

  (3). 下拉列表<select>:

  <select>
    <option  value="备选值1">
    <option  value="备选值2">
    <option  value="备选值3">

  a. 现象: 

    1). 一个<select>下包含多个<option>

    2). 每个<option>上都有一个写死的备选值value属性

  b. 如何: 只要在父元素<select>上写一个v-model="变量"即可

  c. 原理: 

    1). 加载数据时: v-model会读取程序中的变量值,自动跟<select>下每个option身上写死的value值做比较。哪个option上写死的value值与变量值一致,则哪个option被选中。反之,其余value值与变量值不相等的option,就不选中

    2). 当用户主动切换select中的选中项后,v-model只会将选中的option的value值自动更新回程序中变量里保存。

  d. 示例: 选择城市,切换城市图片

  17_v-model.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素

  1.2 找可能发生变化的位置: 

  本例中: 2处发生变化

    select中选中的option由用户主动切换而改变——都用双向绑定v-model

    img的src属性随程序自动变化

    select中选中项的value值,刚好就是img的src属性需要的值,所以虽然两处变化,但是只需要一个变量src即可,保存选中的图片路径,也就是img需要的图片路径-->

  <div id="app">

    请选择城市: <select id="sel" v-model="src">

      <option value="img/bj.jpg">北京</option>

      <option value="img/sh.jpg">上海</option>

      <option value="img/hz.jpg">杭州</option>

    </select><br/>

    <br/>

    <br/>

    <img style="width:300px" :src="src">

  </div>

  <script>

    //2. 创建new Vue()对象

    new Vue({

      el:"#app",

      //3. 创建模型对象

      //本例中: 因为界面上只需要一个变量,所以data中

      data:{

        src:"img/bj.jpg"

      }

    })

  </script>

</body>

</html>

运行结果: 

  (4). 复选框checkbox单独使用:

  <input  type="checkbox">同意

  a. 现象: 没有value属性值

  b. 如何: <input  type="checkbox"  v-model="变量">

  c. 强调: 绑定给checkbox的值也应该是bool类型的值

  d. 原理: 

  1). . 加载数据时: v-model会先取出变量值,将变量值赋值给checkbox的checked属性。如果checked属性为true,则当前checkbox选中,否则如果checked属性值为false,则当前checkbox就不选中。

   2). 当用户切换当前checkbox的选中状态后,v-model会将当前checkbox的checked属性的新状态bool值,自动更新回程序中的变量里保存。

  f. 示例: 点同意,启用元素; 不同意,禁用元素

  day02剩余/18_v-model_checkbox.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素

  1.2 找可能发生变化的位置: 

  本例中: 4处发生变化

    checkbox由用户主动修改其选中状态——v-model绑定

    其余三个表单元素的disabled属性随checkbox的选中状态的改变而被动发生变化——:disabled

    又因为所有元素的变化,都紧盯checkbox的选中状态。所以,只需要一个变量保存checkbox的选中状态即可!

  -->

  <div id="app">

    <br/>

    <!--不同意(agree=false)时,禁用(disabled=true)

        同意(agree=true)时,启用(disabled=false)

      让disabled属性值与agree相反即可!-->

    用户名:<input :disabled="!agree"><br/>

    <br/>

    密码:<input :disabled="!agree" type="password"><br/>

    <br/>

    <input type="checkbox" v-model="agree">同意<br/>

    <br/>

    <button :disabled="!agree">注册</button>

  </div>

  <script>

    //2. 创建new Vue()对象

    new Vue({

      el:"#app",

      //3. 创建模型对象

      //本例中: 因为界面中只需要一个变量agree保存是否容易的状态

      data:{

        agree:false //开局默认不同意

      }

    })

  </script>

</body>

</html>

运行结果:

三. 绑定样式: 

1. 绑定内联样式: 3种方式: 

  (1). 将元素的style属性看做一个大的字符串来绑定: 

  a. <元素   :style="变量名">
    data:{
      变量名:"css属性名1: 属性值1; css属性名2:属性值2;..."
    }

  b. 结果: 运行时,vue会把变量的字符串值,直接放到元素的style属性后,作为元素的内联样式。

  c. 问题: 极其不便于只修改其中某一个css属性值——几乎不用!

  (2). 使用对象语法灵活绑定每个css属性值: 

          style="css属性1:属性值1;css属性2:属性值2;..."

             自动翻译

  a. <元素  :style="{ css属性名1:变量1, css属性名2:变量2 , ... }"

    data:{
      变量1: 属性值1, 

      变量2: 属性值2
    }

  b. 优点: 极其便于修改其中某一个css属性值

  c. 缺点: 如果多个元素刚好都需要绑定同一个css属性,则属性值的变量名极容易冲突!

  d. 示例: 绑定一架飞机的位置: 

  19_style.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    img{

      position:fixed;

    }

  </style>

</head>

<body>

  <div id="app">

    <!--       css属性名:变量名-->

    <img :style="{ left:left, top:top }" src="img/p3.png" >

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        left:"200px",

        top:"100px"

      }

    });

    //在f12 console控制台: 

    //vm.left="300px" 让飞机水平向右飞100px

    //vm.top="50px" 让飞机水平向上飞50px

  </script>

</body>

</html>

运行结果: 

  (3). 将对象写在data中的绑定方式: 

  a. <元素1   :style="变量1">
    <元素2   :style="变量2">
    data:{     //变量1:" css属性1:值1; css属性2:值2;..."

      变量1:{ //先将对象语法编译为字符串
        css属性1:值1,

        css属性2:值2
      },

 变量2:{
        css属性1:值1,

        css属性2:值2
      },

    }

  b. 好处: 既便于修改任意一个元素的css属性,又避免多个元素的css属性发生冲突

  (4). 问题: 如果有些css属性是变化的,有些css属性是固定不变的,怎么办?

  (5). 解决: 固定不变的css属性,写在不带:的style里。变化的css属性,写在带:的style里。运行时,两个style是合并发挥作用的。不会发生覆盖!

<元素 style="固定不变的css属性们" :style="可能变化的css属性们">

  (6). 示例:绑定两架飞机的位置: 

  day02剩余/19_style2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

  </style>

</head>

<body>

  <div id="app">

    <img style="position:fixed" :style="img1Style" src="img/p3.png" >

    <img style="position:fixed" :style="img2Style" src="img/p5.png" >

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        img1Style:{

          left:"200px",

          bottom:"100px",

        },

        img2Style:{

          left:"500px",

          bottom:"150px"

        }

      }

    });

    window.onkeydown=function(e){

      //希望按方向键操作第一架红色飞机移动

      //左37 上38 右39 下40

      

      //作业:实现第二架飞机移动

      //左65 上87 右68 下83

    }

  </script>

</body>

</html>

运行结果:

2. 绑定class: 3种: 

  (1). 将class属性看做一个普通的字符串变量绑定

  a. <元素  :class="变量名"

   data:{
    变量名:" class名1  class名2   class名3  ..."
   }

  b. 缺点: 极其不便于只修改其中某一个class。

  (2). 将class属性看做一个对象来绑定: 

  a. <元素  :class="{ class名1: 变量1, class名2:变量2,... }"

    data:{
      变量1: true或false, //开关,true启用这个class,false就不启用这个class

      变量2: true或false
    }

  b. 优点: 便于修改某一个class

  c. 问题: 如果多个元素都绑定同一种class,但是启用或不启用的状态不同,class的变量名就极容易发生冲突

  d. 示例: 实现手机号带样式的验证: 

  5_class.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    .success {

      background-colorlightGreen;

      colorgreen;

      bordergreen 1px solid;

    }

    .fail {

      background-colorpink;

      colorred;

      borderred 1px solid;

    }

  </style>

</head>

<body>

  <!--VUE 3步

  1. 做界面

  1.1 唯一父元素

  1.2 找可能变化的位置: 

  本例中:2个元素,3处改变

    文本框中的手机号由用户主动修改——v-model

    错误提示span的内容和样式有可能改变

    span的内容改变——{{}}

    span的多个css属性同时变化,所以应该用class方式绑定——:class

  1.3 找可能触发事件的位置

  本例中: 一边输入,一边触发验证——watch-->

  <div id="app">

    手机号:<input type="text" v-model="phone">

    <!--           class名  变量名-->

    <span :class="{success:success, fail:fail}">{{msg}}</span>

  </div>

  <script>

    //2. 创建new Vue()对象

    new Vue({

      el: "#app",

      //3. 创建模型对象:

      //3.1 因为?

      data: {

        phone:"",

        success:false,//开局不应用

        fail:false,//开局不应用

        msg:""

      },

      //3.2 因为phone变量由用户主动改变,所以监视phone变量

      watch: {

        phone(){

          //先定义正则表达式: 

          //1:第一位数字必须是1

          //[3-9]: 第二位数字可在3-9之间任选其一

          //\d{9}: 后九位必须都是数字

          var reg=/^1[3-9]\d{9}$/;

          //用正则验证用户输入的phone的格式

          var result=reg.test(this.phone);

          //如果验证通过: 

          if(result==true){

            //启用success class

            this.success=true;

            //不启用fail class

            this.fail=false;

            //提示信息改为"手机号格式正确"

            this.msg="手机号格式正确";

          }else{//否则如果验证不通过

            //不启用success class

            this.success=false;

            //启用fail class

            this.fail=true;

            //提示信息改为"手机号格式不正确"

            this.msg="手机号格式不正确";

          }

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

总结:

5. 总结: 绑定语法+13种指令

(1). 如果元素的内容需要随变量自动变化:  {{}}

(2). 如果元素的属性值需要随变量自动变化:  :

(3). 控制一个元素显示隐藏: v-show //使用display:none隐藏元素

(4). 控制两个元素二选一显示:  v-if  v-else //使用删除元素方式隐藏元素

(5). 控制多个元素多选一显示隐藏: v-if  v-else-if  v-else

(6). 反复生成多个相同结构不同内容的元素时: v-for  :key

(7). 事件绑定@  $event

(8). 只要绑定的内容中包含HTML片段v-html

(9). 防止用户短暂看到{{}}: v-cloak 或  v-text

(10). 限制元素只在首次加载时绑定一次,之后不再变化: v-once

(11). 阻止vue编译内容中的{{}}: v-pre

(12). 只要想自动获得页面上用户输入或选择的新值: v-model

   (几乎只要表单元素,都用v-model)

6. 绑定样式:

(1). 需要精确修改某一个css属性,就绑定style:

  a. <元素 style="固定样式" :style="{css属性:变量名, ...}"

  data:{
   变量名:css属性值

... : ...

  }

  b. <元素 style="固定样式" :style="变量名"

  data:{
   变量名:{

css属性名: 属性值,

... : ...

   }

  }

(2). 只要批量修改一个元素的多个css属性就绑定class

  a. <元素 class="固定class" :class="{class名:变量名, ...}"

  data:{
   变量名:true或false,

    ... : ...

  }

一. 绑定样式

1. 绑定class:

  (3). 将对象放进data中定义: 

  a. <元素1 :class="变量1">
    <元素2 :class="变量2">
    data:{

      变量1:{ //vue先将对象语法翻译为class字符串,再放入HTML中class属性中

        class名1: true或false, 

        class名2:true或false

      },

      变量2:{

        class名1: true或false, 

        class名2:true或false

      },

    }

  b. 优点: 避免不同元素之间绑定同一class时互相影响

  (4). 问题: 大部分class其实是很稳定的,只有个别class需要切换。

  (5). 解决: 不变的class,应该放在不带:的class中。而变化的class,才放在带:的class中。最终编译时,不带:的class会和带:的class合并为一个class共同起作用。

  <元素  class="固定不变的class"  :class="变化的class"

  (6). 示例: 实现手机号和密码的带样式的验证: 

  5_class2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <style>

    .success{

      background-color:lightGreen;

      color:green;

      border:green 1px solid ;

    }

    .fail{

      background-color:pink;

      color:red;

      border:red 1px solid;

    }

  </style>

</head>

<body>

  <div id="app">

    手机号:<input type="text" v-model="phone">

        <span :class="phoneObj">{{phoneMsg}}</span><br>

    密码:<input type="password" v-model="pwd">

        <span :class="pwdObj">{{pwdMsg}}</span>

  </div>

  <script>

    //2. 创建new Vue()对象

    new Vue({

      el:"#app",

      data:{

        phone:"",

        phoneObj:{

          success:false,

          fail:false

        },

        phoneMsg:"",

        pwd:"",

        pwdObj:{

          success:false,

          fail:false

        },

        pwdMsg:""

      },

      watch:{

        phone(){

          var reg=/^1[3-9]\d{9}$/

          var result=reg.test(this.phone);

          if(result==true){

            this.phoneObj={

            //启用success class,不启用fail class

              success:true, fail:false

            }

            this.phoneMsg="手机号格式正确";

          }else{

            this.phoneObj={

            //不启用success class,启用fail class

              success:false, fail:true

            }

            this.phoneMsg="手机号格式不正确";

          }

        },

        pwd(){

          var reg=/^\d{6}$/

          var result=reg.test(this.pwd);

          if(result==true){

            this.pwdObj={

            //启用success class,不启用fail class

              success:true, fail:false

            }

            this.pwdMsg="密码格式正确";

          }else{

            this.pwdObj={

            //不启用success class,启用fail class

              success:false, fail:true

            }

            this.pwdMsg="密码格式不正确";

          }

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

二. 自定义指令: 

1. 何时: 我们希望在开局时就能对HTML元素执行一些初始化操作,但是,vue中又没有提供对应功能的指令。就可以自定义指令。

   比如: 希望一个文本框在页面加载时,就自动获得焦点,但是,vue自带的指令中,没有指令可以让元素自动获得焦点。此时,就可以自定义一个获得焦点的指令。

2. 如何: 2步: 

  (1). 创建一个指令保存在Vue内存中备用

       指令

  Vue.directive("指令名", {

    //插入后: 当前带有指令的DOM元素被vue挂载到页面上之后自动触发的回调函数

inserted(当前带有指令的DOM元素){

      对当前带有指令的DOM元素执行原生的DOM操作

    }

  })

  (2). 在页面上使用自定义的vue指令

  <元素  v-指令名>

3. 强调: 

  (1). 定义指令,指令名一定不要加v-前缀

      使用指令必须加v-前缀

  (2). 问题: 指令名使用驼峰命名会报错!

      原因: HTML语言很蠢,只认识小写字母,不认识大写字母。

      解决: 将来如果一个名字,有可能用在HTML地盘范围内,则如果包含多个单词,必须-分隔,不能用驼峰命名。

4. 原理: 

  (1). Vue.directive()是创建一个自定义指令对象,保存在Vue类型的内存中备用。

  (2). new Vue()扫描时,发现v-开头的自定义指令,就回去Vue内存中找同名的自定义指令。

  (3). 只要找到同名的自定义指令,就自动执行自定义指令对象中的inserted()函数,并将当前扫描到的带有自定义指令的元素对象传给inserted()的形参变量。
  (4). 在inserted()函数内,可对当前传入的带有自定义指令的DOM元素应用原生的DOM操作。

5. 示例: 自定义指令让元素自动获得焦点

6_directive.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <!--希望在页面加载时,input文本框自动获得焦点-->

    <input id="txt" v-my-focus><button>搜索</button>

  </div>

  <script>

    //DOM中: 

    //先定义自定义指令: 

    Vue.directive("my-focus",{

      //当前元素被加入到页面上之后,自动执行

      inserted(当前DOM元素){

        当前DOM元素.focus();//原生函数

      }

    })

    new Vue({

      el:"#app"

    })

  </script>

</body>

</html>

运行结果: 

三. 计算属性: 

1. 什么是: 自己不保存属性值,而是根据其他属性的属性值,动态计算出自己的属性值。

2. 何时: 今后,如果页面上需要一个值,但是这个值不是直接给的,需要经过复杂的计算过程才能获得时,都用计算属性。

3. 如何: 2步: 

  (1). 定义计算属性: 

  new Vue({
    el:"#app",

    data:{ ... },

    methods:{ ... },

    watch:{ ... },

    computed:{
      计算属性名(){
        复杂的计算过程

        return 属性值
      }
    }
  })

  (2). 在页面上使用计算属性: 

  <元素>{{计算属性名}}</元素>

4. 强调: 

  (1). 计算属性虽然称为属性,但是其本质是一个函数。但是,函数名却是一个名词

  (2). 虽然计算属性本质是一个函数,但是在页面中使用计算属性时,不要加()

5. 原理: 

  (1). 当new Vue()扫描到一个不带()的变量时,会先去data中查找普通的属性。如果没找到,就去计算属性computed中查找。

  (2). 如果找到计算属性,就自动调用计算属性的函数,执行出计算结果,并将计算结果替换到页面中属性名位置显示。

  (3). 并且,vue会自动将首次计算属性计算出的结果,缓存起来,反复使用!避免重复计算!

  (4). 当多次使用同一计算属性时,不会重复执行计算属性的计算过程,而是直接从缓存中取值。

  (5). 当计算属性内部以来的其它变量值发生了变化时,vue会自动重新计算属性的值,并重新缓存起来反复使用。

6. 计算属性computed和普通函数methods差别: 

  (1). methods中的普通函数,如果反复调用几次,就会反复执行几次。不会缓存结果

  (2). computed中的计算属性,即使反复使用多次,也只计算一次,然后将结果缓存起来反复使用。

7. 如何选择:

  (1). 如果更倾向于计算出一个值显示到页面上时,首选computed计算属性

  (2). 如果更倾向于执行一个操作,而不关系结果时,首选methods普通函数

8. 示例: 使用计算属性计算购物车总价: 

7_computed.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <h3>总价:¥{{total.toFixed(2)}}</h3>

    <ul>

      <li v-for="(p,i) of cart" :key="i">

        {{p.pid}} | {{p.pname}} | ¥{{p.price.toFixed(2)}} | {{p.count}} | 小计:¥{{(p.price*p.count).toFixed(2)}}

      </li>

    </ul>

    <h3>总价:¥{{total.toFixed(2)}}</h3>

  </div>

  <script>

    new Vue({

      el:"#app",

      data:{

        cart:[

          {pid:1, pname:"华为", price:5588, count:2},

          {pid:2, pname:"苹果", price:9588, count:1},

          {pid:3, pname:"小米", price:3588, count:3}

        ]

      },

      methods:{

        

      },

      computed:{

        total(){

          console.log(`自动调用了一次total()`)

          //定义变量保存临时汇总中,起始为0

          var result=0;

          //遍历购物车中每个商品对象

          for(var p of this.cart){

            //每遍历一个商品对象就计算当前商品的小计,并将小计累加到临时汇总值result变量中。

            result+=p.price*p.count;

          }

          //返回最终汇总值

          return result;

        }

      }

    })

  </script>

</body>

</html>

运行结果: 

四. 过滤器

1. 什么是: 专门对变量的原始值先加工再显示的一种特殊函数

2. 为什么: 因为程序中有些数据不能直接给人看,需要先加工再给人看。

   比如: 日期/时间、性别

3. 何时: 今后,只要发现变量的原始值不能直接给人看,需要先加工再显示时,都可以用过滤器

4. 如何: 2步: 

  (1). 先创建过滤器函数

      过滤                    ↓

  Vue.filter("过滤器名",function(旧值){
←  return 新值
  })

  (2). 在页面中使用过滤器函数

  <元素>{{变量名 | 过滤器名 }}</元素>

                连接

        |     js地盘        |

5. 强调: 因为过滤器名用在{{}}内,是js的地盘,所以如果过滤器名中包含多个单词,应该用驼峰命名

6. 示例: 使用过滤器过滤性别: 

8_filter.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <!--不想显示1和0,想显示男和女-->

    <h3>性别: {{sex | sexFilter}}</h3>

  </div>

  <script>

    Vue.filter("sexFilter",function(旧值){

      return 旧值==1?"男":"女"

    });

    new Vue({

      el:"#app",

      data:{

        sex:1

      }

    })

  </script>

</body>

</html>

运行结果: 

性别: 男

7. 过滤器传参: 

  (1). 定义时: 

  Vue.filter("过滤器名",function(旧值自定义形参, ...){

    return 新值

  })

  (2). 使用时: 

  <元素>{{变量名 | 过滤器名(实参值1, ...)}}</元素>

  (3). 示例: 使用过滤器显示不同的性别,可以选择不同的语言

  8_filter2.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <!--默认显示中文的男或女-->

    <h3>性别: {{sex | sexFilter }}</h3>

    <!--通过参数改为显示英文的男或女-->

    <h3>性别: {{sex | sexFilter("en")}}</h3>

    <!--通过参数改为显示中文的男或女-->

    <h3>性别: {{sex | sexFilter("cn")}}</h3>

  </div>

  <script>

    Vue.filter(

      "sexFilter",

      //           en英文, 默认或cn中文

      function(旧值,语言){

        if(语言=="en"){

          return 旧值==1?"Male":"Female"

        }else{

          return 旧值==1?"男":"女"

        }

      }

    )

    new Vue({

      el:"#app",

      data:{

        sex:1

      }

    })

  </script>

</body>

</html>

运行结果: 

性别: 男

性别: Male

性别: 男

8. 过滤器可以连用: 

  (1). 定义过滤器时: 

  //充分考虑将来进入这个过滤器的旧值共有几种情况

  //                          ↓

  Vue.filter("过滤器名",function(旧值,..){  })

  (2). 使用过滤器时: 

  <元素>{{变量 | 过滤器1 | 过滤器2 | ... }}</元素>

  (3).示例: 给性别追加图标

  9_filter3.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <!--只显示图标-->

    <!--              1 或 0-->

    <h3>性别: {{sex | sexIcon}}</h3>

    <!--默认显示中文+图标-->

    <!--                          男 或 女-->

    <h3>性别: {{sex | sexFilter | sexIcon }}</h3>

    <!--通过参数改为英文+图标-->

    <!--                               Male或Female-->

    <h3>性别: {{sex | sexFilter("en") | sexIcon}}</h3>

    <!--通过参数改为中文+图标-->

    <!--                                男 或 女-->

    <h3>性别: {{sex | sexFilter("cn") | sexIcon}}</h3>

  </div>

  <script>

    Vue.filter(

      "sexFilter",

      //           en英文, 默认或cn中文

      function(旧值,语言){

        if(语言=="en"){

          return 旧值==1?"Male":"Female"

        }else{

          return 旧值==1?"男":"女"

        }

      }

    )

    //旧值共有几种情况: 

    //进入sexIcon的旧值共6种情况: 

    //0  1  男  女   Male  Female

    Vue.filter("sexIcon",function(旧值){

      //0或1,就直接返回图标

      if(旧值==0){

        return "♀"

      }else if(旧值==1){

        return "♂"

      }else if(旧值=="男"||旧值=="Male"){

        //无论旧值是男还是Male,都拼♂

        return 旧值+"♂"

      }else{

        //无论旧值是女还是Female,都拼♀

        return 旧值+"♀"

      }

    })

    new Vue({

      el:"#app",

      data:{

        sex:0

      }

    })

  </script>

</body>

</html>

运行结果: 

性别: ♀

性别: 女♀

性别: Female♀

性别: 女♀

五. axios:

1. 什么是: 

  (1). 第三方开发的: 下载

  (2). 专门发送ajax请求: 用途

  (3). 基于Promise: 语法

  (4). 的函数库: 一组函数

2. 何时: 今后只要在vue中发送ajax请求,一律用axios

3. 如何: axios发送get请求和发送post请求参数格式不一样

  (0). 配置服务器端接口地址的公共路径部分: 

  axios.defaults.baseURL="http://服务器端基础地址部分"

  比如: 

axios.defaults.baseURL="http://xzserver.applinzi.com"

  (1). get请求: 

  axios.get("服务器端接口地址剩余相对路径部分",{

    params:{ 参数名: 参数值, ... : ... , ...  }

  }).then(result=>{ //必须用箭头函数

//其实result不直接是响应结果

    //result.data才是真正的响应结果

  })

  (2). post请求: 

  axios.post(

"服务器端接口地址剩余相对路径部分",

  "参数名1=参数值1&参数名2=参数值2&..."

  ).then(result=>{

    console.log(result.data);

  })

  (3). 运行时: 

  axios会自动将baseURL和get/post中的相对路径拼接成接口的完整地址再发送请求。

六. *****vue的生命周期: 

1. 问题: 在DOM和jq中,如果希望页面一加载就能自动执行一项任务,可以把代码写在DOMContentLoaded或load事件处理函数中。但是vue中,如果希望在new Vue()加载完之后,自动执行一项任务,写哪儿?

2. 错误: 直接在new Vue()外部的后方写希望后续执行的代码。

3. 原因: 因为new Vue()的加载过程也是异步的,且做的事儿非常多,肯定有延迟。所以,放在new Vue()之后的代码绝对无法保证在vue加载完之后才执行。所以,将来程序的主要流程和代码,都要写在new Vue()内部才行!

4. 解决: 其实new Vue()和普通网页一样,在整个加载过程中也要经历多个阶段——生命周期

5. vue生命周期四个阶段: 

  /*必经阶段*/

  (1). 创建(create)阶段, 创建data对象、访问器属性
  (2). 挂载(mount)阶段 扫描真实DOM树,创建虚拟DOM树,并首次加载数据到页面显示

  /*不必经阶段*/

  (3). 更新(update)阶段 只当修改data中的变量,且影响页面显示时触发

  (4). 销毁(destroy)阶段 只当主动调用vm.$destroy()函数后触发。只删除虚拟DOM树,打断new Vue()与页面之间的联系。但是new Vue()对象还在内存中。

      可用vm.$mount("#app")将断开的new Vue()和页面重新建立虚拟DOM树,重新绑定起来。

6. 每个阶段前后,各有一对儿生命周期钩子函数(回调函数)
 new Vue({中

  /*必经阶段*/

  beforeCreate(){ ... } 没data、访问器属性,没虚拟DOM

  (1). 创建(create)阶段

  created(){...} 有data、访问器属性,没虚拟DOM

  beforeMount(){ ... } 有data、访问器属性,有虚拟DOM,但是,页面内容未加载
  (2). 挂载(mount)阶段

  mounted(){ ... }有data、访问器属性,有虚拟DOM,页面内容也加载完成。——new Vue()首屏加载完成。

  /*不必经阶段*/

  beforeUpdate(){ ... }

  (3). 更新(update)阶段

  updated(){ ... }

  beforeDestroy(){ ... }

  (4). 销毁(destroy)阶段

  destroyed(){ ... }

7. 示例: 触发new Vue()声明周期各个阶段: 

11_first_vue.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <!--先引入vue.js-->

  <script src="js/vue.js"></script>

</head>

<body>

  <!--VUE 3步-->

  <!--1. 先做界面

  1.1 统一的要求: 界面中所有元素,都必须包裹在一个唯一的父元素下,习惯上<div id="app"></div>

  1.2 找到界面中将来可能随程序自动变化的位置,用专门的语法:{{变量名}}来标记/占位

  本例中:span的内容随程序自动变化

    <span>{{n}}</span>

  1.3 找到界面中将来可能触发事件的元素,用专门的语法: @事件名="事件处理函数名" 来标记

  本例中: 两个按钮会触发事件: 

    +按钮, @click="add"

    -按钮, @click="minus"-->

  <div id="app">

    <button @click="minus">-</button>

    <span>{{n}}</span>

    <button @click="add">+</button>

  </div>

  <script>

    //2. 创建一个new Vue()对象,来监控div所包含的区域。

    var vm=new Vue({

      el:"#app",//id选择器

      data:{

        n:0

      },

      methods:{

        add(){this.n++},

        minus(){

          if(this.n>0){

            this.n--

          }

        }

      },

      /*必经阶段*/

      beforeCreate(){

        console.log(`创建阶段前自动触发...`)

      },

      created(){ //模型对象创建后

        console.log(`创建阶段后自动触发...`)

      },

      beforeMount(){

        console.log(`挂载阶段前自动触发...`)

      },

      mounted(){

        console.log(`挂载阶段后自动触发...`)

      },

      /*不是必经阶段*/

      beforeUpdate(){

        console.log(`更新前自动触发...`)

      },

      updated(){

        console.log(`更新后自动触发...`)

      },

      beforeDestroy(){

        console.log(`销毁前自动触发...`)

      },

      destroyed(){

        console.log(`销毁后自动触发...`)

      }

    });

    //点按钮触发修改阶段:

    //触发销毁阶段:

    //vm.$destroy()

    //修复new Vue()与页面的联系

    //vm.$mount("#app")

  </script>

</body>

</html>

运行结果:

8. 在哪里如何发送ajax请求: 

  (1). 首屏数据加载完,new Vue()会自动触发mounted()回调函数/钩子函数。所以,如果希望在首屏加载完之后,自动发送ajax请求,应该放在mounted()中。

  (2). 具体流程: 

  a. 在data中先定义变量,准备接受ajax请求回来的数据

  b. 在mounted()中发送ajax请求,获得响应结果: 

    1). 先输出响应结果,确定是否正确

    2). 再将响应结果赋值给data中之前准备好的变量

  c. 在界面中用绑定语法或指令,显示data中变量的值

  (3). 示例: 请求学子商城首页商品,加载到页面: 

  示例: 11_lifecycle.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <script src="js/axios.min.js"></script>

</head>

<body>

  <div id="app">

    <h3>Welcome {{uname}}</h3>

    <!--显示学子商城首页6个商品的列表-->

    <ul>

      <li>

        编号 | 详细信息 | 单价

      </li>

      <li v-for="(p,i) of arr" :key="i">

        {{p.pid}} | {{p.title}} | ¥{{p.price.toFixed(2)}}

      </li>

    </ul>

  </div>

  <script>

    var vm=new Vue({

      el:"#app",

      data:{

        uname:"dingding",

        arr:[]//准备接服务器端返回的包含6个商品对象的数组

      },

      /*必经阶段*/

      beforeCreate(){

        console.log(`创建阶段前自动触发...`)

      },

      created(){ //模型对象创建后

        console.log(`创建阶段后自动触发...`)

      },

      beforeMount(){

        console.log(`挂载阶段前自动触发...`)

      },

      mounted(){

        console.log(`挂载阶段后自动触发...`);

        axios.get("http://xzserver.applinzi.com/index")

        .then(result=>{

          console.log(result.data);

          this.arr=result.data;

        })

      },

      /*不是必经阶段*/

      beforeUpdate(){

        console.log(`更新前自动触发...`)

      },

      updated(){

        console.log(`更新后自动触发...`)

      },

      beforeDestroy(){

        console.log(`销毁前自动触发...`)

      },

      destroyed(){

        console.log(`销毁后自动触发...`)

      }

    });

  </script>

</body>

</html>

运行结果: 

总结: 

6. 绑定样式:

 (2). 只要批量修改一个元素的多个css属性就绑定class

  a. <元素 class="固定class" :class="{class名:变量名, ...}"

  data:{
   变量名:true或false,

    ... : ...

  }
  b. <元素 class="固定class" :class="变量名"

  data:{
   变量名:{
class名:true或false,

    ... : ...

   }

  }

3. 只要希望在页面加载时自动对元素执行一些初始化操作时就用自定义指令:

(1). 添加自定义指令:

Vue.directive("自定义指令名",{

  inserted(domElem){
    对domElem执行DOM操作

  }

})

(2). 使用自定义指令:

<元素 v-自定义指令名>

4. 今后只要根据其他变量的值动态计算出一个属性值就用计算属性:

<元素>{{计算属性}}</元素>

new Vue({

  el:"#app",

  data:{...},

  methods:{...},

  computed:{
    计算属性名(){

      计算过程

      return 计算结果

    }
  }

})

5. 希望将变量的原始值先加工后再显示给用户看时就用过滤器:

Vue.filter("过滤器名",function(oldVal, 自定义形参,...){

  return 加工后的新值

})

<元素>{{ 变量 | 过滤器(实参值, ...) | ... }}</元素>

6. 只要在vue中发送ajax请求,就用axios

axios.defaults.baseURL="服务器端接口的公共基础地址部分"

axios.get(
  "服务器端接口地址的相对路径",

  {

    params:{ 参数名: 参数值, ...  }

  }
).then(result=>{

  ... result.data...

})

axios.post(
  "服务器端接口地址的相对路径",

  "参数名1=参数值1&参数名2=参数值2&..."
).then(result=>{

  ... result.data...

})

强调: 在vue内使用axios,then中必须用箭头函数,保持then内this与外部this一致,都指向当前new Vue()对象

7. vue生命周期4个阶段 8个钩子函数

beforeCreate(){ ... }

(1). 创建(create)

created(){ ... }

beforeMount(){ ... }

(2). 挂载(mount)

mounted(){ ... 经常在这里发送ajax请求 ... }

beforeUpdate(){ ... }

(3). 更新(update)

updated(){ ... }

beforeDestroy(){ ... }

(4). 销毁(destroy)

destroyed(){ ... }

脚手架安装:

如果npm,安装出错,可尝试以下手段:
1. 运行npm cache clean -f,清空nodejs缓存
再在命令行窗口中输入: npm config set  registry  http://registry.npm.taobao.org,然后,按回车。这是设置npm库为国内淘宝镜像。
然后,再运行 npm config get registry,看到出现淘宝镜像地址,说明上一步配置正确。
再尝试安装npm i -g @vue/cli
2. 如果以上还不行:
先运行npm cache clean -f,清空nodejs缓存,
再切换网络,试试用手机热点,移动网络数据流量下载和安装。排除是否本地宽带有限制。
3. 如果切换网络也不行,或无法使用手机热点,可以尝试:
先运行npm cache clean -f,清空nodejs缓存,
再运行npm install -g cnpm --registry=http://registry.npm.taobao.org
这是在安装npm的替代工具cnpm,并设置默认库为国内淘宝镜像
输入cnpm -v ,看到版本号说明正常
然后运行cnpm i -g @vue/cli
4. 还可以检查系统环境变量:
右键单击我的电脑或此电脑 / 选最下边属性 / 点左侧高级系统设置 / 点右下角环境变量 / 在下方系统变量方块中双击path变量->如果弹出窗口列表中没有nodejs安装目录,就在这里点添加,输入nodejs安装目录(默认为: C:\\Program Files\\nodejs\\)->然后点确定,点确定,点确定
5. 如果以上都不行,就重装nodejs,再从方法1开始尝试


补: 如果报EUNSUPPORTEDPROTOCOL错误,是因为node就是版本太低,升级到最新LTS版本,再试!
如果报错: EUNSUPPORTEDPROTOCOL错误,可以把registry中的https换成http,试试。或者卸载nodejs,重新安装新的LTS版nodejs。

//如果说FEXIST错误,可进入出错提示中的路径,默认为:
C:\\Users\\登录操作系统的用户名\\AppData\\Roaming\\npm\\node_modules
删除@vue文件夹,再试

6. 如果以上都不行就重装系统,再重装nodejs,再试

补: 苹果本除尝试以上办法之外,还可尝试:
#cd到项目目录         
#然后依次执行下面的命令         
rm -rf node_modules        
rm package-lock.json        
npm cache clear --force        
npm install        
#问题解决

正课: 

1. 组件

2. 组件化开发

3. SPA

.$nextTick()

1. 问题: 放在created或vue外部的DOM操作,有可能被new Vue()覆盖掉!

2. 解决: 今后,只要希望写在任何位置的自定义DOM操作,都不会被vue覆盖,就可用$nextTick()

3. 什么是: 专门在vue所有生命周期执行完之后才触发的一个回调函数

4. 如何: 

this.$nextTick(

  ()=>{ 

    希望在所有生命周期结束后才自动执行的操作 

  }

)

5. 示例: 使用DOM为元素绑定单击事件,避免被vue覆盖

1_nextTick.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <button id="btn">click me</button>

  </div>

  <script>

    // var btn=document.getElementById("btn");

    // btn.onclick=function(){

    //   console.log("疼!")

    // }

    setTimeout(function(){

      var vm=new Vue({

        el:"#app",

        created(){//有data和访问器属性,没虚拟DOM树

          console.log("创建阶段完成!")

          this.$nextTick(()=>{

            console.log(`触发$nextTick()`)

            var btn=document.getElementById("btn");

            btn.onclick=function(){

              console.log("疼!")

            }

          })

        },

        mounted(){

          console.log("挂载阶段完成!")

          // var btn=document.getElementById("btn");

          // btn.onclick=function(){

          //   console.log("疼!")

          // }

        }

      });

    },1000)

    // var btn=document.getElementById("btn");

    // btn.onclick=function(){

    //   console.log("疼!")

    // }

  </script>

</body>

</html>

运行结果: 

一. 组件: 

1. 什么是: 拥有专属的HTML+JS+CSS+数据的可重用的独立的页面功能区域。

2. 为什么: 重用

3. 何时: 今后,只要发现网页中有一个功能,可能被多处反复使用,都应该封装为组件。

4. 如何: 2步: 

  (1). 创建一个组件

  Vue.component("组件名",{

    /*特例: */

template:`组件的HTML片段`, el:"#app",

    data(){ data:{ ... }, 

 return { //相当于以前的data
        模型变量:值, 

        ... ...
      }

    },

    /*每个组件内都是一个缩微的小new Vue()

      new Vue()中有什么,组件中也应该有什么*/

    methods:{ ... },

    watch:{ ... },

    computed:{ ... },

    八个生命周期钩子函数...

  })

  (2). 在页面中使用组件: 每个自定义组件其实就是一个自定义的HTML标签而已!

  <组件名></组件名>

  所以,组件名中如果包含多个英文单词,用-分隔。

5. 原理: 每当new Vue()扫描到一个不认识的标签时,都会去内存中,vue类型中找有没有同名的组件。如果找到同名的vue组件,就会做三件事: 

  (1). 复制组件template中的HTML片段代替页面上<组件></组件>标签位置

  (2). 自动调用data()函数,返回一个新创建的模型对象,其中包含当前组件专属的模型变量

  (3). 自动为当前组件区域创建一个缩微版的new Vue(),负责组件这个小区域的所有事宜。

6. 为什么组件的data必须是一个函数: 

  可反复调用;反复创建新对象;避免组件间数据冲突

7. 示例: 封装一个计数器组件: 

12_component.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

</head>

<body>

  <div id="app">

    <ul>

      <li><my-counter></my-counter></li>

      <li><my-counter></my-counter></li>

      <li><my-counter></my-counter></li>

    </ul>

  </div>

  <script>

    //做一个组件和做new Vue()完全一样

    //只不过3步变2步而已,不用new Vue()了!

    Vue.component("my-counter",{

      //1. 做界面

      //1.1 ***唯一父元素***

      //1.2 查找可能发生变化的位置

      //本例中: span的内容随程序变化——{{n}}

      //1.3 触发事件的元素

      //本例中: 两个按钮都能点

        //点+, +1

        //点-, -1

      template:`<div>

        <button @click="minus">-</button>

        <span>{{n}}</span>

        <button @click="add">+</button>

      </div>`,

      //2. 创建模型对象

      //2.1 创建data

      data(){

        return { //相当于以前的data

          n:0

        }

      },

      //2.2 创建methods

      methods:{

        add(){this.n++},

        minus(){if(this.n>0this.n--}

      }

    })

    new Vue({

      el:"#app"

    })

  </script>

</body>

</html>

运行结果: 

二. 组件化开发: 

1. 问题: 前端一个页面的功能和代码量越来越多,但是操作系统禁止多人协作编写一个文件。

2. 解决: 将一个大的页面,划分为多个组件区域,分别保存在不同的文件中,由多人协作开发最后运行时,还能合并在一个页面中运行给人看!——组件化开发

3. 何时: 今后所有的页面,几乎都采用组件化开发。

4. 为什么: 2个好处

  (1). 便于多人协作开发,提高开发效率

  (2). 松耦合,一人出错,不影响全局!

5. 如何: 

  (1). 每当拿到一个页面后,先划分组件区域: 3个原则: 

  a. 位置

  b. 功能

  c. 是否重用

  (2). 为每个组件创建独立的js文件,来保存组件的代码。

  (3). 回到原页面中引入并使用组件标签,将组件重新拼接回一个完整的页面。

6. 组件分类: vue中有三大类组件: 

  (1). 根组件: new Vue()

    整个页面甚至整个项目只有一个new Vue()监控全局

  (2). 全局组件: Vue.component()

    可放在任何位置,没有限制

  (3). 子组件: 

  a. 什么是: 规定只能在指定父组件范围内使用的组件

  b. 如何: 3步: 

    1). 只创建一个普通的js对象,保存组件的内容

       var 子组件对象名={ 组件内容 }

    2). 为父组件添加新成员: components

       父组件:{
         ... : ...,

         components:{ 子组件对象名, ... ,  }
       }

    3). 在父组件界面中: <子组件标签名></子组件标签名>

7. 示例: 实现待办事项列表的界面部分划分组件

13_todo/js/todoAdd.js

var todoAdd={

  template:`<div>

    <input><button>+</button>

  </div>`

};

13_todo/js/todoItem.js

var todoItem={

  template:`<span>

    1 - 吃饭 <a href="javascript:;">×</a>

  </span>`

};

13_todo/js/todoList.js

var todoList={

  template:`<ul>

    <li>

      <todo-item></todo-item>

    </li>

    <li>

      <todo-item></todo-item>

    </li>

    <li>

      <todo-item></todo-item>

    </li>

  </ul>`  ,

  components:{ todoItem }

};

13_todo/js/todo.js

Vue.component("todo",{

  template:`<div>

    <h3>待办事项列表</h3> 

    <todo-add></todo-add> 

    <todo-list></todo-list>

  </div>`,

  components:{

    todoAdd, todoList

  }

})

13_todo/index.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <!--顺序:子组件必须在其父组件之前引入-->

  <script src="js/todoAdd.js"></script>

  <script src="js/todoItem.js"></script>

  <script src="js/todoList.js"></script>

  <script src="js/todo.js"></script>

</head>

<body>

  <div id="app">

    <todo></todo>

    <!-- <todo-item></todo-item>

        报错: 找不到todo-item -->

  </div>

  <script>

    new Vue({

      el: "#app"

    })

  </script>

</body>

</html>

运行结果: 

8. 组件间传值: 

  (1). 问题: 子组件无权使用父组件中的成员。

  (2). 解决: 其实,vue中提供了多种组件间传值的机制。

  (3). 重点讲父给子传值: 3步: 

  a. 父给子: 

   父组件:{
    template:`
      <子组件标签   :自定义属性名="父组件的变量">
    `

  b. 子组件接收属性值: 

    子组件对象:{
      props:[ "自定义属性名" ]
    }

  c. 在子组件内,props中的属性用法和data中的变量用法完全一样!只不过props的属性值来自于外部传入,data中的变量值由自己定义。

  d. 示例: 使用父给子传值,实现待办事项列表功能

  13_todo2/js/todoAdd.js

var todoAdd={

  props:["tasks"],

  template:`<div>

    <input v-model="t"><button @click="add">+</button>

  </div>`,

  data(){

    return {

      t:"" //接住用户输入的新任务

    }

  },

  methods:{

    add(){

      this.tasks.push(this.t);

      //清空文本框内容 

      this.t="";

    }

  }

};

  13_todo2/js/todoItem.js

var todoItem={

  props:["t","i","tasks"],

  template:`<span>

    {{i+1}} - {{t}} <a href="javascript:;" @click="del">×</a>

  </span>`,

  methods:{

    del(){

      //删除tasks数组中i位置的1个元素

      this.tasks.splice(this.i,1);

    }

  }

};

  13_todo2/js/todoList.js

var todoList={

  props:["tasks"],

  template:`<ul>

    <li v-for="(t,i) of tasks" :key="i">

      <todo-item :t="t" :i="i" :tasks="tasks"></todo-item>

    </li>

  </ul>`  ,

  components:{ todoItem }

};

  13_todo2/js/todo.ja

Vue.component("todo",{

  template:`<div>

    <h3>待办事项列表</h3> 

    <todo-add :tasks="tasks"></todo-add> 

    <todo-list :tasks="tasks"></todo-list>

  </div>`,

  data(){

    return {

      tasks:["吃饭","睡觉","打亮亮"]

    }

  },

  components:{

    todoAdd, todoList

  }

})

  13_todo2/index.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <!--顺序:子组件必须在其父组件之前引入-->

  <script src="js/todoAdd.js"></script>

  <script src="js/todoItem.js"></script>

  <script src="js/todoList.js"></script>

  <script src="js/todo.js"></script>

</head>

<body>

  <div id="app">

    <todo></todo>

    <!-- <todo-item></todo-item>

        报错: 找不到todo-item -->

  </div>

  <script>

    new Vue({

      el: "#app"

    })

  </script>

</body>

</html>

运行结果:

二. SPA(Single Page Application)

        单  页面  应用

1. 什么是: 整个应用程序只有一个唯一完整的HTML页面。其它所谓的页面,其实都是组件片段而已。所谓的切换页面,只是切换一个HTML中显示不同的组件片段而已。

2. 为什么: 

多页面应用

单页面应用

请求次数

每切换一次页面,都想服务器端重新发送请求。反复切换页面,就会反复发送请求。请求次数多!

在首次加载时,就将唯一完整的HTML页面和所有其余页面组件一起下载下来。今后,即使反复切换页面,也不需要反复向服务器发送请求。请求次数绝对少.

公共资源

每次切换页面,都要重新请求页面中的bootstrap.css、jquery.js、bootstrap.js等多个页面都要用到的资源。请求次数多,加载慢。

每次切换页面时,唯一完整的HTML外壳没有切换,所以不会重复发送请求,下载css和js文件。所以,请求次数又少了很多,同时加载效率高

加载效率

每次切换页面,都要删除旧的整棵DOM树,重建整棵DOM树。效率低。

每次切换页面时,因为只跟换部分组件片段显示,整个页面没有更换。则DOM树,也只更换部分节点,不用重建整棵DOM树。效率高

页面切换动画

几乎不可能实现页面切换动画。因为页面切换需要同时看到前一个页面的后一半以及后一个页面的前一半。

多页面应用不可能两个页面同时存在,所以无法实现。

比较容易实现页面切换动画。因为单页面应用的所有页面组件已经同时保存在客户端了。同时显示,也是有可能的。

3. 何时: 今后几乎所有的项目都是单页面应用

4. 缺点: 默认,单页面应用要在首次加载时,将所有需要的页面组件都下载到客户端本地,而不管用户是不是想看。所以,首屏加载速度极慢

5. 解决: (未完待续...)

6. 如何: 

  (1). 先创建唯一完整的HTML页面

  要求: 是一个支持vue基本结构的空页面

   <script src="js/vue.js">

   <div id="app">
   </div>
   <script>
     new Vue({
       el:"#app"
     })

  (2). 再创建所有"页面"组件文件: 

  a. 将来项目中,有几个"页面",就要创建几个页面组件文件

  b. 按惯例,所有页面组件都要集中放在一个名为views的文件夹中.

  c. 每个页面组件其实都是一个子组件。

  d. 在唯一完整的HTML页面顶部引入页面组件

  e. 创建404页面组件,也要在唯一完整的HTML页面顶部引入。还要加入到路由字典中最后一项: 

     { path:"*", component:NotFound }

  (3). 创建路由器对象: 

  a. 在唯一完整的HTML页面顶部引入vue-router.js(官方)

  b. 创建路由器对象: 

    1). 按惯例,路由器对象应该保存在router/index.js文件中。

    2). 先创建路由字典

    var routes=[

      {path:"/相对路径", component:页面组件对象名},

      ...
    ]

    3). 再创建路由器对象

    var router=new VueRouter({ routes })

  c. 先引入唯一完整的HTML页面中: 

  d. 必须将router对象加入到new Vue()中,router对象才有权修改页面中的内容。

    new Vue({

      el:"#app",

 router

    })

  e. 在唯一完整的HTML页面中<div id="app">内,添加<router-view></router-view>标签,用于为将来的页面组件占位。

  f. 结果: 路由器对象三大功能

    1). 监视地址栏变化

    2). 查找当前路径对应的页面组件是谁

    3). 将找到的页面组件替换到<router-view>的位置

  (4). 创建除页面以外的其它全局组件或子组件: (页头)

  a. 所有不足以成为一个页面的组件片段都要集中创建在components文件夹中

  b. 所有的组件,暂时都创建为子组件,且都要在唯一完整的HTML页面中引入

  c. 如果是全局组件,则只要在new Vue()之前,使用Vue.component()将子组件对象转为全局组件即可。

    Vue.component("组件标签名", 组件对象名);

  d. 2种情况: 

    1). 如果所有页面都需要显示页头,则只要在<router-view>上方添加<页头组件>标签即可

    2). 如果有的页面有页头,有的页面不需要页头,就应该只在那些需要页头的组件中添加<页头组件>。不需要页头的组件,就不要加组件标签。

7. 示例: 实现单页面应用: 

14_SPA/index.html

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>

  <script src="js/vue.js"></script>

  <script src="views/Index.js"></script>

  <script src="views/Details.js"></script>

  <script src="views/NotFound.js"></script>

  <script src="js/vue-router.js"></script>

  <script src="router/index.js"></script>

  <script src="components/MyHeader.js"></script>

</head>

<body>

  <div id="app">

    <!--让所有页面都有页头-->

    <my-header></my-header>

    <router-view></router-view>

  </div>

  <script>

    //将普通子组件MyHeader转为全局组件

    Vue.component("my-header",MyHeader);

    

    new Vue({

      el:"#app",

      router

    })

  </script>

</body>

</html>

运行结果:

14_SPA/views/Index.js

var Index={

  template:`<div>

    <h3 style="color:blue">这里是首页</h3>

  </div>`

}

14_SPA/views/Details.js

var Details={

  template:`<div>

    <h3 style="color:green">这里是详情页</h3>

  </div>`

}

14_SPA/views/NotFound.js

var NotFound={

  template:`<div>

    <h3 style="color:red">404:你迷路了</h3>

  </div>`

}

14_SPA/router/index.js

/*创建我们自己的路由器对象:2大步*/

//1. 先定义一个路由字典数组,保存所有相对路径与对应的页面组件之间的对应关系。

//  路由们

var routes=[

  //    相对路径         页面组件对象名

  {path:"/", component:Index},

  {path:"/details", component:Details},

  {path:"*", component:NotFound}

];

//2. 再创建一个路由器对象,引入路由字典数组,形成一个整体

var router=new VueRouter({ routes });

//        创建一个路由器对象

//                     引入字典数组   

14_SPA/components/MyHeader.js

var MyHeader={

  template:`<div>

    <h3 style="color:orange">这里是页头</h3>

    <hr>

  </div>`

}

总结: 

补: this.$nextTick(function(){ ... })

  注定只能在本轮生命周期结束后才自动执行的回调函数
  可防止原生DOM操作被vue挂载阶段覆盖

8. 只要希望重用一块独立的功能区域就用组件:

(1). 定义组件

Vue.component(`组件标签名`,{

  template:`HTML内容片段`,

  data(){ return { 变量 } },

  //其余和new Vue()完全相同

})

(2). 在HTML中使用自定义组件

<组件标签名/>或双标记也行

(3). 原理: new Vue()扫描到自定义组件标签,

a.组件的template中的HTML内容代替页面中<组件标签>位置。

b. 并为这个小区域专门创建一个缩微版的vue类型对象

  1). 调用组件的data()函数为当前组件副本创建一个专属数据对象副本。

  2). 引入组件对象中的methods等其他内容到当前组件对象副本中

9. 组件化开发:
(1). 步骤:
a. 拿到页面先划分功能区域

  1). 从上到下,按功能不同划分区域

  2). 按是否重用划分

b. 为每个组件创建独立的.js文件,其中包含一个组件对象及其内容

c. 将所有组件引入唯一完整的html页面中,并在<div id=”app”></div>中添加父组件标签。
(2). 运行时:

a. new Vue()扫描<div id=”app”>,发现父组件标签,创建并替换父组件

b. 父组件扫描自己内部的template内容,创建并替换子组件

(3). 三种组件:

a. 根组件: new Vue() 

b. 全局组件: Vue.component(...)

c. 子组件: 3步

  1). var 子组件对象名={

       内容必须符合组件的要求

     }

子组件对象名必须是驼峰命名

  2). 父组件对象中:{

... ...

components: { 子组件对象名, ... ,... }

}

子组件对象名必须是驼峰命名

  3). 父组件template中用<子组件标签名/>引入子组件内容

     components会将子组件对象名的驼峰命名自动翻译为-分隔

所以, 使用子组件标签时,要用-分隔多个单词

(4). 组件间传参: 父给子

a. 父组件给:

<子组件 :自定义属性名="父组件变量">

b. 子组件取:

props:["自定义属性名"]

结果: 在子组件内,props中的"自定义属性名"与子组件自己data中的变量用法完全相同!
10. SPA

(1). 3步:

a. 先创建唯一完整的HTML页面

  1). 包含vue基本的页面结构

  <div id="app"> new Vue({el:"#app"})

  2). 引入所有必要的文件和组件

  vue-router.js, 其它页面或组件文件, router.js路由器对象所在文件

  3). <div id="app">中用<router-view/>为今后页面组件预留空位

b. 再为每个页面组件创建独立的文件。每个页面组件其实都是一个子组件

c. 创建router.js文件,创建路由器对象

  1). 创建路由字典对象:

  var routes=[

    {path:"/", component:首页组件对象名},

    {path:"/相对路径" , component: 其它页面组件对象名},

    {path:"*", component: 404页面组件对象 }

  ]

  2). 创建路由器对象,并将路由字典对象转入路由器对象中
  var router=new VueRouter({ routes })

  3). 将router对象加入到new Vue()中

  回到唯一完整的HTML页面中: new Vue({ el:"#app", router })

(2). 页头等全局组件:

a. 创建独立的文件保存页头组件的内容

b. 使用Vue.component("my-header",{ ... })将页头创建为全局组件

c. 在唯一完整的HTML页面中引入页头组件文件

d. 使用页头组件标签<my-header/>: 2种:

  1). 如果所有页面都有统一的页头:

  就放在唯一完整的html页面中<router-view>外部上方

  2). 如果有的页面有页头,有的页面没有页头:

  就只放在需要页头的组件中的template中

正课: 

1. SPA

2. 脚手架

3. *****避免组件间样式冲突

4. 学子商城项目

5. *****懒加载

一. SPA

1. 路由跳转: 

  (1). 在HTML中写死的跳转连接: 

  a. 不要用a元素

  b. <router-link to="/相对路径">文本</router-link>

                  和路由字典中path一致

  c. 原理: 其实<router-link to="/xxx"</router-link>
     还是会被翻译为<a href="xxx"></a>

     但是,翻译过程中,vue会对a做一些自动的加工.

  (2). 在程序中自动跳转: 

    this.$router.push("/相对路径")

       咱们创建的路由器对象router

  (3). 路由跳转传参: 3步

  a. 配置路由字典中的路由字典项:

   { path:"/相对路径/:变量名", component: 页面组件对象名, props:true}

    其中: 

    1). /:变量名 为上个页面传到下个页面的值起一个变量名,便于重复使用。

    2). props:true  让地址栏中的上个页面传来的值,自动掉进下一个页面的props中称为一个外来属性/变量。

  b. 跳转时如何携带参数值到下个页面: 

     <router-link  to="/相对路径/参数值">

     或

     this.$router.push("/相对路径/参数值")

    强调: 路由传参,在路由字典项的path中定义变量时必须加:。但是,在跳转时传参时既不用加:,也不用加变量名。只写参数值就够了!

  c. 下个页面: props:[ "变量名" ]

      在下个页面中,就可以像使用自己data中的变量一样使用props中外部传来的变量了。

  d. 强调: 一旦配置了路由参数,则进入该页面时就必须携带参数。如果不带参数值,是不让进的!

  e. 示例: 实现路由跳转传参: 

  14_SPA/router/index.js

/*创建我们自己的路由器对象:2大步*/

//1. 先定义一个路由字典数组,保存所有相对路径与对应的页面组件之间的对应关系。

//  路由们

var routes=[

  //    相对路径         页面组件对象名

  {path:"/", component:Index},

  {path:"/details/:lid", component:Details,props:true},

  {path:"*", component:NotFound}

];

//2. 再创建一个路由器对象,引入路由字典数组,形成一个整体

var router=new VueRouter({ routes });

//        创建一个路由器对象

//                     引入字典数组   

  14_SPA/views/Index.js

var Index={

  template:`<div>

    <h3 style="color:blue">这里是首页</h3>

    <button @click="goto(5)">查看5号商品的详情</button>

    <button @click="goto(10)">查看10号商品的详情</button>

    <button @click="goto(15)">查看15号商品的详情</button>    

  </div>`,

  methods:{

    goto(lid){                  // 变量

      this.$router.push(`/details/${lid}`)

    }

  }

}

  14_SPA/views/Details.js

var Details={

  props:["lid"],

  template:`<div>

    <h3 style="color:green">这里是详情页</h3>

    <h3>显示{{lid}}号商品的详细信息...</h3>

  </div>`

}

运行结果:

二. 脚手架:

1. 什么是: 一套已经包含核心功能和标准文件夹结构的半成品项目。

2. 为什么: 标准化!极其便于协作,降低学习成本。

3. 何时: 今后所有项目,所有新技术,都是在脚手架基础上开发的.

4. 如何: 2大步: 

  (1). 安装可以反复生成脚手架的工具: (老母鸡)

   a. 设置淘宝镜像——下载快
npm config set registry https://registry.npm.taobao.org
b. 安装可生成脚手架代码的命令行工具

npm  i  -g  @vue/cli

当看到: + @vue/cli@版本号 说明安装成功

  (2). 用工具反复为每个项目创建专门的脚手架结构: 

  a. 决定把项目文件夹保存在哪个位置

  b. 再在整个文件夹位置,运行: vue create 自定义项目名

    1). ? Please pick a preset:

  Default ([Vue 2] babel, eslint)

  Default (Vue 3) ([Vue 3] babel, eslint)

> Manually select features

    2). ? Check the features needed for your project:

  ( ) Choose Vue version //按空格切换选中/不选中

  (*) Babel  //ES6翻译为ES5

  ( ) TypeScript  //下周一学VUE3再选

  ( ) Progressive Web App (PWA) Support

  (*) Router //VueRouterSPA应用的核心

  (*) Vuex //下周一讲

  (*) CSS Pre-processors //支持Scss

  ( ) Linter / Formatter //不要选,代码质量检查工具,要求过于严格

  ( ) Unit Testing

>( ) E2E Testing

    3). ? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) N

    其实vue的路由有两种模式: 

    i. hash(#)模式: http://域名:端口号/#/相对路径

    ii. history模式: http://域名:端口号/相对路径

       需要专业的服务器端工程师配合——必须配置服务器端的首页重定向机制。

    4). ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)

  > Sass/SCSS (with dart-sass)

    Sass/SCSS (with node-sass)

    Less

    Stylus

    5). ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)

      In dedicated config files

    > In package.json

    6). ? Save this as a preset for future projects? (y/N) N

    7). 等待。。。

    看到: Successfully created project xzvue. 说明安装成功

5. 使用vscode打开并运行脚手架项目: 

  (1). 右键单击package.json文件,选择"在集成终端中打开"

  (2). 在终端窗口中输入: npm run serve

      看到: App running at:

           - Local:   http://localhost:8080/

  (3). 按住Ctrl,点local:右侧的连接,自动打开浏览器

    问题: 如果无法自动打开可重装chrome浏览器并配置操作系统的默认浏览器为chrome

6. 脚手架文件夹结构: 还是SPA 4部分

  (1). 唯一完整的HTML页面: 一分为三

  a. HTML页面基础结构放在public/index.html中

  b. <div id="#app"><router-view>放在src/App.vue中

  c. new Vue({ router, ... })放在src/main.js中

  d. 运行时: 

    1). src/main.js中引入src/App.vue中的HTML内容

      import App from "./App.vue"

    2). src/main.js中,将new Vue()和App.vue中的内容建立联系

    3). 运行时,唯一完整的html页面头部,自动添加引用: 

      <script src="app.js">

                (包含new Vue和App.vue中所有内容)

    4). 最终main.js、App.vue以及public下的index.html还是合并在一起运行的。

  (2). 页面组件: src/views/

  a. 问题: 保存在js文件中,只能写js,不能写HTML和css。如果写HTML,必须写在字符串中,既没有提示,又没有验证。

  b. 解决: 脚手架规定: 今后所有组件都必须创建为.vue文件。

  c. 什么是: 专门包含一个组件的HTML+CSS+JS的新文件类型

  d. 一个.vue文件中标配包含3大部分: 

    1). <template>专门编写组件的HTML内容</template>
    i. 问题: 默认vscode不认识.vue文件,所以依然没有提示和颜色

    ii. 解决: 安装vscode vetur插件

    2). <script>专门包含组件对象</script>

    i. 强调: 因为vue脚手架采用的是组件化开发,所以,一个组件js对象,要想抛出,并让别人能够引用,必须使用: 

      export default { 组件内容 }

       抛出  默认组件

    ii. 但是,export default {}内的组件内容与前四天所学完全相同!

    3). <style>专门保存这个组件专属的css的区域

  e. 修改.vue文件内容: 

   强调: 热编译: 无需停止或重启项目,只要一修改,立刻自动重新编译,重新运行,自动在界面上显示新内容。

  (3). 路由器对象: src/router/index.js

  a. 相同点: 还是包含2大部分: 路由字典routes和路由器对象router。

  b. 问题: 在我们自定义的SPA应用中,明明是路由器中的路由字典routes中要用页面组件对象。但是,偏偏要先引入html文件,再在routes中使用。极其不直观,容易造成误会!

  c. 解决: 模块化开发: 每个文件都是一个独立的模块对象。模块之间可以直接引用!无需经过任何第三方中介。——直观,便于维护

  d. 如何: 2大步: 

    1). 抛出: 2种: 

    i. 标准: 一个组件或对象包含js代码,则必须使用

      export default { 组件或对象的内容 } 

    ii. 简化: 如果一个组件没有js代码,只有HTML和css代码,则一个.vue文件默认就是一个可被别人引用模块对象。

    2). 引入: import 自定义对象别名 from "相对路径"

  e.结果: 在import所在的模块内,可以像使用自己的对象一样实用引入的模块。

  (4). 全局组件和子组件片段: src/components

  a. 所有组件都要创建为.vue文件,标准都要包含3部分。只要有js都要export default{}抛出.

  b. 全局组件: 2步: 

    1). 先在src/components/先创建一个普通的子组件.vue
    2). 再在main.js中new Vue()之前引入子组件.vue,并将子组件对象转为全局组件

      import 子组件对象别名 from "./components/子组件.vue"

      ... ...

      Vue.component("标签名",子组件对象别名)

7. 示例: 封装页头组件: 

  (1). src/components/新建MyHeader.vue

  (2). 剪切src/App.vue中

  a. <div id="nav">...</div>粘贴到MyHeader.vue中<template>中

  b. <style>内#nav{...}及其子内容粘贴到MyHeader.vue中<style lang="scss">中

  (3). src/main.js中:

    import MyHeader from "./components/MyHeader.vue"

    ...

    Vue.component("my-header",MyHeader)

  (4). src/App.vue中<router-view>上方:<my-header></my-header>

三. 避免组件间样式冲突:

1. 问题: 明明写在关于页面的h1样式,却覆盖了首页中的h1样式。

2. 原因: 其实,虽然我们编写组件时,是在各个.vue文件中分别编写的。但是最终运行时,脚手架会将所有css内容编译到网页的内部样式表中集中保存。结果,不同组件中的选择器,如果碰巧相同,则一定会互相影响。

3. 解决: 2个: 

  (1). 偷懒的,不好的: scoped

  a. <style scoped>

        //范围内的

  b. 规定这个<style>内的所有选择器,只在当前组件内有效!

  c. 原理: 

    1). 为css中的选择器自动添加随机名称的属性选择器: 

    2). 为当前组件中的所有HTML元素自动添加与属性选择器同名的自定义属性。

    3). 不同组件的scoped为每个组件添加的随机的属性选择器名各不相同!即使多个选择器碰面,也不会发生冲突。

    4). 结果: 只有当前组件内部的HTML元素带有的自定义属性名才能匹配当前组件自己的css中的属性选择器。

  d. 问题: 

    1). 效率低: 为所有元素自动添加自定义属性

    2). 只对<style>内的css选择器有效,对于外部引入的css选择器无效!依然会发生冲突

  (2). 主动添加class名:

  a. 如何: 2步: 

    1). 今后,每创建一个组件,都有一个唯一父元素。我们就要给唯一的父元素上加一个与当前组件名相同的class名
    <template>

      <唯一父元素  class="当前组件名">
       ... ...

    2). 只要当前组件内的css选择器,一律用这个唯一父class名开头。

    <style>
     .当前组件名 其它选择器{

       ... ...

     }

  b. 优点: 万能

四. 学子商城项目: 

1. 准备: 

  (1). 图片: 因为图片不是源代码,不需要我们修改就可直接用,所以,图片应该放在public文件夹下。

     直接复制旧项目中img文件夹粘贴到xzvue/public文件夹中

  (2). 公共的bootstrap和jquery: (不是我们自己写的)

  a. 复制旧项目中css文件夹中bootstrap.min.css粘贴到xzvue/public/css文件夹中

  b. 复制旧项目中js文件夹中jquery-3.4.1.min.js和bootstrap.min.js文件,粘贴到xzvue/public/js文件夹中

  c 在唯一完整的HTML页面xzvue/public/index.html头部引入jquery和bootstrap

2. 迁移学子商城首页的静态页面HTML+CSS:

  (1). 删除脚手架自带首页: 

  a. 删除xzvue/src/views/Home.vue文件

  b. 删除xzvue/src/router/index.js
    1). 开头的import Home...

    2). routes中的第一个{ path:"/" ,...}删除

  (2). 创建新的首页组件: 

  a. 在xzvue/src/views/新建Index.vue文件

  b. 在xzvue/src/router/index.js中: 

    1). import Index from "../views/Index"

    2). 在routes内: { path:"/", component: Index }

  (3). 迁移旧项目中的HTML+css到新项目中首页.vue中: 

  a. 复制旧项目中: index.html中<main id="main"...>...</main>及其子内容

      粘贴到xzvue/src/views/Index.vue中<template>内

  b.  复制旧项目中: css/index.css中所有css代码

      粘贴到xzvue/src/views/Index.vue中<style scoped>内

3. 迁移页头

  (1). 复制旧项目中index.html中<header ...></header>粘贴到xzvue/src/components/MyHeader.vue中<template>中

  (2). 复制旧项目中header.html中所有内容粘贴到xzvue/src/components/MyHeader.vue中<template>中<header>的内部

  (3). 复制旧项目中css/header.css中所有代码粘贴到xzvue/src/components/MyHeader.vue中<style scoped>内

4. 迁移详情页

  (1). 删除旧About.vue

  a. 删除xzvue中src/views/About.vue

  b. 注释src/router/index.js中路由字典routes中第二个路由字典项:{ path:"/about", ... }

  (2). 新建Details.vue

  a. 在xzvue/src/views/新建Details.vue文件

  b. 在xzvue/src/router/index.js中:

    import Details from "../views/Details"
   

    var routes=[

      { ... },

      { path:"/details", component: Details }

    ]

  (3). 迁移旧详情页到xzvue:

  a. 复制旧项目中product_details.html中<main ...>...</main>及其所以内容粘贴到xzvue/src/views/Details.vue中<template>中

  b.  复制旧项目中css/product_details.css中所有内容粘贴到xzvue/src/views/Details.vue中<style scoped>内

五. 懒加载: 

1. 单页面应用的致命问题: 首屏加载极慢

2. 原因: 脚手架默认把所有组件集中打包为一个巨大的app.js文件。在首屏一次性全部下载。

3. 解决: 懒加载: 用户想看什么就只下载什么,用户暂时不想看的,就没必要下载!

4. 2种方式: 

  (1). 默认: 异步延迟加载: 

  a. 什么是: 优先下载并显示首页内容。其它页面组件采用底层异步下载的方式,在不影响主屏下载速度的情况下,异步下载。

  b. 优点: 既加快了首屏加载的速度,又能享受单页面应用带来的好处。

  c. 小缺点: 偷跑流量。

  d. 如何: 2步: 

    1). 千万不要在router/index.js开头过早的引入除首页之外的其它页面组件。

      因为凡是你用Import引入的东西,vue都认为是头等重要的!都会打包在app.js中,在首屏下载。

    2). 改造路由字典项: 

    {
      path:"/相对路径",

      component: ()=>import(

       /* webpackChunkName: "自定义js文件名" */

  "../views/页面组件.vue"

      )
    }

  (3). 彻底懒加载: 

  a. 什么是: 如果用户不看哪些页面,就一点儿也不下载。完全等到用户想看某个页面时,才临时下载。

  b. 如何: 2步: 

    1). 也要实现异步懒加载的两步: 

    i. 不要提前引入

    ii. component变成匿名箭头函数

    2). 配置脚手架,去掉prefetch

    i. 在脚手架根目录,创建vue.config.js

    ii. 在vue.config.js中添加以下固定代码: 

    module.exports={

      chainWebpack:config=>{

        config.plugins.delete("prefetch")

        //删除index.html开头的带有prefetch属性的link,不要异步下载暂时不需要的页面组件文件

      },

    }

    iii. 强调: 必须重启npm run serve

  c. 优点: 节约流量

  d. 小缺点: 首次切换页面时需要临时下载页面组件,可能会慢。

总结: 

(3). 路由跳转: 2种:

a. html中: <router-link to="/相对路径">文本<router-link>

b. js中: this.$router.push("/相对路径")

(4). 路由传参:

a. 修改路由字典:

{path:"/相对路径/:自定义参数名", component:页面组件对象, props:true}

b. 跳转时:

<router-link to="/相对路径/参数值"

this.$router.push("/相对路径/参数值")

c. 下一个页面接:

  1). props:[ '自定义参数名' ]

  2). 可将"自定义参数名"用作绑定或程序中都行

11. 脚手架文件夹结构:

(1). 唯一完整的HTML页面: 一分为三:

a. public文件夹

  1). 图片img文件夹放在public文件夹下

  2). 第三方css的压缩版和第三方js的压缩版都放在public文件夹下

  3). 唯一完整的HTML文件index.html中,head中引入第三方的css和js

b. src/App.vue

  1). <template>下包含<div id="app">公共的页头组件和<router-view>

  2). <style>下包含所有网页都要用到的公共css样式,比如css重置代码

c. src/main.js

  1). import引入App.vue,router,axios,以及其他全局组件

  2). 将全局组件对象转为真正的全局组件: Vue.component( "组件标签名", 全局组件对象 )

  3). 包含new Vue()对象

  4). 配置axios并放入原型对象中:

axios.defaults.baseURL="服务器端基础路径"

    Vue.prototype.axios=axios;

(2). 为每个页面创建.vue组件文件,都放在src/views文件夹下。每个.vue文件中:

a. <template>标签内,包含这个页面的HTML内容

b. <script>export default{ ... }</script>中包含组件对象的js内容。

c. <style>标签内包含仅这个页面组件内才使用的css

d. <template>中的HTML内容以及<script>export default{...}</script>中的js内容,和前四天将的是完全一样的写法,绑定,指令,函数,生命周期,axios请求等都一样。前四天怎么用,这里就怎么用。

(3). 路由字典和路由器对象,在src/router/index.js文件中

a. 仅import首页组件对象,不要过早引入其它页面组件

b. 路由字典中首页组件: { path:"/", component:首页组件对象}

c. 其余页面组件都做成懒加载:

  {

    path: '/相对路径',

    component: () => import(/* webpackChunkName: "组件名" */ '../views/其它页面组件.vue')

  }

(4). 全局组件都放在src/components文件夹下,每个全局组件.vue文件中。但是,全局组件必须在main.js引入,并用Vue.component()转化为真正的全局组件,才能在其它组件的HTML中使用。

(5). 运行时: 路由器router对象监视浏览器地址栏路径变化,并查找对应的页面组件内容,先代替App.vue中的<router-view>,然后main.js再将包含要加载的页面内容的App.vue组件内容,替换到唯一完整的index.html中空<div id="app">位置。

12. 避免组件间样式冲突:

(1)<style scoped>
(2)<template>
   <组件父元素 class="组件名">
  </template>
  <style>
   .组件名>子元素选择器{ ... }
  </style>

17. 懒加载:

(1).异步延迟下载:

src/router/index.js

//不要import Details from "../views/Details"

const routes=[
  ...

  {

    path:"/details/:lid",

component: () => import(

/* webpackChunkName: "details" */

'../views/Details.vue'

),

    props:true
]

(2).彻底懒加载:

项目根目录创建vue.config.js

module.exports={

  chainWebpack:config=>{

    config.plugins.delete("prefetch")

    //删除index.html开头的带有prefetch属性的link,不要异步下载暂时不需要的页面组件文件

  },

}

一. 学子商城项目: 

1. 发送axios请求 

  (1). 问题: vue脚手架默认不带axios

  (2). 解决: 右键单击package.json,选择在集成终端中打开。然后在终端中输入: npm  i  -save  axios

  (3). 问题: 所有的页面组件或其他子组件中都随时有可能使用axios发送请求获取数据。

  (4). 解决: 因为所有组件(根组件、全局组件、子组件)都是new Vue()类型的子对象。所以,可以将配置好的axios对象保存到Vue类型的原型对象中。结果,所有组件中都可像使用自己的属性一样使用Vue原型对象中axios对象了

  (5). 如何: 

  a. main.js中: 

    import axios from "axios"

    axios.defaults.baseURL="公共基础地址"

    Vue.prototype.axios=axios

  b. 其它组件中: this.axios.get()或this.axios.post()

2. 动态加载首页商品:

  (1). 在Index.vue中<script>中组件对象内添加: 

   data(){
return {
      p1:{}, //准备接第一个商品

      p2:{}, //准备接第二个商品

      p3:{}, //准备接第三个商品

      others:[] //准备接剩余的后三个商品
    }
   }

  (2). 在mounted()内的axios的.then()中,将响应结果数组中每个商品对象赋值给data中的对应变量; 

    this.p1=result.data[0];

    this.p2=result.data[1];

    this.p3=result.data[2];

    this.others=result.data.slice(-3)//从倒数第三个,一直取到最后

    简写: 

    [this.p1, this.p2,this.p3,...this.others]=result.data;

  (3). 去<template>中绑定每商品: 

  (4). 问题: 明明正常显示,但是还是报错:

   不能读取 undefined 的xxx属性

  a. 原因: 首次加载时,p1对象中没有值,是undefined。undefined加谁都算错!

  b. 解决:  2个办法: 

    (1). 为对象的属性提前加上属性值: 

    (2). 在页面中绑定语法或指令中使用三目屏蔽undefined的情况。

  (5). 绑定第二个(56~66行)、第三个商品(71~78行)

  (6). 绑定首页后三个商品: 

  a. 删除:92行~111行两个div

  b. 只绑定82~91行一个div,所以在82行加v-for

3. 从首页跳转到详情页,并发送请求查询一个商品的信息信息: 

  (1). 改造首页中按钮的旧路径为新路径: 

    :to=`/details/${p1.href.split("=")[1]}`

  (2). 配置路由字典,允许/details路径携带参数

    {  

      path:"/details/:lid",

      component: ()=>import(...),

      props:true

    }

  (3). 在详情页使用props接住上个页面传来的数据,并发送axios请求,获得数据

4. 动态绑定详情页的内容:

  (0). 先从结果对象中取出每个对象或数组,分别保存: 

  a. data(){ product:{}, specs:[], pics:[] }

  b. 在axios的.then()中为data中每个变量赋值

     this.product=result.data.product;

     this.specs=result.data.specs;

  (1). 绑定右上角商品基本信息: 

  (2). 绑定右侧规格列表: 57行

    v-for 遍历 specs数组中每隔规格对象

  a. a解开注释,换成router-link,href换成to,

  b. 添加v-for,并:to="`/details/${sp.lid}`",内容绑定sp.spec

  c. 为当前router-link绑定class active。哪个规格对象的lid商品编号和地址栏中的商品编号一致,才用后active class。

  (3). 问题: 点不同的规格按钮,只有地址栏里的数字变,网页内容不重新加载!

  (4). 原因: 因为每个规格按钮跳回/details页面,只不过携带的值不同而已。所以,vue就认为没必要重新加载详情页组件。如果vue觉得没必要重新加载组件,则不会触发mount阶段,自然不会触发mounted中的axios。而我们的程序只在mounted里写了一个axios请求。如果mounted不执行,则axios请求也不会重新发送,当然得不到新商品信息。

  (5). 解决: 紧紧监视地址栏中的商品编号(props:["lid"])

     只要lid变量值一变,就立刻重新发送请求!

二. http-proxy跨域: 

1. 旧的跨域方式:无论CORS还是JSONP,都要求助于后端才能跨域。

2. 解决: 只要使用vue脚手架开发时,都可以用http-proxy跨域

3. 优点: 纯前端跨域方式。

4. 什么是: 请其他程序代替ajax发送请求。

5. 如何:

  (1). 在脚手架根目录的vue.config.js文件中,再配置一个代理程序: 

  module.exports={

    ... ...

    devServer: {

      proxy: {

        '/': {

          target: `http://服务器端接口地址统一前缀部分`,

          changeOrigin: true,

        }

      }

    }
  (2). main.js中,不用配置axios.defautls.baseURL了

  (3). 重启npm run serve

6. 笔试时,被问到"你的项目是用什么跨域的?":http代理跨域。

7. 总结: 跨域共有几种方式: 3种: cors, jsonp, http代理

三. 插槽(ppt)

总结:

18. http-proxy方式跨域:

在服务器端没有配置CORS或JSONP跨域的情况下:
(1). vue.config.js中

module.exports={
  ... ,

  devServer: {

    proxy: {

      '/': {

        target: `服务器端接口公共基础地址部分`,

        changeOrigin: true,

      }

    }

  }
}
(2). main.js中: 不需要

axios.defaults.baseURL="xxx"

(3). this.axios.get("/接口地址的相对路径")

21. 插槽:

(1). 只要多个组件拥有相同的结构,只是局部的内容不同,都可用插槽实现

(2). 如何: 

  a. 先额外创建一个带插槽的外壳组件,保存其它多个组件相同部分的HTML+CSS+JS。但是,外壳组件中将来可能变化的位置,用<slot></slot>标记

  b. 再定义其它使用外壳组件的组件: 

  1). 引入带插槽的外壳组件: 

  import 外壳组件名 from "./外壳组件.vue"

  export default {

    components:{ 外壳组件名 },

      ...

  }

  2). 在其它组件<template>中: 

  <template>

    <外壳组件 :外壳组件所需属性="变量">

      <当前组件自有的个性化内容>

    </外壳组件>

  </template>

(3). 具名插槽: 

  a. 如果一个外壳组件中,多个部位将来都会被更换,就可用具名插槽

  b. 如何: 

  1). 外壳组件中: 

  <template>

    ...公共部分...

    <slot name="插槽名1"></slot>

    ...公共部分...

    <slot name="插槽名2"></slot>

    ...公共部分...

  <template>

  2). 其它使用外壳组件的组件: 

  <template>

    <外壳组件...>

      <template  #插槽名1>

        第一部分要更换的内容

      </template>

      ...

      <template #插槽名2>
        第二部分要更换的内容

      </template>

    </外壳组件>

  </template>

正课: 

1. vuex

2. TS

一. vuex:(PPT)

二. TS:(PPT)

总结: 

22. Vuex

(1). 今后只要多个组件都需要共用一批数据时,都可用vuex

(2). 3部分: 

  a. state:{ //保存共用的变量 

      变量:初始值,

 ... : ...

    }

  b. mutations:{ //保存专门修改变量的方法 

 set变量名(state, 新值){

state.变量名=新值

 }

 ...

}

  c. actions:{ //保存专门先发送ajax请求,再修改变量的方法 

      方法名(context, 发给服务器的参数值){

        axios.get或post(

 "接口地址",

 发给服务器端的参数值

).then(result=>{

 ...

 context.commit("mutations中某个方法名", 新值)

 ...

})

      }

    }

(3). 任意组件中想用state中的变量:

  a. import { mapState } from "vuex"

  b. export default {

      computed:{

   ...mapState([ "state中某变量名", ... ]),

   其它计算属性

 }

    }

  c. <template>中可用state中某变量名用于绑定和指令

  d. js中可this. state中某变量名来读取变量值

(4). 任意组件中想修改state中的变量值: 

  a. import { mapMutations } from "vuex"

  b. export default {

      methods:{

   ...mapMutations ([ "Mutations中某方法名", ... ]),

   其它方法

 }

    }

  c. js中可用this. Mutations中某方法名(实参值)修改state中变量值

(5). 任意组件中想先发ajax请求再修改state中变量值

  a. import { mapActions } from "vuex"

  b. export default {

      methods:{

   ...mapActions ([ "Actions中某方法名", ... ]),

   其它方法

 }

    }

  c. js中可用this. Actions中某方法名(实参值)发送ajax请求并修改state中变量值。

23. TypeScript

1. 变量: var或let或const 变量名:数据类型=值;

2. 函数: 
  (1). function 函数名(形参:数据类型, ...):返回值类型{
       ... ...
     }

  (2). 不确定参数个数:

  a. 单个参数不确定有没有
    function 函数名(形参1:数据类型, 形参2?:数据类型)...
    或
    function 函数名(形参1: 数据类型, 形参2:数据类型=默认值)...

  b. 多个参数不确定有没有
    function 函数名(形参1:数据类型, ...形参2:数据类型[])...
  (3). 重载:

  a. 先定义空的函数声明,列举所有重载的可能: 

    function 函数名():返回值类型;

    function 函数名(形参1: 数据类型):返回值类型;
    function 函数名(形参1: 数据类型, 形参2:数据类型):返回值类型;

  b. 正式实现功能的函数

    function 函数名(){
      根据arguments中的元素个数,决定具体执行何种逻辑
    }

3. class: 新规定:

  (1). class  类型名{ //必须提前声明属性,才能使用
        属性1: 数据类型=初始值

        属性2: 数据类型=初始值

        ...

        //构造函数必须定义数据类型

        constructor(形参1:数据类型, 形参2:数据类型,...){
this.属性1=形参1;

            this.属性2=形参2;

            ...
        }

        /*方法定义...*/
      }

  (2). 访问修饰符: 今后,只要想控制一个class中属性的使用范围时。

    class 父{
       public 公有属性:数据类型=值

       protected 受保护的属性:数据类型=值

       private 私有属性:数据类型=值

       父的函数(){
         以上三种属性都能用
       }
    }

    class 子 extends 父{
       子的函数(){
          只能用父的公有属性和父的受保护的属性

          不能用父的私有属性
       }
    }

    除父子class以外的程序区域:

    只能用父class的对象的公有属性

  (3). 接口: 保证开发人员按照要求实现类的成员

  a. 先定义接口: 

    interface  I接口名{
      属性名:数据类型

      ...

      方法名(形参:数据类型, ...):返回值类型;
    }

  b. 定义class实现接口的要求: 

    class 类型名 implements  I接口名{
      属性名:数据类型=值

      ...

      方法名(形参:数据类型, ...):返回值类型{
        方法实现
      }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值