Vue

Vue介绍

  • 构建用户界面(UI网页)的框架,关注视图层
  • 框架:综合功能的封装
  • MVVM(数据驱动视图)model view modelview
  • 可以轻松构建单页面应用程序(spa) single page application
  • 核心特点:
    • 数据操作视图更新,解放DOM操作
    • 组件化开发,提高开发效率和维护
  • 官方教程:http://cn.vuejs/org/guide/
  • 不支持IE8及以下

Vue下载

  • 网址::https://cdn.vuejs.org/index.html
  • CDN缓存技术https://cdn.jsdelivr.net/npm/vue/dist/vue.js
  • npm下载 npm i vue

Vue基本使用

  • 引入vue.js文件,通过script标签

  • 会有一个构造函数Vue

  • 实例化构造函数,参数传入一个对象

    new Vue({
        el:'p',
        data:{
        num:0,//数字初始化
        str:'',//字符串初始化
        isSeen:false,//布尔类型初始化
        arr:[],//数组初始化
        obj:{},//对象初始化或者开始为null
    }
    })
    
  • 通过vue的自定义属性,操作页面中的元素,其属性值,相当于放在js中解析,支持javascript的表达式

    <p v-show='false'>对p标签进行隐藏</p>
    <p v-if='false'>
        对p标签进行隐藏
    </p>
    <!--v-if属性值为布尔类型时,一般如果不写值,默认为true
    v-show如果不添加任何值,默认值为false-->
    <p v-text="msg">相当于innerText</p>
    <p v-html> 相当于innerHTML</p>
    <p>{{msg}}</p><!--将数据展示到标签内部{{}}和v-text均可-->
    
    
  • vue可以结合其他类库一起使用,互不影响

Vue实例对象的属性(实例选项)

el

  • 视图,模板

  • 是划分范围,选中页面中的元素

  • 值可以是选择器,DOM对象,jQ对象,建议使用id

  • 如果选择器选中的是多个,只会对一个生效

  • 不可以操作body和html

    new Vue({
        el:document.querySelectorAll('div');//如果选中所有不会生效,还会报错
    })
    

data

  • 数据

  • 其值是一个对象

    new Vue({
        el:'p',
        data:{
            seen:false,
            msg:'我是一段文本',
            list:[1,2,3,4],
            user:{
                name:'张三',
                age:18,
                gender:'男'
            }
        }
    })
    
  • 概念:

    data—数据

    对数据进行管理–>模型model

    el–>视图 模板

  • 注意:

    • 对于对象的修改
      • 对象没有初始化的对象成员如果修改不会更新到视图中
      • 也有方式可以动态添加未初始化的数据成员并且更新到视图中解决方法:Vue.set(app.user,'gender','男')
      • 可以直接对对象进行重新赋值实现视图更新
    • 对于数组的修改
      • 通过数组的一些操作方法可以正常进行视图的更新
      • 对数组进行重新赋值可以更新视图
      • 通过索引设置数组不会更新 vm.list[0]=newValue解决方法:Vue.set(vm.list,0,{newValue})
      • 通过设置数组长度不可以更新视图vm.list.length=newLength

methods

  • 其值是一个对象

  • 专门定义方法的属性,也可以定义事件的回调函数

  • 注意:当在data中定义了和methods中相同的属性名,以data中的为准,methods中不允许重复

  • methods属性不允许使用箭头函数进行定义,会改变this指向(window),但是可以使用ES6方式去定义

    new Vue({
        el:'#app',
        methods:{
            clicked:function(){
                
            },
            //与上面定义的方法相同:clicked(){
                
           // }
        }
    })
    

mounted

  • 类似window.onload

  • 页面加载就执行

    new Vue({
        el:'p',
        data:{},
        mounted:function(){
            document.querySelector('li:nth-child(2)').style.color='red';
        }
    })
    
    

watch

  • 侦听器

  • 监听数据变化的机制

    new Vue({
        el:'p',
        watch:{
            msg:function(newValue,oldValue){
                //newValue 新变化的数据
                //oldValue 旧变化的数据
                console.log(newValue,oldValue)
            }
        }
    })
    

computed

  • 计算属性通道

  • 当视图中的数据处于不确定状态时,采用计算属性

  • 其中属性值为这个属性对应的函数的返回值

  • 在data中定义过的属性,不准在computed中重复定义,所以想要计算某个属性,直接无需在data中定义,在computed直接使用即可,就相当于定义过了

    new Vue({
        el:'p',
        data:{
           student:null, 
        },
        computed:{
            student:function(){
                return this.students.filter(item=>{
                    return this.name=item.name
                })
            }
        }
    })
    

created

  • vue实例对象实例化完成之后出发

    new Vue({
        el:'p',
        created(){
           
        }
    })
    

数据绑定

  • 模型中的数据与视图的对应关系
  • 节点=属性+标签+内容
  • 数据绑定=属性绑定+文本绑定+事件绑定

属性绑定

  • 在Vue中对属性进行数据绑定,使用v-bind:属性名="值"

  • 可以简写<p :class="[cls1]"></p>

  • 语法:

    <p v-bind:title='title'>一段内容</p>
    <script>
        new Vue({
            el:'p',
            data:{
                title:'我是title'
            }
        })
    </script>
    
  • 属性绑定特殊

    • style属性(相对特殊,绑定时,其值为对象)

      <p v-bind:style="{color:'red',backgroundColor:'#ffff'}">
         注意:red加上引号,否则按照变量进行解析会报错
      </p>
      
    • class属性(相对特殊,绑定时,其值为对象,其中属性名为存在的类名,属性值为布尔类型,是否使用该属性名作为类名)

      <p v-bind:class="{test:true,demo:false}">
          
      </p>
      <p v-bind:class="[cls1,cls2]">
          
      </p>
      <script>
          new Vue({
              el:'p',
              data:{
                  cls1:'test',
                  cls2:'demo'
              }
          })
      </script>
      
      
      

文本绑定

  • {{}}不能写在属性中

  • 语法:

    <div id='app'>
        <p v-text='txt'>
    		相当于innerText    
    	</p>
        <p v-text='"我是文本“'>
            此处方式主要,因为Vue的自定义属性内部的属性值,相当于放到js中解析,所以要注意书写格式
        </p>
    	<p v-html='txt1'>
        	相当于innerHTML
    	</p>
    	<p>
        	{{msg}}常用
    	</p>
        <p>
            {{list[0]}}支持访问数组单元
        </p>
        <p>
            {{user.name}}支持访问对象,访问方式还是对象.的方式
        </p>
    </div>
    <script>
    
        new Vue({
            el:'p',
            data:{
                txt:'我是文本',
                txt1:'<h1>我是html结构的文本</h1>',
                msg:'我也是文本',
                list:['a','b','c'],
                user:{
                    name:'张三',
                    age:18
                }
            }
            
        })
    </script>
    
    

事件绑定

  • 语法:v-on:事件名称=“回调函数”

  • 可以简写@事件名称=”回调函数“

  • 当没有事件的回调函数没有传递参数的时候,实际上是有一个默认参数的,叫事件对象,实质和js中学的一样,传入书写固定格式为$event

    <div id="app">
        <a href="javascript:;" v-on:click='clicked'点击</a>//相当于方法的调用,传递的参数相当于实参
        <input type="button" @click="clicked1($event,a)" value='点击我获取事件对象'>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{},
           //提供专门定义方法的属性methods,也可以定义事件的回调函数
            methods:{
                clicked:function(){
                    console.log('点了');
                },
                clicked1:function(ev,n){//事件的声明,参数相当于形参
                    console.log(ev,a);
                    console.log(ev.target);//表示当前点击的DOM对象
                }
            }
        })
    </script>
    
    

事件绑定-修饰符

  • 在vue中通过,.修饰符进行解决一些特殊的逻辑问题,如:冒泡,获取按键信息等
  • 事件修饰符:
    • .stop 阻止冒泡
    • .prevent 阻止表单提交默认行为
      • 以下几点会触发表单的提交行为
        • 点击type为submit的input
        • 在文本框中回车
        • 点击普通按钮<button></button>
        • 建议在form的submit事件中阻止表单自动提交行为
    • .capture
    • .self
    • .once
    • .passive
  • 按键修饰符
    • .13
    • .enter
    • .tab
    • .delete
    • .esc
    • .space
    • .down
    • .left
    • .right
    • .up
  • 系统修饰符
    • .ctrl
    • .alt
    • .shift
    • .meta
  • vue中也存在冒泡
    • 在原生js中阻止冒泡行为通过事件对象.stopPropagation()阻止
  • 这里使用原生js方式也是可以的,但是vue提供自己特有的方式
<div id='app'>
   <div id='parent' @click="clickParent">
       <div id="child" @click.stop="clickChild"><!--阻止冒泡事件-->
           
       </div>
    </div>
    <form @submit.prevent='send'>//阻止表单提交默认行为
        
    </form>
    <input type="text" @keyup="send">
    <input type='text' @keyup.13="send"><!--点击回车按键触发send事件-->
    <input type="text" @keyup.enter="send"><!--点击回车键触发send事件-->
    <input type="text" @keyup.shift.enter="send"><!--点击shift+enter触发send事件-->
    <input type="text" @keyup.ctrl.shift.enter="end"><!--点击ctrl+shift+enter按键触发send事件-->
</div>
<script>
    new Vue({
        el:'#app',
        methods:{
            clickParent(){
                console.log('父亲');
            },
            clickChild(){
                console.log('儿子');//当点击子元素,父元素的点击事件也会触发,默认产生冒泡
            },
            send(){
                console.log('你按下了这些按键……');
            }
        }
    })
	
</script>

事件绑定-this指向

  • 事件绑定中的this指向是vue的实例对象,且不可采用箭头函数的书写方式,因为会改变this的指向(window)

事件绑定-参数

  • 默认参数是事件对象$event

    <p @click='clicked'>
        
    </p>
    <!--等价于-->
    <p @click='clicked($event)'>
        
    </p>
    
  • 传递参数直接<p @click='clicked(a,$event)'></p>

    new Vue({
        el:'p',
        methods:{
            clicked(a,ev){
                  console.log(ev.target);//表示当前点击的DOM对象
    
            }
        }
    })
    

响应式数据绑定

  • 模型中的数据和视图的响应式绑定(自动机制)

  • 自己理解为就是,通过改变模型中的数据data来控制视图的显示

  • vue的实例对象可以直接访问data中的属性,也可以访问methods中的方法,或者vue.$data.属性名

  • 实例:

    <div id="app">
        <h1>
            {{msg}}
        </h1>
        <button @click='changeData'>
            更改一个数据,视图自动变化
        </button>
    </div>
    <script>
        var app=new Vue({
            el:'#app',
            data:{
                msg:'我是文本',
            },
            methods:{
                changeData:function(){//此处不建议采用箭头函数的书写,因为会改变this指向(window)
                    this.msg='我是更改的文本';//这里的this指的是app,vue的实例对象
                    this.demo();
                },
                demo:function(){
            		console.log('我就是一个普通方法');
        		}
            }
        })
    
    </script>
    

双向绑定数据

  • 前提:有自动机制(响应式数据绑定) 模型中的数据与视图相互响应

  • 前面的响应式的数据绑定是:从vue的data中获取数据到视图中

  • 视图上改变vue中的数据data

    <div id="app">
        <input type="text" v-model="txt">//内部相当于为input注册input事件,并且记录input输入的值给txt,通过表单的v-model进行改变模型的data数据
        <button @click='clicked'>
            查看改变的数据
        </button>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                txt:'',
            },
            methods:{
                clicked:function(){
                    console.log(this.txt);
                }
            }
        })
    </script>
    
  • 从视图上更新data数据,并在视图上进行响应(双向绑定)

    <div id="app">
        <h1>
            {{msg}}
        </h1>
        <input type="text" v-model='msg'>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                msg:''
            }
        })
    </script>
    

结构

v-show和v-if区别

  • v-if和v-show都是控制视图的显示或隐藏
  • 两者区别:
    • v-if:直接移除页面中的元素
    • v-show:通过display:none进行控制视图的显示或隐藏元素
    • v-show无论真假始终渲染元素,v-if只有为true才会渲染
    • 所以页面中对元素操作频繁时,用v-show,反之用v-if,另外v-if是惰性的,一旦为false,内部的所有元素都不会渲染

v-if v-else-if v-else的使用

  • 相当于js中的if - else if - else)

  • 注意:

    • v-if与v-else之间不准存在普通标签否则会报错

    • v-if和v-else必须在同一级

      	<!-- 它是一个整体 -->
      	<p v-if='seen'>hello</p> 
      	<!-- 他俩组成一个整体 -->
      	 <p v-if='1==1'>哈哈</p> 
      	 <p v-else>world</p> 
      
      
  • 语法:

    <div id="app">
        <p v-if="age>=17">
            成年
        </p>
        <p v-else>
            未成年
        </p>
        <span v-if="score>=80">
       		优秀 
        </span>
        <span v-else-if='age>=70'>
       		良好 
        </span>
        <span v-else>
       		及格 
        </span>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                age:19,
                score:88,
            }
        })
    </script>
    

v-for使用

  • 相当于js的forin

  • 语法:

    <div id="app">
        
        <ul>
            <li v-for='(item,index) in list'>{{index}}--{{item}}</li>
        </ul>
        <p>
            <span v-for="(item,key,index) in user">{{key}}:{{item}}</span>
        </p>
        <p v-for='n in 10'>
            {{n}}
        </p>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                list:[1,2,3,4],
                user:{
                    name:'张三',
                    age:18
                }
            }
        })
    
    </script>
    
  • 数组更新

    • 常见的数组方法可以让其正常的进行视图更新
    • 或者直接对数组重新赋值
    • 注意以下两种不会更新视图
      • 通过索引直接设置数组不可以更新 vm.list[0]=newValue
      • 通过改变数组长度不可以更新 vm.list.length=newLength
  • 对象更新

    • 对没有初始化的对象成员设置不能更新到视图上
    • 直接重新赋值该对象可以更新到视图上
    • 修改对象的某一个初始化的成员可以更新到视图上

在template标签中使用v-for

  • 当需要遍历多个元素又不想增加额外标签节点时候,使用template标签是vue提供的一个特殊的标签,不会生成渲染结果

  • 注意

    • template上不能使用key,要把key加到真实的节点上

    • 不能使用v-show

      <div id='app'>
          <template v-for='item in todos'>
         		<p>
                  {{item.id}}
              </p> 
              <p>
                  {{item.title}}
              </p>
          </template>
      </div>
      

补充

input事件:表单正在输入触发此事件

微博案例

<div class="weibo" id="app">
    <textarea class="weibo-text"  v-model='msg' @keyup.shift.enter='send'></textarea>
    <input class="weibo-btn" value="发布" type="button" @click='send'>
    <ul class="weibo-list">
        <li v-for='(item,index) in wblist'>{{item}}<span @click='deletewb(index)' :index='index'>删除</span>
        </li>
    </ul>
  </div>
</body>
<script src="./libs/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            msg:'',
            wblist:[],
        },
        methods:{
            send(){
                // console.log('发送');
                this.wblist.unshift(this.msg);
                this.msg=""
            },
        
           
            deletewb(i){
                this.wblist.splice(i,1);

            }
        }
    })
</script>

key的使用

  • vue在进行模板渲染的时候,为了提升性能,并不是每次都创建新的节点,而是当页面有重复的节点,会选择直接复用(此复用是指原位置变化)

  • key的值一般不会使用索引值

  • 只能是简单类型的字符串和数字类型,不能是数组和对象

  • 为了解决它的弊端,存在一个叫key的属性

    <div id="app">
        <div v-if="seen">
                <label for="">用户名:</label>
                <input type="text" key='1' placeholder="username">
            </div>
            <div v-else>
                <label for="">密码:</label>
                <input type="text" key='2' placeholder="password">
            </div>
            <div>
                <button @click='clicked'>切换</button>
          </div>
    </div>
    <script>
            new Vue({
                el:'#app',
                data:{
                    seen:true,
                },
                methods:{
                    clicked:function(){
                        this.seen=false;
                    }
                }
            })
        </script>
    
  • 应用场景:与v-for结合

    <div id="app">
          <ul>
              <li :key='item.id' v-for='item in list'>{{item.name}}
                  <input type="text">
              </li>
          </ul>
        </div>
        <script src="./libs/vue.js"></script>
        <script>
            var app=new Vue({
                el:'#app',
                data:{
                    list:[{
                        id:Math.random(),
                        name:'张三',
                        age:18,
                        gender:'男'
                    },
                    {
                        id:Math.random(),
                        name:'张三1',
                        age:18,
                        gender:'男'
                    },{
                        id:Math.random(),
                        name:'张三2',
                        age:18,
                        gender:'男'
                    }],
                  
                },
                methods:{
                    random(){
                        return 
                    }
                }
              
            })
        </script>
    

指令使用

  • 就是带v-前缀的特殊属性

  • v-once:数据引用一次

    <div id="app">
        <p v-once>
            {{text}}
        </p>
        <p>
          {{text}}  
        </p>
    </div>
    
  • v-cloak解决模板渲染时的页面闪烁,结合css使用

  • 语法:

    <style>
        [v-cloak]{
            display:none;
        }
    </style>
    <div id='app'>
        <p v-cloak>
            {{text}}
        </p>
    </div>
    <script src='./libs/vue.js'></script>
    <script>
        var wm=new Vue({
            el:'#app',
            data:{
                text:'我是',
            },
            
        })
    </script>
    
  • v-pre:表示对应的标签不会再被vue处理

  • 语法:

    <p v-pre>
        {{msg}}
    </p>
    

v-model

  • 跟踪视图改变模型数据

  • 使用

    • 对于type为text和textarea,使用v-model相当于对其进行绑定input事件来进行追踪当前元素内部的value值的变化

      • 其中v-model的修饰符

        v-model.lazy:使其绑定的事件转换为change事件

        v-model.number:使其输入的内容进行数字类型转换

        v-model.trim:使其清除左右两边的空格

        <div id='app'>
            <h1>
                {{username}}
            </h1>
            <input type='text' v-model='username'>
            <textarea v-model='msg'></textarea>
        </div>
        
    • 对于checkbox和radio类型的表单控件,使用v-model相当于对其进行绑定change事件,需要配合value属性,当不设置value时还是控制选中状态

      • 其中多选框需要设置数据为数组类型,为了收集选中的内容

      • 无需通过checked指定默认的选中项

        <div id="app">
             <input type="checkbox" :value='"足球"' v-model='hobby'>足球
             <input type="checkbox" :value='"篮球"' v-model='hobby'>篮球
             <input type="radio"  :value='""' v-model='gender'><input type="radio"  :value='""' v-model='gender'></div>
            <script src="./libs/vue.js"></script>
            <script>
                var app=new Vue({
                    el:'#app',
                    data:{
                       gender:'男',
                       hobby:['足球','篮球']
                      
                    },
                    methods:{
                       
                    }
                  
                })
            </script>
        
    • 对于select类型的表单控件,使用v-model相当于对其进行绑定change事件,可以配合value属性(建议配合)

      • 配合value时,参考的标准为value

      • 不配合value时,参考的标准为当前选中的option的文本内容

        <div id="app">
             <select name="" id="" v-model='number'>
                 <option value="0">--请选择--</option>
                 <option value="1">北京</option>
                 <option value="2">上海</option>
             </select>
            </div>
            <script src="./libs/vue.js"></script>
            <script>
                var app=new Vue({
                    el:'#app',
                    data:{
                       number:1
                      
                    },
                    methods:{
                       
                    }
                  
                })
            </script>
        
        

全选和反选案例

  • 实例:

     <table id="app">
        <tr>
          <th>
            <input type="checkbox" name=""   id="checkAll" :checked='list.length==checkedItem.length' @click='ckClick' 
           />全选/全不选
          </th>
          <th>菜名</th>
          <th>商家</th>
          <th>价格</th>
        </tr>
        <tr :key='item.id' v-for='item in list'>
          <td>
            <input type="checkbox"    class="ck" :value='item.food' v-model='checkedItem'  />
          </td>
          <td>{{item.food}}</td>
          <td>{{item.brand}}</td>
          <td>{{item.price}}</td>
        </tr>
    
      </table>
    </body>
    <script src="./libs/vue.js"></script>
    <script>
            new Vue({
                el:'#app',
                data:{
                    isCk:false,
                    list:[
                        {id:1,food:'红烧肉',brand:'李家',price:'¥200'},
                        {id:2,food:'烧肉',brand:'赵家',price:'¥203'},
                        {id:3,food:'红肉',brand:'孙家',price:'¥205'},
                    ],
                    checkedItem:[],
                },
                methods:{
                    ckClick:function(e){
                        if(e.target.checked)
                        {
                           this.list.forEach(item=>{
                            //    this.checkedItem.push(item.food);
                            console.log(item.food);
                            this.checkedItem.push(item.food)
                            
                            
                           }) 
                        }
    
                    }
                }
            })
    </script>
    
    

动态属性

  • 对于属性绑定和事件绑定中的属性和事件名,变为动态的变量

  • 语法:

    <div id="app">
            <p  @[eve]='clicked' :[prop]='"我是"'><!--注意此处属性值的写法,进入vue管理,属性值当js中解析,所以需要两次引号-->
        </div>
        <script src="./libs/vue.js"></script>
        <script>
            var app=new Vue({
                el:'#app',
                data:{
                    eve:'click',
                    prop:'title',
                },
                methods:{
                clicked(){
                    console.log('我被点了……');
                    
                }
                }
              
            })
        </script>
    

计算属性

  • 当视图中的数据展示处于不确定状态时。当你需要提供一些代码逻辑改变数据,从而进行渲染数据,可以采用计算属性

  • 自己理解:也是双向绑定的一种体现,就是在computed内部计算属性,随着数据的不断改变,从而实现不断重新计算属性,对于事件绑定的事件处理函数均定义在methods中,其余如果涉及到页面渲染的(el,data,computed等),计算属性的方法定义在computed中

  • 触发时机,是页面加载时触发+视图改变促使数据改变

  • 一定要有返回值,返回之后就是属性值

  • 自带缓存技术

      <div id="app">
            <input type="text" v-model='name'>
            <ul>
                <li v-if='student.length'>
                    <span>{{student[0].name}}</span>
                    <span>{{student[0].age}}</span>
                    <span>{{student[0].gender}}</span>
    
                </li>
                <div v-else>
                    什么都没有
                </div>
            </ul>
        </div>
        <script src="./libs/vue.js"></script>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    name: '',
                    students: [
                        { name: '小明', age: 20, gender: '男' },
                        { name: '小画', age: 10, gender: '男' },
                        { name: '小李', age: 22, gender: '男' },
                    ],
                },
                computed: {
                    student: function () {
                        return this.students.filter(item => {
                            return this.name == item.name
                        })
                    }
                },
             
    
            })
        </script>
    

侦听器

  • 监听数据变化的机制

  • 监听数据变化,获取新的数据和旧的数据,没有直接涉及渲染页面,当你想通过数据的变化实现一些特定逻辑,使用watch监视

  • 不需要返回值,不需要绑定模式功能特点

  • 触发机制=数据一改变就触发

      <div id="app">
            <h1>{{name1}}</h1>
            <input type="text" v-model='name'>
            
        </div>
        <script src="./libs/vue.js"></script>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    name: 1,
                    name1:'',
                    seen: false,
                    students: [
                        { name: '小明', age: 20, gender: '男' },
                        { name: '小画', age: 10, gender: '男' },
                        { name: '小李', age: 22, gender: '男' },
                    ],
                },
              watch:{
                    name:function(newV,oldV)
                    {
                        // this.name1=newV.split('').reverse().join('');
                        console.log(1);
                        console.log(Boolean(Number(newV)));
                                      
                        if(!Number(newV)&&newV!='')
                        {
                            this.name=oldV;
                        }
                    }
              }
    
            })
        </script>
    
    

Vue DevTools调试工具

  • vue devtools插件默认只能工作在http协议下
  • 如果想要它在file://本地文件地址协议下工作,必须手动设置启动允许访问文件网址

基础案例

  • todoMVC.com

  • 初始化:

    • 下载模板
    • 使用github把模板下载到本地
    • 切换到下载的目录
    • 模板项目把样式等文件放到第三方包里面,所以我们需要执行npm i安装它的那些依赖项才能正常预览这个项目
  • 需求说明:

  • 任务列表

    • 有数据的时候
    • 没有数据的时候
  • 添加任务

    • 添加任务到列表中
    • 文本框清空
  • 删除单个任务项

  • 单个任务状态切换

  • 删除所有已完成任务

  • 切换所有任务的完成状态(全选功能)

  • 显示所有未完成的数量

  • 数据筛选

    • 展示所有任务
    • 展示已完成任务
    • 展示未完成任务
    • 刷新页面保持筛选状态
  • 数据持久化

    • 这里没有后端接口,我们可以使用本地存储简单处理一下
  • 编辑任务

    • 双击获取编辑状态
    • 回车保存
    • esc取消
  • 代码(index.html)

    <!doctype html>
    <html lang="en">
    
    <head>
    	<meta charset="utf-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1">
    	<title>Template • TodoMVC</title>
    	<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
    	<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
    	<!-- CSS overrides - remove if you don't need it -->
    	<link rel="stylesheet" href="css/app.css">
    </head>
    
    <body>
    	<section class="todoapp" id="app">
    		<header class="header">
    			<h1>todo</h1>
    			<input class="new-todo" placeholder="What needs to be done?" autofocus v-model='message'
    				@keyup.enter='addItem'>
    		</header>
    		<!-- This section should be hidden by default and shown when there are todos -->
    		<section class="main">
    			<input id="toggle-all" class="toggle-all" type="checkbox" @change='toggleChecked' :checked='isAll'>
    			<label for="toggle-all">Mark all as complete</label>
    			<ul class="todo-list">
    				<li :key='item.id' v-for="item in list" :class=" {completed:item.done,editing:item==isEdit}">
    					<div class="view">
    						<input class="toggle" type="checkbox" v-model='item.done' >
    						<label @dblclick="isEdit=item">{{item.title}}</label>
    						<button class="destroy" @click='delItem(item.id)'></button>
    					</div>
    					<!-- {{isEdit}} -->
    					<input class="edit" :value="isEdit.title" @keyup.enter='editItem' @keyup.esc='isEdit={}'>
    				</li>
    			</ul>
    		</section>
    		<!-- This footer should hidden by default and shown when there are todos -->
    		<footer class="footer">
    			<!-- This should be `0 items left` by default -->
    			<span class="todo-count"><strong>{{count}}</strong> item left</span>
    			<!-- Remove this if you don't implement routing -->
    			<ul class="filters">
    				<li>
    					<a class="selected" href="#/">All</a>
    				</li>
    				<li>
    					<a href="#/active">Active</a>
    				</li>
    				<li>
    					<a href="#/completed">Completed</a>
    				</li>
    			</ul>
    			<!-- Hidden if no completed items are left ↓ -->
    			<button class="clear-completed" v-show='isShow' @click='delCompleted'>Clear completed</button>
    		</footer>
    	</section>
    	<footer class="info">
    		<p>Double-click to edit a todo</p>
    		<!-- Remove the below line ↓ -->
    		<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
    		<!-- Change this out with your name and url ↓ -->
    		<p>Created by <a href="http://todomvc.com">you</a></p>
    		<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
    	</footer>
    	<!-- Scripts here. Don't remove ↓ -->
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script src="node_modules/todomvc-common/base.js"></script>
    	<script src="js/app.js"></script>
    </body>
    
    </html>
    
  • 代码(app.js)

    const app = new Vue({
    	el: '#app',
    	data: {
    		message: '',
    		list:JSON.parse(window.localStorage.getItem('todolist'))||[],
    		isEdit: []
    	},
    	watch: {
            //每当list发生变化(添加删除修改)
            //默认只会监视数组成员的添加,删除,无法监视修改到成员中的对象数据修改
            //之前写的形式都是简写
            /*
            list(newValue,oldValue){
            	
            } 
            */
            //深度监视一般针对复杂数据类型:数组,对象之类的
    		list:{
                //handler就是数据改变之后执行的处理函数
    			handler(){
    				window.localStorage.setItem('todolist',JSON.stringify(this.list));
    			},
    			deep:true//深度监视默认为false
    		}
    	},
    	
    	computed: {
    		isAll() {
    			return this.list.every(item => {
    				return item.done
    			})
    		},
    		count() {
    			var count = 0;
    			this.list.forEach(item => {
    				if (!item.done) {
    					count++;
    				}
    			})
    			return count;
    		},
    		isShow() {
    			return this.list.some(item => {
    				return item.done
    			})
    		}
    
    	},
    	methods: {
    		addItem() {
    			this.list.push({
    				id: this.list[this.list.length - 1].id + 1,
    				title: this.message,
    				done: false,
    			});
    			this.message = '';
    		},
    		delItem(id) {
    			this.list.forEach((item, index) => {
    				if (item.id == id) {
    					this.list.splice(index, 1);
    				}
    			})
    
    		},
    		toggleChecked(e) {
    			this.list.forEach(item => {
    				item.done = e.target.checked;
    			})
    		},
    		delCompleted() {
    			for (var i = 0; i < this.list.length; i++) {
    				if (this.list[i].done) {
    					this.list.splice(i, 1);
    					i--;
    				}
    			}
    		},
    		editItem(e) {
    			// console.log(e.target.value);
    			this.isEdit.title = e.target.value;
    			// this.isEdit=null;
    			this.isEdit = {};
    
    		}
    
    	}
    })
    
    
  • 自己写的(index.html)

    <!doctype html>
    <html lang="en">
    
    <head>
    	<meta charset="utf-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1">
    	<title>Template • TodoMVC</title>
    	<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
    	<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
    	<!-- CSS overrides - remove if you don't need it -->
    	<link rel="stylesheet" href="css/app.css">
    </head>
    
    <body>
    	<section class="todoapp" id="app">
    		<header class="header">
    			<h1>todomvc</h1>
    			<!-- 
    				实践建议:所有事件处理函数都起名为onXx,有利于阅读
    			 -->
    			<input class="new-todo" placeholder="What needs to be done?" autofocus v-model='inputTitle'
    				@keyup.13='onAdd'>
    		</header>
    		<!-- This section should be hidden by default and shown when there are todos -->
    		<template v-if='todos.length'>
    			<section class="main">
    				<input id="toggle-all" class="toggle-all" type="checkbox" @change='checkToggle'
    					:checked='todos.length==checkedArr.length'>
    				<label for="toggle-all">Mark all as complete</label>
    				<ul class="todo-list">
    
    					<!-- 
    			
    									任务项:li有三种内置样式
    									completed 已完成状态
    									没有class 未完成状态
    									editing 编辑状态
    								 -->
    					<li :key='todo.id' v-for='(todo,index) in toggleArr'
    						:class='{completed:todo.done,editing:todo==isEdit}'>
    
    						<div class="view">
    							<!-- 
    								表单元素 checkbox
    								双向绑定
    									数据影响视图
    										true:选中
    										false:不选
    									视图影响数据
    										选中 把数据修改为true
    										取消选中 把数据修改为false
    
    									数据改变,就会影响视图更新
    									为啥checkobox 切换把样式也给影响了,因为class也绑定了todo.done这个数据
    									
    										 -->
    							<input class="toggle" type="checkbox" v-model='todo.done' @change='changed(index)' >
    							<label @dblclick="isEdit=todo">{{todo.title}}</label>
    							<button class="destroy" @click='onDelete(index)'></button>
    						</div>
    						<!-- 编辑文本框默认隐藏,当任务项;li拥有editing为编辑模式 -->
    						<input class="edit" :value="todo.title" @keyup.13='onSave' @keyup.esc='isEdit=null'>
    					</li>
    				</ul>
    			</section>
    			<!-- This footer should hidden by default and shown when there are todos -->
    			<footer class="footer">
    				<!-- This should be `0 items left` by default -->
    				<span class="todo-count"><strong>{{count}}</strong> item left</span>
    				<!-- Remove this if you don't implement routing -->
    				<ul class="filters">
    					<li>
    						<a :class="{selected:isShowAll}" href="#/" @click='showAll'>All</a>
    					</li>
    					<li>
    						<a href="#/active" :class="{selected:isShowActive}" @click='showActive'>Active</a>
    					</li>
    					<li>
    						<a href="#/completed" :class="{selected:isShowCompleted}" @click='showCompleted'>Completed</a>
    					</li>
    				</ul>
    				<!-- Hidden if no completed items are left ↓ -->
    				<button class="clear-completed" v-show='isShow' @click='onDeleteCompleted'>Clear completed</button>
    			</footer>
    		</template>
    	</section>
    	<footer class="info">
    		<p>Double-click to edit a todo</p>
    		<!-- Remove the below line ↓ -->
    		<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
    		<!-- Change this out with your name and url ↓ -->
    		<p>Created by <a href="http://todomvc.com">you</a></p>
    		<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
    	</footer>
    	<!-- Scripts here. Don't remove ↓ -->
    	<script src="node_modules/todomvc-common/base.js"></script>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script src="js/app.js"></script>
    </body>
    
    </html>
    
  • 自己写的(app.js)

    var app = new Vue({
    	el: '#app',
    	data: {
    		inputTitle: '',//用户输入的任务名称
    		todos: [
    			{ id: 1, title: '吃饭', done: false },
    			{ id: 2, title: '睡觉', done: true },
    			{ id: 3, title: '打豆豆', done: false },
    			{ id: 4, title: '跑步', done: true },
    
    		],
    		isEdit: {},
    		isSeen: true,
    		toggleArr: [],
    		isShowActive:false,
    		isShowCompleted:false,
    		isShowAll:true,
    		isDelete:{},
    		// todosNew: []
    	},
    	computed: {
    		isShow() {
    			return this.todos.some(item => {
    				return item.done;
    			})
    		},
    		count() {
    			var count = 0;
    			this.toggleArr.forEach(item => {
    				if (!item.done) {
    					count++;
    				}
    			})
    			return count;
    		},
    		checkedArr() {
    			var ckArr = [];
    			this.todos.forEach((item, index) => {
    				if (item.done) {
    					ckArr.push(item);
    				}
    			})
    			return ckArr;
    		},
    		activeArr() {
    
    		},
    		
    
    	},
    	watch: {
    		
    		
    	},
    	methods: {
    		onAdd() {
    			// 1.得到文本框的数据
    			// 2.非空校验
    			if (!this.inputTitle.trim()) {
    				alert('请输入内容');
    				return;
    			}
    			console.log(this.todos);
    			
    			// 3.如果没有问题,把数据添加到任务列表中
    			this.toggleArr.push({
    				id: this.todos[this.todos.length-1].id+1,
    				title: this.inputTitle,
    				done: false
    			});
    			this.todos=this.toggleArr;
    			localStorage.setItem('todos',JSON.stringify(this.todos));
    
    			// 4.清空数据
    			this.inputTitle = '';
    		},
    		onDelete(index) {
    			this.toggleArr.splice(index, 1);
    			this.todos.splice(index,1);
    			localStorage.setItem('todos',JSON.stringify(this.todos));
    
    		},
    		onDeleteCompleted() {
    			var todosNew = [];
    			this.todos.forEach((item, index) => {
    				console.log(1);
    
    				if (!item.done) {
    
    					todosNew.push(item);
    
    				}
    			})
    			this.todos = todosNew;
    		},
    		showActive(e) {
    			this.toggleArr=[];
    			this.todos.forEach(item => {
    				if (!item.done) {
    					this.toggleArr.push(item);
    				}
    			})
    			this.isShowActive=true;
    			this.isShowAll=false;
    			this.isShowCompleted=false;
    			localStorage.setItem('isShowPage',JSON.stringify({
    				isShowAll:this.isShowAll,
    				isShowActive:this.isShowActive,
    				isShowCompleted:this.isShowCompleted
    			}));
    		},
    		showCompleted() {
    			this.toggleArr=[];
    			this.todos.forEach(item => {
    				if (item.done) {
    					this.toggleArr.push(item);
    				}
    			})
    			this.isShowCompleted=true;
    			this.isShowAll=false;
    			this.isShowActive=false;
    			localStorage.setItem('isShowPage',JSON.stringify({
    				isShowAll:this.isShowAll,
    				isShowActive:this.isShowActive,
    				isShowCompleted:this.isShowCompleted
    			}));
    
    		},
    		checkToggle(e) {
    			this.todos.forEach(item => {
    				item.done = e.target.checked;
    			})
    
    		},
    		onSave(e) {
    			this.isEdit.title = e.target.value
    			this.isEdit = null;
    		},
    		showAll(){
    			this.toggleArr=[];
    			this.todos.forEach(item => {
    				this.toggleArr.push(item);
    	
    			})
    			this.isShowAll=true;
    			this.isShowActive=false;
    			this.isShowCompleted=false;
    			localStorage.setItem('isShowPage',JSON.stringify({
    				isShowAll:this.isShowAll,
    				isShowActive:this.isShowActive,
    				isShowCompleted:this.isShowCompleted
    			}));
    
    		},
    		changed(index){
    			// console.log('被点了');
    			localStorage.setItem('todos',JSON.stringify(this.todos));
    			if(!this.isShowAll)
    			{
    				
    				this.toggleArr.splice(index,1);
    			}else{
    				console.log(index);
    				// localStorage.setItem('todos',JSON.stringify(this.todos));
    
    				
    			}
    			
    		}
    
    
    	},
    	mounted: function () {
    		
    		var str=localStorage.getItem('todos');
    		var page=localStorage.getItem('isShowPage');
    		if(!str||!page)
    		{
    			
    			localStorage.setItem('isShowPage',JSON.stringify({
    				isShowAll:this.isShowAll,
    				isShowActive:this.isShowActive,
    				isShowCompleted:this.isShowCompleted
    			}));
    			localStorage.setItem('todos',JSON.stringify(this.todos));
    			str=localStorage.getItem('todos');
    			page=localStorage.getItem('isShowPage')
    		}
    		this.todos=JSON.parse(str);
    		page=JSON.parse(page);
    		this.isShowAll=page.isShowAll;
    		this.isShowActive=page.isShowActive;
    		this.isShowCompleted=page.isShowCompleted;
    		
    		this.todos.forEach(item => {
    			// this.toggleArr.push(item);
    			if(this.isShowAll)
    			{
    				this.showAll();
    			}
    			else if(this.isShowActive)
    			{
    				this.showActive();
    			}
    			else if(this.isShowCompleted)
    			{
    				this.showCompleted();
    			}
    
    		});
    		
    	}
    })
    
    

小知识:

对于表单元素改变的时候

1.对于checkbox来讲,当选中状态改变的时候,无论通过什么方式(DOM,vue)获取checkbox改变之后的状态都建议使用change事件

2.不要使用click点击事件,因为点击事件触发的时候,checkbox的切换状态可能还没有改变,所以通过click事件获取checkbox的checked状态可能存在误差

数组中常用的方法:

​ 1.forEach方法(大多数情况下可以代替for循环)

​ eg:

​ arr.forEach((item,index)=>{

​ })

​ 2.some方法(判断数组中是否包含指定条件的元素)它会遍历数组,然后对每一项执行条件判断,只要有一个满足条件,停止遍历,返回true,如果到循环结束都没有满足条件的元素,返回false

​ eg:

​ arr.some((item,index)=>{

​ return item.title=‘睡觉’

​ })

​ 3.every判断数组是元素是否全部满足某个条件

​ eg:

​ arr.every((item,index)=>{

​ return item.done

​ })

​ 4.filter从数组中根据某个条件过滤数据得到一个新的数组,注意:过滤结果还是一个数组

​ eg:

​ arr.filter((item,index)=>{

​ return item.done

​ })

​ 5.find根据条件查找某个元素,它会遍历找到第一个满足条件的元素,停止遍历,并立即返回

​ eg:

​ arr.find((item,index)=>{

​ return item.id=3

​ })

​ 6.findIndex根据条件查找元素的索引

​ eg:

​ arr.findIndex((item,index)=>{

​ return item.id=3

​ })

​ 7.includes判断数组是否包含指定元素,类似indexof,返回布尔值,一般用于数组中都是普通类型:字符串,数字

​ eg:

​ arr.includes(1);

编辑插件

  • ​ vscode:live-server
  • sublime:sublime server

与服务器通信

  • 类似于jQuery中的ajax库(axios)

学习准备

JSON-server
  • 是一个提供测试环境接口的工具,可以帮我们快速生成一套接口服务,专门用于学习测试

  • 使用步骤

    1.安装:https://github.com/typicode/json-server

    2.在cmd中输入npm install -g json-server

    3.测试是否安装成功:son-server --version

    4.创建一个目录json-server-demo,然后在该目录中创建一个文件叫db.json并写入以下内容:

    {
    "posts": [
     { "id": 1, "title": "json-server", "author": "typicode" }
    ],
    "comments": [
     { "id": 1, "body": "some comment", "postId": 1 }
    ],
    "profile": { "name": "typicode" }
    }
    

    5.最后,进入db.json文件所属的目录,执行json-server --watch db.json

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2TdRkfIh-1573396162120)(E:\black\就业班\vue\assets\snipaste_20191107_173920.png)]

接口说明

  • jsonServer会根据db.json文件中的内容生成一些接口

    • 获取用户列表

      请求方法:get

      请求路径:/users

      请求参数:

      响应数据:用户列表

    • 查询指定用户列表

      请求方法:get

      请求路径:/users?name=姓名&gender=性别&age=年龄

      请求参数:

      Query(查询字符串)参数:

      ​ name:姓名

      ​ age:年龄

      ​ gender:性别

      响应数据:指定用户列表

    • 添加用户

      请求方法:post

      请求路径:/users

      请求参数:

      ​ {

      ​ “name”:用户名称,

      ​ “age”:年龄,

      ​ “gender”:性别

      ​ }

      响应数据:新增用户信息

      注意:请求体数据必须是json格式字符串

    • 删除用户

      请求方法:DELETE

      请求路径:/users/id

      eg:例如删除id为3的用户:http://localhost:3000/users/3

      请求参数:

      响应数据:{}

    • 修改用户:

      请求方法:PATCH

      请求路径:/users/id

      eg:例如修改id为3的用户:http://localhost:3000/users/3

      请求参数:

      body请求体数据:

      {

      ​ “name”:名称,

      ​ “age”:年龄,

      ​ “gender”:性别

      }

      注意:name,age,gender都是可选的,修改谁就传谁

      响应数据:修改之后的用户的完整信息

axios介绍

  • 网址:http://github.com/axios/axios

  • 安装:npm install axios

  • 获取用户列表

    axios({

    ​ method:‘GET’,

    ​ url:‘http://localhost:3000/users’,

    }).then(res=>{

    ​ res是响应对象

    ​ 接口返回的数据在res.data中

    ​ config:本次请求配置信息对象,很少使用

    ​ data:真正响应结果数据

    ​ headers:响应头数据,很少使用

    ​ request:请求对象,几乎不使用

    ​ status:响应状态码

    ​ statusText:响应状态短语

    ​ console.log(res.data);

    })

  • 查询指定用户列表

    axios({

    ​ method:‘GET’,

    ​ url:‘http://localhost:3000/users?id=2’,//查询id为2的用户,或者写在params

    ​ params:{

    ​ id:2,

    ​ name:‘张三’

    ​ },

    }).then(res=>{

    ​ console.log(res);

    })

  • 添加用户

    axios({

    ​ method:‘post’,

    ​ url:‘http://localhost:3000/users’,

    ​ data:{

    ​ name:’’,

    ​ age:’’,

    ​ gender:’’

    ​ }

    }).then(res=>{

    ​ if(res.status==201){

    ​ console.log(res.data);

    ​ }

    })

  • 删除用户:

    axios({

    ​ method:‘DELETE’,

    ​ url:‘http://localhost:3000/users/1’,//删除id为1的用户

    }).then(res=>{

    ​ console.log(res);

    })

  • 修改用户:

    axios({

    ​ method:‘PATCH’,

    ​ url:‘http://localhost:3000/users/2’,//修改id为2的用户

    ​ data:{

    ​ name:’’,

    ​ age:’’,

    ​ gender:’’

    ​ }

    }).then(res=>{

    ​ console.log(res,data);

    })

配置

  • 配置基础路径

axios.defaults.baseURL=‘http://localhost:3000’

axios快捷写法

​ axios.get(url[, config])

​ axios.get(‘http://localhost:3000/users’

​ //可选的配置对象

​ ).then(res => {

​ console.log(res);

​ })

​ // axios.delete(url[, config])

​ // axios.head(url[, config])

​ // axios.options(url[, config])

​ // axios.post(url[, data[, config]])

​ // axios.put(url[, data[, config]])

​ // axios.patch(url[, data[, config]])

axios错误处理

axios({
 method:'GET',
 url:'http://localhost:3000/users',
 
}).then(res=>{
//成功执行then
 //在axios中,默认只有>=200和<400的状态码都认为成功
 //axios在这个请求失败之后不执行then里面的代码
}).catch(err=>{
 //失败执行catch
})

vue和axios结合

 <div id="app">
        <!-- <h1>{{message}}</h1> -->
        <!-- 
            表单的submit事件:当表单中的submit或者button点击的时候,或者表单文本框中敲回车的时候都会触发submit事件
         -->
        <form @submit.prevent="onAdd">
            <div>
                <label for="">姓名</label>
                <input type="text" v-model='user.name'>
            </div>
            <div>
                <label for="">年龄</label>
                <!-- 
                        v-model支持一个修饰符:.number可以把绑定的数据自动转为数字类型
                     -->
                <input type="text" v-model.number='user.age'>
            </div>
            <div>
                <label for="">性别</label>
                <input type="radio" value='' v-model='user.gender' name="gender"><input type="radio" value='' v-model='user.gender' name="gender"></div>
            <div>
                <input type="submit" value="添加">
            </div>
        </form>
        <table>

            <thead>
                <tr>
                    <th>id</th>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>性别</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="user in users" :key="user.id">
                    <td>{{user.id}}</td>
                    <td>{{user.name}}</td>
                    <td>{{user.age}}</td>
                    <td>{{user.gender}}</td>
                    <td>
                        <button @click='onClick(user.id)'>删除</button>
                    </td>
                </tr>

            </tbody>
        </table>
    </div>
    <script src="./libs/vue.js"></script>
    <script src="./node_modules/axios/dist/axios.js"></script>
    <script>
        axios.defaults.baseURL = 'http://127.0.0.1:3000';
        // // 1.发送请求获取数据
        // axios({
        //     method: 'GET',
        //     url: '/users',

        // }).then(res => {//then方法中的回调函数,只有当请求成功的时候才会执行
        //     // 2.将请求的数据赋值给vue来使用 (vue中的数据一定要提前初始化)
        //     //  把vue实例中的users=res.data

        //     app.users = res.data;
        //     // 3.将数据绑定到模板中
        // })
        const app = new Vue({
            el: '#app',
            data: {
                message: 'hello vue.js',
                // 根据接口要求包括视图,抽象出需要绑定的数据字段
                // 这里的名字也不要瞎起,因为这个数据要提交给后端接口
                user: {
                    name: '',
                    age: '',
                    gender: ''
                },
                users: []//存储用户数据列表
            },
            methods: {
                onAdd() {
                    console.log(23);

                    // 1.获取表单数据
                    // 1.1根据接口和视图抽象出数据放到初始化到data中
                    // 建议表单相关的数据放到单独的数据对象中
                    // 1.2然后使用v-model把数据分别绑定到对应的表单元素上
                    // 1.3完了利用VueDevTools调试工具测试数据是否绑定OK
                    // 

                    // 2.请求提交
                    // json-server 默认提供了CORS跨域 后端跨域 前端不需要做任何处理
                    // 在后端的CORS请求中,浏览器会先发出一个方法请求方法OPTIONS的预检请求
                    // 整个过程都是自动的,不需要代码参与,浏览器默认行为

                    axios({
                        method: 'POST',
                        url: '/users',
                        data: this.user,


                    }).then(res => {
                        console.log(res);
                        // 添加成功 重新获取最新的用户数据列表
                        this.loadUsers();
                    }).catch(err => {
                        window.alert('添加失败' + err);
                    })

                    // 3.根据响应结果进行后续处理
                },
                loadUsers() {
                    axios({
                        method: 'GET',
                        url: '/users',

                    }).then(res => {
                        // 注意:then方法中的函数务必使用箭头函数,否则里面的this指向有问题
                        //tnen方法中的回调函数,只有当请求成功的时候才会执行
                        // 2.将请求的数据赋值给vue来使用 (vue中的数据一定要提前初始化)
                        //  把vue实例中的users=res.data

                        this.users = res.data;
                        // 3.将数据绑定到模板中

                        // 不太建议在created中写大量的业务逻辑代码
                        // 我们更推荐把这些功能封装成一个一个的函数放到methods中
                    })
                },
                onClick(id){
                    if(!window.confirm('Are you sure?'))
                    {
                        return
                    }
                        axios({
                            method:'DELETE',
                            url:'/users/'+id,//建议所有字符串都使用es6模板字符串方式
                            
                        }).then(res=>{
                            this.loadUsers();
                        }).catch(err=>{
                            window.alert('删除失败!');
                        })
                }
            },
            // 实例选项:created
            // 他是一个函数,他会在vue初始化完成以后自动调用
            // 我们可以在它里面访问this,获取vue实例
            // 常见的场景就是:在页面加载好以后请求获取数据列表
            created() {
                // // 1.发送请求获取数据
                // axios({
                //     method: 'GET',
                //     url: '/users',

                // }).then(res => {
                //     // 注意:then方法中的函数务必使用箭头函数,否则里面的this指向有问题
                //     //tnen方法中的回调函数,只有当请求成功的时候才会执行
                //     // 2.将请求的数据赋值给vue来使用 (vue中的数据一定要提前初始化)
                //     //  把vue实例中的users=res.data

                //     this.users = res.data;
                //     // 3.将数据绑定到模板中

                //     // 不太建议在created中写大量的业务逻辑代码
                //     // 我们更推荐把这些功能封装成一个一个的函数放到methods中
                // })

                // 方法也可以直接通过vue实例进行调用
                this.loadUsers();
            }
        })
    </script>

组件基础

  • 引入组件的原因:为了避免更多的冗余标签及逻辑等
  • 一个标签<span-btn>代表一个组件,写一个标签相当于一个组件的实例
  • 注册一个全局组件(要写在vue实例对象之前)Vue.component(组件名称,组件对象)
    • 组件名称一般采用abc 单词或abc-d双词(全小写)进行定义
  • 页面结构template有且只有一个根节点
  • data是一个带返回值的函数(因为组件和组件之间要保存独立的数据)
 <div id="app">
        <span-btn></span-btn>
        <span-btn></span-btn>

    </div>
    <script src="./libs/vue.js"></script>
   
    <script>
        Vue.component('span-btn',{
            template:`<div>
            
                <span>{{count}}</span>
                <button @click='changeCount'>按钮</button>
            </div>`,
            data(){
                return {
                    count:0,
                }
            },
            methods:{
                changeCount(){
                    this.count++;
                }
            }
        })
        new Vue({
            el:'#app',
        })
    </script>

全局组件

  • 只要你实例化一个vue对象,就自动注册了这个全局组件(拥有)
  • 注意:全局组件要写在所有vue实例对象的最前面
  • Vue.component(组件名称,组件对象)

局部组件

  • 在哪个实例对象上注册组件,就对哪个实例对象起作用

  • components:{组件名称:组件对象}

      <div id="app">
            <span-btn></span-btn>
            <span-btn></span-btn>
    
        </div>
        <div id="app1">
            <h1>我是vue实例2</h1>
            <span-btn></span-btn>
            <span-btn></span-btn>
    
        </div>
        <script src="./libs/vue.js"></script>
        <script>
            new Vue({
                el: '#app1',
                components:{
                    "span-btn":{
                        template:`<div>我试一下</div>`
                    }
                }
            })
           
            new Vue({
                el: '#app',
            });
    
        </script>
    

组件总结

  • 特点:
    • 组件是一个特殊的Vue实例
    • 没有el实例选项,有template属性
    • 组件和vue实例对象的区别是组件中的data是带有返回值的函数
    • template代表页面的结构(有且只有一个根节点)
    • 组件之间是独立的
  • 局部组件和全局组件之间区别:注册位置不同,应用范围不同

组件嵌套

  • 一旦形成组件嵌套,就有了父子关系
  • 组件嵌套形式
    • 下面展示的是组件和组件之间的纯嵌套
    • Vue实例对象与组件之间的嵌套(就是在视图中直接写组件标签)
 
    <div id="app1">
        <h1>我是vue实例2</h1>
        <span-btn></span-btn>
        <span-btn></span-btn>

    </div>
    <script src="./libs/vue.js"></script>
    <script>
        Vue.component("span-btn",{
            template:`<p>
            我是你爸爸
            <span-child></span-child>
            <child></child>
            </p>`,
            components:{
                "child":{
                    template:`<p style="color:red">我是直系亲属</p>`
                }
            }
        });
        Vue.component('span-child',{
            template:`<p>我是你儿子</p>`
        })
        new Vue({
            el: '#app1',
            
        })
       
      
    </script>

组件通信的几种情况

  • 父组件=>子组件通过props
  • 子组件=>父组件
  • 兄弟组件=>兄弟组件

父组件给子组件传值Props

  • 给谁传值就在谁身上定义属性(Vue自定义属性),属性值为要传递的数据的名字
  • 在子组件内部接收属性,通过props,其值为数组(字符串数组)
  • 子组件中使用该数据,名字也为props中的写字符串数组的名字
<!--Vue实例对象与子组件进行嵌套1-->  
<div id="app1">
        <foods :foodlist='list'></foods>  
    </div>
    <script src="./libs/vue.js"></script>
    <script>
      
        new Vue({
            el: '#app1',
            data:{
                list:['红烧肉','糖醋里脊','四喜丸子'],
            },
            components:{
                'foods':{
                    template:`<div>我是饭{{foodlist}}</div>`,
                    props:['foodlist'],
                    methods:{
                        fnc(){
                            console.log(this.foodlist);//this指向是当前的组件对象,可以直接访问内部的data数据,或者props传过来的值,与Vue实例对象内部的this一样
                        }
                    }
                }
            }
        })


    </script>
<!--Vue实例对象与子组件之间进行传递值2-->
    <div id="app1">
        <child :cities="citylist"></child>
    </div>
    <script src="./libs/vue.js"></script>
    <script>
      
        new Vue({
            el: '#app1',
            data:{
                citylist:['上海','北京','天津','重庆'],
            },
            components:{
                "child":{
                    template:`<div>
                        我去过的城市有:
                        <ul>
                            <li v-for="city in cities">{{city}}</li>
                        </ul>
                    </div>`,
                    props:['cities'],
                }
            }
            
        })


    </script>

<!--组件与组件之间嵌套时,进行父传子-->
    <div id="app1">
        <!-- <foods :foodlist='list'></foods>   -->
        <father></father>
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        Vue.component('father',{
            template:`<div>
                我是父亲
                <child :num="count"></child>
            </div>`,
            data(){
                return {
                    count:0,
                }
            },
            components:{
                "child":{
                    template:`<div>
                        我是儿子
                        {{num}}
                    </div>`,
                    props:['num']
                }
            }
        })
        new Vue({
            el: '#app1',
           
           
        })


    </script>




SPA

  • 单页面应用程序:
    • 多个模块访问一个一个网页
    • 一次向服务器获取资源
  • 优点:
    • 减缓网络延迟
    • 完全组件化开发
  • 缺点:
    • 首屏加载慢–>按需加载不刷新页面,只请求js模块
    • 不利于SEO-服务端渲染
    • 开发难度略高

SPA原理

  • 实现在前端自由切换的模块–事件监听onhashchange进行实现自由切换
  • 对当前的模块由记忆功能–页面刷新锚点之前点击的锚点还存在,只需要通过锚点获取响应的内容
  • 不引起页面刷新—锚点不会引起页面刷新
  • 解决:
    • 解决页面不刷新以及模块之间进行切换
      • 前端路由–通过hash值获取当前锚点(模块)
      • 通过事件监听hash改变onhashchange
<!--js实现路由的实现过程-->
 	<a href="#bj">北京</a>
    <a href="#sh">上海</a>
    <a href="#sz">深圳</a>
    <a href="#gz">广州</a>
    <div class="container"></div>
    <script>
        var fnc= function () {
            var dom = document.querySelector('.container');
            var path = window.location.hash.substr(1);
            switch (path) {
                case 'bj':
                    dom.innerHTML = '北京';
                    break;
                case 'sh':
                    dom.innerHTML = '上海';
                    break;
                case 'sz':
                    dom.innerHTML = '深圳';
                    break;
                case 'gz':
                    dom.innerHTML = '广州';
                    break;
                default:
                    break;
            }
        };
        fnc();
        window.onhashchange =fnc;
    </script>

VueRouter路由

  • 需要下载vue-router插件

  • 通过to属性实现不同模块之间的跳转

  • 步骤

    • 引入vue-router.js文件
    • 定义导航<router-link>可以不设置
    • 第一容器<router-view>必须有
    • 实例化VueRouter
    • 配置路由表,一个地址对应一个组件(模块)
    • 挂载路由
      <div id="app">
          <router-link to='/main'>主食</router-link>
          <router-link to='/meat'>肉食</router-link>
          <router-link to='/soup'></router-link>
          <router-view></router-view>
    
      </div>
      <script src="./libs/vue.js"></script>
      <script src="./vue-router.js"></script>
      <script>
          var router=new VueRouter({
              routes:[
                  {
                      path:'/main',
                      component:{
                          template:`<div>吃什么吃,啥也没有</div>`
                      }
                  },
                  {
                      path:'/meat',
                      component:{
                          template:`<div>塞牙缝</div>`
                      }
                  },
                  {
                      path:'/soup',
                      component:{
                          template:`<div>喝西北风</div>`
                      }
                  }
              ]
          })
          new Vue({
              el:'#app',
              router
          })
      </script>
    

动态路由

  • 动态路由传参
  • 获取参数$route.params=>获取所有动态路由参数的集合
 <div id="app">
      <router-link to='/foods/主食'>主食</router-link>
      <router-link to='/foods/肉食'>肉食</router-link>
      <router-link to='/foods/汤'></router-link>
      <router-view></router-view>

  </div>
  <script src="./libs/vue.js"></script>
  <script src="./vue-router.js"></script>
  <script>
      var router=new VueRouter({
          routes:[
              {
                  path:'/foods/:foodname',
                  component:{
                      template:`<div>吃什么吃,啥也没有{{$route.params.foodname}}</div>`
                  }
              }
          ]
      })
      new Vue({
          el:'#app',
          router
      })
  </script>

VueRouter中to的其他赋值方式

  • 字符串

  • 变量

  • 对象

  • 对象name形式

     <div id="app">
            <router-link to='/bj'>北京</router-link>
            <router-link to='/sh'>上海</router-link>
            <!-- 变量 -->
            <router-link :to='shStr'>上海(变量)</router-link>
            <!-- 对象 -->
            <router-link :to='{path:"/sh"}'>上海(对象)</router-link>
            <!-- 对象name形式 -->
            <router-link :to='{name:"sd"}'>上海(对象name形式)</router-link>
    
            <router-link to='/sz'>深圳</router-link>
            <router-link to='/gz'>广州</router-link>
            <router-view></router-view>
    
        </div>
        <script src="./libs/vue.js"></script>
        <script src="./vue-router.js"></script>
        <script>
            var router = new VueRouter({
                routes: [
                    {
                        path: '/bj',
                        component: {
                            template: `<div>北京</div>`
                        }
                    },
                    {
                        name:'sd',
                        path: '/sh',
                        component: {
                            template: `<div>上海</div>`
                        }
                    },
                    {
                        path: '/sz',
                        component: {
                            template: `<div>深圳</div>`
                        }
                    }, {
                        path: '/gz',
                        component: {
                            template: `<div>广州</div>`
                        }
                    }
                ]
            })
            new Vue({
                el: '#app',
                data:{
                    shStr:'/sh'
                },
                router
            })
        </script>
    
    <!--传递参数的to的不同赋值形式-->
    <div id="app">
            <router-link to='/bj/首都'>北京</router-link>
            <router-link to='/sh/是吗'>上海</router-link>
            <!-- 变量 -->
            <router-link :to='shStr'>上海(变量)</router-link>
            <!-- 对象 -->
            <router-link :to='{path:"/sh/是吗3"}'>上海(对象)</router-link>
            <!-- 对象name形式 -->
            <router-link :to='{name:"sd",params:{ab:"我是"}}'>上海(对象name形式)</router-link>
    
            <router-link to='/sz'>深圳</router-link>
            <router-link to='/gz'>广州</router-link>
            <router-view></router-view>
    
        </div>
        <script src="./libs/vue.js"></script>
        <script src="./vue-router.js"></script>
        <script>
            var router = new VueRouter({
                routes: [
                    {
                        path: '/bj/:abc',
                        component: {
                            template: `<div>北京{{$route.params.abc}}</div>`
                        }
                    },
                    {
                        name:'sd',
                        path: '/sh/:ab',
                        component: {
                            template: `<div>上海{{$route.params.ab}}</div>`
                        }
                    },
                    {
                        path: '/sz',
                        component: {
                            template: `<div>深圳</div>`
                        }
                    }, {
                        path: '/gz',
                        component: {
                            template: `<div>广州</div>`
                        }
                    }
                ]
            })
            new Vue({
                el: '#app',
                data:{
                    shStr:'/sh/是吗1'
                },
                router
            })
        </script>
    

重定向

  • 拦截谁就在谁的路由上写redirect

      <div id="app">
            <router-link to='/bj/首都'>北京</router-link>
            <router-link to='/sh/是吗'>上海</router-link>
            <router-link to='/sz'>深圳</router-link>
            <router-link to='/gz'>广州</router-link>
            <router-view></router-view>
    
        </div>
        <script src="./libs/vue.js"></script>
        <script src="./vue-router.js"></script>
        <script>
            var router = new VueRouter({
                routes: [
                    {
                        path: '/bj/:abc',
                        component: {
                            template: `<div>北京{{$route.params.abc}}</div>`
                        }
                    },
                    {
                        name: 'sd',
                        path: '/sh/:ab',
                        component: {
                            template: `<div>上海{{$route.params.ab}}</div>`,
                           
                        },
                        redirect:'/bj/abc'
                    },
                    {
                        path: '/sz',
                        component: {
                            template: `<div>深圳</div>`
                        }
                    }, {
                        path: '/gz',
                        component: {
                            template: `<div>广州</div>`
                        }
                    }
                ]
            })
            new Vue({
                el: '#app',
              
                router
            })
        </script>
    

编程式导航

  • 实现跳转

    • a标签
    • window.location.href
    • 采用代码行为进行跳转
      • $.router.push() 推进一条记录,点击返回回到上次的地址
      • $.router.replace()替换当前记录,不能返回,替换记录
      • $.router.go()为正值前进,为负值后退
     <div id="app">
    
            <router-view></router-view>
    
        </div>
        <script src="./libs/vue.js"></script>
        <script src="./vue-router.js"></script>
        <script>
            // 1. 实例化一个导航路由
            // 2. 导航为 A, B, C, D
            // 3. 实现A => B, B => C, 然后从C返回时, 直接回到A
            // 4. 实现A => B, B => C, C => D 从D返回时 不能返回
            // 5. 实现A => B, B => C, C => D 从D返回直接返回到A 在A中直接前进到D
            var router = new VueRouter({
                routes: [
                    {
                        path: '/',
                        redirect: '/A'
                    },
                    {
                        path: '/A',
                        component: {
                            template: `<div>我是A,我要去B<button @click='goB'>去B</button></div>`,
                            methods: {
                                goB() {
                                    this.$router.push('/B');
                                }
                            }
                        },
                    },
                    {
                        path: '/B',
                        component: {
                            template: `<div>我是B,我要去C<button @click='goC'>去C</button></div>`,
                            methods: {
                                goC() {
                                    this.$router.replace('/C');
                                }
                            }
                        },
                    },
                    {
                        path: '/C',
                        component: {
                            template: `<div>我是C,我要去D<button @click='goD'>去D</button></div>`,
                            methods: {
                                goD() {
                                    this.$router.replace('/D');
                                }
                            }
                        },
                    },
                    {
                        path: '/D',
                        component: {
                            template: `<div>我是D,我要去A<button @click='goA'>去A</button></div>`,
                            methods: {
                                goA() {
                                    // this.$router.push('/cy');
                                }
                            }
                        },
                    },
                   
                ]
            })
            new Vue({
                el: '#app',
                router
            })
        </script>
    

激活路由样式

  • 当前选中的模块会有一个class类名class="router-link-exact-active router-link-active"

  • 所以可以通过设置当前拥有类名的标签,产生一定的样式

     <style>
            .router-link-active{
                color: red;
                font-size: 40px;
            }
     </style>
    

路由嵌套

  • 二级路由设置通过children属性进行设置,属性值也是一个数组,和routes一样
 <div id="app">
        <router-link to='/hot'>热点</router-link>
        <router-link to='/edu'>教育</router-link>
        <router-link to='/soc'>社会</router-link>
        <router-link to='/music'>音乐</router-link>
        <router-view></router-view>
    </div>
    <script src="./libs/vue.js"></script>
    <script src="./vue-router.js"></script>
    <script>
        var router=new VueRouter({
            routes:[
                {
                    path:'/',
                    redirect:'/hot'
                },
                {
                    path:'/hot',
                    component:{
                        template:`<div>热点</div>`
                    }
                },
                {
                    path:'/edu',
                    component:{
                        template:`<div>教育</div>`
                    }
                },
                {
                    path:'/soc',
                    component:{
                        template:`<div>社会</div>`
                    }
                },
                {
                    path:'/music',
                    children:[
                        {
                            path:'',
                            component:{
                                template:`<p>我是默认的二级路由内容</p>`
                            }
                        },
                        {
                            path:'/music/pop',
                            component:{
                                template:`<div>流行音乐</div>`
                            }
                        },
                        {
                            path:'/music/class',
                            component:{
                                template:`<div>古典音乐</div>`
                            }
                        },
                        {
                            path:'/music/jazz',
                            component:{
                                template:`<div>爵士音乐</div>`
                            }
                        }
                    ],
                    component:{
                        template:`<div>音乐
                            <router-link to="/music/pop">流行音乐</router-link>
                            <router-link to="/music/class">古典音乐</router-link>
                            <router-link to="/music/jazz">爵士音乐</router-link>
                            <router-view></router-view>
                        </div>`
                    }
                }
            ]
        })
        new Vue({
            el:'#app',
            router
        })
    </script>
Logo

前往低代码交流专区

更多推荐