vue笔记

一:vue介绍

1:为什么要学习vue

  • 饭碗级别技术

  • 应用非常广泛 github

  • 生态好(插件,ui库)

2:vue是什么

https://cn.vuejs.org/

  • 框架 提供了一整套东西 从一个框架切换成另一个框架不容易
  • 库 jquery 提供了一些api 从一个库切换到另一个库很方便

渐进式JavaScript 框架

作者:尤雨溪

angular 谷歌 2013 指令系统

react facebook 2014 diff算法

vue 尤雨溪

4:vue的优点

  • 渐进式框架
  • 轻量级
  • 数据双向绑定
  • 指令系统 v-xx
  • 组件化系统 --单页面应用
  • 虚拟dom

5:vue缺点

兼容 ie9+

二:vue使用和下载

  • 安装

    • 生产环境 项目开发结束,部署上线阶段
    • 开发环境 项目属于开发阶段(提示报错)
  • 引入

    开发版本

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    

    生产版本

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    
  • 使用

    • 引入vue.js
    • 实例化vue对象
    • 作用域范围

    代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
       
        <title>Document</title>
         <!-- 1:引入vue -->
        <script src="./js/vue.js"></script>
    </head>
    <body>
        <!-- 3:创建一个作用域范围 --视图层-->
        <div id="app">
    
        {{msg}}
    
        </div>
    </body>
    <script>
    //    console.log(Vue)
    //  操作数据
    //   var app=new Vue({配置信息})
    
    // 2:实例化一个vue对象
    var app=new Vue({
        el:"#app",   //vue实例的作用范围标签
        data:{   //数据层   作用域范围内使用到的数据都在这里定义
            msg:"中公it"
        }
    })
    // 操作dom元素
    // var app=document.getElementById('app');
    // var arr=[11,22,33,44];
    // var str="中公成都校区"
    // app.innerHTML=str;
    
    </script>
    </html>
    

​ 错误集锦

[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.
//不能把vue挂载到html或者body

Vue is not defined
//vue.js文件没有引入

三:插值表达式和函数

vue核心:数据驱动(数据改变,视图层跟着改变) 声明式渲染

  • 插值表达式 Mustache语法 双花括号 {{}}

    • 语法:

        {{变量/表达式/函数名()}}
        //注意:不能写if语句和for循环
      
       
- 代码
  
  ```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
    </head>
    <body>
       
        <div  id="app">
         msg
        {{msg}}
        {{msg+'0511'}}
        <hr/>
        {{num}}
        {{num+50}}
        <hr/>
        {{flag}}
        {{flag?"中公it":"中公尤久业"}}
        <hr/>
        {{arr}}
        <hr/>
        {{obj.name}}{{obj.age}}
       <hr/>
       {{fn()}}
        </div>
    </body>
    <script>
  
  var app=new Vue({
        el:"#app",    
        data:{   
            msg:"中公it",
            flag:false,
            num:25,
            arr:["aaa","bbb","ccc"],
            obj:{
                name:"张三",
                age:25
            }
        },
        methods:{  //所有用到的方法都在这里定义
           fn:function(){
              alert("123") 
           },
           fn1(){
               console.log(123)
           }
        }
    })
  
  </script>
    </html>

vue实例的配置信息

el 作用域范围标签

data 定义数据

methods 定义方法

四:vue指令

  • 什么是指令:以v-开头 v-xx,给标签添加的一些新属性【指令就是标签的属性】

  • 使用:

      <标签名 v-指令名="变量/表达式"></标签名>
    
  • 提供了哪些指令

    • v-html

      • 用来进行数据绑定

      • v-html和{{}} 的区别: 如果还没渲染 v-html不会显示任何内容,{{}}会显示{{msg}};

        v-html会解析字符串html,插值表达式不会解析;

    • v-text

      • 用来进行数据绑定
    • v-html和v-text的区别

      v-html会解析字符串html,v-text不会解析;

  • v-bind

    • 实现属性绑定

    百度
    简写
    百度

    ```
    
        
    - 代码
       
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>常用指令</title>
            <script src="./js/vue.js"></script>
        </head>
        <body>
           
            <div  id="app">
              <p>{{msg}}</p>
              <p v-html="msg"></p>
              <hr/>
              <p>{{htmlstr}}</p>
              <p v-html="htmlstr"></p>
              <hr/>
              <p v-text="msg"></p>
              <p v-text="htmlstr"></p>
              <hr/>
              <a v-bind:href="url">百度</a>
                 <img v-bind:src="imgurl"/>
                 <img :src="imgurl"/>
                 <p :title="'p标记'">这是一个p标记</p>
                 {{'p标记'}}
             </div>
          </body>
          <script>
      
          var app=new Vue({
              el:"#app",   
              data:{   
                  msg:"中公it",
                  htmlstr:"<a href='#'>百度</a>",
                  url:"http://www.baidu.com",
                imgurl:"https://upload.jianshu.io/admin_banners/web_images/4995/af694827b911ab360fe44d5252422109849c5760.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540"
              }
          })
      
        </script>
          </html>
    
    • 动态样式绑定

      • class

        • 代码

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>常用指令</title>
              <script src="./js/vue.js"></script>
              <style>
                  .col{
                      width: 100px;
                      height: 100px;
                      background-color: rebeccapurple;
                  }
                  .bg{
                      border: 2px solid red;
                  }
              </style>
          </head>
          <body>
             
              <div  id="app">
                
                 <div class="col bg"></div>
                   <!-- 1:直接写一个变量赋值 -->
               <div :class="classN"></div>
             <!-- 2:class添加一个动态对象  用true或者false去控制 -->
                 <div :class="classobj"></div>
          
                 <div :class="{'col':false}"></div>
                 <div :class="{'col':msg=='中公'}">123</div>
          
                 <!-- 3:class添加[] 用三元运算符去控制 -->
               <div :class="[1==1?'col':'col bg']">456</div>
          
                 <!-- 4:字符串拼接 -->
          
                 <div :class="'col '+classN">789</div>
              
            </div>
          </body>
          <script>
          
          var app=new Vue({
              el:"#app",   
              data:{   
                  classN:"bg",
                  classobj:{
                    'col':true,
                    'bg':true
                  },
                  msg:"中公it",
                  htmlstr:"<a href='#'>百度</a>",
                  url:"http://www.baidu.com",
                imgurl:"https://upload.jianshu.io/admin_banners/web_images/4995/af694827b911ab360fe44d5252422109849c5760.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540"
              }
          })
          
      ```
    • style

    动态绑定style的值是一个对象

    如果属性名有"-" 需要加引号,若不想加引号要改成驼峰命名法 比如 font-size 改写成fontSize

    • 代码
        
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Document</title>
              <script src="./js/vue.js"></script>
          </head>
          <body>
             <!-- 
                 动态绑定style的值是一个对象
                 如果属性名有"-" 需要加引号,若不想加引号要改成驼峰命名法  比如 font-size  改写成fontSize
            -->
              <div  id="app">
                  <div style="width:100px;height:100px;background-color:red"></div>
    
                <div :style="{width:'100px',height:'100px','background-color':'pink'}"></div>
                  <div :style="{width:size,height:'100px',backgroundColor:'pink'}"></div>
                <div :style="styleobj"></div>
    
                <div :style="[stylea,styleb]">各位帅哥美女们</div>
    
               {{msg}}
    
              </div>
    
           
          </body>
          <script>
    
          var app=new Vue({
              el:"#app",   
              data:{   
                  size:"100px",
                  msg:"123",
                  styleobj:{
                      width:'100px',
                      height:'100px',
                      'background-color':'pink'
                  },
                  stylea:{
                      border:"5px solid red",
                      fontSize:'330px'
                  },
                  styleb:{
                      color:'blue'
                }
              }
          })
    
        </script>
          </html>
    
  • v-if

    • 作用:节点的插入和删除

    • 注意:v-if和v-else-if还有v-else的标签要紧邻,否则报错

      • 使用
        <标签 v-if="变量/表达式">内容</标签>
          //值为true插入节点,值为false删除节点、
      
        <标签 v-if="变量/表达式">内容</标签>
          <标签 v-else>内容</标签>
      
          <div v-if="变量/表达式">登录  注册</div>
        <div v-else-if="变量/表达式">xxx欢迎你</div>
          <div v-else-if="变量/表达式">xxx欢迎你</div>
          <div v-else>xxx欢迎你</div>
      
      
    • v-show

      • 作用:元素的显示和隐藏(通过样式display控制显示隐藏)

      • 什么时候使用v-if 什么时候使用v-show

        频繁的切换元素 使用v-show,只有一两次切换元素使用v-if

      • 使用

        <标签 v-show="变量/表达式">内容</标签>
        
      • v-if和v-show的区别

        • v-if true就插入节点,false删除节点[没有对应的html内容]
        • v-show true就显示,false隐藏[css的display属性控制显示隐藏]
      • 代码

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
            <script src="./js/vue.js"></script>
        </head>
        <body>
            <!-- 
                <标签 v-if="变量/表达式">内容</标签>
                值为true插入节点,值为false删除节点
        
                <标签 v-if="变量/表达式">内容</标签>
                <标签 v-else>内容</标签>
        
                <div v-if="变量/表达式">登录  注册</div>
                <div v-else-if="变量/表达式">xxx欢迎你</div>
                <div v-else-if="变量/表达式">xxx欢迎你</div>
                <div v-else>xxx欢迎你</div>
             -->
           
            <div  id="app">
        
               <div v-if="flag">登录  注册</div>
               <div v-if="!flag">xxx欢迎你</div>
              
                 <div v-if="flag">登录  注册</div>
                 <div v-else>xxx欢迎你</div>
               <div v-if="score>=90&&score<=100">
                    <p>优秀</p>
                 </div>
                 <div v-else-if="score<90&&score>70">良好</div>
                 <div v-else>合格</div>
        
                 <!--注意: v-if v-else-if  v-else 所对应的标签必须紧邻,否则会报错 -->
        
                  <div>
                      <span v-show="msg=='国际'">国际</span>
                      <span v-show="msg=='国内'">国内</span>
                  </div>
              </div>
          </body>
          <script>
          var app=new Vue({
              el:"#app",   
              data:{   
                  flag:true,
                  score:89,
                  msg:"国际"
              }
          })
        
          </script>
          </html>
        
    • v-for

      • 作用:用于对数组或对象进行遍历显示

      • 使用

        <标签 v-for="值 in 数组/对象"></标签>
        <标签 v-for="(值,键) in 数组/对象"></标签>
        
      • 多层嵌套

        • 注意:内层的键值对的标识符不能和外层的冲突
      • 代码

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
            <script src="./js/vue.js"></script>
        </head>
        <body>
            <!-- 
                <标签 v-for="值 in 数组/对象"></标签>
                <标签 v-for="(值,键) in 数组/对象"></标签>   //推荐
             -->
           
            <div  id="app">
                
                <ul>
                    <li v-for="(item,index) in arr">
                      {{index+1}}----{{item}}
                    </li>
                </ul>
                <hr/>
                <ul>
                    <li v-for="(val,key) in obj">
                      {{key}}---  {{val}}
                    </li>
                </ul>
                <hr/>
                <ul>
                    <li v-for="item in userarr">
                        {{item.name}}-{{item.age}}-{{item.sex}}
                      
                            <ol>
                                <li v-for="val in item.scorearr">
                                    {{val.scorename}}-{{val.scorenum}}
                                </li>
                            </ol>
                    </li>
                </ul>
            </div>
        </body>
        <script>
        
        var app=new Vue({
            el:"#app",   
            data:{   
               arr:["aaa","bbb","ccc","ddd"],
               obj:{
                   name:"小小",
                   age:21,
                   sex:"男"
               },
               userarr:[
                   {name:"张三",age:24,sex:"男",
                   scorearr:[
                       {scorename:"china",scorenum:96},
                       {scorename:"english",scorenum:12},
                       {scorename:"math",scorenum:120}
                   ]
                },
                   {name:"李四",age:23,sex:"男",
                   scorearr:[
                       {scorename:"china",scorenum:96},
                       {scorename:"english",scorenum:12},
                       {scorename:"math",scorenum:120}
                   ]},
                   {name:"狗蛋",age:25,sex:"男",
                   scorearr:[
                       {scorename:"china",scorenum:96},
                       {scorename:"english",scorenum:12},
                       {scorename:"math",scorenum:120}
                   ]},
                   {name:"翠花",age:21,sex:"女",
                   scorearr:[
                       {scorename:"china",scorenum:96},
                       {scorename:"english",scorenum:12},
                       {scorename:"math",scorenum:120}
                   ]}
               ]
            }
        })
        </script>
        </html>
        
      • v-for的key值

        https://cn.vuejs.org/v2/guide/list.html#%E7%BB%B4%E6%8A%A4%E7%8A%B6%E6%80%81>

        ”就地复用“问题 加key值, key唯一标识

        当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略

        代码:

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
            <script src="./js/vue.js"></script>
        </head>
        <body>
           
            <div  id="app">
               
                 <div v-if="flag">
                    姓名: <input type="text" key="a1" />
                 </div>
                 <div v-else>
                   年龄: <input type="text" key="a2" />
                 </div>
        
                 <ul>
                     <li v-for="(item,index) in arr" :key="item.id">
                        <input type="checkbox" /> {{item.name}} {{index}}
                     </li>
                 </ul>
        
            </div>
        </body>
        <script>
        
        var app=new Vue({
            el:"#app",   
            data:{   
                flag:true,
                arr:[
                    {id:1,name:"香蕉"},
                    {id:2,name:"苹果"},
                    {id:3,name:"西瓜"},
                    {id:4,name:"梨"}
                ]
            }
        })
        
        </script>
        </html>
        

五:总结

  • vue的安装下载
    • 生产环境
    • 开发环境
  • vue的模板
    • 引入vue
    • 新建vue实例
      • el 挂载点 指定vue的作用域范围 body
      • data 定义数据
      • methods 定义方法
    • 定义作用域范围
  • vue的插值表达式 mustache语法 {{变量/表达式}} 详细点
  • vue指令
    • v-html
    • v-text
    • v-bind
      • 动态绑定class 4种写法
      • 动态绑定style
    • v-if
    • v-show
    • v-if和v-show区别
    • v-for v-for的key值

day 02

一:常用的指令

v-on 绑定事件

  • 基础的事件绑定

    • 使用

      <标签 v-on:click="函数()"></标签>
      简写
      <标签 @click="函数()"></标签>
      
    • 代码

  • 事件传参

    • 使用

      <标签 v-on:click="函数(实参)"></标签>
      
      new Vue({
        el:"#app",
        data:{},
        methods:{
          事件函数(形参){
          }
        }
      })
      
  • this的使用

    this指向的是vue实例

  • 事件对象

    • 使用

      new Vue({
        el:"#app",
        data:{},
        methods:{
          事件函数(形参){  //没有传入实参,那么这个形参就是事件对象
          }
        }
      })
      
      <标签 @事件名="事件函数"></标签>    //不需要传入实参
      
  • 传实参还要有事件对象

    • 使用

      new Vue({
        el:"#app",
        data:{},
        methods:{
          事件函数(形参,事件对象形参){  
          }
        }
      })
      
      <标签 @事件名="事件函数(实参,$event)"></标签>    //形参和实参位置要一一对应
      
    • 代码

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Document</title>
          <script src="./js/vue.js"></script>
          <style>
              .box{
                  width: 200px;
                  height: 200px;
                  background-color: pink;
              }
          </style>
      </head>
      <body>
         
          <div  id="app">
              <button v-on:click="fn()">点我</button>
              <button v-on:click="fn">点我</button>
              <button @click="fn()">点我</button>
      
              <ul>
                  <li v-for="(item,index) in arr" :key="index">
                      {{item}}  <button @click="del(index)"> 删除</button>
                  </li>
              </ul>
      
              <div class="box" @mousemove="move"></div>
      
              <a href="http://www.baidu.com" @click="run('成都',$event)">百度</a>
          </div>
      </body>
      <script>
      var app=new Vue({
          el:"#app",   
          data:{   
              msg:"中公it",
              arr:["香蕉","葡萄","苹果"]
          },
          methods:{
              fn(){
                  alert("好好")
              },
              del(ind){  //删除
              //    alert(ind)
              // this指向的是vue实例
                 console.log(this.msg)
              },
              move(event){   //移动事件
                  // 如果函数没有传入实参,methods里面的事件函数定义的形参就是事件对象
              console.log(event.offsetX)
              },
              run(city,event){   //阻止默认行为
               event.preventDefault();
               alert(1111,city)
              }
          }
      })
      
      // var box=document.querySelector(".box");
      // box.οnmοusemοve=function(event){
      //   console.log(event.offsetX)
      // }
      
      // var a=document.querySelector("a")
      // a.οnclick=function(event){
      //     // 阻止元素的默认行为
      //     event.preventDefault()
      //     alert(1111);
      // }
      
      </script>
      </html>
      

v-model

  • 作用:表单元素数据绑定

架构思想:MVVM

数据双向绑定:数据层改变,视图层跟着改变; 视图层改变,数据层跟着改变;

修饰符

  • 什么是修饰符: 修饰我们事件和v-model的符号,简化我们的一些操作

  • 使用

    <标签   @事件名.修饰符="事件函数"></标签名>
    
    <input v-model.修饰符="变量"/>
    
  • 事件修饰符

    https://cn.vuejs.org/v2/guide/events.html

    • .stop 阻止事件冒泡

    • .prevent 阻止默认行为

    • .self 只当在 event.target 是当前元素自身时触发处理函数

    • .once 一次性事件绑定

    • 键盘修饰符 (键盘事件) .enter .up .down .left .right

    • 代码

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Document</title>
          <script src="./js/vue.js"></script>
          <style>
              .box{
                  width: 300px;
                  height: 300px;
                  background-color: pink;
              }
              .small{
                  width: 150px;
                  height: 150px;
                  background-color: red;
              }
          </style>
      </head>
      <body>
         
          <div  id="app">
      
              <input type="text" @keydown.enter="down"/>
      
               <!-- 阻止默认行为 -->
             <a href="http://www.baidu.com" @click.prevent="fn">百度</a>
                <!-- 一次性绑定 -->
             <button @click.once="pay">确认支付</button>
      
             <div class="box" @click="fn1">
                 <!-- 阻止冒泡 -->
                 <div class="small" @click.stop="fn2"></div>
                 
             </div>
      
               <!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
             <div class="box" @click.self="fn1">
              <!-- 阻止冒泡 -->
              <div class="small" @click="fn2"></div>
             </div>
      
          <div class="box" @click="fn1">
              <!-- 修饰符可以串联 -->
              <a href="http://www.baidu.com" @click.stop.prevent="fn">百度</a>
              
          </div>
        </div>
        </body>
        <script>
      
        var app=new Vue({
            el:"#app",   
            data:{   
                
            },
            methods:{
                fn(){
                    alert(1111);
                },
                pay(){
                    alert("支付完成")
                },
                fn1(){
                    alert("大盒子box")
                },
                fn2(){
                    alert("小盒子small")
                },
                down(){
                    console.log("enter");
                    
                }
            }
        })
         </script>
        </html>
      

表单修饰符

​ .lazy 不会实时数据绑定,在changge事件中进行数据绑定

​ .number 将输入的内容数据类型转换为数值

​ .trim 过滤首尾空白字符

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
    </head>
    <body>
       
        <div  id="app">
    
            <input type="text"  v-model.lazy="username"  />
        {{username}}
    
           年龄:<input type="text" v-model.number="age"/>
    
           <input type="text" v-model.trim="str"/>
        </div>
    </body>
    <script>
    
    var app=new Vue({
        el:"#app",   
        data:{   
           username:"",
           age:0,
           str:""
        }
    })
    
    </script>
    </html>
    

二:面试题

1:说说你对this的理解,如何改变this指向

this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象。
一、全局环境:
全局环境就是在里面,这里的this始终指向的是window对象。
二、局部环境:
1)在全局作用域下直接调用函数,this指向window。
2)对象函数调用,哪个对象调用就指向哪个对象。
3)使用 new 实例化对象,在构造函数中的this指向实例化对象。
4)使用call或apply改变this的指向。

day 03

一:生命周期

​ 生命周期:一个vue实例生命的周期,也就是vue实例从开始到结束的过程,从创建到销毁的过程;

生命周期阶段

  • 创建阶段

    • beforeCreate 创建之前
      • 作用:一般不使用,做一些需要在创建之前做的事情
      • 注意:这里不能操作data里面的数据,因为data没有被初始化
    • created 创建之后
      • 作用:一般在这里发送数据请求,开启定时器
      • 注意:这里是最早操作data里面的数据的地方
  • 挂载阶段

    • beforeMount 挂载之前

      ​ 注意:在这个函数里,页面没有真正渲染,只是有了虚拟dom(js对象)

    • mounted 挂载之后

      • 作用: 把虚拟dom真正的渲染到页面上,页面上有了真实dom

      • 从这里开始可以使用$refs获取dom节点

      • 初始化一些插件相关的配置,也可以发送数据请求

  • 更新阶段

    • beforeUpdate 更新之前
      • 作用:做一些判断 是否需要更新
    • updated 更新之后
  • 销毁阶段

    • beforeDestroy 销毁之前
      • 作用 销毁之前做一些保存
    • destroyed
      • 作用 释放定时器相关的内容, 销毁之后,事件绑定都不存在了

代码

  <!DOCTYPE html>
  <html lang="en">
  <head>
   <meta charset="UTF-8">
   <title>Document</title>
   <script src="./js/vue.js"></script>
  </head>
  <body>
  
   <div  id="app">

   {{msg}}
   <button  @click="fn">点击</button>

   </div>
  </body>
  <script>
      var app=new Vue({
    el:"#app",   
    data:{   
        msg:"中公it"
    },
    methods:{
     fn(){
         console.log(123)
     }
    },
    //创建阶段
    beforeCreate(){
        //这里不能操作data里面的数据,因为data没有被初始化
        console.log(this.msg);
        console.log("创建之前");
        console.log(document.querySelector("#app"));
        },
created(){
    // 一般在这里发送数据请求,开启定时器
    console.log(this.msg);
    console.log("创建之后");
    console.log(document.querySelector("#app"));
},
//挂载阶段
beforeMount(){
    console.log("挂载之前");
    console.log(document.querySelector("#app"));
},
mounted(){
    console.log("挂载之后");
    console.log(document.querySelector("#app"));
},
//更新阶段
beforeUpdate(){
    // 做一些判断  是否需要更新
    console.log("更新之前");
    console.log(document.querySelector("#app"));
},
updated(){
    
    console.log("更新之后");
    console.log(document.querySelector("#app"));
},
//销毁阶段
beforeDestroy(){
    // 销毁之前做一些保存
    console.log("销毁前");
},
destroyed(){
    // 释放定时器相关的内容
    // 销毁之后,事件绑定都不存在了

    console.log("销毁后");
   
}
})

  setTimeout(()=>{
      app.$destroy();   //销毁vue实例
  },10000)

  </script>
  </html>

二:监听器 watch

  • 作用 :来监听data里面数据的变化,一旦数据变化了就会自动执行相关的函数

  • 分类:

    • 浅监听:监听基本数据类型
    • 深监听:监听引用数据类型
  • 使用

    var app=new Vue({
       el:"#app",   
       data:{   
          a:2,
          b:0
       },
      watch:{   //监听器
       data中的变量(新值,旧值){   //浅监听
            //变量的值发生变化,就会执行这里的代码
       },
       data中变量:{
           deep:true,   //深监听 【监听引用类型】
           handler(新值){
              //代码块
           }
       }
     }
    })
    
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
    </head>
    <body>
       
        <div  id="app">
             <input type="text" v-model.number="a"/>
             +
             <input type="text" v-model.number="b"/>
             =
             <input type="text" v-model="c"/>
        
             <hr/>
           <p>姓名:{{user.name}}</p>
           <p>年龄:{{user.age}}    <button @click="ageadd">年龄增长</button></p>
             </div>
      </body>
    
      <script>
    
      var app=new Vue({
    
      el:"#app",   
      data:{   
          a:0,
          b:0,
          c:0,
          user:{
              name:"小小",
              age:25
          }
      },
      methods:{
          ageadd(){
              this.user.age++;
          }
      },
      watch:{
          a(newv,oldv){
              // console.log(123)
              // console.log(newv)   //变化之后的值
              // console.log(oldv)   //变化之前的值
              this.c=newv+this.b
          },
          b(newv,oldv){
              this.c=newv+this.a
          },
          user:{   //深度监听
              deep:true,
              handler(newv){
                   console.log(1);
                   console.log(newv)
              }
          }
      }
       })
    
      </script>
    
      </html>
    

三:computed计算属性

  • 含义:计算 动词 属性 名词

    • 计算属性本质是属性,这个属性需要计算(函数)得到
    • 使用场景 :购物车
  • 使用

    var app=new Vue({
         el:"#app",   
         data:{  },  //定义默认数据的地方
         computed:{  //通过计算得到属性
            属性名:function(){   //值一定是一个函数,而且这个函数一定要有返回值
                return;
            }
         }
       }
     })
    
    {{属性名}}
    
  • 注意:

    • 计算属性的值一定是一个函数,而且这个函数一定要有返回值
    • 计算属性的函数里面的this同样是当前vue实例
    • 页面第一次加载时,计算属性的函数会执行,给属性赋初始值
    • 依赖属性变化的时候,计算属性的函数会再次执行
    • 计算属性可以直接在模板里渲染,说明他也是当前vue实例对象的属性
  • 面试题:watch和computed的区别?

    watch: 监听data和computed中数据的变化;可以进行异步操作;

    computed: 定义属性(该属性通过计算得到);不能进行一步操作;通过return 返回处理的数据;

  • computed的get和set

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
    </head>
    <body>
       
        <div  id="app">
             <input type="text" v-model.number="a"/>
             +
             <input type="text" v-model.number="b"/>
             =
             <input type="text" v-model="c"/>
        {{c}}
    
        </div>
    </body>
    <script>
    
    var app=new Vue({
        el:"#app",   
        data:{   
            a:0,
            b:0
            // ,c:0
        },
        computed:{
            // c(){
            //     // 页面第一次加载时,该函数会(第一次)默认执行,给属性赋初始值
            //     //依赖属性变化的时候,该函数会再次执行
            //     console.log("computed")
            //     return this.a+this.b  //this同样是vue实例
            // }
            c:{
                get(){   //c被获取时执行这里(直接读取c的值不会执行)
                   console.log("get");
                   return this.a+this.b;
                },
                set(v){  //c被设置时执行这里  v就是设置的值
                    console.log("set",v);
                }
            }
        }
       
    })
    </script>
    </html>
    

错误集锦

1:Method “allcheck” has already been defined as a data property.

data中的变量名和methods中的方法名冲突了

day 04

组件

  • 组件化开发优势:复用性强 ,便于后期维护和开发
  • 什么是组件:包含了html,css,js的一个整体(js对象),是一个独立的功能
  • 单页面应用(SPA)
  • VUE的核心:数据驱动,组件化

一:组件使用

1-1:组件的注册
1:全局注册
  • 语法

      Vue.component('组件名',{组件配置对象})
    
  • 使用(调用)组件

    <组件名></组件名>
    
  • 注意:

    • 全局注册组件需要放在vue实例化之前,所有的vue实例都可以使用
    • template指定了组件的模板信息
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
        <style>
            ul{
                padding: 0;
                margin: 0;
                list-style: none;
                width: 500px;
                display: flex;
                height: 50px;
                border: 1px solid;
               
            }
            li{
                width: 99px;
                height: 50px;
                border-right: 1px solid;
                line-height: 50px;
                text-align: center;
            }
            .active{
                background-color: yellow;
                color:red;
            }
        </style>
    </head>
    <body>
       
        <div  id="app">
           <mycom></mycom>
           <hr/>
           <mycom></mycom>
        </div>
    
        <h3>---------分割线-------</h3>
        <div id="main">
            <mycom></mycom>
        </div>
    </body>
    <script>
    Vue.component("mycom",{
        // template指定组件的模板
       template: "<div><a href='#'>全局组件</a></div>"
    })
    
    // 全局注册组件需要放在vue实例化之前,所有的vue实例都可以使用
    
    var app=new Vue({
        el:"#app",   
        data:{   
           
        }
    })
      var main=new Vue({
          el:"#main",
          data:{}
      })
      </script>
      </html>
    
2:局部注册
  • 语法

    var app=new Vue({
        el:"#app",   
        data:{    
        },
        components:{
            组件名:{配置对象},
            组件名:{配置对象}
        }
    })
    
  • 使用

    <组件名></组件名>
    
  • 注意

    • 局部组件只能在当前所在的实例去使用
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
        <style>
            ul{
                padding: 0;
                margin: 0;
                list-style: none;
                width: 500px;
                display: flex;
                height: 50px;
                border: 1px solid;
               
            }
            li{
                width: 99px;
                height: 50px;
                border-right: 1px solid;
                line-height: 50px;
                text-align: center;
            }
            .active{
                background-color: yellow;
                color:red;
            }
        </style>
    </head>
    <body>
       
        <div  id="app">
            <mycon></mycon>
        </div>
    
        <h3>---------分割线-------</h3>
        <div id="main">
            <!-- <mycon></mycon> -->
        </div>
    </body>
    <script>
        var app=new Vue({
          el:"#app",   
          data:{    
          },
          components:{
              // 组件名:{配置对象}
              // 局部组件只能在当前所在的实例去使用
              mycon:{
                 template:`
                    <div>
                       <h3>我是局部组件</h3>
                    </div>
                 ` 
              }
          }
      })
       var main=new Vue({
          el:"#main",
          data:{}
      })
      </script>
      </html>
    
1-2:组件命名:
  • 1:不能是html标签名(html新增的语义标签:header,nav,main,aside,section,slider,article,footer…)
  • 2:命名尽量语义化
  • 3:组件名不能使用驼峰命名了,使用组件的时候需要用- 链接 比如组件名为myHead 使用
  • 4:单标签只可以使用一次,后面的将不再显示
1-3:template配置项
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
        <style>
            ul{
                padding: 0;
                margin: 0;
                list-style: none;
                width: 500px;
                display: flex;
                height: 50px;
                border: 1px solid;
               
            }
            li{
                width: 99px;
                height: 50px;
                border-right: 1px solid;
                line-height: 50px;
                text-align: center;
            }
            .active{
                background-color: yellow;
                color:red;
            }
        </style>
    </head>
    <body>
       
        <div  id="app">
            <mycon></mycon>
            <mybb></mybb>
        </div>
        <template id="aa">
            <div>
                <div>
                 <h3>我是局部组件</h3>
                </div>
                <div>789</div>
            </div>
        </template>
    
        <template id="bb">
           <div>123</div>
        </template>
     
    </body>
    <script>
       var app=new Vue({
          el:"#app",   
          data:{    
          },
          components:{
              // 组件名:{配置对象}
              // 局部组件只能在当前所在的实例去使用
              mycon:{
                 template:"#aa"
              },
              mybb:{
                  template:"#bb"
              }
          }
      })
      </script>
      </html>
    
错误集锦

1:Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

组件模板有且只有一个根元素

2:Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the “name” option.

不知道元素 你确定注册了这个组件了吗?

1-4:组件嵌套
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
        <style>
            ul{
                padding: 0;
                margin: 0;
                list-style: none;
                width: 500px;
                display: flex;
                height: 50px;
                border: 1px solid;
               
            }
            li{
                width: 99px;
                height: 50px;
                border-right: 1px solid;
                line-height: 50px;
                text-align: center;
            }
            .active{
                background-color: yellow;
                color:red;
            }
        </style>
    </head>
    <body>
        <div  id="app">
           <mycon></mycon>
           <hr/>
           <mybb></mybb>
        </
    </body>
    <script>
    Vue.component("mycd",{
        template:"<a href='#'>00000</a>"
    })
    
    var app=new Vue({
        el:"#app",   
        data:{    
        },
        components:{
            // 组件名:{配置对象}
            // 局部组件只能在当前所在的实例去使用
            mycon:{
               template:`
                  <div>
                     mycon
                     <hr/>
                     <mycd></mycd>
                      <aa></aa>
                  </div>
               `,
               components:{
                   aa:{
                       template:`
                          <div>aaa</div>
                       `
                   }
               }
            },
            mybb:{
                template:`
                   <div>
                      mybb
                   </div>
                `
            }
        }
    })
    </script>
    </html>
    
1-5:组件的配置选项(重点)
  • The “data” option should be a function that returns a per-instance value in component definitions.

    data选项应该是一个函数

  • 组件中的data是一个函数,返回一个对象

    • 组件的data不能是一个对象,因为一旦是一个对象,所有组件都会共享一个数据源,造成了数据的污染
    • 组件data必须是一个函数,函数返回一个对象,每次返回都会重新创建一个新对象
    • 组件的数据应该是独立的
  • template是指定模板的

  • 其他配置选项和vue实例一模一样的

  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="./js/vue.js"></script>
        <style>
            ul{
                padding: 0;
                margin: 0;
                list-style: none;
                width: 500px;
                display: flex;
                height: 50px;
                border: 1px solid;
               
            }
            li{
                width: 99px;
                height: 50px;
                border-right: 1px solid;
                line-height: 50px;
                text-align: center;
            }
            .active{
                background-color: yellow;
                color:red;
            }
        </style>
    </head>
    <body>
       
        <div  id="app">
           <mycon></mycon>
           <hr/>
           <mybb></mybb>
        </div>
    </body>
    <script>
    Vue.component("mycd",{
        template:`
           <div>
              mycd全局组件
              {{msg}}
              <button @click="fn">更改msg</button>
           </div>
        `,
        data:function(){
            return {
                msg:"成都"
            }
        },
        methods:{
            fn(){
                this.msg="nihao"
            }
        }
    })
    var app=new Vue({
        el:"#app",   
        data:{    
        },
        components:{
            // 组件名:{配置对象}
            // 局部组件只能在当前所在的实例去使用
            mycon:{
               template:`
                  <div>
                     mycon
                     <hr/>
                     <mycd></mycd>
                      <aa></aa>
                  </div>
               `,
               components:{
                   aa:{
                       template:`
                          <div>aaa</div>
                       `
                   }
               }
            },
            mybb:{
                template:`
                   <div>
                      mybb
                   </div>
                `
            }
        }
    })
    
    </script>
    </html>
    

二:vue-cli vue脚手架开发

  • vue-cli脚手架工具:快速搭建一个vue项目

2.1 、 安装vue-cli

​ 确保电脑安装了node环境,输入以下命令检测是否安装了node

node -v     //显示版本号说明安装了node    如果提示node不是内部命令,那就就去安装node

node的安装地址:https://nodejs.org/en/download/

npm install vue-cli -g    //全局安装脚手架工具

如果电脑上安装了cnpm淘宝镜像,可以使用cnpm 安装

cnpm install vue-cli -g

如果没有淘宝镜像先安装淘宝镜像

npm install cnpm -g --registry=http://registry.npm.taobao.org

z执行完了 执行 cnpm/npm install vue-cli -g 该命令 安装脚手架工具

vue -V    //显示版本号说明安装成功
2.9.6

2.2 、初始化vue项目

  • 找到需要存储项目的目录

  • 在该目录下执行命令

    vue init webpack 项目名(英文)
    
  • 启动项目

    npm run dev
    or
    npm start
    

day 05

一:回顾

1-1、组件

  • 组件的含义:包含了html,css,js的独立的js对象(vue实例)
  • vue是单页面应用
  • 组件化开发优势:复用性强,便于后期维护和开发

1-2、组件定义

  • 局部注册

  • 全局注册

    Vue.component("组件名",{
       template:`<div>有且只能有一个根元素</div>`,
       data(){
         return {
      
        }
      }
    })
    

1-3、组件命名规范

1-4、组件嵌套

1-5、组件的配置项

1-6、vue-cli脚手架

错误

​ 命令行出现:

​ canot found module ‘user-home’

​ 找不到模块

发给别人一个vue项目

  • 删除项目目录下的node_modules包

  • 压缩项目文件夹,将文件夹发给别人即可

如何运行别人发给你的vue项目

  • 把别人发给你的项目文件,解压
  • 进入解压后的文件夹里 cmd
  • 运行 cnpm i 或者npm i 下载项目依赖包
  • 下载完成后,执行npm run dev

二:脚手架搭建的项目中组件使用

  • 定义组件

    • 在components目录下新建xxx.vue文件

    • 在xxx.vue文件下写入代码

      <template>
          <div>
              这个是myhead组件
          </div>
      </template>
      <script>
      export default {
          
      }
      </script>
      <style>
      
      </style>
      
  • 使用组件

    • 引入,注入,调用

      // 组件模板
      <template>
        <div>
           我的第一个vue项目
           <!-- 3:调用 -->
           <Myhead></Myhead>
        </div>
      </template>
      // js
      <script>
      // 1:引入
      import Myhead  from './components/Myhead'
      export default {
          components:{  //2:注入
            Myhead
          }
      }
      </script>
      

三:组件数据通信

  • 组件之间数据的相互使用

3-1、父向子通信

  • 1:父组件调用子组件,给子组件自定义属性

    //父组件
    <子组件  属性1="值" 属性2="值"></子组件>
    <子组件  :属性="变量"></子组件>
    
    
  • 2:子组件通过props接收父组件传来的自定义属性

    <script>
       export default {
         props:['属性1''属性2']
       }
    </script>
    
  • 3:子组件视图上直接使用该自定义属性

    {{属性1}}
    <li v-for="item in 属性1"></li>
    
  • 代码

    父组件

     // 组件模板
      <template>
        <div>
         
           <!-- 1  自定义属性 -->
           <Myhead :mycon="num" :mycom="flag" :dataarr="arr[0]"></Myhead>
    
           <Myhead :dataarr="arr[1]"></Myhead>
           <Myhead :dataarr="arr[2]"></Myhead>
       <hr/>
          <Myhead v-for="(item,index) in arr" :key="index" :dataarr="item"></Myhead>
       
    
        </div>
      </template>
      // js
      <script>
    
      import Myhead  from './components/Myhead'
      export default {
          data(){
              return {
                flag:true,
                num:25,
                arr:[
                  {
                    title:"学院动态",
                    list:[
                      '中国上市企业市值500强出炉 中公教育位居',
                      '中国上市企业市值500强出炉 中公教育位居',
                      '中国上市企业市值500强出炉 中公教育位居'
                    ]
                  },
                  {
                    title:"学员活动",
                    list:[
                      '中国上市企业市值500强出炉 中公教育位居1',
                      '中国上市企业市值500强出炉 中公教育位居1',
                      '中国上市企业市值500强出炉 中公教育位居1'
                    ]
                  },
                  {
                    title:"常见问题",
                    list:[
                      '中国上市企业市值500强出炉 中公教育位居2',
                      '中国上市企业市值500强出炉 中公教育位居3',
                      '中国上市企业市值500强出炉 中公教育位居4'
                    ]
                  }
                ]
              }
          },
          components:{  
            Myhead
          }
      }
      </script>
      // css
      <style>
    
      </style>
    

    子组件

    <template>   
     <div>
           <div class="list">
               <!-- 3:直接使用属性 -->
               {{mycon}} ----{{mycom}}
               
               <h3>{{dataarr.title}}</h3>
               <ul>
                   <li v-for="(item,index) in dataarr.list" :key="index">
                       {{item}}
                   </li>
               </ul>
           </div>
        </div>
    </template>
    <script>
    
    export default {
        props:['mycon','mycom','dataarr'],  //2:通过props接收父组件传来的数据
        data(){
            return {
               arr:[
                   "中国上市企业市值500强出炉 中公教育位居",
                   "中国上市企业市值500强出炉 中公教育位居",
                   "中国上市企业市值500强出炉 中公教育位居"
               ]
            }
        }
    
    }
    </script>
    <style>
    </style>
    
    
props验证
  • 为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验

  • 如何实现校验

    //方式1
    props:['属性1','属性2']
    
    //方式2   (String,Number,Boolean,Array,Object Date Function Symbol)
    props:{
       属性1:类型 ,    //只有类型检测
       属性2:[类型1,类型2] 
    }
    
    //方式3  
    props:{
       属性1:{
          type:类型 ,
          default:默认值,
          required:true/false,
          validator:function(val){
            //validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告
          }
       }
       
    }
    

    代码

    <template>
        <div>
           <div class="list">
               <!-- 3:直接使用属性 -->
               {{mycon}} ----{{mycom}}
               
               <h3>{{dataarr.title}}</h3>
               <ul>
                   <li v-for="(item,index) in dataarr.list" :key="index">
                       {{item}}
                   </li>
               </ul>
           </div>
        </div>
    </template>
    <script>
    
    export default {
        // props:['mycon','mycom','dataarr'],  //2:通过props接收父组件传来的数据
        // props验证,为了使组件内部使用数据的可靠性,我们对外部传递进来的数据进行校验
        props:{
            // mycon:String , //类型验证
            mycon:{
                type:String,
                default:"hellworld"   //默认值
            },
            // mycom:[String,Number],
            mycom:{
               type:String,
               required:false,    //是否必须传入   true必须  false可以不传入
               validator:function(val){   // 验证函数
                //    validator函数一定要有返回值,返回true表示验证通过,false验证不通过 控制台有警告
                 if(val.length<5){
                     return true;
                 }else{
                     return false;
                 }
    
               }
            },
            dataarr:{
                type:Object   //类型校验
            }
        },
        data(){
            return {
               arr:[
                   "中国上市企业市值500强出炉 中公教育位居",
                   "中国上市企业市值500强出炉 中公教育位居",
                   "中国上市企业市值500强出炉 中公教育位居"
               ]
            }
        }
    
    }
    </script>
    <style>
    
    </style>
    

    注意:vue是单向数据流,子组件不能修改props中的属性,但是在父组件中修改,传到子组件的数据也会跟着变化

    代码

    父组件

    <template>
      <div>
        {{num}}  <button @click="fn">修改num</button>
        <hr/>
        <Myfoot  :n="num"></Myfoot>
      </div>
    </template>
    
    <script>
    import Myfoot from './components/Myfoot'
    export default {
        data(){
            return {
               num:25
            }
        },
        components:{  
          Myfoot
        },
        methods:{
            fn(){
                console.log(this.num)
                this.num++
            }
        }
    }
    </script>
    // css
    <style>
    
    </style>
    
    

    子组件

    <template>
        <div>
           {{n}}--{{m}}
           
        </div>
    </template>
    <script>
    export default {
        props:['n'],
        data(){
            return {
               m:6
            }
        }
    }
    </script>
    

3-2、子向父通信

  • 1:父组件调用子组件时,给子组件自定义事件

    //父组件
    <Child @自定义事件名="函数"></Child>
    
  • 2:在父组件中的methods中定义自定义事件对应的函数

    //父组件
    methods:{
      函数名(){} 
    }
    
  • 3:在子组件中通过this.$emit(‘事件名’,参数)触发自定义事件

    //子组件
    this.$emit('事件名',参数)
    
  • 代码

    父组件

    <template>
      <div>
        {{m}}
          <hr/>
          <!-- 1:自定义事件  @abc="函数名" -->
         <Child @abc="fn"></Child>
      </div>
    </template>
    
    <script>
    import Child from './components/Child'
    export default {
       data(){
         return {
           m:""
         }
       },
       components:{
         Child
       },
       methods:{
         fn(val){   //2:父组件中定义自定义事件对应的函数fn
           console.log("wo 被触发了")
          //  val就是触发abc事件传递过来的参数
          this.m=val;
         }
       }
    }
    </script>
    

    子组件

    
    
    <template>
        <div>
            {{msg}}
            <button @click="change">触发abc</button>
    
        </div>
    </template>
    <script>
    export default {
        data(){
            return {
                msg:"子组件==成都"
            }
        },
        created(){
            // 3:触发自定义事件的
            // this.$emit('事件名',参数)
            // console.log(this)
            // this.$emit('abc',this.msg)
        },
        methods:{
            change(){
                 this.$emit('abc',this.msg)
            }
        }
    }
    </script>
    
    

3-3、非父子通信

中央事件总线:eventbus

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6D9S7l4D-1611110597372)(F:\Web\vue\day05\参考\day05\非父子组件传值.png)]

需求:非父子组件A和B,从A组件向B组件传值

  • 1:main.js中定义一个公共区域 (新的vue实例)

    // 1:创建公共区域(新的vue实例)
    var eventbus=new Vue();
    // 2:公共区域挂载到Vue的原型上面(每一个vue实例都可以访问它原型上的成员)
    Vue.prototype.$bus=eventbus;
    
  • 2:在B组件中给公共区域自定义事件

    <template>
        <div>
            {{m}}
        </div>
    </template>
    <script>
    export default {
        data(){
            return {
               m:"123"
            }
        },
        created(){
            // // console.log(this,111)
            // var that=this;
            // // 公共区域自定义事件--接收值
            // this.$bus.$on('abc',function(v){
            //     console.log("我被触发了",v)
            //     console.log(this)
            //    that.m=v
            // })
            //--------------
            // console.log(this,111)
            
            // 公共区域自定义事件--接收值
            this.$bus.$on('abc',(v)=>{
                console.log("我被触发了",v)
                console.log(this)
               this.m=v
            })
        }
    }
    </script>
    
  • 3:在A组件中触发公共区域的自定义事件

    <template>
        <div>
            <button @click="fn">传值</button>
        </div>
    </template>
    <script>
    export default {
        data(){
            return {
               msga:"a向b传值" 
            }
        },
        methods:{
            fn(){
                // 触发公共区域的自定义事件传值
                
                this.$bus.$emit('abc',this.msga)
            } 
        }
    }
    </script>
    

day 06

一:回顾

父向子

子向父

非父子

二:ref

ref 被用来给元素或子组件注册引用信息

2-1、ref 注册在普通dom元素上

​ 获取到的是dom元素

2-2、ref加载子组件上

可以获取或使用组件的所有数据和方法

2-3、ref加载循环渲染

利用v-for和ref获取一组数组

注意:

1:ref需要在dom渲染完成后才会有,使用的时候确保dom已经渲染完成,比如

mounted钩子函数中可以使用

2:ref如果是循环出来的,ref的值是一个数组,要拿到单个的ref只需要循环就可以了

代码

<template>
  <div>
    <!--1 ref加在普通的dom元素上  -->
      <h2 ref="myh2">中公成都校区在施工</h2>
      <input type="text" ref="myinput" />
      <button @click="fn">获取h2</button>

      <!--2 ref应用在循环渲染 -->
      <ul>
        <li ref="list" v-for="(item,index) in arr" :key="index">
          {{item}}
        </li>
      </ul>
      <button @click="fn1">获取li元素</button>

      <!-- 3ref设置在子组件上 -->
      <Myref ref="myref"></Myref>
       <button @click="fn2">获取子组件</button>
  </div>
</template>

<script>
import Myref from './components/Myref'
export default {
  data(){
    return {
      arr:['aaa','bbb','cccc']
    }
  },
  methods:{
    fn(){
      // 所有的ref的注册信息都会注册到组件的$refs对象上
      console.log(this.$refs)
      console.log(this.$refs.myh2.innerHTML)
    },
    fn1(){
      // ref注册到循环渲染的元素上,得到的是一个数组[li,li,li]
      console.log(this.$refs.list[0].innerHTML)
    },
    fn2(){
      console.log(this.$refs.myref.msg)
      this.$refs.myref.msg="hello  晋老师"
    }
  },
  components:{
    Myref
  }
}
</script>


三:vue中使用jquery

3-1:外部链接

​ 在index.html中,通过script标签直接引入外部链接

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>myvue</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

3-2、npm

安装

cnpm install  jquery -D
  • 1:局部使用(那个组件用,那个组件引用)
import $ from 'jquery'
  • 2:全局使用(所有的组件都可以用)
//main.js
import $ from 'jquery'
Vue.prototype.$=$
//在每一个组件中使用
this.$("#box").hide()

四:插槽(Slot)

插槽用于将所携带的内容,插入到指定的某个位置

父组件向子组件传html标签

4-1、匿名插槽

  • 使用

    • 子组件

      <template>
        <h2>成都中公</h2>
        <slot></slot>
         <div>我是成都学员-程序员</div>
      </template>
      
    • 父组件

      //msg数据在父组件中定义
      <Mychild>
         <p>{{msg}}</p>   
      </Mychild>
      
  • 代码

    父组件

    <template>
      <div>
      
         <Mychild>
           <ul>
             <li v-for="(item,index) in arr" :key="index">{{item}}</li>
           </ul>
         </Mychild>
    
         <hr/>
         <Mychild>
            <p>{{msg}}</p>
         </Mychild>
          
      </div>
    </template>
    
    <script>
    import Mychild from './components/Mychild'
    export default {
      data(){
        return {
            msg:"我是p标签123",
            arr:['aaa','bbb','ccc']
        }
      },
      methods:{
      
        
      },
      components:{
        Mychild
      }
    }
    </script>
    

    子组件

     <h2>成都中公</h2>
     <slot></slot>
     
     <div>我是成都学员-程序员</div>

  </div>

}


# 4-2、具名插槽

- 代码

- 父组件

  ```html
  <template>
    <div>
       <Mychild>
         <div slot="foot">
           我是cen要的内容
         </div>
         <div slot="cen">
           我是子组件的内容
         </div>
       </Mychild>
    </div>
  </template>
  
  <script>
  import Mychild from './components/Mychild'
  export default {
    data(){
      return {
          msg:"我是p标签123",
          arr:['aaa','bbb','ccc']
      }
    },
    methods:{
    
      
    },
    components:{
      Mychild
    }
  }
  </script>
  ```

- 子组件

  ```html
  <template>
      <div>
          <!-- slot可以有默认值,如果找不到就显示默认值 -->
         <h2>成都中公</h2>
         <slot name="cen"></slot>
         <div>我是成都学员-程序员</div>
         <slot name="foot">
             默认值
         </slot>
      </div>
  </template>
  <script>
  export default {
      
  }
  </script>
  
  ```

# 4-3、作用域插槽(难)

>  子组件做循环(循环的结构从父组件传来)或者某一部分他的dom结构应该由外部传递进来的时候,使用作用域插槽
>
>  使用作用域插槽,子组件会向父组件的作用域里传递数据,父组件使用插槽,要包含在template中 

- 语法

v-bind:自定义prop名=“数据” //子组件中传值

<template v-slot:插槽名字=“props”>
{{props.prop名}}


- 代码

父组件

```html
<template>
  <div>
     <Mychild>
        <!-- <template v-slot:list="props">
         <li>{{props.item}}</li>
       </template>  -->
       <template v-slot:one="props">
          <div>{{props.msg}}---{{props.tit}}</div>
       </template>
     </Mychild>
  </div>
</template>

<script>
import Mychild from './components/Mychild'
export default {
  data(){
    return {
        msg:"我是p标签123",
        arr:['aaa','bbb','ccc']
    }
  },
  methods:{
  
    
  },
  components:{
    Mychild
  }
}
</script>

子组件


<template>
    <div>
        <!-- <ul>
           <slot name="list" v-for="item in arr"  :item="item"></slot>
       </ul>  -->

      <slot name="one" :msg="msg" :tit="name"></slot>
      <slot name="two"></slot>
    </div>
</template>
<script>
export default {
    data(){
        return {
            arr:[
                '香蕉','苹果','西瓜'
            ],
            msg:"nihao",
            name:"张三"
        }
    }
}
</script>


五:is属性

5-1、改变默认标签的约束

ol ul table他们的内部存放的标签是有约束的

  • 代码

    父组件

    <template>
      <div>
         <table>
            <tr is="Mytd">
            </tr>
         </table>
      </div>
    </template>
    
    <script>
    import Mychild from './components/Mychild'
    import Mytd from './components/Mytd'
    export default {
      data(){
        return {
           
        }
      },
      methods:{
      
        
      },
      components:{
        Mychild,
        Mytd
      }
    }
    </script>
    

子组件

<template>
  <div>
    <h3>123</h3> 
  </div>
</template>

5-2、动态组件

使用is属性渲染组件的内容

代码


<template>
  <div>
    <ul>
        <li @click="fn('Catgory')">分类</li>
        <li  @click="fn('Cart')">购物车</li>
        <li  @click="fn('Mine')">我的</li>
    </ul>
    <div :is="com">
      
    </div>
  </div>
</template>

<script>
import Catgory from './components/Catgory'
import Cart from './components/Cart'
import Mine from './components/Mine'
export default {
  data(){
    return {
       com:'Catgory'
    }
  },
  methods:{
     fn(val){
       this.com=val;
     }
    
  },
  components:{
    Catgory,
    Cart,
    Mine
  }
}
</script>




day 06+

一:回顾

  • ref dom元素和子组件 this.$refs

    • 普通元素
    • 循环遍历元素 -数组
    • 子组件
  • vue中jquery的使用

    • 外部链接 index.html script

    • npm

      • 局部 import $ from ‘jquery’

      • 全局

        import $ from ‘jquery’

        Vue.prototype. = = =

  • 插槽 slot

    • 匿名插槽
    • 具名插槽
    • 作用域插槽
  • is属性

    • 改变标签默认约束
    • is动态组件

二:路由

SPA:single page application单页面应用,只有一个页面,不断切换内部展示的内容

路由:根据不同的url地址,映射不同的组件内容

2-1、路由(vue-router)安装

​ 1:安装vue-router

npm/cnpm  i  vue-router --save

​ 2:配置路由 —main.js

import Vue from 'vue'
import App from './App.vue'
// 1:引入vue-router
import VueRouter from 'vue-router'

// 引入组件
import Index from './components/Index'
import Mya from './components/Mya'

// 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
Vue.use(VueRouter);
// /:3: 路由配置---路由映射关系
 var routes=[
  {
   path:'/',
   component:Index
  },
  {
    path:'/mya',
    component:Mya
  }
 ]

//  4:实例化路由对象
var router=new VueRouter({routes:routes})

Vue.config.productionTip = false

new Vue({
  el: '#app',
  // 5:把路由对象注入到根实例中
  // 在每一个组件中都可以使用this.$router和this.$route
  router:router,
  components: { App },
  template: '<App/>'
})

3:在组件中使用router-link 和router-view组件

<router-link to="/about">关于我们</router-link>
渲染成   <a href="/about">关于我们</a>
<router-view></router-view>
路由出口   网址上访问  /about  router-view的位置上映射该路由对应的组件

2-2:路由的封装

1:src目录下新建router/index.js,index.js中写入如下代码

import Vue from 'vue'
// 1:引入vue-router
import VueRouter from 'vue-router'

// 2:给vue安装路由插件,让每一个组件中都可以使用router-link和router-view组件
Vue.use(VueRouter);


// 引入组件
import Index from '../components/Index'
import Mya from '../components/Mya'
import Myb from '../components/Myb'

// /:3: 路由配置---路由映射关系
var routes=[
    {
     path:'/',
     component:Index
    },
    {
      path:'/mya',
      component:Mya
    },
    {
      path:'/myb',
      component:Myb
    }
   ]
  
  //  4:实例化路由对象
  var router=new VueRouter({routes:routes})

//   导出router,vuerouter实例
export default router;


2:在main.js中导入router

// 把路由实例导入
import  router from './router'
new Vue({
  el: '#app',
  // 5:把路由对象注入到根实例中
  // 在每一个组件中都可以使用this.$router和this.$route
 // router:router,
   router,
  components: { App },
  template: '<App/>'
})

3:组件中使用router-link和router-view组件即可

2-3:路由重定向–404配置

// /:3: 路由配置---路由映射关系
var routes=[
    {
     path:'/',
     component:Index
    },
    {
        path:'*',     //*是所有的,上面没有匹配到就都渲染这里
        // redirect:'/'   //redirect重定向   
        // 还可以映射到到一个组件中404
        component:Notfound    
    }
   ]

2-4:路由懒加载

需要时再加载

//上面不需要再引入组件
var routes=[
    {
     path:'/',
     component:()=>import('../components/Index')
    },
    {
      path:'/mya',
      component:()=>import('../components/Mya')
    },
    {
      path:'/myb',
      component:()=>import('../components/Myb')
    },
    {
        path:'*',     //*是所有的,上面没有匹配到就都渲染这里
        // redirect:'/'   //redirect重定向   
        // 还可以映射到到一个组件中404
        component:()=>import('../components/Notfound')    
    }
   ]

2-5、编程式导航

  • 声明式导航:html中的router-link组件实现页面跳转

  • 编程式导航:通过js代码实现页面跳转

    • 向history中添加记录

      <router-link to="/"></router-link>
      
      this.$router.push('/mya')
      this.$router.push({path:'/mya'})
      this.$router.push({path:'prodetail',query:{id:1}})    //只能是query数据
      this.$router.push({name:'detail',params:{newsid:2}})   //对应params数据
      
    • 不会向history中添加记录

      this.$router.replace('/mya')
      
    • 历史记录的前进及后退

      this.$router.go(-1)  //后退
      this.$router.go(1)  //前进
      
    • 代码

      index.vue

      <template>
      <div>
          index
          <button @click="fn">
              跳转到mya
          </button>
      </div>
      </template>
      <script>
      
      export default {
          methods:{
              fn(){
                  // 跳转到mya
                  //  会添加到history中,router-link也会添加
                  console.log(this.$router)
                  // this.$router.push('/mya')
                  this.$router.push({path:'/mya'})
      
              //    不会添加到history中
                  // this.$router.replace('/myb')
      
              }
          }
      }
      </script>
      

      mya.vue

      <template>
          <div>
              <button @click="fn">传值</button>
      
              <button @click="go">前进</button>
               <button @click="back">后退</button>
          </div>
      </template>
      <script>
      export default {
          data(){
              return {
                 msga:"a向b传值" 
              }
          },
          methods:{
              fn(){
                  // 触发公共区域的自定义事件传值
                  
                  this.$bus.$emit('abc',this.msga)
              } ,
              go(){//前进
                 this.$router.go(1)
              }
              ,
              back(){//后退
                 this.$router.go(-1)
              }
          }
      }
      </script>
      

2-6、动态路由

​ http://www.abc.com/newsdetial/102

  • newsdetial:表示新闻详情路由

  • 102 表示新闻id

    那么怎么配置这样的路由呢?

    路由规则页面

    var routes=[
        {
           path:'/detail/:newsid',    //   匹配detail/5
           component:()=>import('../components/Detail')
        }
       ]
    
  • 从某组件中跳转到Detail组件中

    <router-link to="/detail/1"></router-link>
    
  • Detail.vue组件中获取动态参数

    this.$route.params.newsid
    
  • 代码

    index.vue

    <template>
    <div>
        index
        
        <ul>
            <li v-for="(item,index) in arr" :key="index">
                <!-- <router-link to="/detail/2"> -->
                <router-link :to="'/detail/'+item.id">
                    {{item.title}}
                </router-link>
            </li>
        </ul>
    </div>
    </template>
    <script>
    
    export default {
        data(){
           return {
               arr:[
                   {id:1,title:"张钟文考上清华了"},
                   {id:2,title:"涛涛明天结婚"},
                   {id:3,title:"成都下雨了"},
               ]
           }
        }
    }
    </script>
    

    Detail.vue

    <template>
        <div>
            detail
            {{obj.title}}
        </div>
    </template>
    <script>
    export default {
        data(){
           return {
               arr:[
                   {id:1,title:"张钟文考上清华了"},
                   {id:2,title:"涛涛明天结婚"},
                   {id:3,title:"成都下雨了"},
               ],
               obj:{}
           }
        },
        created(){
            // console.log(this.$route.params.newsid)  //{newsid: "102"}
        //    获取网址上的动态newsid
           var id=this.$route.params.newsid;
        //    根据获取到的id查找对应的下标
           var index=this.arr.findIndex(item=>{
               return item.id==id
           })
        //    根据下标找到数组对应的值赋给obj,展示到页面上
           this.obj=this.arr[index]
    
       }
    }
    </script>
    

2-7、路由参数

​ 路由中传递 参数除了刚才的params外,还可以使用query数据

​ http://www.xx.com/p?id=8

路由规则页面

var routes=[
   {
      path:'/prodetail',    //  匹配 prodetail
      component:()=>import('../components/Prodetail')
    }
]

从某组件中跳转到Prodetail组件中

<router-link to="/prodetail?proid=8"> 
<router-link :to="'/prodetail?id='+item.id">

Prodetail.vue组件中获取路由参数

this.$route.query.id

代码

  • index.vue

    <ul>
            <li v-for="(item,index) in arr" :key="index">
                <!-- <router-link to="/prodetail?proid=8"> -->
                <router-link :to="'/prodetail?id='+item.id">
                    {{item.title}}
                </router-link>
            </li>
        </ul>
    
  • Prodetail.vue

    <template>
        <div>
            prodetail
        </div>
    </template>
    <script>
    export default {
        created(){
            console.log(this.$route.query.id)
            
        }
    }
    </script>
    
  • 面试题

    r o u t e r 和 router和 routerroute的区别?

    route这里存储的是属性,比如route.query、route.params

    router这里存储的是方法,比如router.push()、router.replace()、router.go(-1)

2-8、嵌套路由

  • 路由配置

    var routes=[
       {
          path:'/myb',
          component:()=>import('../components/Myb'),
          children:[
            {
              path:'/myb/aa',
              component:()=>import('../components/Myaa'),
            },
            {
              path:'/myb/bb',
              component:()=>import('../components/Mybb'),
            }
          ]
        },
    ]
    
  • Myb组件中

    给谁设置二级路由,就在他的组件里写router-link和router-view

    <template>
        <div>
          
           <router-link to="/myb/aa">aa</router-link>
            <router-link to="/myb/bb">bb</router-link>
    
            <div class="box">
                <router-view></router-view>
            </div>
    
        </div>
    </template>
    <script>
    export default {
        data(){
            return {
               m:"123"
            }
        },
        created(){
            // // console.log(this,111)
            // var that=this;
            // // 公共区域自定义事件--接收值
            // this.$bus.$on('abc',function(v){
            //     console.log("我被触发了",v)
            //     console.log(this)
            //    that.m=v
            // })
            //--------------
            // console.log(this,111)
            
            // 公共区域自定义事件--接收值
            this.$bus.$on('abc',(v)=>{
                console.log("我被触发了",v)
                console.log(this)
               this.m=v
            })
        }
    }
    </script>
    <style>
      .box{
        width: 500px;
        height: 500px;
        background-color: pink;
      }
    </style>
    

2-9、路由进阶

1-路由模式
  • hash模式 # 默认模式

  • history模式

      var router=new VueRouter(
        {routes,
         mode:'history'
        }
      )
    
2-命名路由
  • 路由规则

    var routes=[
      {
          path:'/mya',
          name:'minea',   //命名路由
          component:()=>import('../components/Mya')
        }
    ]
    
  • 路由跳转

    //1种
    <router-link :to="{name:'detail',params:{newsid:item.id}}">
                    xxxxxx
    </router-link>
    //2种
     <router-link :to="{name:'minea'}">
                Mya
     </router-link>
     
    //3种
    this.$router.push({name:'detail',params:{newsid:2}})
    
3-别名
4-命名视图

一个组件里有多个router-view时,我们要给router-view定义name属性

router-view添加name ,Myb组件

  <router-link to="/myb/aa">aa</router-link>
        <router-link to="/myb/bb">bb</router-link>
          <!-- 默认的 -->
          <router-view></router-view>
        <div class="box">
            <router-view name="a"></router-view>
        </div>
        <router-view name="b"></router-view>

路由规则

var routes=[
  {
      path:'/myb',
      component:()=>import('../components/Myb'),
      children:[
        {
          path:'/myb/aa',
          // component:()=>import('../components/Myaa'),
          components:{
             default:()=>import('../components/Myaa'),
              a:()=>import('../components/Login'),
              b:()=>import('../components/Mya')
          }
        },
        {
          path:'/myb/bb',
          component:()=>import('../components/Mybb'),
        }
      ]
    }
]

day 07

一:路由守卫

https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#组件内的守卫

  • 1:什么是路由守卫:

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航

全局守卫

  • 守卫的所有的路由—进入每一个路由之前执行

    • to:即将要进入的目标 路由对象

    • from:当前导航正要离开的路由

    • next 一定要调用该方法来 resolve 这个钩子 进入导航

      next()
      next(false)
      next('/login')
      
  • main.js中实现(还可以把该全局守卫放到router/index.js文件中)

    import  router from './router'
    router.beforeEach((to, from, next) => {
      //商城项目进入购物车页面要先进行判断 ,登录成功next()登录不成功next('/login')
      // 
        console.log(to)
       // console.log(to.path,to.name);
        if(to.path=='/mine'){
           next('/login')
         }else{
            next()
         }
      // next()
    })
    
    

路由独享守卫

​ 你可以在路由配置上直接定义 beforeEnter 守卫:

​ 写在那个路由上,守卫的就是那个路由

var routes=[
  {
        path:'/mine',
        component:()=>import('../components/view/Mine'),
        beforeEnter(to,from,next){
          // console.log(to)
          next();
        }
 }
]

组件内的守卫

  • 在路由组件内直接定义以下路由导航守卫
    • beforeRouteEnter
    • beforeRouteUpdate (2.2 新增)
    • beforeRouteLeave
<script>
export default {
  beforeRouteEnter (to, from, next) {  //进入路由之前
     console.log("进入路由之前")
     console.log(this) ;   //undefined
     next()
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
      // 你可以通过传一个回调给 next来访问组件实例
      next(vm => {
      // 通过 `vm` 访问组件实例
       console.log(vm)
      })


  },
  beforeRouteUpdate (to, from, next) {
    console.log("我被调用了")
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
     console.log("我离开了")
     next()
  }
}
</script>

二、vuex-状态管理

  • 解决的复杂组件之间的数据通信问题

  • vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享,它采用集中式存储管理应用的所有组件的状态,

2-1、使用vuex统一管理状态的好处

  • 能够在vuex中集中管理共享的数据,易于开发和后期维护
  • 能够高效的实现组件之间的数据共享,提高开发效率
  • 存储在vuex中的数据是响应式的,能够实时保持数据和页面的同步

2-2、vuex的使用

  • 1:安装vuex的依赖包

    cnpm install vuex --save
    
  • 2:导入vuex包(src目录下新建store目录,store目录下新建index.js文件)

  • 3:创建store对象

    //index.js
    
    import Vue from 'vue'
    //1: 导入vuex包
    import Vuex from 'vuex'
    Vue.use(Vuex)
    
    // 2创建store对象
    export default new Vuex.Store({
        
    })
    
  • 将store对象挂载到vue实例中

    import store from './store'
    new Vue({
      el: '#app',
      store,
      components: { App },
      template: '<App/>'
    })
    

2-3、vuex的核心概念

1:state
  • state提供唯一的公共数据源,所有共享的数据都要统一放到Store的state中进行存储

    export default new Vuex.Store({
        //store数据源,提供唯一公共数据
        state:{
           count:20
        }
    })
    
  • 组件访问state中数据的第一种方式

    this.$store.state.全局数据名称
    
    //代码
    <template>
        <div>
         count的值是{{$store.state.count}}
    
        </div>
    </template>
    <script>
    export default {
        mounted(){
            console.log(this.$store.state.count)
        }
    }
    </script>
    
    
  • 组件访问state中数据的第二种方式

    // 1:从vuex中按需导入mapState函数
    import {mapState} from 'vuex'
    computed:{
            // 2将全局数据,映射为当前组件的计算属性
            ...mapState(['count'])
        }
        
        
    //代码
    <template>
        <div>
           {{count}}
        </div>
    </template>
    <script>
    // 1:从vuex中按需导入mapState函数
    import {mapState} from 'vuex'
    export default {
        computed:{
            // 2将全局数据,映射为当前组件的计算属性
            ...mapState(['count'])
            // 等同于
            // count(){
            //     return this.$store.state.count;
            // }
        }
    }
    </script>
    
2:mutation
  • mutation用于变更store中的数据

    • 只能通过mutation变更store中的数据,不可以直接操作store中的数据
    • 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化
  • 定义mutation

    export default new Vuex.Store({
        state:{
           count:20
        },
        mutations:{  //定义mutation 变更store中的数据
           add(state){
             //变更状态
             state.count++;
           }
        }    
    })
    
  • 组件中触发mutation中的方法(第一种)

    //this.$store.commit('mutation中的方法')
    this.$store.commit('add')
    
    //代码
    <template>
        <div>
         count的值是{{$store.state.count}}
         <button @click="add">自增1</button>
        </div>
    </template>
    <script>
    export default {
        mounted(){
            console.log(this.$store.state.count)
        },
        methods:{
           add(){
            //    触发mutation
                 this.$store.commit('add')
           } 
        }
    }
    </script>
    
  • 组件中触发mutation中的方法(第二种)

    // 1:从vuex中按需导入mapMutations函数
    import {mapMutations} from 'vuex'
    //2:将指定的mutations函数,映射为当前组件的methods函数
    methods:{
            //将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['add'])
    }
    
    //代码
    <template>
        <div>
           {{count}}
           <button @click="add">自增1</button>
        </div>
    </template>
    <script>
    // 1:从vuex中按需导入mapState,mapMutations函数
    import {mapState,mapMutations} from 'vuex'
    export default {
        computed:{
            // 2将全局数据,映射为当前组件的计算属性
            ...mapState(['count'])
            // 等同于
            // count(){
            //     return this.$store.state.count;
            // }
        },
        methods:{
            //将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['add'])
           
    
        }
    }
    </script>
    
  • 触发mutations时传递参数

    • 定义传参的mutation

      export default new Vuex.Store({
          state:{
             count:20
          },
          mutations:{  //变更store中的数据   step形参
             addN(state,step){
                 state.count +=step
             }
          }    
      })
      
    • 组件中调用并传参(第一种)

      //传递多个值用对象传递
      this.$store.commit('addN',实参)
      
      //代码
      <template>
          <div>
           count的值是{{$store.state.count}}
           <button @click="add">自增1</button>
           <button @click="addN(8)">自增N</button>
          </div>
      </template>
      <script>
      export default {
          mounted(){
              console.log(this.$store.state.count)
          },
          methods:{
             add(){
              //    触发mutation
                   this.$store.commit('add')
             },
             addN(n){
              //    触发mutation传递参数,传多个写在对象里面
              this.$store.commit('addN',n)
             }
          }
      }
      </script>
      
    • 组件中调用并传参(第二种)

      //代码
      <template>
          <div>
             {{count}}
             <button @click="add">自增1</button>
             <button @click="addN(9)">自增N</button>
          </div>
      </template>
      <script>
      // 1:从vuex中按需导入mapState,mapMutations函数
      import {mapState,mapMutations} from 'vuex'
      export default {
          computed:{
              // 2将全局数据,映射为当前组件的计算属性
              ...mapState(['count'])
              // 等同于
              // count(){
              //     return this.$store.state.count;
              // }
          },
          methods:{
              //将指定的mutations函数,映射为当前组件的methods函数
              ...mapMutations(['add','addN'])
          }
      }
      </script>
      
3:action
  • action用于处理异步任务,和逻辑操作

  • 如果通过异步操作或者执行逻辑操作变更数据,必须通过action,而不能使用mutation,但是在action中还是通过触发mutation的方式间接变更数据

  • 定义action

    const store=new Vuex.Store({
       state:{},
       mutations:{},
       actions:{
            addAsync(context){
                setTimeout(()=>{
                  //在actions中,不能直接修改state中的数据
                  //必须通过context.commit()触发某个mutation才行
                  context.commit('add')
                },1000)
            }
        }
    })
    
    //代码
    import Vue from 'vue'
    //1: 导入vuex包
    import Vuex from 'vuex'
    Vue.use(Vuex)
    
    // 2创建store对象
    export default new Vuex.Store({
        //store数据源,提供唯一公共数据
        state:{
           count:20
        },
        mutations:{  //变更store中的数据
            // 只有mutation中定义的函数,才有权利修改state中的数据
           add(state){
             state.count++;
            // 不要在mutations函数中执行异步操作
            // setTimeout(()=>{
            //     state.count++;
            // },1000)
    
           },
           addN(state,step){
               state.count +=step
           }
        } ,
        actions:{
            addAsync(context){
                setTimeout(()=>{
                  //在actions中,不能直接修改state中的数据
                  //必须通过context.commit()触发某个mutation才行
                  context.commit('add')
                },1000)
            }
        }
    
    })
    
  • 组件中访问action中的函数(第一种)

    this.$store.dispatch('函数名')
    
    //代码
    <template>
        <div>
         count的值是{{$store.state.count}}
         <button @click="add">自增1</button>
         <button @click="addN(8)">自增N</button>
    
         <button @click="addAsync">自增1+async</button>
        </div>
    </template>
    <script>
    export default {
        mounted(){
            console.log(this.$store.state.count)
        },
        methods:{
           add(){
            //    触发mutation
                 this.$store.commit('add')
           },
           addN(n){
            //    触发mutation传递参数,传多个写在对象里面
            this.$store.commit('addN',n)
           },
           addAsync(){
            //    触发action中的函数
            this.$store.dispatch('addAsync')
           }
        }
    }
    </script>
    
  • 组件中访问action中的函数(第二种)

    // 1:从vuex中按需导入mapActions函数
    import {mapState,mapMutations,mapActions} from 'vuex'
    //将指定的actions函数,映射为当前组件的methods函数
    methods:{
      ...mapActions(['addAsync'])
    }
        
    //代码
    
     <template>
        <div>
           {{count}}
           <button @click="add">自增1</button>
           <button @click="addN(9)">自增N</button>
    
            <button @click="addAsync">自增1+async</button>
        </div>
    </template>
    <script>
    // 1:从vuex中按需导入mapState,mapMutations函数
    import {mapState,mapMutations,mapActions} from 'vuex'
    export default {
        computed:{
            // 2将全局数据,映射为当前组件的计算属性
            ...mapState(['count'])
            // 等同于
            // count(){
            //     return this.$store.state.count;
            // }
        },
        methods:{
            //将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['add','addN']),
            ...mapActions(['addAsync'])
        }
    }
    </script>   
    
  • 触发action异步任务时携带参数

    • 定义action

      const store=new Vuex.Store({
         state:{},
         mutations:{},
          addNAsync(context,step){
                  setTimeout(()=>{
                      //在actions中,不能直接修改state中的数据
                      //必须通过context.commit()触发某个mutation才行
                      context.commit('addN',step)
                    },1000)
              }
      })
      
      //代码
      import Vue from 'vue'
      //1: 导入vuex包
      import Vuex from 'vuex'
      Vue.use(Vuex)
      
      // 2创建store对象
      export default new Vuex.Store({
          //store数据源,提供唯一公共数据
          state:{
             count:20
          },
          mutations:{  //变更store中的数据
              // 只有mutation中定义的函数,才有权利修改state中的数据
             add(state){
               state.count++;
              // 不要在mutations函数中执行异步操作
              // setTimeout(()=>{
              //     state.count++;
              // },1000)
      
             },
             addN(state,step){
                 state.count +=step
             }
          } ,
          actions:{
              addAsync(context){
                  setTimeout(()=>{
                    //在actions中,不能直接修改state中的数据
                    //必须通过context.commit()触发某个mutation才行
                    context.commit('add')
                  },1000)
              },
              addNAsync(context,step){
                  setTimeout(()=>{
                      //在actions中,不能直接修改state中的数据
                      //必须通过context.commit()触发某个mutation才行
                      context.commit('addN',step)
                    },1000)
              }
          }
      
      })
      
    • 组件中访问action中的函数(第一种)

      <template>
          <div>
           count的值是{{$store.state.count}}
           <button @click="add">自增1</button>
           <button @click="addN(8)">自增N</button>
      
           <button @click="addAsync">自增1+async</button>
           <button @click="addNAsync(5)">自增N+async</button>
          </div>
      </template>
      <script>
      export default {
          mounted(){
              console.log(this.$store.state.count)
          },
          methods:{
             add(){
              //    触发mutation
                   this.$store.commit('add')
             },
             addN(n){
              //    触发mutation传递参数,传多个写在对象里面
              this.$store.commit('addN',n)
             },
             addAsync(){
              //    触发action中的函数
              this.$store.dispatch('addAsync')
             },
             addNAsync(n){
                 this.$store.dispatch('addNAsync',n)
             }
          }
      }
      </script>
      
    • 组件中访问action中的函数(第2种)

      <template>
          <div>
             {{count}}
             <button @click="add">自增1</button>
             <button @click="addN(9)">自增N</button>
      
              <button @click="addAsync">自增1+async</button>
      
              <button @click="addNAsync(3)">自增N+async</button>
          </div>
      </template>
      <script>
      // 1:从vuex中按需导入mapState,mapMutations函数
      import {mapState,mapMutations,mapActions} from 'vuex'
      export default {
          computed:{
              // 2将全局数据,映射为当前组件的计算属性
              ...mapState(['count'])
              // 等同于
              // count(){
              //     return this.$store.state.count;
              // }
          },
          methods:{
              //将指定的mutations函数,映射为当前组件的methods函数
              ...mapMutations(['add','addN']),
              ...mapActions(['addAsync','addNAsync'])
          }
      }
      </script>
      
4:getter
  • getter用于对store中的数据进行加工处理形成新的数据,类似于vue中的计算属性

  • store中数据发生变化,getter的数据也会跟着变化

  • 定义getter

    export default new Vuex.Store({
     getters:{
            getcount:state=>{
                return '当前最新的数量是【'+ state.count +'】';
            }
        }
    })
    
  • 使用getters的第一种方式

    this.$store.getters.名称
    

    代码

    <template>
        <div>
         count的值是{{$store.state.count}}
         <hr/>
         {{$store.getters.getcount}}
         <hr/>
         <button @click="add">自增1</button>
         <button @click="addN(8)">自增N</button>
    
         <button @click="addAsync">自增1+async</button>
         <button @click="addNAsync(5)">自增N+async</button>
        </div>
    </template>
    <script>
    export default {
        mounted(){
            console.log(this.$store.state.count)
        },
        methods:{
           add(){
            //    触发mutation
                 this.$store.commit('add')
           },
           addN(n){
            //    触发mutation传递参数,传多个写在对象里面
            this.$store.commit('addN',n)
           },
           addAsync(){
            //    触发action中的函数
            this.$store.dispatch('addAsync')
           },
           addNAsync(n){
               this.$store.dispatch('addNAsync',n)
           }
        }
    }
    </script>
    
  • 使用getters的第二种方式

    import {mapGetters} from 'vuex'
    computed:{
      ...mapGetters(['名称'])
    }
    

    代码

    <template>
        <div>
           {{count}}---{{getcount}}
           <button @click="add">自增1</button>
           <button @click="addN(9)">自增N</button>
    
            <button @click="addAsync">自增1+async</button>
    
            <button @click="addNAsync(3)">自增N+async</button>
        </div>
    </template>
    <script>
    // 1:从vuex中按需导入mapState,mapMutations函数
    import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'
    export default {
        computed:{
            // 2将全局数据,映射为当前组件的计算属性
            ...mapGetters(['getcount']),
            ...mapState(['count'])
            // 等同于
            // count(){
            //     return this.$store.state.count;
            // }
        },
        methods:{
            //将指定的mutations函数,映射为当前组件的methods函数
            ...mapMutations(['add','addN']),
            ...mapActions(['addAsync','addNAsync'])
        }
    }
    </script>
    

day 08

一:axios

  • 地址:http://www.axios-js.com/
  • 含义:易用、简洁且高效的(基于promise)http库 (一个封装好的http插件)
  • 特点:
    • 支持node端和浏览器端
    • 支持promise
    • 丰富的配置项
1-1、使用
  • 安装

    cnpm  install  axios  -S
    
    
  • 引入

    //每个组件里单独引入
    import axios from axios
    
1-2、常用的方法
  • get方法

    • 语法:

      axios.get(请求地址,配置对象).then(res=>{ }).catch(err=>{})
      
      //不传参数
      axios.get('url')
      .then(res=>{
        
      })
      .catch(err=>{
        
      })
      
      //传参数 第一种
      axios.get('url?id=5&name=张三')
      .then(res=>{
        
      })
      .catch(err=>{
        
      })
      //传参数 第二种
      axios.get('url'{
         params:{
           参数1:值,
           参数2:值
         }
      })
      .then(res=>{
        
      })
      .catch(err=>{
        
      })
      
    • 代码

      <template>
          <div>
             <li v-for="item in arr" :key="item.id">{{item.title}}</li>
          </div>
      </template>
      <script>
      import axios from 'axios'
      export default {
          data(){
              return {
                 arr:[]
              }
          },
          created(){
              // console.log(axios);
               
      
          //    axios.get('http://localhost:4000/arr')
          //    .then(res=>{
          //        console.log(res.data);
          //    })
      
          //      axios.get('http://localhost:4000/arr?id=2')
          //    .then(res=>{
          //        console.log(res.data);
          //    })  
           
              axios.get('http://localhost:4000/arr',{
                  params:{
                      id:2
                  }
              })
              .then(res=>{
                  console.log(res.data);
                  this.arr=res.data;
               })  
                   }
      }
      </script>
      
  • post方法

    • 语法:

      axios.post(url[,数据[,配置项]])
      axios.post('/user')
        .then((res) =>{
          console.log(response);
        })
        .catch((error)=> {
          console.log(error);
        });
      
      axios.post('/user', {
          参数1:,
          参数2:})
        .then((res) =>{
          console.log(response);
        })
        .catch((error)=> {
          console.log(error);
        });
      
    • 代码

      <template>
          <div>
             <li v-for="item in arr" :key="item.id">{{item.title}}</li>
      
      
             <button @click="get">get请求</button>
             <button  @click="post">post请求</button>
          </div>
      </template>
      <script>
      import axios from 'axios'
      export default {
          data(){
              return {
                 arr:[],
                 tit:"成都it"
              }
          },
          methods:{
              get(){   //get请求
                    // console.log(axios);
          //   不传参数
          //    axios.get('http://localhost:4000/arr')
          //    .then(res=>{
          //        console.log(res.data);
          //    })
          //   传参  第一种
            //      axios.get('http://localhost:4000/arr?id=2')
          //    .then(res=>{
          //        console.log(res.data);
          //    })  
           //   传参  第二种
              axios.get('http://localhost:4000/arr',{
                  params:{
                      id:2
                  }
              })
              .then(res=>{
                  console.log(res.data);
                  this.arr=res.data;
               })  
              },
              post(){  //post请求
      
                  // axios.post(url[,数据[,配置项]])
                  // axios.post('http://localhost:4000/arr')
                  // .then(res=>{
                  //     console.log(res.data);
                  // })
                  // .catch(err=>{
                  //     console.log(err);
                  // })
      
                   axios.post('http://localhost:4000/arr',{
                       title:this.tit
                   })
                  .then(res=>{
                      console.log(res.data);
                  })
                  .catch(err=>{
                      console.log(err);
                  })
      
              }
          }
      }
      </script>
      
  • all方法

    • 可以实现发送多次请求,请求成功之后再做处理

    • 在项目开发中,一个页面的数据需要请求多个接口地址,那我们就执行多并发请求

    • 语法

      //    执行多并发请求
          // axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
          // axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果
          // 语法:
          axios.all([函数1(),函数2(),函数3()])
          .then(
              axios.spread((结果1,结果2,结果3)=>{
                  
              })
          )
      
    • 代码

      <template>
          <div>
             <li v-for="item in arr" :key="item.id">{{item.title}}</li>
             <button @click="get">get请求</button>
             <button  @click="post">post请求</button>
          </div>
      </template>
      <script>
      import axios from 'axios'
      export default {
          data(){
              return {
                 arr:[],
                 tit:"成都it"
              }
          },
          methods:{
              get(){   //get请求
                    // console.log(axios);
          //   不传参数
          //    axios.get('http://localhost:4000/arr')
          //    .then(res=>{
          //        console.log(res.data);
          //    })
          //   传参  第一种
            //      axios.get('http://localhost:4000/arr?id=2')
          //    .then(res=>{
          //        console.log(res.data);
          //    })  
           //   传参  第二种
              axios.get('http://localhost:4000/arr',{
                  params:{
                      id:2
                  }
              })
              .then(res=>{
                  console.log(res.data);
                  this.arr=res.data;
               })  
              },
              post(){  //post请求
      
                  // axios.post(url[,数据[,配置项]])
                  // axios.post('http://localhost:4000/arr')
                  // .then(res=>{
                  //     console.log(res.data);
                  // })
                  // .catch(err=>{
                  //     console.log(err);
                  // })
      
                   axios.post('http://localhost:4000/arr',{
                       title:this.tit
                   })
                  .then(res=>{
                      console.log(res.data);
                  })
                  .catch(err=>{
                      console.log(err);
                  })
      
              },
              getuser(){
                  return axios.get('http://localhost:4000/user')
              },
              getarr(){
                  return axios.get('http://localhost:4000/arr') 
              }
          },
          created(){
          //    执行多并发请求
          // axios.all([函数1(),函数2(),函数3()]) 一个函数返回一个结果,多个函数返回多个结果
          // axios.spread((结果1,结果2,结果3)=>{}) 去获取每个函数返回的结果
          // 语法:
          // axios.all([函数1(),函数2(),函数3()])
          // .then(
          //     axios.spread((结果1,结果2,结果3)=>{
      
          //     })
          // )
      
          axios.all([this.getuser(),this.getarr()])
          .then(axios.spread((users,arrs)=>{
              console.log(users);
              console.log(arrs);
          }))
      
          }
      }
      </script>
      
1-3、axios Api
  • 可以通过向 axios 传递相关配置来创建请求

    axios({配置对象})
    axios({
       url:"地址",
       method: 'post',   //请求方式
       data: {      //传递的参数
        参数1: 值,
        参数2: 值
      }
    })
    .then(res=>{   //res是axios返回的请求对象  res.data才是后端返回的数据
    
    })
    .catch(err=>{
      console.log(err)
    })
    
1-4、axios的create方法
  • create方法不是用来请求数据的,而是用来创建一个axios实例对象,定义一些初始化配置

  • 语法

    let $axios=axios.create({配置对象})
    
    let $axios=axios.create({
        baseURL:'http://xxxx.com/aaa',
        timeout:3000  //请求时长
    })
    
    $axios.get('/getuser',{
       params:{
           参数1:值
       }
    })
    .then(res=>{})
    .catch(err=>{})
    
    
    
1-5、拦截器
  • 拦截器 :发送请求之前做一些事情,返回数据之前做一些事情

  • 使用

    // 请求拦截器   修改请求头 【在请求头上加token】
    $axios.interceptors.request.use(function (config) {
        console.log(55555)
        // 请求发送之前做一些事情
        return config;  //一定要return config 请求无法发送
      }, function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
      });
    
    // 响应拦截器   返回数据的校验  状态是否正确  信息的提取
    $axios.interceptors.response.use(function (response) {
        // 对响应数据做点什么
        return response;
      }, function (error) {
        // 对响应错误做点什么
        return Promise.reject(error);
      });
    
1-6、axios的封装
  • 1:src目录下面新建http目录,目录下新建index.js文件

    import axios from 'axios'
    
    //初始化配置
    const $axios=axios.create({
        baseURL:'http://localhost:4000',
        timeout:3000
    })
    
    
    // 请求拦截器   修改请求头 【在请求头上加token】
    $axios.interceptors.request.use(function (config) {
       
        // 请求发送之前做一些事情
        return config;  //一定要return config 请求无法发送
      }, function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
      });
    
    // 响应拦截器   返回数据的校验  状态是否正确  信息的提取
    $axios.interceptors.response.use(function (response) {
        // 对响应数据做点什么
        return response;
      }, function (error) {
        // 对响应错误做点什么
        return Promise.reject(error);
      });
    
    
    
    export default $axios;
    
  • 2:main.js文件中引入

    import $axios from './http'
    Vue.prototype.$axios=$axios
    
  • 3:每一个组件中使用

    this.$axios.get(url).then(res=>{})
    this.$axios.post(url).then(res=>{})
    //this.$axios没有all方法    要使用all方法需要   axios.all
    

二、jsonserver模拟数据

安装

npm install json-server -g

新建一个文件夹myjson,在该文件夹下面新建data.json文件

{
   "arr":[
      {
         "id":1,
         "title":"111111111"
      },
      {
          "id":2,
          "title":"222222"
       },
       {
          "id":3,
          "title":"33333333"
       }
  ],
  "user":[
     {
        "id":1,
        "name":"张三"
     }
  ]
}

执行命令行

json-server -w data.json -p 4000

三、跨域问题

  • 跨域问题:因为浏览器的同源策略,不能访问其他网站的资源

  • 同源策略:协议相同,域名相同,端口号相同

  • 解决方法:

    • jsonp script的src属性
    • iframe
    • cors 服务端
    • 后端允许跨域
  • vue里面如何解决跨域问题—webpack配置跨域代理 —生产环境不存在

    • config文件下面的index.js 中找 proxyTable

      proxyTable: {   // 【设置代理!!!】
            '/api': {  //使用"/地址名"来代替"请求网址" 
              target: 'http://localhost:3000/',   //源地址 
              changeOrigin: true,  //改变源 
              pathRewrite: {  // 路径重写
                '^/api': '' // 当请求的路径开头有  /地址名  的时候就替换成请求网址
              }
      } 
      
      
    • 组件中请求要修改链接

      axios.get('/api/xxx/yyy').then(res=>{})
      

四:项目中接口的使用

  • 1:找个地方不是vue项目目录中 ,解压umall-api.zip

  • 2:解压之后,umall-api目录中打开命令窗口 cnpm install

  • 3:打开Navcat软件,新建数据库 shop_db ,然后把shop_db有数据.sql导入到数据库中

  • 4:打开项目目录下的config/global.js文件,修改

    exports.dbConfig = {
        host: 'localhost', //数据库地址
        user: 'root',//数据库用户名
        password: '你的密码',//数据库用户密码
        port: 3306,
        database: 'shop_db' // 数据库名字
    }
    
  • 启动项目

    npm start
    

五、项目启动

  • 网址上打开http://localhost:8080/出现空白

    var routes=[
        {
         path:'/',
         component:()=>import('../components/Home'),
         redirect:'/index',   //添加这一句  重定向
         children:[]
         }
    ]
    

六、问题

Access to XMLHttpRequest at ‘http://localhost:3000/api/getcate’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

出现跨域问题

七、vant

轻量、可靠的移动端 Vue 组件库

安装

cnpm i vant -S

引入 main.js

import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);

使用

<!-- banner图 -->
    <van-swipe class="my-swipe banner" :autoplay="3000" indicator-color="white">
        <van-swipe-item v-for="item in bannerarr" :key="item.id">
           <img :src="item.img" alt />
        </van-swipe-item>
</van-swipe>   

day 09 项目

一:项目

1:接口地址的封装
  • http目录下新建api.js文件

    // 存放所有的接口地址
    export default {
         baseUrl:'http://localhost:3000',    //接口网址
         cateUrl:'/api/getcate',    //获取分类信息(首页)
         bannerUrl:'/api/getbanner',  //获取轮播图信息(首页)
         seckillUrl:'/api/getseckill',  //获取限时秒杀信息(首页)
        indexgoodUrl: '/api/getindexgoods'  //获取商品信息(首页)
    }
    
  • main.js中引入,让每一个组件都可以直接使用

    imprt $api from './http/api'
    Vue.prototype.$api=$api
    
  • 在组件中使用

    this.$api.名称
    
2:会员登录
  • 1:已经登录成功

  • 2:登录成功用户信息保存到本地

    • login.vue

       methods:{
              login(){
                  this.$axios.post(this.$api.loginUrl,{
                      phone:this.phone,
                   	password:this.password
                  })
                  .then(res=>{
                     if(res.data.code===200){
                      //  登录成功,登录信息存储在本地的localStorage
                      // localStorage中只能存储字符串  永久存储  除非手动删除  userInfo自定义的
                      localStorage.setItem('userInfo',JSON.stringify(res.data.list))
                      
                       this.$router.push('/mine');
                     }else{
                       Toast.fail(res.data.msg);
                     }
                     this.phone="";
                     this.password="";
                  })
              }
      
  • :3:在购物车和我的路由添加自定义meta字段,来记录页面是否需要身份验证

    var routes=[
       {
            path:'/cart',
            // 元信息  记录进入该页面是否需要身份验证
            meta:{reqiresAuth:true},   
            component:()=>import('../components/view/Cart'),
           },
           {
            path:'/mine',
            meta:{reqiresAuth:true},
            component:()=>import('../components/view/Mine')
           }
    ]
    
  • 4:路由拦截

// 全局守卫--守卫的是所有的路由--进入每一个路由之前执行
 router.beforeEach((to, from, next) => {
    // 设置路由拦截
    // $route.matched  一个数组,包含当前路由的所有嵌套路径片段的路由记录
      // to.matched数组中包含$route对象的检查元信息
      // arr.some()判断该数组是否有元信息,返回布尔值
      // 判断哪些路由有元信息
     if(to.matched.some(record=>record.meta.reqiresAuth)){
        //需要登录身份验证的执行这里
          //  判断是否登录
          if(localStorage.getItem("userInfo")){
            next()
          }else{
            next('/login')
          }
     }else{
      // 不需要登录身份验证的执行这里
      next()
     }
})
  • 5:设置请求拦截和响应拦截

    import axios from 'axios'
    
    import router from '../router'
    import {Toast} from 'Vant'
    
    // 创建了axios的实例--初始化配置---每次请求都会先执行这里
    // 每次请求会把baseURL放到请求的url的前面-前提是使用$axios请求,axios请求不会执行这个文件
    const $axios=axios.create({
        baseURL:'/api',   ///api此处的api是跨域里的替换的地址
        timeout:3000
    })
    
    
    // 请求拦截器   修改请求头 【在请求头上加token】
    $axios.interceptors.request.use(function (config) {
        // console.log(config,"config")
        // 请求发送之前做一些事情
    
           var userInfo=JSON.parse(localStorage.getItem('userInfo'))
          // 修改请求头,添加token
          // 每一个请求都会加token
          config.headers.Authorization=userInfo?userInfo.token:"";
       
    
        return config;  //一定要return config 请求无法发送
      }, function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
      });
    
    // 响应拦截器   返回数据的校验  状态是否正确  信息的提取
    $axios.interceptors.response.use(function (response) {
        // 对响应数据做点什么
        //  token过期我们拦截,跳转到登录页面
        if(response.data.code==403){
          Toast.fail(response.data.msg)
          //  router.push('/login')
        }
        
        return response;
      }, function (error) {
        // 对响应错误做点什么
        return Promise.reject(error);
      });
    export default $axios;
    
4:购物车
  • 请求数据

    export default {
        data(){
            return {
              cartlist:[]
            }
        },
        created(){
            var uid=JSON.parse(localStorage.getItem('userInfo')).uid
            this.$axios.get(this.$api.cartlistUrl+"?uid="+uid)
            .then(res=>{
                // console.log(res);
                
                this.cartlist=res.data.list.map(item=>{
                    item.checked=false;
                    return item;
                })
                
            })
        }
    }
    
  • 页面展示

     <van-swipe-cell>
                <ul>
                    <li>
                    <div class="circle">
                        <img src="../../assets/images/shopping_04.jpg" alt="" id="sure">
                    </div>
                    <img src="../../assets/images/shopping_01.jpg" alt="" class="liimg01">
                    <div class="name">
                        <h5>欧莱雅面霜</h5>
                        <span>规格:50g</span>
                        <p>¥123.00</p>
                    </div>
                    <div class="number">
                        <div class="minus">-</div>
                        <input type="text" value="1" class="varible"></input>
                        <div class="add">+</div>
                    </div>
                </li>
                </ul>
                <template #right>
                   <div>删除</div>
                </template>
            </van-swipe-cell>
    
3:token
  • 什么是token:

    Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码是否正确,并作出相应的提示,这时就有了token

  • token的定义

    token是服务端生成的一串字符串,来作为客户端进行请求的一个令牌,当第一次登陆后服务端生成一个token然后将token返回给客户端,以后客户端只需要带着这个token去请求数据就可以了,不需要再次带上用户名和密码

  • 使用token的目的

    减轻服务器的压力,减少了频繁的查询数据库

  • 工作原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UD3K0kML-1611110597377)(F:\Web\vue\day09\参考\day09./token.png)]

day 10 项目

一:1问题收集

  • 1:单词不一致---------复制粘贴
  • 2:先缕思路,用自己的话把写出来—一步步实现,每一步都没问题了 再往下走
  • 3:每天第一件事先梳理笔记

二:页面详情页

  • 1:准备详情页

  • 2:配置详情页的路由

  • 3:首页 实现产品跳转

    <li v-for="item in recommedarr"  :key="item.id">
        <img :src="$api.baseUrl+item.img" alt />
        <div>
        <h5>{{item.goodsname}}</h5>
        <span>¥</span>
        <!-- 保留两位小数 -->
        <span>{{item.price.toFixed(2)}}</span>
        <p>已售800件</p>
        <router-link :to="'/detail/'+item.id">
        立即抢购
        </router-link>
        </div>
    </li>
    
  • 4:详情页中接收数据并展示

    export default {
        data(){
            return {
                proobj:null,
                num:1
            }
        },
        created(){
            // 1获取url网址上的id值
            let id=this.$route.params.id;
            // console.log(id);
            // 2 根据id去服务端请求数据
            this.$axios.get(this.$api.goodsinfoUrl,{
                params:{
                    id
                }
            })
            .then(res=>{
                console.log(res.data.list[0]);
                if(res.data.code==200){
                    if(res.data.list.length>0){
                        this.proobj=res.data.list[0];
                    }
                }
            })
    
        }
    }
    </script>
    
  • 5:加入购物车

    • 1:添加点击事件

    • 2:实现添加到数据库的功能

       methods:{
              addcart(){
                  // 1获取需要传递的数据
                  const data={
                      uid:JSON.parse(localStorage.getItem('userInfo')).uid  ,       //当前登录用户的uid
                      goodsid:this.$route.params.id,
                      num:this.num
                  }
                  // console.log(data)
                  //  2通过请求加入到数据库
                  this.$axios.post(this.$api.cartaddUrl,data)
                  .then(res=>{
                    if(res.data.code==200){
                        Toast.success('加入购物车成功');
                          
                      }
                  })
      
              }
          }
      

三:分类页面

  • 结构 路由

四:路由搭建

  • 一级路由
    • login登录
    • / 首页
  • 二级路由
    • 系统设置下的菜单管理、角色管理 管理员管理
    • 商城管理下的内容

五:ElementUi

  • 饿了么提供的基于 Vue 2.0 的桌面端组件库(mintui移动端)

  • 安装

    npm i element-ui -S
    
  • 引入main.js

    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    
    
    Vue.use(ElementUI)
    
  • 使用

  • 登录页

day 12

一:管理员列表页面

<template>
  <div>
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>管理员列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 面包屑 -->
    <el-button type="primary" @click="tzadd()">添加</el-button>
    <!-- data  数据  users--数组 -->
    <el-table
      :data="users"
      style="width: 100%"
      row-key="id"
      border
      lazy
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    >
      <el-table-column prop="id" label="用户编号"></el-table-column>
      <el-table-column prop="username" label="用户名"></el-table-column>
      <el-table-column prop="roleid" label="所属角色"></el-table-column>

      <el-table-column prop="status" label="状态">
        <template slot-scope="item">
          <el-tag effect="dark" v-if="item.row.status==1">启用</el-tag>
          <el-tag effect="dark" v-else type="danger">禁用</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="address" label="操作">
        <template slot-scope="item">
          <el-button type="primary" @click="edit(item.row.uid)">编辑</el-button>
          <el-button type="danger" @click="del(item.row.uid)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- current-change当前页数改变会被触发 -->
    <el-pagination background @current-change="pagechange"  :page-size="size" layout="prev, pager, next" :total="total"></el-pagination>
  </div>
</template>
<script>
import axios from 'axios'
export default {
  data() {
    return {
      users: [],
      size: 1, //每一页显示条数
      nowpage: 1, //当前是第几页
      total:0   //总数
    };
  },
  methods: {
    pagechange(n){ //当前页数改变会被触发
      this.nowpage=n;
      this.getusers()
      .then(res=>{
        if(res.data.code==200){
          this.users=res.data.list
        }else{
          this.$message({
                type: "error",
                message:res.data.msg,
          });
        }
      })
       
    },
    getusers(){
         return  this.$axios.get(this.$api.userlistUrl, {
            params: {
              size: this.size,
              page: this.nowpage,
            },
         })
    },
    getusercount(){
       return this.$axios.get(this.$api.usercountUrl)
    },
    tzadd() {
      //添加按钮 跳转到添加页面
      this.$router.push("/user/add");
    },
    edit(uid) {
      //编辑
      this.$router.push("/user/" + uid);
    },
    del(uid) {
      //删除
      // 1出现提示信息

      this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          // 成功
          this.$axios.post(this.$api.menudeleteUrl, { uid }).then((res) => {
            if (res.data.code == 200) {
              // console.log(res.data)
              this.users = res.data.list; //更新页面上的数据
              this.$message({
                type: "success",
                message: "删除成功!",
              });

              // this.menuAsyncAdd().then((res) => {
              //   console.log(res.data.list, "123");
              //   this.users = res.data.list;
              // });
            }
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },
  },
  mounted() {
    axios.all([this.getusers(),this.getusercount()])
    .then(axios.spread((users,usercount)=>{
         this.users=users.data.list
         this.total=usercount.data.list[0].total;
    }))
   
    
  },
};
</script>

二:富文本编辑器使用

1:下载

cnpm install  wangeditor  --save

2:组件中使用

<div id="desc"></div>

import E from 'wangeditor'

data(){
  return {
     editor:null
  }
}

mounted(){
    this.editor = new E("#desc");
    this.editor.create();
}
//添加内容
  this.editor.txt.html(this.info.description);
  //获取内容
  this.editor.txt.html();

三:左边aside设置

1:设置路由

2:实现下面的代码

<template>
  <el-col >
    <el-menu
      :default-active="defaultActive"
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      router
      active-text-color="#ffd04b"
    >
      <el-menu-item index="/home">
        <i class="el-icon-menu"></i>
        <span slot="title">首页</span>
      </el-menu-item>
      <el-submenu :index="item.id.toString()" v-for="item in userinfo.menus" :key="item.id">
        <template slot="title">
          <i :class="item.icon"></i>
          <span>{{item.title}}</span>
        </template>
        <el-menu-item :index="val.url" v-for="val in item.children" :key="val.id">
            {{val.title}}
        </el-menu-item>
      </el-submenu>
      
    </el-menu>
  </el-col>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
   data(){
    return {
      defaultActive:""
    }
   },
   computed:{
     ...mapGetters(['userinfo'])
   },
   mounted(){
     console.log(this.$route)
    //  页面加载时,控制左边菜单选中效果
    this.defaultActive=this.$route.meta.select;
   }
}
</script>

四:分类实现

<template>
  <div>
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item :to="{ path: '/menu' }">菜单列表</el-breadcrumb-item>
      <el-breadcrumb-item>菜单{{tip}}</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 面包屑 -->
    <el-form :model="info" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="分类名称" prop="catename">
        <el-input v-model="info.catename"></el-input>
      </el-form-item>
      <el-form-item label="上级分类" prop="pid">
        <el-select v-model="info.pid" placeholder="请选择">
          <el-option label="顶级分类" :value="0">顶级分类</el-option>
          <!-- <el-option 
          v-for="item in getmenulist" 
          :key="item.id"
          :label="item.title" :value="item.id"
          ></el-option>-->
        </el-select>
      </el-form-item>
      <el-form-item label="图片">
        <el-upload
          action="#"
          list-type="picture-card"
          :on-preview="handlePictureCardPreview"
          :on-remove="handleRemove"
          :auto-upload="false"
          :on-change="filechange"
          :file-list="fileList"
        >
          <i class="el-icon-plus"></i>
        </el-upload>
        <el-dialog :visible.sync="dialogVisible">
          <img width="100%" :src="dialogImageUrl" alt />
        </el-dialog>
      </el-form-item>

      <el-form-item label="状态" prop="status">
        <el-switch v-model="info.status"></el-switch>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">{{tip}}</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

export default {
  data() {
    return {
      dialogImageUrl: '',
      dialogVisible: false,
      fileList:[],
      img:"",
        // 图片
      tip: "添加",
      info: {
        catename: "",
        pid: "",
        status: true,
      },
      rules: {
        catename: [
          { required: true, message: "请输入分类名称", trigger: "blur" },
          { min: 1, max: 20, message: "分类名称长度不符合", trigger: "blur" },
        ],
        pid: [{ required: true, message: "请输入上级分类", trigger: "change" }],
      },
    };
  },
  computed: {

  },
  methods: {
      handleRemove(file, fileList) {
        // console.log(file, fileList);
      },
      handlePictureCardPreview(file) {
        this.dialogImageUrl = file.url;
        this.dialogVisible = true;
      },
      filechange(file){
        console.log(file)
        this.img=file.raw
      },
    //   图片
    submitForm(formName) {
      //提交
      this.$refs[formName].validate((valid) => {
        if (valid) {
          //   验证规则都满足时,会执行这里
          //    不管是添加还是修改都在这里操作
          // json序列化,防止数据变化,上面的数据跟着变化
          let data = JSON.parse(JSON.stringify(this.info));
          //    默认url为添加
          let url = '/api/cateadd';
          //    如果能获取到id ,url的值变为修改
          if (this.$route.params.id) {
            url = '/api/cateedit';
            data.id = Number(this.$route.params.id);
          }
          data.status = data.status ? 1 : 2;
          
        //   获取图片
        //FormData将本地数据(图片,.xlsx,.docx)上传或导入数据库
        //FormData可以把所有表单元素的name与value组成一个string提交到后台
         let form=new FormData();
         for(let i in data){
             form.append(i,data[i])
         }
         if(this.img){
             form.append('img',this.img)
         }
         console.log(form);

          // 发送请求
          this.$axios.post(url, form).then((res) => {
              console.log(res);
            // if (res.data.code == 200) {
            //   this.$router.push("/menu");
            // } else {
            //   this.$message.error(res.data.msg);
            // }
          });
        }
      });
    },
  },
  created() {
    // 页面刷新  需要数据
    let cateid = this.$route.params.id;
    if (cateid) {
      this.tip = "修改";
      // 根据id获取id对应的该条数据 赋值给info
      this.$axios.get('/api/cateinfo', {
          params: {
            id: cateid,
          },
        })
        .then((res) => {
           console.log(res.data.list);
          this.info = res.data.list;
        //   把img转化为filelist需要的格式
        this.fileList=this.info.img?[{url:this.info.img}]:[]
        //   // 修改status的值为true或者false
          this.info.status = this.info.status == 1 ? true : false;
        });
    }
  },
};
</script>

<style>
</style>

五:商品规格和商品分类

<template>
  <div>
    <!-- 面包屑导航 -->
    <el-breadcrumb separator=">">
      <el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
      <el-breadcrumb-item>
        <a href="/category">商品分类列表</a>
      </el-breadcrumb-item>
      <el-breadcrumb-item>商品分类{{ tip }}</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 菜单 -->

    <el-form :model="info" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="一级分类" prop="pid">
        <el-select v-model="info.first_cateid" placeholder="请选择" @change="catechange">
          <el-option value>请选择</el-option>
          <el-option
            v-for="item of firstarr"
            :key="item.id"
            :value="item.id"
            :label="item.catename"
          >{{ item.catename }}</el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="二级分类" prop="pid">
        <el-select v-model="info.second_cateid" placeholder="请选择">
          <el-option value>请选择</el-option>

          <el-option
            v-for="item of secondarr"
            :key="item.id"
            :value="item.id"
            :label="item.catename"
          >{{ item.catename }}</el-option>
        </el-select>
      </el-form-item>

      <!-- 商品规格 -->
      <el-form-item label="商品规格" prop="pid">
        <el-select v-model="info.specsid" placeholder="请选择" @change="specschange">
          <el-option value>请选择</el-option>
          <el-option
            v-for="item of specsarr"
            :key="item.id"
            :value="item.id"
            :label="item.specsname"
          >{{ item.specsname }}</el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="规格属性"  prop="pid">
        <el-select v-model="info.specsattr" multiple placeholder="请选择">
          <el-option value>请选择</el-option>

          <el-option
            v-for="(item,index) in specsattrarr"
            :key="index"
            :value="item"
            :label="item"
          >{{ item }}</el-option>
        </el-select>
      </el-form-item>

      <el-form-item label="状态">
        <el-switch v-model="info.status"></el-switch>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">添加</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
  data() {
    return {
      tip: "添加",
      firstarr: [],
      secondarr: [],
      specsarr:[],
      specsattrarr:[],
      info: {
        first_cateid: "",
        second_cateid: "",
        specsid:"",  //规格
        specsattr:[],
        catename: "",
        status: true,
      },
    };
  },
  computed: {
    // ...mapGetters(["roleArr"]),
  },
  created() {
    // 获取一级分类
    this.$axios
      .get("/api/catelist", {
        params: {
          pid: 0,
        },
      })
      .then((res) => {
        console.log(res.data, "123");
        this.firstarr = res.data.list;
      });
      // 获取商品规格
      this.$axios.get('/api/specslist')
      .then(res=>{
        console.log(res,"规格")
        this.specsarr=res.data.list
      })
    //获取商品下的一条
    // this.roles = this.roleArr;
    if (this.$route.params.id) {
      this.tip = "修改";
      //获取到当前路由地址编号对应的菜单信息
      this.$axios
        .get("/api/goodsinfo", {
          params: { id: this.$route.params.id },
        })
        .then((res) => {
          console.log(res.data, 1);
          this.info = res.data.list;
          // 让二级分类的列表全部展示出来
          this.catechange(this.info.first_cateid);
          // 规格属性去展示
          this.specschange(this.info.specsid)

          console.log(this.info.specsattr,"specsattr");
          this.info.specsattr=this.info.specsattr?this.info.specsattr.split(','):[]
;
          //处理和数据库中不一样的数据类型
          // this.info.status = this.info.status == 1 ? true : false;
        });
    }
  },
  methods: {
    catechange(val) {
      //一级分类的内容改变
      if (val == "") {
        return false;
      }
      //  val 下拉列表的value属性值
      //  alert(val)
      // 根据一级分类的id获取二级分类的内容
      this.$axios
        .get("/api/catelist", {
          params: {
            pid: val,
          },
        })
        .then((res) => {
          // console.log(res.data, "123");
          this.secondarr = res.data.list;
        });
    },
    specschange(val){
        if (val == "") {
        return false;
      }
      //  val 下拉列表的value属性值
      //  alert(val)
      // 根据一级分类的id获取二级分类的内容
      this.$axios
        .get("/api/specsinfo", {
          params: {
            id: val,
          },
        })
        .then((res) => {
          console.log(res.data, "12345");
          this.specsattrarr = res.data.list[0].attrs;
        });
    },
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          //验证规则满足时,才执行数据添加操作
          //获取一下表单中的数据,用JSON序列化一下,防止数据变化后页面跟着变化
          // let data = JSON.parse(JSON.stringify(this.info));
          // //如果现在访问的是动态路由,则执行修改操作,否则执行添加操作
          // let url = this.$api.cateaddUrl;
          // if (this.$route.params.id) {
          //   url = this.$api.cateeditUrl;
          //   data.id = this.$route.params.id; //执行修改接口时的必要条件
          // }
          // //数据库中的status字段不是布尔值,所以要自行转换一下
          // data.status = data.status ? 1 : 2;
          //  let form = new FormData();
          //  for (let i in data) {
          //    form.append(i, data[i]);
          //  }
          // if (this.img) {
          //   form.append("img", this.img);
          // }
          //发起post请求,请求接口项目中的菜单添加接口,完成数据的保存
          // this.$axios.post(url,form).then((res) => {
          //   if (res.data.code == 200) {
          //     this.$router.push("/category");
          //   } else {
          //     this.$message({
          //       showClose: true,
          //       message: res.data.msg,
          //       type: "error",
          //     });
          //   }
          // });
        }
      });
    },
  },
};
</script>

day13

一:秒杀活动

<template>
  <div>
    <el-form :model="info" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="活动期限">
      <!--1:::::-->
      
        <el-date-picker
          v-model="dateval"
          value-format="timestamp"
          type="datetimerange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :picker-options="pickerOptions"
          @change="timechange"
        ></el-date-picker>
      </el-form-item>

      <el-form-item label="状态">
        <el-switch v-model="info.status"></el-switch>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">添加</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
  data() {
    return {
      tip: "添加",
      dateval:[],
      info: {
        begintime: "",
        endtime: "",
        status: true,
      },
      pickerOptions: {
          shortcuts: [{
            text: '最近一周',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit('pick', [start, end]);
            }
          }, {
            text: '最近一个月',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
              picker.$emit('pick', [start, end]);
            }
          }, {
            text: '最近三个月',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
              picker.$emit('pick', [start, end]);
            }
          }]
        },
    };
  },
  created() {
    if (this.$route.params.id) {
      this.tip = "修改";
      //获取到当前路由地址编号对应的菜单信息
      this.$axios
        .get("/api/seckinfo", {
          params: { id: this.$route.params.id },
        })
        .then((res) => {
          console.log(res.data, 1);
          this.info = res.data.list;
          //2-------
          this.dateval=[this.info.begintime,this.info.endtime]
        });
    }
  },
  methods: {
  //333333333333333333333333333
      timechange(e){  //时间获取
        //  console.log(e)
        this.info.begintime=e[0];
        this.info.endtime=e[1]
      },
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          console.log(this.info)
        }
      });
    },
  },
};
</script>

二:退出

  • 1:退出的结构
  • 2:添加事件 执行actions中的方法,同时跳转 (既要清除store中的数据还要清除sessStorage中的数据)
  • 3:actions要调用mutations中的退出方法

三:项目打包

vua 项目中执行命令:

npm run build

根目录下面生成一个dist目录

把dist目录交给后端,但是这一次我们把dist目录复制到接口目录下,打开app.js文件

app.use(express.static('dist'))

浏览器打开http://localhost:3000/login就可以访问项目了

四:vue-cli脚手架升级

1:如果安装旧版本 卸载
npm uninstall vue-cli -g

安装

npm install @vue/cli -g

查看版本

vue -V
or
vue --version
2:初始化项目
vue create 项目名

启动项目

yarn serve
3:跨域配置

在vue的项目根目录下新建vue.config.js文件(一定是vue.config.js)

module.exports={
    devServer:{
        proxy:{
            '/api': {  //使用"/地址名"来代替"请求网址" 
            target: 'http://localhost:3000/',   //源地址 
            changeOrigin: true,  //改变源 
            pathRewrite: {  // 路径重写
              '^/api': '' // 当请求的路径开头有  /地址名  的时候就替换成请求网址
            } 
        }
        }
    }
}

五:过滤器

文本格式化—内容显示

  • 局部定义

    new Vue({
       filters:{  //局部定义过滤器  val
        名字:function(val){
           return val+"111"
        }
      },
       showprice(val,zk){
           if(!val){
            return;
           }
           return val*zk/10;
        }
    })
    
    //定义过滤器可以传其他参数
    
  • 使用过滤器

    {{ price | 过滤器的名字 }}    //price 作为过滤器的第一个参数
    
    {{ price | 过滤器的名字(第二个参数) }} 
    
  • 全局定义

    main.js中定义

    // Vue.filter(过滤器的名字,()=>{
    
    // })
    
    
    Vue.filter('isTrue',function(val){
       if(val==1){
         return true
       }else{
         return false;
       }
    })
    
  • 组件中使用

    {{ price | 过滤器的名字 }}    //price 作为过滤器的第一个参数
    
  • filters的封装

    • 1:src目录下新建filters/index.js文件

        const isNullOrEmpty=function(val){
              if(val==null || val=="" ||typeof(val)==undefined){
                  return "hello"
              }else{
                  return "nihao"
              }
          }
          const isTrue=function(val){
              if(val==1){
                       return true
                     }else{
                       return false;
                     }
          }
      
          export {
              isTrue,
              isNullOrEmpty
          }
      
      
    • 2:main.js文件中导入

      import * as filters from './filters'
      //Object.keys把有键值对的对象转换为数组
      Object.keys(filters).forEach(key=>{
        console.log(key)
        Vue.filter(key,filters[key])
      })
      
      
    • 3:组件中直接使用过滤器

      {{name | isNullOrEmpty}} 
      

day14

一:vue-cli4脚手架搭建项目配置

vue create 项目名称     //初始化项目

//1:选择手动配置还是默认配置
     default  默认配置
     Manually select features  手动配置
     
//2:我们选择手动配置   
    ?Please pick a preset:Manually select features
    ?CHeck the features needed for your project
     (*)Babel
     ( )TypeScript
     ( )Progressive Web App ( PWA) Support
     (*)Router
     (*)Vuex
     (*)CSS Pre-processors
     (*)Linter / Formatter
     ( )Unit Testing
     ( )E2E Testing
  //3:选择vue的版本  
  Choose a version of Vue.js that you want to start the project with <Use arrow keys>
    >2.x
     3.x <Preview>
 //4:让我们检查一下我们刚选的配置,然后问我们要不要使用history mode,其实呢就是我们的页面路由含不含有#;这里我们选择Y
     ?Please pick a preset:Manually select features
     ?CHeck the features needed for your project:Babel,Router,Vuex,CSS Pre-processors,Linter
     ?Use history mode for router?(Requires proper server setup for index fallback in production) (y/n)
 //4:选择CSS预编译器,这里我们选择Less,其实都差不多选哪个都行,选你会用的,实在不行也可以直接用原生怼;
 ?Pick a CSS pre-processor (PostCSS Autoprefixer and CSS Modules are supported by default):
     Sass/SCSS(With dart-sass)
     Sass/SCSS(With dart-sass)
     Less
    >Stylus
 //5:选择 ESLint 代码校验规则,提供一个插件化的javascript代码检测工具,这里我们选择ESLint + Prettier; 
 ?Pick a linter /formatter config
 ESLint with error prevention only
 ESLint + Airbnb config
 ESLint + Standard config
>ESLint +Prettier
//6选择什么时候进行代码校验,Lint on save 保存就检查,Lint and fix on commit   fix 或者 commit 的时候检查,这里我们选择第一个,稳;
?Pick additional lint features:(Press <space> to select,<a> to toggle all,<i>to invert selection)
>(*) Lint on save
 ( ) Lint and fix on commit
 //7这里呢就是选择把我们的配置保存到哪个文件中了,In dedicated config files 存放到独立文件中,In package.json 存放到 package.json 中,这里我们选择放到package.json中;
 ?Where do you prefer placing config for Babel ,ESLint ,etc.?
 In dedicated config files
 In package.json
//8\这里呢就是问我们要不要把我们的配置保存下来,为之后的项目。这里我们就选择不了,每次都手动配置一下也不花时间
?Save this as a preset for future projects?(y/N) n

结束了

启动项目

npm run serve
or
yarn  serve

二:stylus

https://stylus.bootcss.com/

富于表现力、动态的、健壮的 CSS预处理器

  • 使用

    • 1:下载

      cnpm install stylus  -dev--save
      
      cnpm install stylus-loader  -dev--save
      
    • 2:使用

      <style scoped lang="stylus">
      
      </style>
      
    • 3:语法

      .box
          width 100px
          height 100px
          background red
      
      // 变量
      fontSize=30px
      $bgColor=pink
      body
        font-size fontSize
        background-color $bgColor
      
      // 函数
      fonts(){
         border:2px solid 
         font-weight bold
         width 100px
         height 100px
      }
      
      .small
        fonts()
        border 2px solid
        background blue
      // 选择器
      
      // textarea
      // input 
      //    color blue
      //    background-color red
      
      textarea
      input 
         color blue
         &:hover
           color red
      
      // 插值
      vendor(prop,args)
         -webkit-{prop} args
         -moz-{prop} args
         {prop} args
      
      border-radius()
        vendor('border-radius',arguments)
      
      border-shadow()
        vendor('border-shadow',arguments)
      
      .small 
         border-radius 10px
      

三:Scss

世界上最成熟、最稳定、最强大的专业级CSS扩展语言!

  • 下载

    cnpm install node-sass --save-dev
    cnpm install sass-loader --save-dev
    
  • 使用

    <style lang="scss"></style>
    
  • 语法

    // .box{
    //   width: 200px;
    //   height:200px;
    //   background: pink;
    //   >a{
    //      text-decoration: none;
    //   }
    // }
    .box{
      width: 200px;
      height:200px;
      background: pink;
      a{
         text-decoration: none;
          span{
    
          }
      }
      &:hover{
        background: red;
      }
    }
    

四:动画

https://cn.vuejs.org/v2/guide/transitions.html

vue中动画的使用场景:

  • v-if 条件渲染
  • v-show 条件展示
  • 动态组件 is
  • 组件根节点

transition组件

transition组件有个name属性,name属性就是动画名称,name值是自定义

在进入/离开的过渡中,会有 6 个 class 切换。

【从隐藏到显示】

动画名-enter 进入前

动画名-enter-active 进入中

动画名-enter-to 进入后

【从显示到隐藏】

动画名-leave 离开前

动画名-leave-active 离开中

动画名-leave-to 离开后

<template>
  <div id="app">
    <button @click="state=!state">切换</button>

    <transition name="move">
         <div class="box" v-show="state"></div>
    </transition>

  </div>
</template>

<script>


export default {
  data(){
   return {
     state:true
   }
  }
}
</script>

<style lang="scss">
.box{
  width: 300px;
  height:300px;
  background: red;
}

.move-enter{
  transform: translateX(100px);
}
.move-enter-active{
  transition:all 10s linear;
}
.move-enter-to{
  transform: translateX(-100px);
}


.move-leave{
  transform: translateY(-50px);
}
.move-leave-active{
  transition:all 10s linear;
}
.move-leave-to{
   transform: translateY(200px);
}

</style>

transtion结合animate.css使用

https://cdn.jsdelivr.net/npm/animate.css@3.5.1 浏览器打开下载animate.css

https://animate.style/#documentation 查找有哪些动画

 //引入animate.css 
// main.js中引入 ---每一个组件都可以使用
// 单独的组件中引入
 <transition 
      enter-class=""
      enter-active-class="animated fadeIn"
      enter-to-class=""

      leave-class=""
      leave-active-class="animated fadeOut"
      leave-to-class=""
    >
         <div class="box" v-show="state"></div>
    </transition>
    
    <style>
    @import url('./assets/animate.css');
    </style>
    

transtion 只能有0个或1个子节点

transtion-group可以有0个或多个子节点

<template>
  <div id="app">
    <button @click="state=!state" class="animated">切换</button>

    <!-- <transition name="move">
         <div class="box" v-show="state"></div>
    </transition> -->

    <transition 
      enter-class=""
      enter-active-class="animated fadeIn"
      enter-to-class=""

      leave-class=""
      leave-active-class="animated fadeOut"
      leave-to-class=""
    >
         <div class="box" v-show="state"></div>
    </transition>


    <ul>
      <transition-group 
      enter-class=""
      enter-active-class="animated fadeIn"
      enter-to-class=""

      leave-class=""
      leave-active-class="animated fadeOut"
      leave-to-class=""
    >
      <li v-for="(item,index) in arr" :key="index">
        {{item}}
        <button @click="del(index)">删除</button>
      </li>
      </transition-group >
    </ul>

  </div>
</template>

<script>


export default {
  data(){
   return {
     state:true,
     arr:['香蕉','juzi' ,'bbb']
   }
  },
  methods:{
    del(ind){
      this.arr.splice(ind,1)
    }
  }
}
</script>

<style>
@import url('./assets/animate.css');
.box{
  width: 300px;
  height:300px;
  background: red;
}



</style>

day14+(TS)

Typescript的学习

1:什么是Typescript

​ 1)Typescript不是新的语言,是JavaScript语言的超集

​ 2)Typescript是由微软开发的开源编程语言,增加了非常有用的编译时类型检查特性*,最终被编译成JavaScript来执行

2:为什么要学习Typescript

  1. 提升自身技能

2)增加了代码的可读性和可维护性,开发大型项目

3)如果不显示定义类型,能够自动做出类型推论

4)Typescript拥有活跃的社区

缺点:增加学习成本 增加了设定类型的开发时间

3:如何去使用Typescript

ts的学习,学习的是语法,好多的语法,es5,es6…已经学过了

  • ts文件可以直接引入的

    <script src="index.ts" ></script>
    
  • 环境安装

​ ts的很多语法浏览器是不支持,安装环境,将ts进行编译,编译成浏览器可以执行的js

安装·

npm install -g typescript

检测是否安装成功

tsc -v     //可以查看到版本说明安装成功
  • 编译文件

    tsc xxx.ts    //同级目录下生成一个同名的js文件
    tsc --outDir  ./js   xxx.ts    //把ts文件编译到指定的目录
    
  • 自动编译 tsconfig.json

      ts --init   //生成tsconfig.json配置文件
    

    “outDir”:‘出口文件名’ 出口文件设置
    “rootDir”:“入口文件名” 入口文件设置

    修改tsconfig.json,配合tsc -w 实时热更新

4:Typescript类型介绍

ts类型是非常严格的,定义了什么类型就必须是什么类型

let b=10;   //正确  规定b是数值类型
b="123"    //Type '"123"' is not assignable to type 'number'.2 b="123"
  • 声明变量

语法:

let 变量名:数据类型; //声明变量并指定数据类型,给变量赋值必须是指定的该数据类型

let 变量名:数据类型= 值;

代码:

let num:number;
num=58;
// num="str"  错误的
let n:number=45;
console.log(n)

在typescript中给我们提供了以下数据类型

  • 字符串(string)
  • 数字类型(number)
  • 布尔类型(boolean)
/**
 * 字符串
 */
let str:string;
str="123"

let str1:string="78";
/**
 * number 数值
 */
 let n:number;
 n=12;
 let n2:number=23;
 console.log(n2)

 /**
 * 布尔   boolean
 */
let bool:boolean;
bool=true;
  • null和undefined

    /* 
     null和undefined是所有类型的子类型
     undefined表示未定义的值
      null代表空  一个对象被人为的定义为空对象
    */
      let tmp:undefined;
    tmp=undefined;
    //    let tmp1:number=undefined;  //错误的
    
    let tmp2:null=null;
    // tmp2={}  报错
    
    
  • 数组类型(array)

/**
*数组  
const arr=[1,"25",true]
const arr1=new Array()
*/
// ts中有两种方式定义数组 ---数组中每一个数据的元素类型指定什么类型就只能是什么类型
// 1--在元素类型后面加上[]--
const arr:string[]=["1","5"];

// arr[0]="45";
// arr[1]='true';
// console.log(arr)


// 2---使用数组泛型  Array<元素类型>

const arr1:Array<number>=[25,36];
let arr2:Array<boolean>;
arr2=[true,false]
  • any类型

    /**
     *  any任意类型 可以是任何数据类型
     */
    
     let a:any;
     a=5;
     a="123";
     a=true;
    const arr5:any[]=[45,"bugai",true];
     const arr6:Array<any>=[45,"123"]
     console.log(arr5,arr6)
    
  • 元组类型(tuple)

     /**
     *  元组类型  表示一个已知元素数量和类型的数组,各元素的类型不必相同
     * 
     * 语法:const 变量名:[类型1,类型2,类型3]=[14,"1ew",true]
     * 注意:前面有几个类型  后面就只能有几个值
     *       每一对类型和值必须一一对应
     */
    
    //  let arr7:[string,boolean,number,string]=[];//错误[]没有值,数量不统一
    let arr7:[string,boolean,number,string]=["hello",true,25,"58"];
    
  • 枚举类型(enum)

    /**
     * 枚举类型
     *   我们的值只会出现在一定范围中
     *     性别:   男    女    保密
     *     状态:   成功    失败
     *     请求方式 : get,post,delete...
     * 
     *   语法:
     *   enum 枚举名 {标记名1,标记名2,标记名3}
     *         默认    0         1      2
     *    enum 枚举名 {标记名1=值,标记名2=值,标记名3=值}
     * 
     *   使用
     * let 变量:枚举名=枚举名.标记名
     * 
     */
    enum Sex {man,women}
    let s:Sex=Sex.women
    console.log(s)
    
    enum Status {success=200,err=400}
    let result:Status=Status.success
    console.log(result)
    
  • void类型

    /**
     * 
     * void 没有任何类型  主要指的是函数没有返回值
     */
    
     function fn():void{
        console.log(1)
     }
     fn()
    
  • never类型

    /**
      * never 类型  异常
      */
    
      function fn2(): never {
          throw new Error()
      }
    
  • 类型断言

    /**
     * 类型断言:告诉编译器你比它更了解这个类型,并且它不应该发出错误,就是告诉不要去
     * 对这个值进行类型检查
     * 
     */
    function fn3(x:any):any{
      if(typeof x==='string'){
        //  return (<string>x).length
        return (x as string).length
      }
    }
    

5:ts中函数定义

1:为函数定义类型
/****为函数定义类型 
 *   1:参数类型定义
 *   2:返回值类型定义
 * 第一种:
 * function 函数名(参数:数据类型):返回值数据类型{
 *    return  值;  //值的类型必须和返回值的数据类型一致
 * }
 * 第二种:
 * let 函数名=function(参数:数据类型):返回值数据类型{
 * 
 * }
 * void  函数没有返回值
*/

function show(num:number):void{
  console.log(num)
}
show(24)

function show1(x:string,y:string):string{
    return x+y;
}
console.log(show1("中公","优就业"))


let show2=function (x:number,y:string):void{
  console.log(x+y)
}
show2(14,"it")

let show3=function():string{
   return "web前端"
}
console.log(show3())

可选参数
/**可选参数
 * js中参数是可选的  不传时候值为undefined
 * ts 可选参数使用  ?  定义 
 * 注意:ts中没有配置可选参数,你不传值  就会报错
*/

function get(x:string,y?:number):string{
  return x+y;
}

console.log(get("中公",520))
console.log(get("中公"))
默认参数
/******默认参数  末尾的参数才能设置默认参数 */

function getd(x:number,y:number=25):number{
  return x+y;
}
console.log(getd(12,23))
console.log(getd(12))
剩余参数
/****剩余参数
 * 
 * 你想同时操作多个参数,或者你并不知道会有多少参数传递进来
 */

 function get1(...data:any[]){
    console.log(data)
 }

//  get1(1,2,3,true)
//  get1(1)
// 剩余参数要放在尾部
function get2(x:number,...data:any[]){
  console.log(data)
}
get2(25,"123","aaa","bbb")

6:ts装饰器

装饰器是对类,函数,属性的一种装饰,可以针对其添加一些额外的行为

通俗点理解就是在原有代码的外层包装了一层处理器

装饰器其实就是一个函数

启用装饰器特性,需要在tsconfig.json里启用experimentalDecorators编译器选项

"experimentalDecorators": true,

装饰器就是在调用某个方法,某个属性,某个类,某个参数之前执行一段函数,

定义装饰器

function 装饰器名(target){

}  //不能传参数

function  装饰器名(参数:类型){

     return function(target){

     }

}

类装饰器

function 装饰器名(target){

}

@装饰器名

class 类名{

} //该类被实例化的时候装饰器函数就会被调用

代码

function add(target:any){
  console.log(target,"target")
  target.prototype.num=12;
}

@add     //后面不能加任何符号
class Stu{
   constructor(){
     console.log("构造函数")
   }
}

let s1=new Stu()
console.log(s1)

可以传参的装饰器

function add(num:number){
  return function(target:any){  //return 的函数才是真正的装饰器
     console.log(target,"target")
  }
}

@add(15)     //后面不能加任何符号
class Stu{
   constructor(){
     console.log("构造函数")
   }
}

let s1=new Stu()
console.log(s1)
方法装饰器

function 装饰器名(类的原型对象,方法名,方法属性描述){

}

class 类名{

​ @装饰器名

​ 方法名(){

​ }

}

function add(target:any,name:any,desc:any){
  
     console.log(target,"target")
     console.log(name,"方法名")
     console.log(desc,"属性描述")
 
}
class Stu{
   @add     //后面不能加任何符号
   run(){
     console.log("nihao")
   }
}

let s1=new Stu()
console.log(s1)
属性装饰器

function 装饰器名(类的原型对象,属性名){

}

class 类名{

​ @装饰器名

public 属性名

}

function add(target:any,propertyKey:any){
  
     console.log(target,"target")
     console.log(propertyKey)
     
 
}
class Stu{
   @add     //后面不能加任何符号
   public name:string;
   constructor(n:string){
      this.name=n;
   }
}

let s1=new Stu("zhans")
console.log(s1)
参数装饰器
function required(target:any, propertyKey: string, parameterIndex: number){
   console.log(target)   //类的原型对象
   console.log(propertyKey)  //方法名
   console.log(parameterIndex)   //参数的索引位置
}

class Greeter {
 greeting: string;

 constructor(message: string) {
     this.greeting = message;
 }


 greet(@required name: string) {
     return "Hello " + name + ", " + this.greeting;
 }
}

7:vue-cli脚手架升级

1:如果安装旧版本 卸载
npm uninstall vue-cli -g

安装

npm install @vue/cli -g

查看版本

vue -V
or
vue --version
2:初始化项目
vue create 项目名

启动项目

yarn serve
3:目录结构对比
4:使用ts语法开发

vue-property-decorator 是vue社区提出的一个对vue使用class类方式开发的一个重要包

1:创建组件About.vue
<template>
    <div>
        about
    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{

}
</script>
<style lang="stylus">

</style>
2:引入组件
<template>
  <div id="app">
   <!-- 3---调用组件 -->
   <About></About>
  </div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'

// 2---使用引入的组件
@Component({
  components:{
     About
  }
})
export default class App extends Vue{

}
</script>
<style lang="stylus">

</style>
3:新语法–定义data数据
<template>
    <div>
        about
        {{str}}
        <li v-for="(item,index) in arr" :key="index">
            {{item}}
        </li>
    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
    str:string="中公it优就业"   //没有data  直接定义
    private arr:any[]=["aaa","Bbb","cccc"];
}
</script>
4:新语法—生命周期
<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
    str:string="中公it优就业"   //没有data  直接定义
    private arr:any[]=["aaa","Bbb","cccc"];
    created(){
        console.log("生命周期created")
    }
    mounted(){
        console.log("mounted")
    }
}
</script>
新语法–没有了methods
<template>
    <div>
        <li v-for="(item,index) in arr" :key="index">
            {{item}} <button @click="del(index)">删除</button>
        </li>
        <button @click="changestr">修改str</button>
    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
    str:string="中公it优就业"   //没有data  直接定义
    private arr:any[]=["aaa","Bbb","cccc"];
    changestr(){//修改str
     this.str="北京中公教育科技有限公司"
    }

    del(index){
        this.arr.splice(index,1)
    }
}
</script>
新语法—没有了computed 直接写前面带上get
<template>
    <div>
        about
        {{str}}
        <li v-for="(item,index) in arr" :key="index">
            {{item}} <button @click="del(index)">删除</button>
        </li>

        {{mystr}}
        <button @click="changestr">修改str</button>
    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue} from 'vue-property-decorator'
@Component({})
// 使用class方式创建类
export default class About extends Vue{
    str:string="中公it优就业"   //没有data  直接定义
    private arr:any[]=["aaa","Bbb","cccc"];
    created(){
        console.log("生命周期created")
    }
    mounted(){
        console.log("mounted")
    }

    changestr(){//修改str
     this.str="北京中公教育科技有限公司"
    }

    del(index){
        this.arr.splice(index,1)
    }

    get mystr(){
        return "我的公司是"+this.str
    }
}
</script>
新语法 没有watch 在前面加上watch装饰器
// 监听
// 浅监听 @Watch('监听的变量')
    @Watch('str')
    changefn(newval:string,oldval:string){
        console.log("新值是"+newval,"旧值是"+oldval)
    }
    // 深度监听 
    // @Watch(监听的变量,{immediate:true,deep:true})
    @Watch('arr',{immediate:true,deep:true})
    changearr(newval:any){
console.log("新值为",newval)
    }
组件通信------父传子

父组件

<template>
  <div id="app">
   <!-- 3---调用组件 -->
   <About :message="message"></About>
  </div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'

// 2---使用引入的组件
@Component({
  components:{
     About
  }
})
export default class App extends Vue{
     message:string="欢迎来到中公教育"
     num:number=25;
}
</script>

子组件

<template>
    <div>
      {{message}}
    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue, Watch,Prop} from 'vue-property-decorator'

@Component({})
// 使用class方式创建类
export default class About extends Vue{
//    @Prop 属性装饰器
// @Prop(String) readonly message:string |undefined
//   1:传入message属性,只读,是string类型,可以传入可以不传入
 
//  @Prop([String,Number]) readonly message?:string |number
//   2: 传入message属性,只读  可以是number或string  ?表示不传入  !表示必须传入
    @Prop({
        type:String,   //多个就写[Number,String]
        default:"hellow",
        required:true,
        validator:function(val){
            return val.length>3;
        }
    })
    private message:string|undefined
}
</script>
<style lang="stylus">

</style>
组件通信 子传父

@Emit装饰器

父组件

<template>
  <div id="app">
    {{message}}
   <!-- 3---调用组件 -->
   <About @to-parent-fn="changefn"></About>
  </div>
</template>
<script lang="ts">
import {Component ,Vue} from 'vue-property-decorator'
// 1--引入组件
import About from './components/About.vue'

// 2---使用引入的组件
@Component({
  components:{
     About
  }
})
export default class App extends Vue{
     message:string="欢迎来到中公教育"
     num:number=25;
     changefn(val:string){
       this.message=val
     }
}
</script>
<style lang="stylus">

</style>

子组件

<template>
    <div>
     <!-- 直接调用@Emit -->
     <!-- <button @click="toParentFn">传值1</button> -->
<button @click="go">传值1</button>

    </div>
</template>

<script lang="ts">
// lang="ts"表示使用ts语法
//   引入类装饰器
import {Component ,Vue, Watch,Emit} from 'vue-property-decorator'

@Component({})
// 使用class方式创建类
export default class About extends Vue{
    private msg="我是中公讲师"
 
// 1---不传入事件名,表示下面紧贴的函数就是自定义事件
// @Emit()
// toParentFn(){
//     return this.msg;  //return 的值就是传递给父组件的值
// }

// 2  传入事件名,事件名就是该自定义事件
//  @Emit('to-parent-fn')
//  toParentFn(){
//    return this.msg+"2222"
//  }

// 3--跟方法1 相同  只不过间接调用
@Emit()
toParentFn(){
    return this.msg+"33333"
}

go(){
    this.toParentFn()
}

}
</script>
<style lang="stylus">

</style>
配置文件

手动在根目录下创建vue.config.js文件

module.exports={
     devServer:{
         proxy:{
             '/api':{
                 target:'http://xxx.com',
                 ws:true,
                 changeOrigin:true
             }
         }
     }
}

重新启动项目

错误集锦

‘n’ was also declared here.

n被使用过了

SSR服务器端渲染

https://www.nuxtjs.cn/

整个网站先在服务器中运行,然后返回一个完整的html字符串,将这个字符串当成响应内容输出给浏览器

ssr 解决的就是搜索引擎记录页面信息的问题

1-1:ssr有什么好处?

有利于搜索引擎抓取我们的页面

build之后,会静态化page页面,所以访问速度快

1-2:什么是Nuxt.js

Nuxt.js是基于vue的服务器端的渲染框架

1-3:使用

Vue.js本身没有附带SSR,但是有很好的库可以很容易地将SSR添加到我们的Web应用程序中。最受欢迎的两个库是vue-server-renderer和Nuxt.js。

1-3-1:vue-server-renderer

  • 1:安装依赖

    新建一个文件夹,安装Vue与SSR依赖包vue-server-renderer

    mkdir ssr-test //新建空文件夹
    cd ssr-test //进入目录
    npm init //初始化,生成 package.json
    npm install vue vue-server-renderer express --save// 安装
    
  • 创建启动文件

    在当前目录下创建一个app.js文件,并编写如下内容:

    const Vue = require('vue')
    const server = require('express')()
    const fs = require('fs')
    
    //读取模板---index.html
    const renderer = require('vue-server-renderer').createRenderer({
        template: fs.readFileSync('./index.html', 'utf-8')
    })
    
    // 此参数是vue 生成Dom之外位置的数据  如vue生成的dom一般位于body中的某个元素容器中,
    //此数据可在header标签等位置渲染,是renderer.renderToString()的第二个参数,
    //第一个参数是vue实例,第三个参数是一个回调函数。
    const context = {
        title: 'Vue-ssr-demo',
        meta: `<meta name="viewport" content="width=device-width, initial-scale=1.0">
               <meta http-equiv="X-UA-Compatible" content="ie=edge">`
    }
    server.get('*', (req, res) => {
        //创建vue实例   主要用于替换index.html中body注释地方的内容,
        //和index.html中 <!--vue-ssr-outlet-->的地方是对应的
        const app = new Vue({
            data: {
                url: req.url,
                data: ['这是一个ssr示例'],
                title: '学习vue-ssr服务端渲染'
            },
            //template 中的文本最外层一定要有容器包裹, 和vue的组件中是一样的,
            //只能有一个父级元素,万万不能忘了!
            template: `
                <div>
                    <p>{{title}}</p>
                    <p v-for='item in data'>{{item}}</p>
                </div>
            `
        })
    
        //将 Vue 实例渲染为字符串  (其他的API自己看用法是一样的)
        renderer.renderToString(app, context, (err, html) => {
            if (err) {
                res.status(500).end('err:' + err)
                return
            }
            //将模板发送给浏览器
            res.end(html)
        })
    })
    server.listen(8080)
    
  • 引入模板

    在app.js的平级目录处创建一个index.html,空内容

  • 启动项目

    在命令行中进入项目目录,执行如下命令启动项目:

    node app.js
    
  • 然后在浏览器中打开http://localhost:8080

1-3-2:Nuxt.js

安装
  • 安装create-nuxt-app工具

    cnpm install -g create-nuxt-app
    
  • 使用create-nuxt-app创建nuxtjs项目

    create-nuxt-app myapp-nuxt
    

    创建nuxt项目的过程中,会问我们选择哪种渲染方式,一定要要选择Universal(通用的,普遍的)

    spa 是单页面应用,这种模式下,文件不会ssr渲染

  • nuxt项目创建完毕后,先进入到项目中,然后运行

    cd myapp-nuxt
    npm run dev
    

    在浏览器中运行 http://localhost:3000

修改服务端口

package.json

"config": {
    "nuxt": {
        "host": "0.0.0.0",
        "port": "3333"
    }
},
"script":{
    
}

重新启动项目

引入css文件

​ nuxtjs没有所谓的index.html入口页,这个index.html实际是由nuxt.config.js编译而成的

  • 引入公共的css文件

    nuxt.config.js

     css: [
         'assets/css/resert.css'
      ],
    
rem文件引入

在 static 目录下,建立 js/rem.js 文件。

static 目录是存放独立的文件的。

我们应该使用 script 标签引入 rem.js 文件,但 nuxtjs 中没有 html 页面,需要写在 nuxt.config.js 中。

在 nuxt.config.js 文件中:

head: {
    script:[
        { src: '/js/rem.js', async: true, defer: true }
    ]
}
路由
  • 1:主入口文件layouts/default.vue类似于我们vue中的App.vue

    <template>
      <div>
        <!-- 引入的组件 -->
        <Head></Head>
    <!-- 类似于router-view -->
        <Nuxt></Nuxt>
        <!-- 引入的组件 -->
        <Foot></Foot>
      </div>
    </template>
    <script>
    // ~  别名,固定的,是根目录
    import Head from '~/components/Head.vue'
    import Foot from '~/components/Foot.vue'
    export default {
       components:{
         Head,
         Foot
       }
    }
    </script>
    <style>
    </style>
    
    
  • components 目录下新建全局公用的组件库,该目录下新建Head.vue和Foot.vue

    • Head.vue

      <template>
        <div>
         head
        </div>
      </template>
      
    • Foot.vue

      <template>
        <div>
          <ul>
            <li>
            <!--nuxt-link相当于 router-link-->
              <nuxt-link to="/">首页</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/cat">分类</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/cart">购物车</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/mine">我的</nuxt-link>
            </li>
          </ul>
        </div>
      </template>
      <style scoped>
      div ul {
        display: flex;
        justify-content: space-around;
        align-items: center;
      }
      div li {
        text-align: center;
      }
      div a {
        color: white;
        text-decoration: none;
      }
      div .nuxt-link-exact-active {
        color: green !important;
      }
      </style>
      
  • pages下面创建路由组件

  • index.vue是路由 ‘/’ 对应的组件, ’路由 ‘/mine’ 对应的组件是在pages下新建mine/index.vue

  • 然后就可以实现路由跳转了

获取远程数据

下载

cnpm i axios -S

引入

import axios from 'axios'

使用

ayncData方法会在组件每次加载之前被调用,它可以在服务端或路由更新之前被调用

<script>
import axios from 'axios'
export default {
  data(){
    return {
    
    }
  },
 async  asyncData(){
    let  {data} = await axios.get('http://jsonplaceholder.typicode.com/posts')
    // console.log("data",data)
    return {arr:data};  //这个return会把结果和data属性值自动合并,视图层直接调用即可
  }

}
</script>

如果不喜欢es6的async/awai,那么也可以使用回调函数

asyncData({ params },callback){
     axios.get('http://jsonplaceholder.typicode.com/posts')
     .then(res=>{
       callback(null,{
         arr:res.data
       })
     })
 }

注意:在视图层可以直接使用return 返回来的数据

{{arr}}
打包

项目都开发完毕之后,我们需要将开发环境下的碎片化的文件做合并,这个过程叫做打包(发布)

如果 mode: ‘universal’ 模式

需要到package.json 文件中添加 “generate”: “nuxt generate”,

npm run generate

运行之后就会生成dist目录

如果 mode: ‘spa’ 模式

npm run build
部署

将上一步的dist文件夹放入网站服务器下

后端服务可以是nodejs

新建文件夹 www

dist
app.js

app.js

//cnpm install express
const express =require('express');
const app=express()

app.use(express.static('dist'))
app.listen(8888)

用户打开浏览器 访问该项目了 http://localhost:8888

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值