vue

文章目录

1.vue框架介绍和原理

1.1.了不起的vue

1.1.1.官方介绍

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

减少DOM的使用

1.1.2.渐进式

框架做分层设计,每层都可选,不同层可以灵活接入其他方案。而当你都想 用官方的实现时,会发现也早已准备好,各层之间包括配套工具都能比接入 其他方案更便捷地协同工作。

一个个放入,放多少就做多少。

1.1.3.MV*模式(MVC/MVP/MVVM)

1.1.3.1.MVC

"MVC": model view controller

用户的对View操作以后,View捕获到这个操作,会把处理的权利交移给 Controller(Pass calls);Controller会对来自View数据进行预处理、决定 调用哪个Model的接口;然后由Model执行相关的业务逻辑(数据请求); 当Model变更了以后,会通过观察者模式(Observer Pattern)通知View; View通过观察者模式收到Model变更的消息以后,会向Model请求最新的数 据,然后重新更新界面。

把业务逻辑和展示逻辑分离,模块化程度高。但由于View是强依赖特定的 Model的,所以View无法组件化,无法复用.

1.1.3.2.MVP

"MVP": model view presenter

和MVC模式一样,用户对View的操作都会从View交移给Presenter。 Presenter会执行相应的应用程序逻辑,并且对Model进行相应的操作;而这 时候Model执行完业务逻辑以后,也是通过观察者模式把自己变更的消息传 递出去,但是是传给Presenter而不是View。Presenter获取到Model变更的 消息以后,通过View提供的接口更新界面

View不依赖Model,View可以进行组件化。但Model->View的手动同步逻辑麻烦,维护困难

1.1.3.3.MVVM

"MVVM": model view viewmodel

MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。你只需要在View的模版语法 当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。 当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上 去,当用户对View进行操作(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染。

解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高 了代码的可维护性。对于大型的图形应用程序,视图状态较多,ViewModel 的构建和维护的成本都会比较高。

vue就是是基于MVVM的思想设计的,虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

var vm = new Vue({
  // 选项
})

1.2.Vue体验

  • 直接下载并用 <script> 标签引入,Vue 会被注册为一个全局变量。

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    
  • 命令行工具vue cli

    Vue 提供了一个官方的 CLI,为单页面应用 (SPA) 快速搭建繁杂的脚手架。

    npm install ‐g @vue/cli
    
  • 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <script src="lib/vue.js"></script>
    </head>
    <body>
            <div id="box">
                    4.定义Vue语法模板:
                    {{ 20 + 10 }}
                    {{ 10>5?myName:'bbb'}}-{{myAge}}
            </div>
            <div id="box2">
                    非Vue语法模板:
                    {{ 20 + 10 }}
            </div>
            <script>
                    //1.初始化Vue,并赋值给变量vm
                  var vm =  new Vue({
                      		//2.element代表初始化id为box的元素
                            el:"#box",
                      		//3.在data中定义状态(指随时都可以变化的)
                            data:{ 
                                   myName:"stonebridge", 
                                   myAge:200
                            }
                    })
            </script>
    </body>
    </html>
    

1.3.数据绑定原理

当你把一个普通的 JavaScript 对象传入Vue 实例作为data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转
为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知watcher,从而使它关联的组件重新渲染。

注意:vue3 的 变化

Object.defineProperty有以下缺点。

  1. 无法监听es6的Set、Map 变化;
  2. 无法监听Class类型的数据;
  3. 属性的新加或者删除也无法监听;
  4. 数组元素的增加和删除也无法监听。

针对Object.defineProperty的缺点,ES6 Proxy都能够完美得解决,它唯一的缺点就是,对IE不友好,所以vue3在检测到如果是使用IE的情况下(没错,IE11都不支持Proxy),会自动降级为Object.defineProperty的数据监听系统。

2.响应式渲染

2.1.模板语法

2.1.1.插值

  1. 文本 {{}}

  2. 纯HTML

    ​ v-html,防止XSS,CSRF(

    • 前端过滤
    • 后台转义(< > < >)
    • 给cookie 加上属性 http

    示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <script src="lib/vue.js"></script>
    </head>
    <body>
            <div id="box">
                    {{myhtml}}
            <div v-html="myhtml"></div>
    
            </div>
            <script>
            new Vue({
                    el:"#box",
                    data:{
                            myhtml:"<a href=javascript:location.href='http://www.baidu.com?cookie='+document.cookie>点击这里</a>"
                    }
            })
            </script>
    </body>
    </html>
    

    显示效果:

2.1.2.指令:是带有v-前缀的特殊属性(指令)

  • v-bind 动态绑定属性

    在标签内设置值使用{{value}};设置标签属性时则使用单引号或者双引号即可

    <div v-bind:class=" isActive?'red':'yellow' ">动态切换背景色-1</div>
    

    isActive是data里面的状态值

  • v-if 动态创建/删除

    <div v-if="isCreated">我是动态创建和删除</div>
    

    isCreated是data里面的状态值

  • v-show 动态显示/隐藏

    <div v-show="isShow">我是动态的隐藏和显示</div>
    

    v-show和v-if是区别

    • v-show:是设置标签的样式display:none
    • v-if:是完全不会在页面生成标签
  • v-on:click 绑定事件

    <button v-on:click="handlerClick()">change</button>
    

    或者简写

    <button @click="handlerClick()">change</button>
    
  • v-for 遍历

    <li v-for="(item,index) in datalist">
        {{index+5}}----{{item}}
    </li>
    
  • v-model 双向绑定表单

    <input type="text" v-model="mytext"/>
    

    当页面的input中输入数据时,v-model会实时刷新到data对应的mytext状态中。v-model 双向绑定的指令,把整个输入框的value值和状态的值mytext绑定。因为是双向的,在浏览器的console中设置vm.mytext=‘张三’,输入框就会显示张三。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="lib/vue.js"></script>
</head>
<style>
        .red{
          background:red;
        }
    
        .yellow{
          background:yellow;
        }
      </style>
<body>
        <div id="box">
                {{myName}}
                <!-- 指令 v-on 绑定事件的指令,可以简写为@-->
                <button v-on:click="handlerClick()">change</button>
                <button @click="handlerClick()">change</button>
                <!-- 指令 v-show -->
                <div v-show="isShow">我是动态的隐藏和显示</div>
                <!-- 指定 v-if-->
                <div v-if="isCreated">我是动态创建和删除</div>
                <!-- 指令 v-bind 动态绑定,class, style,src, alt;可简写为:-->
                <div v-bind:class=" isActive?'red':'yellow' ">动态切换背景色-1</div>
                <div :class=" isActive?'red':'yellow' ">动态切换背景色-2</div>
                <!-- 指令 v-for遍历数组的指令-->
                <ul>
                        <li v-for="(item,index) in datalist">
                          {{index+5}}----{{item}}
                        </li>
                 </ul>
        </div>
        <script>
              new Vue({
                      el:"#box",
                      data:{
                              myName:"stonebridge",
                              isShow:true,
                              isCreated:true,
                              isActive:true,    
                              datalist:['aa','bbb','cc','dddd','dddd']                                       
                      },
                      methods:{
                        // handlerClick:function(){
                        // }  
                        handlerClick(){
                                console.log("handleClick")
                                this.myName='张三'
                                this.isShow = !this.isShow
                                this.isCreated = !this.isCreated
                                this.isActive = !this.isActive
                                this.datalist = ['111','222','333','444']
                        }
                      }
              })
        </script>
</body>
</html>

2.1.3.指令简写

  • v-bind:src => :src
  • v-on:click => @click

示例1:功能演示

<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="lib/vue.js"></script>
</head>
<style>
        .red{
          background:red;
        }
    
        .yellow{
          background:yellow;
        }
      </style>
<body>
        <div id="box">
                {{myName}}
                <!-- 指令 v-on 绑定事件的指令,可以简写为@-->
                <button v-on:click="handlerClick()">change</button>
                <button @click="handlerClick()">change</button>
                <!-- 指令 v-show -->
                <div v-show="isShow">我是动态的隐藏和显示</div>
                <!-- 指定 v-if-->
                <div v-if="isCreated">我是动态创建和删除</div>
                <!-- 指令 v-bind 动态绑定,class, style,src, alt;可简写为:-->
                <div v-bind:class=" isActive?'red':'yellow' ">动态切换背景色-1</div>
                <div :class=" isActive?'red':'yellow' ">动态切换背景色-2</div>
                <!-- 指令 v-for遍历数组的指令-->
                <ul>
                        <li v-for="(item,index) in datalist">
                          {{index+5}}----{{item}}
                        </li>
                 </ul>
        </div>
        <script>
              new Vue({
                      el:"#box",
                      data:{
                              myName:"stonebridge",
                              isShow:true,
                              isCreated:true,
                              isActive:true,    
                              datalist:['aa','bbb','cc','dddd','dddd']                                       
                      },
                      methods:{
                        // handlerClick:function(){
                        // }  
                        handlerClick(){
                                console.log("handleClick")
                                this.myName='张三'
                                this.isShow = !this.isShow
                                this.isCreated = !this.isCreated
                                this.isActive = !this.isActive
                                this.datalist = ['111','222','333','444']
                        }
                      }
              })
        </script>
</body>
</html>

示例2:简单留言板功能

<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="lib/vue.js"></script>
</head>
<body>
        <div id="box">
                <!-- <input type="text"> -->
                <!-- v-model 双向绑定的指令,把整个输入框的value值和状态的值mytext绑定。因为是双向的,在浏览器的console中设置vm.mytext='张三',输入框就会显示张三。-->
                <input type="text" v-model="mytext"/>
                <button @click="handleClick()">add</button>
                <div v-if="datalist.length===0">代办事项空空</div>
                <ul v-else>
                    <li v-for="(data,index) in datalist">
                        {{data}}
                        <button @click="handleDel(index)">del</button>
                    </li>
                </ul>
        </div>
        <script>
              var vm= new Vue({
                        el:"#box",
                        data:{
                                mytext:"",
                                datalist:[]
                        },
                        methods:{
                                handleClick(){
                                        console.log("add",this.mytext)
                                        this.datalist.push(this.mytext)
                                        //重置input
                                        this.mytext=""
                                },
                                handleDel(myindex){
                                        console.log("del",myindex)
                                        this.datalist.splice(myindex,1)
                                }
                                // handleInput(evt){
                                //     // console.log("input",evt.target.value)
                                //     this.mytext = evt.target.value

                                // }
                        }
                })
        </script>
</body>
</html>

2.2.class 与 style

2.2.1.绑定HTML Class

  • 对象语法

    <div :class="classobj">动态切换class-对象写法</div>
    

    给某个标签的class属性设置多个参数时,可以通过在data中设置一个classObj类,需要设置的类名为key,value为true或者false设置是否为该类名。

    var vm = new Vue({
          el:"#box",
          data:{
                classobj:{
                      aa:true,
                      bb:true,
                      cc:true
                }
          }
    })
    

    可以通过设置classobj的属性为true和flase进行修改,如果新增vue初始化时没有的属性可以通过Vue.set(vm.classobj,“dd”,true)设置

  • 数组语法

     <div :class="classarr">动态切换class-数组写法</div>
    

    给某个标签的class属性设置多个参数时,可以通过在data中设置一个classarr数组,需要设置的类名存放在数组中即可。

    var vm = new Vue({
          el:"#box",
          data:{
            	classarr:["aa","bb","cc"]
          }
    })
    

    可以通过设置classarr数组内的值进行修改(push或者splice)即可

2.2.2.绑定内联样式

  • 对象语法

    <div :style="styleobj">动态切换style-对象写法</div>
    
    var vm = new Vue({
          el:"#box",
          data:{
            styleobj:{
              backgroundColor:"red"
            }
          }
    })
    

    可以通过vm.styleobj.backgroundColor='yellow’修改,可以通过Vue.set(vm.styleobj,“fontSize”,“40px”)进行新增属性

  • 数组语法

    <div :style="stylearr">动态切换style-数组写法</div>
    
    var vm = new Vue({
          el:"#box",
          data:{
                stylearr:[{
                  background:"red"
                }]
          }
    })
    

    可以通过vm.stylearr.push({fontSize:“40px”})新增或者修改样式

注意:需要将 font-size =>fontSize

2.2.3.示例1:点击页面列表任意一列,该列点击后变色

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>Document</title>
    <script src="lib/vue.js"></script>
    <style>
        .active {
            background-color: red;
        }
    </style>
</head>
<body>
<div id="box">
    <ul>
        <li v-for="(item,index) in datalist" :class=" current===index? 'active':'' " @click="handleClick(index)">
            {{item}}
        </li>
    </ul>
</div>
<script>
    //eslint 工具
    new Vue({
        el: '#box',
        data: {
            datalist: ['首页', '列表', '购物车'],
            current: 0
        },
        methods: {
            handleClick(index) {
                this.current = index
            }
        }
    })
</script>
</body>
</html>

2.3.条件渲染

2.3.1.v-if、v-else-if 、v-else

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <ul>
        <li v-for="data in list" :key="data.id">
            <div>{{data.name}}</div>
            <div>{{data.price}}</div>
            <div v-if="data.state===0">未支付</div>
            <div v-else-if="data.state===1">待发货</div>
            <div v-else="data.state===2">交易完成</div>
        </li>
    </ul>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            list: [
                {
                    id: 1,
                    name: "手机1",
                    price: 2000,
                    state: 0  //未支付
                },
                {
                    id: 2,
                    name: "手机2",
                    price: 3000,
                    state: 1  //待发货
                },
                {
                    id: 3,
                    name: "手机3",
                    price: 4000,
                    state: 2 //已完成
                }
            ]
            //我的订单数据
        }
    })
</script>
</body>
</html>

2.3.2.template v-if(包装元素template 不会被创建)

多个标签或者复杂的标签结构都被一个变量控制时,为了书写简单和不破坏原有结构。可以使用template,template 就是一个包装元素, 不会真正创建在页面上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <template v-if="isCreated">
        <div>1111</div>
        <div>2222</div>
        <div>3333</div>
    </template>
</div>

<script>
    var vm = new Vue({
        el: "#box",
        data: {
            isCreated: true
        }
    })
</script>
</body>
</html>

2.3.3.v-show

v-show的作用是动态隐藏和显示,如果需要动态创建和删除节点可以使用v-if

<!-- 指令 v-show -->
<div v-show="isShow">我是动态的隐藏和显示</div>
<!-- 指定 v-if-->
<div v-if="isCreated">我是动态创建和删除</div>

2.4.列表渲染

2.4.1.v-for (特殊 v-for=“n in 10”)

  • a. in

    <ul>
        <li v-for="item,key,index in obj">
            {{index+1}}.{{key}}:{{item}}
        </li>
    </ul>
    
  • b. of

    <!-- 2.对象遍历 -->
    <ul>
        <li v-for="item,key,index of obj">
            {{index+1}}.{{key}}:{{item}}
        </li>
    </ul>
    

    v-for遍历既可以遍历数组,也可以遍历对象,也可以遍历数字

  • 案例

    <!DOCTYPE html>
    <html lang="en">
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
            <title>Document</title>
            <script src="lib/vue.js"></script>
    </head>
    <body>
            <div id="box">
                    <ul><li v-for="(item,index) of datalist">{{item}}----{{index}}</li></ul>
                    <hr><!-- 1.数组遍历 -->
                    <ul><li v-for="item,key,index in obj">{{index+1}}.{{key}}:{{item}}</li></ul>
                    <hr><!-- 2.对象遍历 -->
                    <ul><li v-for="item,key,index of obj">{{index+1}}.{{key}}:{{item}}</li></ul>
                    <hr><!-- 3.数字的遍历,是从1开始的 -->
                    <ul><li v-for="n in 10">{{n}}</li></ul>
            </div>
            <script>
                    new Vue({
                            el:'#box',
                            data:{
                                    datalist:["aaa","bbb","ccc"],
                                    obj:{
                                            name:"stonebridge",
                                            age:100,
                                            loaction:"NanJing"
                            		}
                            },
                            
                    })
            </script>
    </body>
    </html>
    

2.4.2.key

key的作用:跟踪每个节点的身份,从而重用和重新排序现有元素

理想的key值是每项都有的且唯一的 id。data.id

例如:在循环列表中。第一次创建页面,会根据data中的数组创建新的虚拟DOM,虚拟DOM会包含节点信息,节点内容,节点的key。根据虚拟DOM节点然后创建出真实的DOM节点(例如<li>1111</li><li>2222</li>)页面正常显示的节点信息。新增,修改,删除一条数据。vue根据新的数组创建新的虚拟DOM。对比原先的DOM和新建虚拟DOM的区别,有差异的节点进行更新。

设置虚拟DOM并不一定能找到快读对比的方法,所以在列表中要设置有效的key,便于新的虚拟DOM和原DOM的进行对比较。

  • 如果该列表不会或者很少进行修改可以不用设置key。
  • 如果时只进行新增操作,可以设置index为key。但是修改或者删除就不能设置index,因为效率太低。
  • 最好的在展示的数据设置一个唯一的id属性作为key。

2.4.3.数组更新检测

  • 使用以下方法操作数组,可以检测变动。push() pop() shift() unshift() splice() sort() reverse()

  • filter(), concat() 和 slice() ,map(),新数组替换旧数组

    例如: concat()使用vm.datalist.concat([“111”,“222”])只会显示所以的数据,并不会改变原vm.datalist的值。如果要改变可vm.datalist=vm.datalist.concat([“111”,“222”])

    综上只要能影响原数组即可

  • 不能检测以下变动的数组

    vm.items[indexOfItem] = newValue

    vm.item[1]=‘aaa’

    此时数组可以改变,但是vue无法检测到,故不会刷新。原因是ES5中get,set无法拦截,在vue3.0中会进行处理

    解决

    1. Vue.set(example1.items, indexOfItem, newValue)
    2. Vm.datalist.splice(0,1,“aaaa”);

2.4.4.应用:显示过滤结果

  1. 代码1

    <!DOCTYPE html>
    <html lang="en">
    <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Document</title>
            <script src='lib/vue.js'></script>
    </head>
    <body>
            <div id="box">
                    <input type="text" @input="handleInput"/>
                    <ul><li v-for="data in datalist">{{data}}</li></ul>
            </div>
            <script>
                var vm =  new Vue({
                    el:"#box",
                    data:{
                        datalist:["aaa","bbb","ccc","ddd","add","cee","eee"],
                        originlist:["aaa","bbb","ccc","ddd","adbd","cee","eee"]
                    },
                    methods:{
                        handleInput(evt){
                            console.log(evt.target.value)
                            // 过滤数组 filter
                            this.datalist = this.originlist.filter(item=>item.includes(evt.target.value))
                        }
                    }
                })
            </script>
    </body>
    </html>
    
  2. 代码2

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="lib/vue.js"></script>
    </head>
    <body>
    <div id="box">
        <input type="text" v-model="mytext"/>
        <ul>
            <li v-for="data in mymethod()">{{data}}</li>
        </ul>
    </div>
    
    <script>
        var vm = new Vue({
            el: "#box",
            data: {
                mytext: "",
                datalist: ["aaa", "bbb", "ccc", "ddd", "add", "cee", "eee"]
            },
            methods: {
                mymethod() {
                    // 依赖此状态的函数也会重新执行
                    return this.datalist.filter(item => item.includes(this.mytext))
                }
            },
        })
    </script>
    </body>
    </html>
    
  3. 效果:

2.5.事件处理器

2.5.1.监听事件-直接触发代码(不推荐)

<!-- 3.可以直接写代码(不推荐) -->
<button @click="count++">add3-直接写代码</button>

2.5.2.方法事件处理器-写函数名handleClick

函数不写小括号,故参数只有事件对象。需要的参数从事件对象中获取

<!-- 1.不写小括号默认传递的参数的事件对象 -->
<button @click="handleAdd1">add1-函数-传事件对象</button>
handleAdd1(evt){
    console.log(evt)
}

2.5.3.内联处理器方法-执行函数表达式handleClick($event,其他参数)

$event事件对象,此方式既可以传递事件对象,也可以传递其他参数

<!-- 2.写小括号,可以传递自己的参数,也可以传递事件对象,但是事件对象必须用$event声明()  (推荐) -->
<button @click="handleAdd2($event,2)">add2-函数表达式-手动传事件对象&自己的参数</button>

2.5.4.事件修饰符

  • stop

  • prevent

  • capture

  • self

  • once

  • passive

    每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。我们加上passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作

    这里一般用在滚动监听,@scroll,@touchmove 。因为滚动监听过程 中,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使 滑动卡顿。我们通过passive将内核线程查询跳过,可以大大提升滑动的流畅度。

示例:

<ul @click="handleUlClick">
    <!--4. 事件修饰符stop的作用是handleLiClick事件触发后,阻止冒泡,上面的handleUlClick就不会执行了 -->
    <li @click.stop="handleLiClick">11111</li>
</ul>
<!-- 5.事件修饰符prevent(阻止默认行为),a链接只要有href,点击后就会跳转,这是他的默认行为。prevent可以阻止它,并且给他定义一个新的函数handleAClick -->
<a href="www.baidu.com" @click.prevent="handleAClick">跳转</a>
<!-- 6.事件修饰符once,点击一次后就不可以再点 -->
<button @click.once="handleAware">抽奖</button>

2.5.6.按键修饰符

<input type='text' @keyup.enter="handlekeyUp"/>       
<!-- <input type='text' @keyup.enter.ctrl="handlekeyUp"/>点击ente和ctrl键后触发handlekeyUp函数 -->
<!-- <input type='text' @keyup.enter.65="handlekeyUp"/>点击ente和A键后触发handlekeyUp函数-->

2.5.7.案例:模拟模态窗口登录页面

<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script type="text/javascript" src="lib/vue.js"></script>
        <style>
                #overlay {
                        background: rgba(0, 0, 0, 0.6);
                        width: 100%;
                        margin: auto;
                        position: fixed;
                        top: 0;
                        left: 0;
                        right: 0;
                        bottom: 0;
                }
                #center {
                        background: #ffff;
                        border-radius: 5px;
                        /* 边框圆角 */
                        padding-top: 15px;
                        padding-left: 30px;
                        padding-bottom: 15px;
                        width: 290px;
                        height: 160px;
                        position: fixed;
                        margin: auto;
                        left: 0;
                        right: 0;
                        top: 0;
                        bottom: 0;
                }
        </style>
</head>
<body>
        <div id='box'>
                <button @click='isShow=true'>登录</button>
                <!-- self.如果点击的事件源是他本身就会触发否则不会触发 -->
                <div id="overlay" v-show="isShow" @click.self="isShow=false">
                        <div id='center'>
                                <div>用户名:<input type="text"></div>
                                <div>密码:<input type="password"></div>
                                <div><button @click='isShow=false'>登录</button></div>
                        </div>
                </div>
        </div>
        <script>
                new Vue({
                        el: "#box",
                        data: {
                                isShow: false
                        },
                        methods: {

                        }
                })
        </script>
</body>
</html>
11234416354

2.6.表单控件绑定/双向数据绑定

因为是双向绑定,故无论是页面点击操作表单,或者是设置data里面的状态,双方显示和数据方面都会同步。

2.6.1.input/textarea标签

在data设置字符串接受即可

<!-- 1.只要有value值的标签type=text\password等,就可以使用v-model -->
<input type="password" v-model="mytext" />
{{mytext}}
<textarea v-model="mytext"></textarea>
<!-- 使用第三方组件 -->
<el-input v-model="gasLevelForm.multiple" type="text" maxlength="3" "this.value=this.value.replace(/\D/g,'')" style="width:200px;"/>

2.6.2.checkbox类型的多选

在data设置数组接受被选中的多个值

<!-- 2.针对多选的checkBox的, 使用v-model绑定数组,勾选的选项会自动添加到数组中-->
<h2>多选-checkbox</h2>
<input type="checkbox" v-model="checkGroup" value="vue" />vue
<input type="checkbox" v-model="checkGroup" value="react" />react
<input type="checkbox" v-model="checkGroup" value="jquery" />jquery
<button @click="handleSubmit()">提交</button>
<script type="text/javascript">
        var vm = new Vue({
            el: "#box",
            data: {
                checkGroup: [],
            },
            methods: {
                handleSubmit() {
                    console.log("提交", this.checkGroup)
                }
            },
        })
</script>

2.6.3.checkbox类型的单选

在data设置boolean类型接受被选中的值,选中设置为true

<!-- 3.checkBox只有一个,使用v-model绑定boolean值就可以了-->
<h2>checbox -记住用户名</h2>
<input type="checkbox" v-model="isRem" />记住用户名
<button @click="handleLogin()">登录</button>
<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            isRem: false,
        },
        methods: {
            handleLogin() {
                console.log("登录", this.isRem)
            }
        }
    })
</script>

2.6.4.type是radio的单选

如果type是radio,此时为单选,使用v-model绑定,因为只单选一个数据使用字符串接受即可

<h2>radio - 单选</h2>
<input type="radio" v-model="favorlang" value="vue" name="favor" />vue
<input type="radio" v-model="favorlang" value="react" name="favor" />react
<input type="radio" v-model="favorlang" value="jquery" name="favor" />jquery
<button @click="handleSelect()">单选提交</button>
<script type="text/javascript">
    var vm =   new Vue({
        el:"#box",
        data:{
            favorlang:"vue" //因为是双向绑定的,故初始化时vue会默认选中
        },
        methods: {
            handleSelect(){
                console.log(this.favorlang)
            }
        },
    })
</script>

2.6.5.购物车案例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <style>
        li {
            display: flex;
            justify-content: space-around;
            padding: 10px;
        }

        li img {
            width: 100px;
        }
    </style>
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
    
<body>
<div id="box">
    <!-- 1.当list长度为0时,显示为空,不为空则显示所有数据 -->
    <div v-if="list.length===0">购物车空空如也</div>
    <!-- 2.使用template包装元素控制所有的list遍历结果 -->
    <template v-else>
        <!-- 3.设置全选,在data中用状态isAllChecked接受,
            触发函数handleAllChecked勾选或取消勾选所有的数据。 -->
        <input type="checkbox" @change="handleAllChecked" v-model="isAllChecked"/>全选/全不选
        <ul>
            <!-- 4.遍历list设置唯一值id为key -->
            <li v-for="(item,index) in list" :key="item.id">
                <!-- 5.checkbox的多选设置value为item,勾选时value值添加到checkGroup数组中,
                    当checkGroup进行变化时,会触发sum()计算总金额
                同时触发函数checked,判断是否需要当全部勾选后勾选全部勾选 -->
                <input type="checkbox" v-model="checkGroup" :value="item" @change="checked()"/>
                <img :src="item.pic"/>
                <div>
                    <div>名称:{{item.name}}</div>
                    <div>价格:¥{{item.price}}</div>
                </div>
                <div>
                    <!-- 6.当进行点击加减号时,number开始变化,当减少到1时,减少按钮置灰。
                    当增加到限购数量时,加号按钮置灰 -->
                    <button @click="item.number--" :disabled="item.number===1">-</button>
                    {{item.number}}
                    <button @click="item.number++" :disabled="item.number===item.limit">+</button>
                </div>
                <div>
                    
                    <button @click="handleDelete(index,item.id)">删除</button>
                </div>
            </li>
        </ul>
        <div style="background-color: yellow;">
            <!-- 7.sum()函数依赖checkGroup数组,数组变化时,sum()会调用计算 -->
            总金额 : {{ sum() }}
        </div>
    </template>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            checkGroup: [],
            isAllChecked: false,
            list: [
                {
                    name: "商品1",
                    price: 10,
                    number: 1,
                    id: 1,
                    limit: 5,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品2",
                    price: 20,
                    number: 2,
                    id: 2,
                    limit: 10,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品3",
                    price: 30,
                    number: 3,
                    id: 3,
                    limit: 15,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                }
            ]
        },
        methods: {
            sum() {
                //依赖checkgroup,
                var total = 0
                // 8.使用箭头函数遍历checkGroup数据计算总额
                this.checkGroup.forEach(item => {
                    total += item.price * item.number
                })
                // console.log(total)
                return total
            },
            //删除方法
            handleDelete(index, deleteid) {
                console.log(deleteid)
                //删除checkgroup数组的对应的元素
                // filter 
                // 9.使用filter函数过滤删除的项,将过滤结果重新设置到checkGroup
                this.checkGroup = this.checkGroup.filter(item => item.id !== deleteid)
                //删除原数组
                this.list.splice(index, 1)
                //判定是否全选勾上
                this.checked()
            },
            //全选
            handleAllChecked(ev) {
                // console.log("checked",ev.target.checked)
                // console.log(this.isAllChecked)
                // 10.设置全选和全不选
                if (this.isAllChecked) {
                    this.checkGroup = this.list
                } else {
                    this.checkGroup = []
                }
            },
            //判定是否全选
            checked() {
                //11.根据已选的长度和总长度的判断是否需要勾选上全选
                if (this.checkGroup.length === this.list.length) {
                    this.isAllChecked = true
                } else {
                    this.isAllChecked = false
                }
            }
        },
    })
    var arr = [{id: 1}, {id: 2}, {id: 3}]
    var newarr = arr.filter(item => item.id !== 2)
    console.log(newarr)
</script>
</body>
</html>

效果:

2.6.6.修饰符

  1. .lazy :失去焦点同步一次

    用户名:<input type="text" v-model.lazy="mytext"/>
    

    使用v-model和data的状态双向绑定时,使用.lazy修饰符后并不会马上同步,只有在input标签失去焦点后才同步

  2. .number :格式化数字

    <body>
        <div id="box">
          年龄:<input type="number" v-model.number="myage"/>
        </div>
       
        <script type="text/javascript">
        var vm = 	new Vue({
            el:"#box",
            data:{
              myage:18,
            },
          })
        </script>
    </body>
    

    即使input标签设置的number类型,且在data中定义是数字,但是输入还是字符串,加上myage即可

  3. .trim : 去除首尾空格

    密码:<input type="password" v-model.trim="mypassword"/>
    

    在输入后会自动去除首位的空格

2.7.计算属性

2.7.1.计算属性computed

解决问题:复杂逻辑,模板难以维护

示例:如果想得到的结果需要复杂的处理过程,出现模板过重,难以维护。可以采用计算属性computed处理

<div id="box">
    <!-- 模板过重,难以维护, -->
    {{myname.substring(0,1).toUpperCase()+myname.substring(1)}}
</div>	

<script type="text/javascript">
var vm =  new Vue({
    el:"#box",
    data:{
        myname:"kerwin"
    }
})
</script>

计算属性特点:

  • 逻辑计算,防止模板过重,有缓存
  • 监听:依赖修改 ,get方法必须return

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<script src="lib/vue.js"></script>
</head>
<body>
    <div id="box">
        <!-- 计算属性  -->
        <h2>
          计算属性-有缓存
        </h2>
        <div> {{ mycomputedname }}</div>
        <div> {{ mycomputedname }}</div>
        <div> {{ mycomputedname }}</div>
        <h2>
          方法-表达式
        </h2>
        <div> {{ mymethodname() }}</div>
        <div> {{ mymethodname() }}</div>
        <div> {{ mymethodname() }}</div>
        <h2>计算属性- 可以被赋值</h2>
    </div>	
    <script type="text/javascript">
       var vm =  new Vue({
          el:"#box",
          data:{
            myname:"kerwin"
          },
          methods: {
            mymethodname(){
              console.log("mymethodname")
              // 基于依赖的改变, 重新执行一次。
              return this.myname.substring(0,1).toUpperCase()+this.myname.substring(1)
            }
          },
          //定义计算的属性的地方
          computed:{
            //简写
            mycomputedname(){
              console.log("mycomputedname")
              // 基于依赖的改变, 重新执行一次。
              return this.myname.substring(0,1).toUpperCase()+this.myname.substring(1)
            }
          }
        })
    </script>
</body>
</html>

上需求是将用户名首字母大写,如果页面多处使用,使用方法时会计算多次;如果使用计算属性有缓存只会计算一次,除非数据发生改变。

示例2:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>

    <script src="lib/vue.js"></script>
</head>
<body>
    <div id="box">
       <input type="text" v-model="mytext"/>
       {{ mydatalist }}
       <ul>
           <li v-for="data in mydatalist">
               {{data}}
           </li>
       </ul>
    </div>

    <script>
      var vm =  new Vue({
           el:"#box",
           data:{
            mytext:"",
            datalist:["aaa","bbb","ccc","ddd","add","cee","eee"]
           },
           computed: {
               mydatalist(){
                   // 依赖此状态的函数也会重新执行
                   return this.datalist.filter(item=>item.includes(this.mytext))
              }
           },
       })
    </script>
</body>
</html>

购物车案例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <style>
        li {
            display: flex;
            justify-content: space-around;
            padding: 10px;
        }
        li img {
            width: 100px;
        }
    </style>
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <div v-if="list.length===0">购物车空空如也</div>
    <template v-else>
        <input type="checkbox" v-model="isAllCheckedComputed"/>全选/全不选
        <ul>
            <li v-for="(item,index) in list" :key="item.id">
                <input type="checkbox" v-model="checkGroup" :value="item"/>
                <img :src="item.pic"/>
                <div>
                    <div>名称:{{item.name}}</div>
                    <div>价格:¥{{item.price}}</div>
                </div>
                <div>
                    <button @click="item.number--" :disabled="item.number===1">-</button>
                    {{item.number}}
                    <button @click="item.number++" :disabled="item.number===item.limit">+</button>
                </div>
                
                <div>
                    <button @click="handleDelete(index,item.id)">删除</button>
                </div>
            </li>
        </ul>
        {{checkGroup}}
        <div style="background-color: yellow;">
            总金额 : {{ sum() }}
        </div>
    </template>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            checkGroup: [],
            // isAllChecked:false,
            list: [
                {
                    name: "商品1",
                    price: 10,
                    number: 1,
                    id: 1,
                    limit: 5,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品2",
                    price: 20,
                    number: 2,
                    id: 2,
                    limit: 10,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品3",
                    price: 30,
                    number: 3,
                    id: 3,
                    limit: 15,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                }
            ]
        },

        methods: {
            sum() {
                //依赖checkgroup,
                var total = 0
                this.checkGroup.forEach(item => {
                    total += item.price * item.number
                })
                // console.log(total)
                return total
            },

            //删除方法
            handleDelete(index, deleteid) {
                console.log(deleteid)
                //删除checkgroup数组的对应的元素
                // filter 
                this.checkGroup = this.checkGroup.filter(item => item.id !== deleteid)
                //删除原数组
                this.list.splice(index, 1)
            }
        },
        computed: {
            isAllCheckedComputed: {
                set(isChecked) {
                    // console.log(isChecked)
                    if (isChecked) {
                        this.checkGroup = this.list
                    } else {
                        this.checkGroup = []
                    }
                },
                get() {
                    return this.checkGroup.length === this.list.length
                }
            }
        }
    })
</script>
</body>
</html>

2.7.2.监听器watch

watch: 监听,观察 --注重过程

不用return 调用

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <style>
        li {
            display: flex;
            justify-content: space-around;
            padding: 10px;
        }
        li img {
            width: 100px;
        }
    </style>
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <div v-if="list.length===0">购物车空空如也</div>
    <template v-else>
        <input type="checkbox" v-model="isAllChecked"/>全选/全不选
        <ul>
            <li v-for="(item,index) in list" :key="item.id">
                <input type="checkbox" v-model="checkGroup" :value="item" @change="checked()"/>
                <img :src="item.pic"/>
                <div>
                    <div>名称:{{item.name}}</div>
                    <div>价格:¥{{item.price}}</div>
                </div>
                <div>
                    <button @click="item.number--" :disabled="item.number===1">-</button>
                    {{item.number}}
                    <button @click="item.number++" :disabled="item.number===item.limit">+</button>
                </div>

                <div>
                    <button @click="handleDelete(index,item.id)">删除</button>
                </div>
            </li>
        </ul>
        {{checkGroup}}
        <div style="background-color: yellow;">
            总金额 : {{ sum() }}
        </div>
    </template>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            checkGroup: [],
            isAllChecked: false,
            list: [
                {
                    name: "商品1",
                    price: 10,
                    number: 1,
                    id: 1,
                    limit: 5,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品2",
                    price: 20,
                    number: 2,
                    id: 2,
                    limit: 10,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                },
                {
                    name: "商品3",
                    price: 30,
                    number: 3,
                    id: 3,
                    limit: 15,//限购
                    pic: "https://static.maizuo.com/pc/v5/usr/movie/44dc08914d508fc47c8267c6ca73f2d8.jpg"
                }
            ]
        },

        methods: {
            sum() {
                //依赖checkgroup,
                var total = 0
                this.checkGroup.forEach(item => {
                    total += item.price * item.number
                })
                return total
            },

            //删除方法
            handleDelete(index, deleteid) {
                console.log(deleteid)
                //删除checkgroup数组的对应的元素
                // filter 
                this.checkGroup = this.checkGroup.filter(item => item.id !== deleteid)
                //删除原数组
                this.list.splice(index, 1)
                //判定是否全选勾上
                this.checked()
            },

            //判定是否全选
            checked() {
                if (this.checkGroup.length === this.list.length) {
                    this.isAllChecked = true
                } else {
                    this.isAllChecked = false
                }
            }
        },

        watch: {
            //监听状态的改变
            isAllChecked(data) {
                console.log("isAllChecked状态改变了", data)
                if (this.isAllChecked) {
                    this.checkGroup = this.list
                } else {
                    if (this.checkGroup.length === this.list.length) {
                        this.checkGroup = []
                    }
                }
            }
        }
    })
</script>
</body>
</html>

监听状态的使用:定义一个和属性绑定名相同的监听器,例如函数的形式获取其属性值的变化,然后进行操作。

例如isAllChecked(data)就监听data中的isAllChecked状态值,一旦其发生改变就会触发监听,进一步触发里面的操作

2.8.Mixins

混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。

混入对象可以包含任意组件选项。 当组件使用混入对象时

所有混入对象的选项将被混入该组件本身的选项。

如果引入的代码中的方法和自身定义的方法重名,不用担心。使用相同名称的方法时会优先使用自身定义的方法。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick()">click</button>
    {{total}}
</div>
<script type="text/javascript">
    //mixin 公共方法引入,
    const obj = {
        methods: {
            handleClick() {
                console.log("click--外部定义")
            },
            handleClick1() {
                console.log("click1")
            }
        },
        computed: {
            total() {
                return "1111111111"
            }
        },
    }
    new Vue({
        el: "#box",
        data: {},
        mixins: [obj], //混入obj对象
        methods: {
            handleClick() {
                console.log("click--内部定义")
            }
        },
    })
</script>
</body>
</html>

3.fetch&axios

3.1.fetch

fetch是业界取代XMLHttpRequest的新标准,但是兼容性不好,故不推荐使用,仅作了解。

3.1.1.fetch的get请求

将返回报文转化为json格式;res.text() 字符串 res.json() json对象

fetch("./json/test.json?name=kerwin")
    .then(res=>res.json())  //
    .then(res=>{
           console.log(res)
     })

3.1.2.fetch的post请求

  • Content‐Type设置为application/x‐www‐form‐urlencoded告诉服务端参数的是通过&拼接字符串
  • Fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: ‘include’})
fetch("**", {
    method: 'post',
    headers: {
        "Content‐Type": "application/x‐www‐form‐urlencoded"
    },
    body: "name=kerwin&age=100",								
    credentials: "include"									
}).then(res => res.json()).then(res => {
    console.log(res)
});

3.1.3.fetch的post请求

Content‐Type设置为application/json告诉服务端参数的是JSON格式的

fetch("https://m.vip.com/server.html", {
    method: 'post',
    headers: {
        "Content‐Type": "application/json"
    },
    body: JSON.stringify({
        name: "kerin",
        age: 100
    })
}).then(res => res.json()).then(res => {
    console.log(res)
});

3.2.axios

3.2.1.axios的get请求

axios.get("json/maoyan.json").then(res=>{
    this.datalist=  res.data.movieList
})

3.2.2.axios的post请求

axios.post("json/maoyan.json", "name=kerwin&age=100").then(res=>{
	this.datalist=  res.data.movieList
})
        
axios.post("json/maoyan.json",{
    name:"kerwin",
    age:100
}).then(res=>{
    this.datalist=  res.data.movieList
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>fetch</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<script type="text/javascript" src="lib/vue.js"></script>
<script src="lib/axios.js"></script>
</head>
<body>
    <div id="box">
        <button @click="handleClick()">axios</button>     
        
        <ul>
            <li v-for="item in datalist" :key="item.id">
                <img :src=" handleImg(item.img) "/>
                <h3>{{item.nm}}</h3>
                <p>{{item.showInfo}}</p>
            </li>
        </ul>
    </div>
    <script type="text/javascript">
    	
        new Vue({
            el:"#box",
            data:{
              datalist:[]
            },
            methods: {
                handleClick(){
                    axios.get("json/maoyan.json").then(res=>{
                        this.datalist=  res.data.movieList
                    })
                },
                handleImg(path){
                    return path.replace('w.h','')+'@1l_1e_1c_128w_180h'
                }
            }    
        })
    </script>
</body>
</html>

4.组件

4.1.组件

4.1.1.虚拟dom与diff算法key的作用

  1. 把树按照层级分解

  2. 同key值对比

<img src="https://stonebridge.oss-cn-shanghai.aliyuncs.com/vue/17104937662.png"  style="zoom:67%;" />
  1. 同组件对比

4.1.2.为什么组件化

扩展HTML元素,封装可重用的代码

4.1.3.组件注册方式

  • 全局组件

  • 局部组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <kerwin-navbar>
    </kerwin-navbar>
    <kerwin-tabbar></kerwin-tabbar>
</div>
<script>
    //全局组件定义方式
    Vue.component("kerwinNavbar", {
        template: `
            <div style="background:yellow;">
                <button @click="handleBack()">返回</button>
                <span>导航栏--{{myname}}</span>
                <button @click="handleHome()">首页</button>
                <child></child>
                <tabbarchild></tabbarchild>
            </div>
            `,//模板
        methods: {
            handleBack() {
                console.log("返回按钮")
            },
            handleHome() {
                console.log("homeclick")
            }
        },
        computed: {},
        watch: {},
        data() {
            return {
                myname: "child-name",
                form: {
                    dzrqStartDate: '',
                    dzrqEndDate: '',
                    sfdz: '',
            	}
            }
        }
    })
    //全局组件定义方式。
    Vue.component("kerwinTabbar", {
        template: `
                <div style="background:red">
                    tabbbar
                    <child></child>
                    <tabbarchild></tabbarchild>
                </div>
            `,
        components: {
            //局部定义
            "tabbarchild": {
                template: `<div>tabbarchild</div>`
            }
        }
    })
    // 定义一个孩子组件
    Vue.component("child", {
        template: `
                <div>child</div>
    })
    // 根组件
    new Vue({
        el: "#box",
        data: {
            myname: "kerwin"
        }
    })
</script>
</body>
</html>
  1. 起名字 :js使用定义组件名(kerwinNavbar)驼峰定义, html 使用组件时使用-链接符(kerwin-tabbar)
  2. dom片段没有代码提示没有高亮显示 - vue单文件组件解决
  3. css 只能写成行内。- vue单文件组件解决
  4. template包含一个根节点(自定义组件需要有一个root element )
  5. 组件是孤岛,无法【直接】访问外面的组件的状态或者方法。- 间接的组件通信来交流。
  6. 自定义组件可以有data,methods,computed…,但是data 必须是一个函数
  7. 所有的组件都在一起, 太乱了 — vue单文件组件解决

4.1.4.组件编写方式与Vue实例的区别

  1. 自定义组件需要有一个root element
  2. 父子组件的data是无法共享
  3. 组件可以有data,methods,computed…,但是data 必须是一个函数

4.1.5.组件通信

4.1.5.1.父子组件传值 (props down, events up)
4.1.5.2.属性验证
props: {name: Number} //Number, String, Boolean, Array, Object, Function, null(不限制类型)
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <style>
        .navbar {
            background: red;
        }
    </style>
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <!-- {{msg}} -->
    <navbar mytitle="首页" :myshow="false" :mymsg="msg"></navbar>
    <navbar mytitle="列表" :mymsg="msg"></navbar>
    <navbar mytitle="购物车" :mymsg="msg"></navbar>
    <navbar :myshow="false" :mymsg="msg"></navbar>
</div>
<script type="text/javascript">
    // 父组件 --> 子 组件 ,传属性 是为了 让组件 最大化复用
    Vue.component("navbar", {
        template: `<div style="background:yellow">
            <button v-show="myshow">返回</button>
            <span>导航--{{mytitle}}--{{mymsg}}</span>
            <button v-show="myshow">首页</button>
        </div>`,
        // props:["mytitle","myshow"] //父组件传来的属性, 在这里进行接收
        // props:{
        //   mytitle:String,
        //   myshow:Boolean
        // } //属性验证
        props: {
            mytitle: {
                type: String,
                default: "默认名字"
            },
            myshow: {
                type: Boolean,
                default: true
            },
            mymsg: {
                type: String,
                default: "000"
            }
        }
    })
    // 根组件
    new Vue({
        el: "#box",
        data: {
            msg: "11111111111111111111"
        }
    })
</script>
</body>
</html>

父组件使用子组件时,如果要给子组件传值,只需要在使用组件时绑定属性。子组件在props设置接收的对应名称和类型。

4.1.5.3.事件机制(子组件调用父组件方法或者给父组件传值)
  • 使用 $on(eventName) 监听事件
  • 使用 $emit(eventName) 触发事件

示例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <child @aaaaa="handleEvent"></child>
</div>
<script>
    Vue.component("child", {
        data() {
            return {
                money: 10000000
            }
        },
        template: `
          <div style="background:yellow">
            child-<button @click="handleClick">click-child</button>
          </div>
        `,
        methods: {
            handleClick() {
                // console.log("click",this.money)
                //分发
                this.$emit("aaaaa", this.money)
            }
        }
    })
    //root component
    new Vue({
        el: "#box",
        methods: {
            handleEvent(data) {
                console.log("父组件中定义的", data)
            }
        },
    })
    // 子传父 - 需要再子组件身上监听事件,等子组件通过this.$emit 触发事件。
</script>
</body>
</html>

子传父 - 需要在子组件上定义监听事件,等子组件通过this.$emit 触发事件。

父组件在使用子组件child的时候通过 @aaaaa="handleEvent"设置子组件可以通过aaaa的名称触发父组件中handleEvent()方法;在子组件中设置button设置点击触发函数handleClick()方法,该方法通过this.$emit(“aaaaa”, this.money)发分发机制触发handleEvent()函数,将this.money传值给父组件的函数

示例2.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <navbar @myevent="change"></navbar>
    <sidebar v-show="isShow"></sidebar>
</div>
<script>
    Vue.component("navbar", {
        template: `
            <div style="background-color: yellow;">
              导航栏 --<button @click="handleClick">click</button>
            </div>
          `,
        methods: {
            handleClick() {
                this.$emit("myevent")
            }
        },
    })

    Vue.component("sidebar", {
        template: `
            <div style="background-color: blue;width:200px;" >
                <ul>
                  <li>好友</li>
                  <li>设置</li>
                  <li>退出</li>
                </ul>
            </div>
        `
    })
    var vm = new Vue({
        el: "#box",
        data: {
            isShow: false
        },
        methods: {
            change() {
                this.isShow = !this.isShow
            }
        }
    })
</script>
</body>
</html>
4.1.5.4. ref(父组件向子组件传值)
  • ref放在标签上, 拿到的是原生节点
  • ref放在组件上, 拿到的是组件对象,通信功能
<input ref="mytext"/> this.$refs.mytext

父组件在使用子组件child时,设置ref=“mychild”,点击父组件的的button按钮触发函数handleAdd()执行this.$refs.mychild.childname = “22222222222222222”,即将child组件的状态childname设置为22222222222222222。子组件页面显示的childname也会改变

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <div ref="mydiv">111111</div>
    <input type="text" ref="mytext"/>

    <child ref="mychild"></child>
    <button @click="handleAdd">add</button>
</div>
<script type="text/javascript">
    Vue.component("child", {
        data() {
            return {
                childname: "111111111111111111111111"
            }
        },
        template: `
            <div style="background:yellow">child--{{childname}}</div>
          `
    })
    new Vue({
        el: "#box",

        methods: {
            handleAdd() {
                // console.log(this.$refs.mychild.childname)
                this.$refs.mychild.childname = "22222222222222222"
            }
        }
    })
    /*
      1. ref放在标签上, 拿到的是原生节点
      2. ref放在组件上, 拿到的是组件对象,通信功能
    */
</script>
</body>
</html>
4.1.5.5.中间人模式

当两个兄弟组件需要进行通信的时候常用的方法有中间人模式和中央总线模式

中间人模式即通过第三方(例如共同的父组件)作为中间人进行通信

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .item {
            overflow: hidden;
            padding: 10px;
            width: 400px;
            border:1px solid red;
        }

        .item img {
            width: 100px;
            float: left;

        }
        .film{
            border:1px solid black;
            height: 1500px;
        }

        .filminfo {
            width: 300px;
            min-height: 200px;
            background: yellow;
            position: fixed;
            right:50px;
            top:100px;
            border:1px solid blue;
        }
    </style>
    <script src="lib/vue.js"></script>

</head>

<body>
    <div id="box">
       <button @click="handleAjax">ajax</button>
       <film-item v-for="data in datalist" :key="data.filmId" :item="data" @myevent="handleEvent"></film-item>
       <film-detail :myinfo="info"></film-detail>
    </div>
    <script>

        Vue.component("filmDetail",{
            props:["myinfo"],
            template:`
                <div class="filminfo">
                    {{myinfo}}    
                </div>
            `
        })

        Vue.component("filmItem",{
            props:["item"],
            template:`
                <div class="item">
                    <div>{{item.name}}</div>
                    <button @click="handleClick()">详情</button>
                </div>
            `,
            methods:{
                handleClick(){
                    // console.log(this.item.synopsis)
                    this.$emit("myevent",this.item.synopsis)
                }
            }
        })

       var vm=  new Vue({
            el: "#box",
            data:{
               datalist:[],
               info:"111111111111111111111"
            },
            methods: {
                handleAjax(){
                        this.datalist = [{filmId:"1",name:"电影1",synopsis:"详情111111111"},{filmId:"2",name:"电影2",synopsis:"详情22222"},{filmId:"3",name:"电影3",synopsis:"详情333333"}]
                },

                handleEvent(info){
                    console.log("父组件定义",info)
                    this.info = info
                }
            },
        })
    </script>
</body>
</html>

在父组件film-item中,希望将data中的info数据传递到另一个组件filmDetail。在组件filmItem中点击详情按钮触发组件内的函数handleClick(),通过事件机制$emit通过myevent绑定触发父组件的handleEvent函数将接受值设置父组件状态的info。通过<film-detail :myinfo=“info”></film-detail>将info传递给子组件filmDetail,通过props接收,赋值给filmDetail显示出来。

4.1.5.6.中央事件总线

中央事件总线不同于中间人模式,只需要定义空的vue实例var bus = new Vue() 。通过bus.$emit发布触发订阅者监听的函数,并且传值出来。

订阅者通过bus.$on订阅bus。设置被发布者触发函数,以及接受发布者传递的参数。

发布者通过bus.$emit触发订阅者监听的函数,并传值。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <child1></child1>
    <child2></child2>
</div>
<script type="text/javascript">
    //1.定义中央事件总线 bus
    var bus = new Vue() //空的vue实例
    // bus.$on  bus.$emit
    Vue.component("child1", {
        template: `
            <div>child1<button @click="handleClick()">详细信息</button></div>
          `,
        methods: {
            handleClick() {
                //2.发布者通过 bus.$emit触发kerwin事件,凡是在同一中间事件总线的的监听者会触发该事件,并能接受参数。
                bus.$emit("kerwin", "男人看了沉默,,,,,")
            }
        }
    })

    Vue.component("child2", {
        template: `
            <div>child2-订阅者</div>
          `,

        mounted() {
            console.log("mounted-生命周期-dom创建完之后就会触发")
			//1.通过bus.$on订阅kerwin事件,接受传递的参数data。当执行bus.$emit("kerwin", "男人看了沉默,,,,,")就会触发该函数
            bus.$on("kerwin", (data) => {
                console.log("订阅者child2----", data)
            })
        }
    })
    var vm = new Vue({
        el: "#box",
        methods: {},
    })
</script>
</body>
</html>

4.1.6.属性可不可以修改?

严格来说,Vue子组件不能随便更改父组件传递过来的属性,但是可以通过props 配合 $emit 改变父组件传入的值。

  • 父组件

    <my-input :warning.sync="warning" />
    在父组件传入值时,需要有xxx.sync修饰符;
    
  • 子组件

    $emit('update:warning',val)
    子组件可以在$emit('update:xxxx',val),派发事件修改传入的值;
    

4.1.7.v-once 用在组件上有什么用?

4.1.8.v-model可以用在组件通信(用于组件封装)

使用v-model可以封装一个具有样式,封装的便捷性的公用组件,使用时像原生标签一样使用。

  1. 定义一个kerwinInput组件,封装input标签,使用同input一样。

  2. 父组件使用该组件时(将kerwinInput的驼峰写法使用时用-)

    <kerwin-input type="number" title="年龄" v-model="age"></kerwin-input>
    

    其中的type,title在kerwinInput组件中props用type,title接受,v-model对应的value。

  3. 在kerwinInput的props接收type,title以及value。就会在页面中显示title以及输入框的type属性。

  4. 封装输入框组件的最终目的实现状态和页面的双向绑定。

    • 输入框中用到的v-model其实是一个包含:value=""和@input的功能。即能将数据显示出来,当输入框数据被改变时实时同步到状态值中。
    • 在使用组件时使用v-model=“age”,即通过:value=“”功能将父页面中的值传递到组件props中,再通过:value=“value” 在组件中显示父页面的data值;给子组件中input绑定handleInput方法,当输入值是就会触发。通过$emit将值传递到父组件中的data的状态接收。
    Vue.component("kerwinInput", {
            props: ["type", "title", "value"],
            template: `<div>
                <label>{{title}}</label>
                <input :type="type" style="background:red" @input="handleInput" :value="value" /> 
             </div>`,
            methods: {
                handleInput(evt) {
                    this.$emit("input", evt.target.value)
                }
            },
        })
    
  5. 使用

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Examples</title>
        <meta name="description" content="">
        <meta name="keywords" content="">
        <link href="" rel="stylesheet">
        <script type="text/javascript" src="lib/vue.js"></script>
    </head>
    <body>
    <div id="box">
        <kerwin-input type="text" title="姓名" v-model="username"></kerwin-input>
        <kerwin-input type="number" title="年龄" v-model="age"></kerwin-input>
        <kerwin-input type="password" title="密码" v-model="password"></kerwin-input>
        <button @click="handleSubmit">submit</button>
        <button @click="handleReset">reset</button>
    </div>
    <script>
        Vue.component("kerwinInput", {
            props: ["type", "title", "value"],
            template: `
              <div>
              <label>{{ title }}</label>
              <input :type="type" style="background:red" @input="handleInput" :value="value"/>
              </div>`,
            methods: {
                handleInput(evt) {
                    // console.log(evt.target.value)
                    this.$emit("input", evt.target.value)
                }
            },
        })
        var vm = new Vue({
            el: "#box",
            data: {
                isShow: false,
                username: "default username",
                age: 0,
                password: "default passowrd"
            },
            methods: {
                handleSubmit() {
                    console.log(this.username, this.age, this.password)
                },
                handleReset() {
                    this.username = ""
                    this.age = 0
                    this.password = ""
                }
            },
        })
    </script>
    </body>
    </html>
    

4.1.9.动态组件

  • <component>元素,动态地绑定多个组件到它的 is 属性
  • <keep-alive>保留状态,避免重新渲染
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <keep-alive>
        <component :is="isWhich"></component>
    </keep-alive>
    <footer>
        <ul>
            <li @click=" isWhich='home' ">
                首页
            </li>
            <li @click=" isWhich='list' ">
                列表
            </li>
            <li @click=" isWhich='shopcar' ">
                购物车
            </li>
        </ul>
    </footer>
</div>

<script type="text/javascript">
    Vue.component("home", {
        template: `<div>
            home
            <input type="text"/>
          </div>`
    })

    Vue.component("list", {
        template: `<div>
            list
          </div>`
    })

    Vue.component("shopcar", {
        template: `<div>
            shopcar
          </div>`
    })

    var vm = new Vue({
        el: "#box",
        data: {
            isWhich: "home"
        }
    })
</script>
</body>
</html>

4.2.slot插槽 (内容分发)

4.2.1.概念

当父组件使用子组件时,需要将自定义的特殊模块放在子组件的指定位置。混合父组件的内容与子组件自己的模板。这就是插槽的作用。

父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。故父组件中自定义的模块就就可以触发父组件的内容

注意 v­-slot只能添加在<template> 上, 文本节点也可以当具名插槽内容插入

4.2.2.示例

将父组件template的内容根据v-slot:left或者v-slot:right插入子组件navbar中name="left"或者name="right"的插槽位置

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <navbar>
        <template v-slot:left>
            <div>
                <button>返回</button>
                <i>图标</i>
            </div>
        </template>
        <template v-slot:right>
            <div>
                <button>搜索</button>
                <i>图标</i>
            </div>
        </template>
    </navbar>
</div>
<script type="text/javascript">
    Vue.component("navbar", {
        template: `<div>
            <slot name="left"></slot>
            navbar  
            <slot name="right"></slot>
          </div>`
    })
    new Vue({
        el: "#box",
        data: {}
    })
</script>
</body>
</html>

4.3.过渡效果(transition过渡)

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。

4.4.生命周期

4.4.1.生命周期简介

什么是生命周期: 从Vue创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期。

**生命周期钩子函数:**就是生命周期事件的别名;

4.4.2.初始

**beforeCreate:**实例刚刚在内存中被创建出来,此时还没有初始化 datamethods 属性;data的数据访问不到,且html还没有编译

**created:**实例已经在内存中创建好,此时 datamethods 已经创建好,此时还没有开始编译模板;data 数据可以访问到,也可以修改,但是模板还没有编译。

4.4.3.挂载

**beforeMount:**此时已经完成了模板编译,但是还没有挂载到页面中; data 的数据可以访问和修改,而且此时的模板已经编译好了,还没有更新到页面中

**mounted:**这个时候已经把编译好的模板挂载到页面指定的容器里;此时编译的模板更新到页面中了

4.4.4.更新

**beforeUpdate:**状态更新之前执行此函数,此时的 data 中的数据是最新,但是界面上显示的还是旧的,因为此时还没有开始重新渲染DOM节点;此时修改输入框的内容,data中的数据已经更新了,但是页面上显示的还是旧数据;

**updated:**实例更新完毕之后调用此函数,此时data 中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了;此时 data 的内容已经更新,页面显示的也是最新的数据。

4.4.5.销毁

**beforeDestroy:**实例销毁之前调用,在这一步,实例让然完全可用。

**destroyed:**实例销毁后调用,调用后,Vue实例指向的所以东西会被解绑,所有的事件监听器会被移除,所有的子实力也会被销毁。

4.4.6.组件的生命周期

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    {{myname}}
    <button @click="handleClick">click</button>
    <child v-if="isCreated"></child>
</div>
<script type="text/javascript">
    Vue.component("child", {
        template: `
          <div>child</div>
        `,
        mounted() {
            window.onresize = function () {
                console.log("resize-1111111")
            }
        },
        destroyed() {
            window.onresize = null
        }
    })
    //创建根组件(每个组件都有8个生命周期, 如果需要 ,自己用)
    var vm = new Vue({
        el: "#box",
        data: {
            myname: "kerwin",
            isCreated: true
        },

        methods: {
            handleClick() {
                this.myname = "xiaoming"
                //状态修改之后, 异步更新dom

                var obox = document.getElementById("box")
                console.log("11111111", obox.innerHTML)
            }
        },
        beforeCreate() {
            console.log("beforeCreate", this.myname)
        },
        created() {
            this.myname = "1111111111" + this.myname
            console.log("created", "初始化工作", this.myname)
        },
        beforeMount() {
            this.myname = "2222222222" + this.myname
            console.log("beforeMount", "dom挂载之前得,无法访问dom", "自己在服务端渲染,这个函数会在客户端和服务端各自触发一次")
        },
        mounted() {
            console.log("mounted", "dom挂载完成了,访问dom-swiper,", "监听事件,ajax,设置定时器")
        },
        beforeUpdate() {
            console.log("beforeUpdate")
        },
        updated() {
            console.log("updated", "获取到更新后得dom,依赖于dom操作的库,需要知道状态更新完,什么时候dom更新。")
            var obox = document.getElementById("box")
            console.log("22222222222", obox.innerHTML)
        },
        beforeDestroy() {
            console.log("beforeDestroy")
        },
        destroyed() {
            console.log("destroyed-取消定时器,window事件进行解绑操作")
        }
    })
    //  vue组件 生命周期 ?
    //  vue组件的 钩子函数?
    //  vue组件的生命周期钩子函数?
</script>
</body>
</html>

4.5.swiper

4.5.1.swiper-静态

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .kerwin {
            width: 600px;
            height: 300px;
            background: yellow;
        }
    </style>
</head>
<body>

<div class="swiper-container kerwin">
    <div class="swiper-wrapper">
        <div class="swiper-slide">Slide 1111111111</div>
        <div class="swiper-slide">Slide 2222222222</div>
        <div class="swiper-slide">Slide 3333333333</div>
    </div>
    <!-- Add Arrows -->
    <div class="swiper-button-next"></div>
    <div class="swiper-button-prev"></div>
    <div class="swiper-pagination"></div>
</div>
<script>
    new Swiper(".kerwin", {
        loop: true,
        // direction:"vertical",
        navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
        },
        pagination: {
            el: '.swiper-pagination',
        },
    })
</script>
</body>
</html>
  1. 引入样式href=“lib/swiper/css/swiper.css”
  2. 引入js函数src=“lib/swiper/js/swiper.js”
  3. 必须使用class="swiper-container"定义一个swiper轮播容器,所有的轮播组件都在这里。
  4. 使用class="swiper-wrapper"包裹要轮播的内容,具体轮播内容在class="swiper-slide"显示。
  5. <div class=“swiper-button-next”></div>和<div class=“swiper-button-prev”></div>定义前后翻页,同时需要在new Swiper的时候定义前后翻页在navigation中。
  6. <div class=“swiper-pagination”></div>定义最下面的翻页,同时需要在new Swiper的时候定义在pagination中。
  7. new Swiper中定义loop: true,可以轮播循环。
  8. direction:"vertical"设置轮播方向
  9. 在new Swiper时会将初始化swiper轮播容器,此时轮播中所有节点的都应该完整,如果初始化后修改轮播容器中的节点将无法正常使用,除非再次初始化。

4.5.2.swiper-动态

如果更加ajax请求动态生成轮播图,需要修改轮播后重新初始化;此时为修改了轮播的内容如果没有进行初始化就会出现无法正常使用。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .kerwin {
            width: 600px;
            height: 300px;
            background: yellow;
        }
    </style>
</head>
<body>
<div class="swiper-container kerwin">
    <div class="swiper-wrapper">
    </div>
    <!-- Add Arrows -->
    <div class="swiper-button-next"></div>
    <div class="swiper-button-prev"></div>
    <div class="swiper-pagination"></div>
</div>
<script>
    //模拟ajax
    setTimeout(() => {
        var list = ["aaaa", "bbbb", "cccc"]
        var newlist = list.map(item => `<div class="swiper-slide">${item}</div>`)
        var owraper = document.querySelector(".swiper-wrapper")
        owraper.innerHTML = newlist.join("")
        // console.log(newlist)
        //初始化
        new Swiper(".kerwin", {
            loop: true,
            // direction:"vertical",
            navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
            },
            pagination: {
                el: '.swiper-pagination',
            },
        })
    }, 2000)
    //swiper  初始化过早
</script>
</body>
</html>

4.5.3.swiper-vue

在mounted()中加载轮播数据,但是状态立即被改变, dom异步更新。无法确定合适dom更新后进行new Swiper初始化,因此只能在updated() 中进行new Swiper初始化。导致的问题就是任何dom更新就会触发new Swiper初始化。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .swiper-container {
            width: 600px;
            height: 300px;
        }
    </style>
</head>
<body>
<div id="box">
    <div class="swiper-container kerwin">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="data in datalist">
                {{data}}
            </div>
        </div>
        <div class="swiper-pagination"></div>
    </div>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
        },
        mounted() {
            setTimeout(() => {
                this.datalist = ["aaaa", "bbbbb", "ccccc"]
                //状态立即被改变, dom异步更新
                console.log("mouted", document.querySelectorAll(".swiper-slide").length)
            }, 2000)
        },
        updated() {
            //任何dom更新都会被触发重新初始化Swiper
            console.log("updated", document.querySelectorAll(".swiper-slide").length)
            new Swiper(".kerwin", {
                loop: true,
                // direction:"vertical",
                navigation: {
                    nextEl: '.swiper-button-next',
                    prevEl: '.swiper-button-prev',
                },
                pagination: {
                    el: '.swiper-pagination',
                },
            })
        },
    })
</script>
</body>
</html>

4.5.4.swiper-vue组件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .swiper-container {
            width: 600px;
            height: 300px;
        }

        .swiper-container img {
            width: 100%;
        }
    </style>
</head>
<body>
<div id="box">
    <swiper v-if="datalist.length" :myoptions="{
      loop:true,
      direction:'vertical'
    }">
        <div class="swiper-slide" v-for="data in datalist">
            {{data}}
        </div>
        <template #pagination>
            <div class="swiper-pagination"></div>
        </template>
    </swiper>
</div>
<script type="text/javascript">
    Vue.component("swiper", {
        props: ["myoptions"],
        template: `
      <div class="swiper-container kerwin">
        <div class="swiper-wrapper">
          <slot></slot>
        </div>
        <slot name="pagination"></slot>
      </div>
      `,
        mounted() {
            var baseoptions = {
                loop: false,
                pagination: {
                    el: '.swiper-pagination',
                },
            }
            new Swiper(".kerwin", Object.assign(baseoptions, this.myoptions))
        },
        destroyed() {
            console.log("destroyed")
        },
    })
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
        },
        mounted() {
            setTimeout(() => {
                this.datalist = ["aaaa", "bbbbb", "ccccc"]
            }, 2000)
        }
    })
</script>
</body>
</html>
  1. 定义swiper的轮播组件
  2. 定义插槽由用户自定义轮播内容
  3. 定义插槽<slot name=“pagination”></slot>控制轮播分页
  4. 用户传递myoptions参数控制轮播的具体配置信息
  5. <swiper v-if=“datalist.length”>通过控制轮播显示
  6. 此时通过定义组件的形式,避免初始化时或者更新完成后都要调用初始化方法,只需要在组件调用mounted()即可,当使用组件传递的参数改变就可以重新初始化。

5.指令

5.1.自定义指令directives - 对普通DOM元素进行底层操作

  1. 注册一个全局自定义指令v-focus

  2. 如果想注册局部指令,组件也接受一个directives的选项

5.2.钩子函数

5.2.1.参数 el,binding,vnode,oldvnode

5.2.2.bind,inserted,update,componentUpdated,unbind

5.3.函数简写

例如给div增加一个修改背景颜色的指令

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <div v-hello="  'red'  ">1111111111111111111111</div>
    <div v-hello=" 'blue'  ">2222222222222222222222</div>
    <div v-hello=" 'yellow' ">3333333333333333333333</div>
    <div v-hello="mycolor">55555555555555</div>
</div>

<script type="text/javascript">
    Vue.directive("hello", {
        //指令的生命周期-1
        inserted(el, binding) {
            console.log("绑定当前指令的标签插入父节点就会执行", binding.value)
            el.style.background = binding.value
        },
        update(el, binding) {
            // console.log("")
            el.style.background = binding.value
        }
    })
    var vm = new Vue({
        el: "#box",
        data: {
            mycolor: "red"
        }
    })
</script>
</body>
</html>

inserted在绑定元素插入父节点时调用,故在初始化时完成时调用修改背景颜色,但是如果指令传递的参数修改时则无法触发。故此时需要用到update方法再次触发,修改背景颜色。因此导致代码出现重复。

进行函数简写优化处理,此时初始化完成后或者指令对应参数修改都会触发,完成修改

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<div id="box">
    <div v-hello="  'red'  ">1111111111111111111111</div>
    <div v-hello=" 'blue'  ">2222222222222222222222</div>
    <div v-hello=" 'yellow' ">3333333333333333333333</div>
    <div v-hello="mycolor">55555555555555</div>
</div>
<script type="text/javascript">
    Vue.directive("hello", function (el, binding) {
        el.style.background = binding.value
    })
    var vm = new Vue({
        el: "#box",
        data: {
            mycolor: "red"
        }
    })
</script>
</body>
</html>

5.4.vnode:虚拟节点

虚拟dom(vnode:虚拟节点):在vue中虚拟dom是用JS对象模拟真实的dom节点,和diff算法搭配方便对比,最后找到哪些节点可以复用,哪些不可以复用。

vnode.context:当前节点所在的组件对象,真正的vue组件对象,即vue的实例,相当于var vm = new Vue({})

5.5.利用指令完成swiper轮播

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .swiper-container {
            width: 600px;
            height: 300px;
        }
    </style>
</head>

<body>
<div id="box">
    <div class="swiper-container kerwin">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(data,index) in datalist" v-swiper="{
            current:index,
            length:datalist.length
          }">
                {{data}}
            </div>
        </div>
        <div class="swiper-pagination"></div>
    </div>
</div>
<script type="text/javascript">
//虚拟dom(vnode:虚拟节点):在vue中虚拟dom是用JS对象模拟真实的dom节点,和diff算法搭配方便对比,最后找到哪些节点可以复用,哪些不可以复用。
    Vue.directive("swiper", {
        inserted(el, binding, vnode) {
            console.log(vnode)
            console.log(vnode.context)
            console.log(vnode.context.datalist.length)
            if (binding.value.current === binding.value.length - 1) {
                new Swiper(".kerwin", {
                    loop: true,
                    pagination: {
                        el: '.swiper-pagination',
                    },
                })
            }
        }
    })
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
            // myswiper:null
        },
        mounted() {
            setTimeout(() => {
                this.datalist = ["11111", "2222222", "333333", "55555", "666666"]
                //状态同步更新, dom异步更新
            }, 2000)
        }
    })
</script>
</body>
</html>

使用指令在inserted中执行,通过判断当前索引是否是最后一个确定是否初始化swiper

5.6.nextTicks

所有的dom都插入到节点中了,更新完dom后执行this.$nextTick(),比updated都晚。只会跟踪当前节点,不会跟踪所有的更新的,其他的更新不会影响。

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

在mounted()中datalist更新后,在dom中完成渲染后,执行updated()后再执行this.$nextTick()的内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Examples</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
    <link rel="stylesheet" href="lib/swiper/css/swiper.css">
    <script src="lib/swiper/js/swiper.js"></script>
    <style>
        .swiper-container {
            width: 600px;
            height: 300px;
        }
    </style>
</head>
<body>
<div id="box">
    <div class="swiper-container kerwin">
        <div class="swiper-wrapper">
            <div class="swiper-slide" v-for="(data,index) in datalist">
                {{data}}
            </div>
        </div>
        <div class="swiper-pagination"></div>
    </div>
</div>

<script type="text/javascript">
    //vnode 虚拟节点
    var vm = new Vue({
        el: "#box",
        data: {
            datalist: [],
            // myswiper:null
        },
        mounted() {
            setTimeout(() => {
                this.datalist = ["11111", "2222222", "333333", "55555", "666666"]
                //状态同步更新, dom异步更新
                this.$nextTick(() => {
                    console.log("我执行的比updated都晚,但我只会执行一次。")
                    new Swiper(".kerwin", {
                        loop: true,
                        pagination: {
                            el: '.swiper-pagination',
                        },
                    })
                })
            }, 2000)
        },
        updated() {
            console.log("updated")
        }
    })
</script>
</body>
</html>

6.过滤器

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

示例:item.img显示之前进行kerwinfilter过滤器,'myname’执行过滤器链,依次执行uppercase和sub

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>fetch</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    <script type="text/javascript" src="lib/vue.js"></script>
    <script src="lib/axios.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick()">axios</button>
    <ul>
        <li v-for="item in datalist" :key="item.id">
            <img :src=" item.img |  kerwinfilter"/>
            <h3>{{item.nm}}</h3>
            <p>{{item.showInfo}}</p>
        </li>
    </ul>
    <div>{{ 'myname' | uppercase | sub }}</div>
</div>
<script type="text/javascript">

    Vue.filter("kerwinfilter", function (path) {
        return path.replace('w.h', '') + '@1l_1e_1c_128w_180h'
    })
    Vue.filter("uppercase", function (str) {
        return str.toUpperCase()
    })

    Vue.filter("sub", function (str) {
        return str.substring(0, 2)
    })

    new Vue({
        el: "#box",
        data: {
            datalist: []
        },
        methods: {
            handleClick() {
                axios.get("test.json").then(res => {
                    // console.log(res.data.movieList)
                    this.datalist = res.data.movieList
                })
            }
        }
    })
</script>
</body>
</html>

7.单文件组件

7.1.单文件组件

https://cn.vuejs.org/v2/guide/single-file-components.html

7.2.脚手架的(vue-cli)的安装

想从零搭建你自己的构建工具,这时你需要通过Vue Loader 手动配置 webpack。过程较长,也可以使用vue的vue-cli工具快生成一个基于vue的单文件体系的项目,项目会自动安装所有需要的依赖,配置好开箱即用。

脚手架从作用就是快速创建vue项目

npm install -g @vue/cli (一次安装) 
vue --version 查看版本
vue create myapp
*npm run serve 开发环境构建
*npm run build 生产环境构建(把代码压缩,进行合并为一个文件)
*npm run lint 代码检测工具(校验代码的书写,修复)
1.style标签 加上scoped属性,css局部生效
2.style标签 加上lang="scss",支持scss

7.2.1.首先安装node.js

7.2.2.根据提示完成安装

7.2.3.配置环境变量查看是否安装成功和版本号

node -v
npm -v

7.2.4.使用如下命令安装npm的国内镜像

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

7.2.5.安装vue脚手架

老版可以用cnpm代替npm来安装依赖包

cnmp安装结束后,在cmd命令窗口使用如下命令安装vue-cli构建工具

//如果之前有安装,安装新版需先卸载旧版本
npm uninstall vue-cli -g

//老版本
cnpm install -g vue-cli
//新版本
npm install -g @vue/cli

安装后查看vue脚手架版本

vue --version

7.2.6.使用脚手架vue-cli来构建项目

7.2.7.创建项目

使用cmd命令窗口cd到项目目录下

在项目目录下使用如下命令初始化构建项目(初始化一个项目,项目名称是myapp),项目名不能有大写字母

7.2.8.进入项目中,启动项目

cd myvue
npm run serve

7.2.9.访问地址

7.3.项目分析

7.3.1.文件说明

7.3.2.项目入口

  1. index.html(主入口页面)

    <!DOCTYPE html>
    <html lang="">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <title><%= htmlWebpackPlugin.options.title %></title>
      </head>
      <body>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    
  2. mian.js

    mian.js作为项目js的入口

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    
    Vue.config.productionTip = false
    
    new Vue({
      router,
      store,
      components:{App:App},
    }).$mount('#app')
    两种一样,都是实例化vue后挂载的首页的app节点上
    // new Vue({el:"#app"})
    
    • 以上两种方式都是都是new Vue()实例化vue后挂载首页index.html的app元素上,可理解为像之前那样初始化app元素

    • router路由:页面跳转是根据路由配置的。通过import store from './store’导入index.js

      index.js

      import Vue from 'vue'
      import VueRouter from 'vue-router'
      import Film from '../views/Film.vue'
      import Cinema from '../views/Cinema.vue'
      import Center from '../views/Center.vue'
      Vue.use(VueRouter) // 注册模块
      
      const routes = [
        {
          path: '/film',
          component: Film
        },
      ]
      
      const router = new VueRouter({
        routes
      })
      
      export default router
      

      通过export default router导出组件,没有规定名字,可以引入的页面起名字

    • components:{App:App},使用了导入的App组件。这是一个vue的单文件组件

    • vue项目一般喜欢单页面开发,并不是只在整个团队都在一个页面进行开发,换成render的渲染方式。把导入了vue(vue.js也是通过import Vue from 'vue’引入),导入了App.vue。实例化vue把外面的App.vue组件进行render渲染,然后挂载在app节点上,整个App.vue的内容就在index.html的app中展示出来。

  3. App.vue

    <template>
        <div>
          {{myname}}
          <ul>
            <li>111</li>
            <li>222</li>
            <li>333</li>
          </ul>
          <router-view></router-view>//路由视图
        </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          myname: 'stronebridge'
        }
      }
    }
    </script>
    

7.3.3.示例分析

  1. 在components创建Navbar.vue组件

    <template>
          <div>
                navbar
                <button @click="handleClick">click</button>
                <slot></slot>
          </div>
    </template>
    <script>
          export default{
                methods:{
                      handleClick(){
                            this.$emit("myevent")
                      }
                }
          }
    </script>
    
  2. SideBar

    <template>
          <div>
                <ul>
                      <li>1111</li>
                      <li>2222</li>
                      <li>3333</li>
                </ul>
          </div>
    </template>
    
  3. App.vue

    <template>
        <div>
          <h1>状态更新</h1>
          {{myname}}
          <button @click="handleClick">click</button>
          <h1>TO</h1>
          <input type="text" v-model='mytext'>
          <button @click="handleClick1">click1</button>
          <ul>
            <li v-for="data in datalist">
              {{data}}
            </li>
          </ul>
          <h1>抽屉</h1>
          <navbar @myevent="handleEvent">
            <button @click='isShow=!isShow'>slot-button</button>
          </navbar>
          <sidebar v-show='isShow'></sidebar>
        </div>
    </template>
    
    <script>
    import navbar from './components/Navbar'
    import sidebar from './components/Sidebar'
    import Vue from 'vue'
    Vue.component('navbar',navbar)//注册全局组件
    
    export default {
      data () {
        return {
          myname: 'stronebridge',
          mytext: "",
          datalist:[],
          isShow:false
        }
      },
      components:{
        sidebar:sidebar//局部注册组件
      },
      methods:{
        handleClick(){
          this.myname="张三"
        },
        handleClick1 (){
          console.log(this.mytext)
          this.datalist.push(this.mytext)
          this.mytext=''
        },
        handleEvent(){
          this.isShow=!this.isShow;
        }
      }
    }
    </script>
    <style scoped> 
    /* scoped做局部样式 */
      li {
        background: red;
      }
    </style>
    
    1. 在App.vue中html代码写在<template>标签中,html只能有一个根节点。

    2. JavaScript写在<script></script>中,必须写在export default。根据ES6语法因为在vue初始化时要导出

    3. 在<style>标签写样式代码

    4. 无论是html还是js还是css都要书写规范,不然会因为样式启动时会报错,可以项目中创建vue.config.js中配置

      module.exports={
          lintOnSave: false
      }
      
    5. 引入组件是首先在<script>引入,组件可以可以相互传值

      import navbar from './components/Navbar'
      

      然后注册组件

      • 全局注册组件(要引入vue)

        import Vue from 'vue'
        Vue.component('navbar',navbar)//注册全局组件
        
      • 局部注册

        如果组件名和命名一致可简写

        export default {
          data () {
           
          },
          components:{
            sidebar:sidebar//局部注册组件
              //可简写
              //sidebar
          },
          methods:{
           
          }
        
    6. 在<style>写的样式会影响到引入组件的dom元素,只需要加scoped就可以只引用本页面,不影响组件节点

  4. 效果

8.vue-router

8.1.SPA概念

单页面应用(SinglePage Web Application,SPA)多页面应用(MultiPage Application,MPA)
组成—个外壳页面和多个页面片段组成多个完整页面构成
资源共用(css,js)共用,只需在外壳部分加载不共用,每个页面都需要加载
刷新方式页面局部刷新或更改整页刷新
url模式a.com/#/pageonea.com/pagetwo.html
用户体验页面片段间的切换快,用户体验良好页面切换加载缓慢,流畅度不够,用户体验比较差
转场动画容易实现无法实现
数据传递容易依赖url传参、或者cookie .localStorage等
搜索引擎优化(SEO)需要单独方案.实现较为困难、不利于SEO检索。可利用服务器端渲染(SSR)优化实现方法简易
适用范围高要求的体验度、追求界面流畅的应用适用于追求高度支持搜索引擎的应用
开发成本较高,常需借助专业的框架较低,但页面重复代码多
维护成本相对容易相对复杂

单文件组件:将组件放在一个单独的页面中(.vue文件)中,在利用vue路由在一个页面中放多个组件,每个组件都在一个vue文件。可以通过路由控制文件显示与不显示。

如果只是控制显示完全可以用状态数据结合v-show控制

<component :is="home"></component>
<home v-show="which==='home'">home</home>
<list v-show="which==='list'">list</list>
<shopcar v-show="which==='shopcar'">shopcar</shopcar>

如果不让路径和组件关联起来,如果复制分享链接给别人。如果能把链接上增加一个(例如.html?#home)信息。动态切换需要的组件的显示。如果在单页面中没有路由,分享的连接永远是首页。

解决办法就是采用路由

8.2.一级路由

8.2.1.vue项目结构

约定俗称的配置,没有强制要求

image-20210127235242228

8.2.2.创建页面组件

  1. Film.vue

    <template>
          <div>
                Film
          </div>
    </template>
    
  2. Cinema.vue

    <template>
          <div>
                Cinema
          </div>
    </template>
    
  3. Center.vue

    <template>
          <div>
                Center
          </div>
    </template>
    
  4. 定义一个导航栏的功能组件

    <router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。

    设置active-class=‘kerwinactive’,当点击某个标签是就会渲染成带active-class='kerwinactive’属性的li标签,就可以增加样式控制

    <template>
          <nav>
                <ul>  
                      <!-- <li><a href="#/film"></a></li> -->
                      <!-- to他跳转到哪里 tag是渲染成什么标签 active-class是切换的时候回给他添加一个属性 -->
                      <router-link to="/film" active-class='kerwinactive' tag='li'>电影</router-link>
                      <router-link to="/cinema" active-class='kerwinactive' tag='li'>影院</router-link>
                      <router-link to="/center" active-class='kerwinactive' tag='li'>我的</router-link>
                </ul>
          </nav>
    </template>
    <style lang='scss' scoped>
          ul{
                display: flex; 
                li{
                      flex: 1;
                }
          
          }
          .kerwinactive{
                color: red;
          }
    </style>
    
  5. 配置主页面App.vue

    <template>
        <div>
          //声明式导航
          <tabbar></tabbar>
          <!-- 路由容器 -->
          <router-view></router-view>
        </div>
    </template>
    <script>
    import tabbar from './components/Tabbar.vue'
    export default {
      data () {
        return {
        }
      },
      components:{
        tabbar: tabbar
      }
    }
    </script>
    

    在App.vue中通过<router-view></router-view>作为路由容器,当访问相关的路由就会在路由容器中显示对应的组件

8.2.3.配置路由

  1. 引入vue,VueRouter组件,以及自定义组件
  2. 注册路由模块,已经创建了全局的组件router-view组件,使用页面只需要使用router-view,访问相应路由对应内容就会显示在页面
  3. 配置路由映射(routes取名不限制)
    • path对应访问路径,component对应相关组件,与最上面的导入相对应任何匹配
    • 配置通配重定向到指定组件,可以是"/",或者"*"。通配符优先级很低只有其他任何相符的匹配后才会使用
  4. 实例化路由
  5. 导出路由实例
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Film from '../views/Film.vue'
import Center from '../views/Center.vue'
import Cinema from '../views/Cinema.vue'

//路由配置文件
Vue.use(VueRouter)//注册路由模块,已经创建了全局的组件router-view组件

//配置路由映射,routes1取名不限制
const routes1 = [
  { 
    path:"/film",//film路径会加载Film组件
    component:Film
  },
  {
    path:"/cinema",
    component:Cinema
  },
  {
    path:"/center",
    component:Center
  },
  //重定向/,*通配符优先级最低
  {
    // path:"/",
    path:"*",
    redirect:'/film'
  },
]
//实例化路由
const router = new VueRouter({
  routes: routes1
})
//导出路由实例
export default router

8.2.4.最终效果

8.3.嵌套路由(二级路由)

8.3.1.嵌套路由的应用场景

8.3.2.具体实现

8.3.2.1.目录
8.3.2.2.创建vue文件
  1. 首页主页面

    <template>
        <div>
          //声明式导航
          <tabbar></tabbar>
          <!-- 路由容器 -->
          <router-view></router-view>
        </div>
    </template>
    <script>
    import tabbar from './components/Tabbar.vue'
    export default {
      data () {
        return {
        }
      },
      components:{
        tabbar: tabbar
      }
    }
    </script>
    
  2. Films.vue

    <template>
          <div>
                <div style="height: 300px;background: yellow;">大轮播</div>
                <ul>
                      <li>正在热映</li>
                      <li>即将上映</li>
                </ul>
                <!-- /film/nowplaying Nowplaying -->
                <!-- /film/comingson  Comingsoon -->
                <router-view></router-view>
          </div>
    </template>
    

    在Films.vue配置路由容器<router-view></router-view>

  3. Comingsoon.vue(即将上映)

    <template>
          <div>
                Comingsoon
          </div>
    </template>
    
  4. Nowplaying.vue

    <template>
          <div>
                Nowplaying
                </ul>
          </div>
    </template>
    
8.3.2.3.配置嵌套路由路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Film from '../views/Film.vue'
import Nowplaying from '../views/films/Nowplaying.vue'
import Comingsoon from '../views/films/Comingsoon.vue'

//路由配置文件
Vue.use(VueRouter)//注册路由模块,已经创建了全局的组件router-view组件

//配置路由映射,routes1取名不显示
const routes1 = [
  { 
    path:"/film",//film路径会加载Film组件
    component:Film,
    //嵌套路由
    children:[
      {
        //路径简写
        path:"nowplaying",
        component:Nowplaying
      },
      {
        path:"/film/comingsonn",
        component:Comingsoon
      },
      {
        path:'',
        redirect:'/film/nowplaying'
      }
    ]
  },
]
//实例化路由
const router = new VueRouter({
  mode:'history',
  routes: routes1
})
//导出路由实例
export default router

nowplaying组件和comingsonn组件作为Films组件的子组件,故在配置路由是也必须配置的Films路由下作为子节点

8.4.编程式导航

8.4.1.应用场景

8.4.2.实现

8.4.2.1.请求发起页面
<template>
      <div>
            Nowplaying
            <ul>
                  <li v-for='data in datalist' @click="handleClick(data.id)" :key="data.id">{{data.title}}</li>
            </ul>
      </div>
</template>
<script>
export default {
      data(){
            return{
                  datalist:[{
                        id:111,
                        title:'夺冠'
                  },{
                        id:222,
                        title:'夺冠2'
                  },{
                        id:333,
                        title:'夺冠3'
                  }]
            }
      },
      methods:{
            handleClick(id){
                  //$router是项目index.js定义导出的路由对象,在main.js初始化的时候把$router挂到this中
                  //this.$router就可以拿到路由对象
                  //1.路由
                  // this.$router.push(`/detail/${id}`)
                  //2.路由名字
                  // this.$router.push({
                  //       name:"kerwinDetail",
                  //       params:{
                  //             myid:id
                  //       }
                  // })
                  //3.get请求query方式跳转,获取myid:this.$route.query.myid
                  this.$router.push(`/detail?myid=${id}`)
            }
      }
}
</script>

$router是项目index.js定义导出的路由对象(export default router),在main.js初始化的时候把$router挂到this中

this.$router就可以拿到路由对象

在接受参数的页面可以获取this.$route,这是当前匹配的路由对象

8.4.2.2.跳转方式说明
  1. 方式1

    • 路由配置

      此处因为传递的myid不确定,动态可变的,所以需要设置“:”设置动态路由

      {
          path:"/detail/:myid",//动态路由
          component:Detail,
      },
      
    • 携带参数发起请求

      this.$router.push(`/detail/${id}`)
      
    • 获取参数

      console.log("获取当前路由",this.$route)
      
  2. 方式2

    • 路由配置

      {
         path:"/detail/:myid",
         component:Detail,
         name:'kerwinDetail'
      },
      
    • 携带参数发起请求

      this.$router.push({
          name:"kerwinDetail",
          params:{
              myid:id
          }
      })
      
  3. get请求query方式跳转,获取myid:this.$route.query.myid

    使用push可以跳转到指定路由对应网页,

    • 该方式是使用get方式传值,不需要额外设置路由,只设置detail即可

    • 携带参数发起请求

    this. r o u t e r . p u s h ( ‘ / d e t a i l ? m y i d = router.push(`/detail?myid= router.push(/detail?myid={id}`)
    ```

    • 获取参数

      console.log("获取当前路由",this.$route)
      
8.4.2.3.详情页面接收请求,获取参数进行后续操作

this.$route当前匹配的路由对象信息

<template>
      <div>
            {{msg}}
      </div>
</template>
<script>
      export default{
            data(){
                  return{
                        msg:""
                  }
            },
            mounted(){
                  //this.$route当前匹配的路由对象信息
                  console.log("获取当前路由",this.$route)
                  // console.log("获取当前路由携带的参数myid:"+this.$route.params.myid)
                  // console.log("获取当前路由携带的参数myid:"+this.$route.query.myid)
                  this.msg="id为:"+this.$route.params.myid+"电影的详细信息"
            }
      }
</script>

8.5.vue支持两种模式

8.5.1.模式类型

  • hash #/home(默认模式)
  • history /home

8.5.2.设置路由模式

//实例化路由
const router = new VueRouter({
  mode:'history',
  routes: routes1
})

8.5.3.路由原理

  • hash路由 ==> location.hash 切换

    window.onhashchange 监听路径的切换

  • history路由==> history.pushState 切换

8.6.导航守卫(路由拦截)

8.6.1.应用场景

在未登陆的请求下,去查看个人信息,跳转被拦截跳转登陆页面

8.6.2.全局前置守卫

使用 router.beforeEach 注册一个全局前置守卫:

在路由注册处配置

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中

每个守卫方法接收三个参数:

  1. to: Route: 即将要进入的目标 路由对象
  2. from: Route: 当前导航正要离开的路由
  3. next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

示例:

router.beforeEach((to, from, next) => {
  // ...
  let auth = ['/center','/order','/detail/:myid','/card']
  if(auth.includes(to.fullPath)){
    if(!localStorage.getItem('token')){
        //跳转到登录页面
      next('/login')
    }
  }else{
     //放行
    next()
  }
})

8.6.3.组件内的守卫

在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

示例:

<script>
      export default{
            beforeRouteEnter(to,from,next){
                  if(!localStorage.getItem('token')){
                        next('/login')
                  }else{
                        next()
                  }
            }
      }
</script>

8.7.路由懒加载

8.7.1.应用场景

当项目开发完毕通过执行npm run build进行打包构建应用时,会生成一个dist文件夹,其中会生成两个js文件

JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

8.7.2.解决方案

js体积过大,当用户首次加载的时候导致加载时间过长,这叫做首屏加载过慢。故解决文件的方法就是路由懒加载

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

路由配置懒加载,即在当请求该路由的时候才会去加载组件数据

const routes1 = [
  { 
    path:"/film",//film路径会加载Film组件
    component:()=>import('../views/Film.vue'),
    //嵌套路由
    children:[
      {
        //路径简写
        path:"nowplaying",
        component:()=>import('../views/Film.vue')
      },
      {
        path:"/film/comingsonn",
        component:()=>('../views/films/Comingsoon.vue')
      },
    ]
  },
  {
    path:"/cinema",
    component:()=>import('../views/Cinema.vue')
  },
  {
    // path:"/",
    path:"*",
    redirect:'/film'
  },
]

此时通过npm run build则会在js中打包多个组件

8.7.3.把组件按组分块

有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。

{
    path:"/detail/:myid",
    component:()=>import(/* webpackChunkName: "group-foo" */ './Detail.vue'),
    name:'kerwinDetail'
},

8.8.配置反向代理

如果前端应用程序和后端API服务器未在同一主机上运行,则在开发过程中需要将API请求代理到API服务器。可以通过中的devServer.proxy选项进行配置vue.config.js

devServer.proxy 可以是指向开发API服务器的字符串:

module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}

这将告诉开发服务器将任何未知请求(与静态文件不匹配的请求)代理到http://localhost:4000

警告

devServer.proxy设置为字符串时,将仅代理XHR请求。如果要测试API URL,请不要在浏览器中打开它,而应使用Postman之类的API工具。

如果要对代理行为进行更多控制,则还可以使用path: options成对的对象。有关完整选项,请参阅http-proxy-middleware

module.exports = {
  devServer: {
    proxy: {
      '^/api': {
        target: '<url>',
        ws: true,
        changeOrigin: true
      },
      '^/foo': {
        target: '<other_url>'
      }
    }
  }
}

示例:

配置文件需要重启

module.exports = {
  // lintOnSave: false
  devServer: {
    proxy: {
      '/ajax': {
        target: 'https://m.maoyan.com',
        changeOrigin: true
      }
      // '/kerwin': {

      // }
    }
  }
}
module.exports = {   
devServer:{
    host: 'localhost',//target host
    port: 8080,
    //proxy:{'/api':{}},代理器中设置/api,项目中请求路径为/api的替换为target
    proxy:{
        '/api':{
            target: 'http://192.168.1.30:8085',//代理地址,这里设置的地址会代替axios中设置的baseURL
            changeOrigin: true,// 如果接口跨域,需要进行这个参数配置
            //ws: true, // proxy websockets
            //pathRewrite方法重写url
            pathRewrite: {
                '^/api': '/' 
                //pathRewrite: {'^/api': '/'} 重写之后url为 http://192.168.1.16:8085/xxxx
                //pathRewrite: {'^/api': '/api'} 重写之后url为 http://192.168.1.16:8085/api/xxxx
           }
    }}
},
//...
}

8.9.Chrome安装vue开发插件

image-20210127223032626

9.Element-UI

9.1.安装

推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。

npm i element-ui

9.2.查看项目配置文件中,是否有版本号

9.3.在main.js中引入element组件

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
    el: '#app',
    router,
    components: { App },
    template: '<App/>'
})

9.4.使用

<template>
      <div>
            <h1>你好,hello,{{name}}</h1>
            <el-radio v-model="radio" label="1">备选项</el-radio>
            <el-radio v-model="radio" label="2">备选项</el-radio>
      </div>
</template>
<script>
      export default{
            data(){
                  return {
                        name: '张三',
                        radio:'2'
                  }
            },
      }
</script>

9.5.补充

10.betterscroll

11.vuex

12.移动端事件

13.补充

13.1.this.$router.push和this.$router.replace

  • this.$router.push:跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面。

  • this.$router.replace:跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面。

    应用场景:当用户登录成功进入首页,此时无法后退到登录页面

  • this.$router.go(n):向前或者向后跳转n个页面,n可为正整数或负整数

13.2.自定义全局组件

13.2.1.自定义组件

<template>
    <div>
        <el-table :data="tableData.data" :stripe="tableData.props.stripe" style="width: 100%">
            <el-table-column v-for="item of tableData.head" :label="item.label" :align="item.align" :key="item.index">
                <el-table-column v-for="item1 of item.chirldren"
                                 :prop="item1.prop"
                                 :label="item1.label"
                                 :width="item1.width"
                                 :align="item1.align"
                                 :key="item1.index"
                >
                </el-table-column>
            </el-table-column>
        </el-table>
    </div>
</template>

<script>
export default {
    name: "DataTable",
    props: {
        tableData: {
            type: Object,
            default() {
                return {}
            }
        }
    }
}
</script>
<style scoped>
</style>
  1. props接受传递到组件的参数,然后再html页面中显示出来

  2. 当接收数据类型为对象类型

    props: {
            tableData: {
                type: Object,
                default() {
                    return {}
                }
            }
        }
    
  3. 当接收数据类型为数据类型

    props: {
            data: {
                type: Array,
                default() {
                    return []
                }
            }
        }
    

13.2.2.页面使用该组件

<template>
    <div>
		<!--3.使用DataTable组件,通过tableData(与props定义接收一致)给组件传值-->
        <DataTable :tableData="tableData"></DataTable>
    </div>
</template>

<script>
//1.导入组件
import DataTable from "../components/DataTable";

export default {
    components: {
        // 2.注册局部组件
        DataTable,
    },
    data() {
        return {
            tableData: {
                props: {stripe: true},
                head: [
                    {
                        index: 1, label: '', align: 'center', chirldren: [
                            {prop: 'index', label: '序号', width: '50', align: 'center'},
                        ]
                    },
                    {
                        index: 2, label: '案件信息', align: 'center', chirldren: [
                            {prop: "ah", label: "案号", width: "120", align: "center"},
                            {prop: "cbbm", label: "承办部门", width: "120", align: "center"},
                        ]
                    }
                ],
                data: [{
                    index: 1,
                    ah: "(2018)苏民2号",
                    cbbm: "执行局",
                }]
            },
        };
    },
};
</script>

13.2.2.最终效果

13.3.对象数据快速合并

13.4.Element UI实现分页

事件名称说明回调参数
size-changepageSize 改变时会触发每页条数
current-changecurrentPage 改变时会触发当前页
prev-click用户点击上一页按钮改变当前页后触发当前页
next-click用户点击下一页按钮改变当前页后触发当前页

说明:

  1. 通过@current-change绑定函数,当页码改变时会触发该函数
  2. 通过@size-change绑定函数,当用户选择每页显示的数据发生改变时触发该函数
  3. 通过:page-sizes设置每页显示条目个数,选择后data中的size发生改变,会触发@current-change绑定函数,实现查询。
  4. prev-click和next-click可以触发函数,如果不设置触发函数点击后修改currentPage后触发current-change对应的函数
  5. 通过设置layout设置组件布局,子组件名用逗号分隔
  6. 通过设置:total显示总条数
  7. 在data中要定义总条数total,当前页currentPage,每页显示条目个数size
  8. 向后台传递的分页数据为currentPage和size
<template>
    <div>
        <div style="display: flex;justify-content: flex-end">
            <el-pagination
                background
                @current-change="currentChange"
                @size-change="sizeChange"
                :page-sizes="[20, 30, 40]"
                layout="sizes,prev, pager, next, jumper, ->, total"
                :total="total">
            </el-pagination>
        </div>
    </div>
</template>
<script>
    export default {
        name: "EmpBasic",
        data() {
            return {
                total: 100,
                currentPage: 1,
                size: 10,
            }
        },
        mounted() {
            this.initEmps();
        },
        methods: {
            sizeChange(size) {
                this.size = size;
                this.initEmps();
            },
            currentChange(currentPage) {
                this.currentPage = currentPage;
                this.initEmps();
            },
            initEmps(type) {
                this.loading = true;
                let url = '/employee/basic/?currentPage=' + this.currentPage + '&size=' + this.size;
                this.getRequest(url).then(resp => {
                    this.loading = false;
                    if (resp) {
                        this.emps = resp.data;
                        this.total = resp.total
                    }
                })
            }
        }
    }
</script>

13.5.Element UI实现表格嵌套内容点击展开和折叠

13.5.1.效果

image-20210223160907349

13.5.2.在列表主题中的重点属性和方法

  1. 事件:select:当用户手动勾选数据行的 Checkbox 时触发的事件
  2. 事件:select-all 当用户手动勾选全选 Checkbox 时触发的事件
  3. 事件:row-click 当某一行被点击时会触发该事件
  4. 属性:row-key 行数据的 Key,用来优化 Table 的渲染;在使用 reserve-selection 功能与显示树形数据时,该属性是必填的。类型为 String 时,支持多层访问:user.info.id,但不支持 user.info[0].id,此种情况请使用 Function。
  5. 属性:expand-row-keys 可以通过该属性设置Table目前的展开行,需要设置row-key属性才能使用,该属性为展开行的keys数组。
  6. 属性:expand-change 当用户对某一行展开或者关闭的时候会触发该事件(展开行时,回调的第二个参数为 expandedRows;树形表格时第二参数为 expanded)
  7. 属性:type 对应列的类型。如果设置了selection则显示多选框;如果设置了index则显示该行的索引(从 1 开始计算);如果设置了expand则显示为一个可展开的按钮
  8. 核心思想是:保证点击某一行的时候,expand-row-keys中有且仅有该行的id,要收起该行时,清空expand-row-keys中的值。

13.5.3.实现

<template>
    <div>
        <!--        1.主体列表-->
        <el-table :data="tableData.data" style="width: 100%" border
                  @select="selectChange" 
                  @select-all="selectChange"
                  @row-click="expandChange"
                  @expand-change="expandChange"
                  :row-key="getRowkey"
                  :expand-row-keys="expands">
            <!--        2.展开或者隐藏行信息-->
            <el-table-column>
                <el-table-column type="expand">
                    <template slot-scope="props">
                        <!--      3.detailData.data展开或者隐藏行的数据-->
                        <el-table :data="detailData.data" border style="width: 100%">
                            <!--      4.detailData.head展开或者隐藏行的表头信息-->
                            <el-table-column v-for="item of detailData.head"
                                             :label="item.label"
                                             :align="item.align"
                                             :key="item.index">
                                <el-table-column v-for="item1 of item.chirldren"
                                                 :prop="item1.prop"
                                                 :label="item1.label"
                                                 :width="item1.width"
                                                 :align="item1.align"
                                                 :key="item1.index"
                                >
                                </el-table-column>
                            </el-table-column>
                        </el-table>
                    </template>
                </el-table-column>
            </el-table-column>
            <!--            5.其他非隐藏行的数据信息-->
            <el-table-column v-for="item of tableData.head" :label="item.label" :align="item.align" :key="item.index">
                <el-table-column v-for="item1 of item.chirldren"
                                 :prop="item1.prop"
                                 :label="item1.label"
                                 :width="item1.width"
                                 :align="item1.align"
                                 :key="item1.index"
                >
                </el-table-column>
            </el-table-column>
        </el-table>
    </div>
</template>

<script>
export default {
    name: "testForm3",
    data() {
        return {
            //列表主要数据
            mainData: [],
            //每条列表主要数据对应的到账明细数据
            detailData: {
                head: [{
                    index: 1, label: '案款明细', align: 'center', chirldren: [
                        {prop: "index", label: "序号", width: "60", align: "center"},
                        {prop: "jkr", label: "缴款人", width: "80", align: "center"},
                        {prop: "jksj", label: "缴款时间", width: "100", align: "center"},
                        {prop: "kpsj", label: "开票时间", width: "100", align: "center"},
                        {prop: "ffwcsj", label: "发放完成时间", width: "100", align: "center"},
                        {prop: "pjhm", label: "票据号码", width: "100", align: "center"},
                        {prop: "dzje", label: "到账金额", width: "80", align: "right"},
                        {prop: "zrje", label: "转入金额", width: "80", align: "right"},
                        {prop: "zcje", label: "转出金额", width: "80", align: "right"},
                        {prop: "ffje", label: "发放金额", width: "80", align: "right"},
                        {prop: "bbje", label: "本笔余额", width: "80", align: "right"},
                        {prop: "ffcs", label: "发放次数", width: "80", align: "center"},
                        {prop: "zrscDzsh", label: "自然时长(单位:天,以到账时间计算)", width: "120", align: "center"},
                        {prop: "zrscKpsh", label: "自然时长(单位:天,以开票时间计算)", width: "120", align: "center"},
                        {prop: "yqffts", label: "延期发放天数", width: "80", align: "center"},
                        {prop: "sjscDzsh", label: "实际时长(单位:天,已到账时间计算)", width: "120", align: "center"},
                        {prop: "sjscKpsh", label: "实际时长(单位:天,已开票时间计算)", width: "120", align: "center"},
                    ]
                }],
                data: [{
                    index: 1,
                    jkr: "张三",
                    jksj: "2021-12-01",
                    kpsj: "2021-12-01",
                    ffwcsj: "2021-12-01",
                    pjhm: "254188552",
                    dzje: "2254.251",
                    zrje: "2254.251",
                    zcje: "2254.251",
                    ffje: "2254.251",
                    bbje: "2254.251",
                    ffcs: "7",
                    zrscDzsh: "6",
                    zrscKpsh: "6",
                    yqffts: "6",
                    sjscDzsh: "6",
                 ]
            },

            getRowkey(row) {
                return row.id
            },
            expands: [],
            tableData: {
                props: {stripe: true},
                head: [
                    {
                        index: 1, label: '', align: 'center', chirldren: [
                            {prop: 'index', label: '序号', width: '50', align: 'center'},
                        ]
                    },
                    {
                        index: 2, label: '案件信息', align: 'center', chirldren: [
                            {prop: "ah", label: "案号", width: "120", align: "center"},
                            {prop: "cbbm", label: "承办部门", width: "90", align: "center"},
                            {prop: "larq", label: "立案日期", width: "90", align: "center"},
                            {prop: "cbr", label: "承办人", width: "90", align: "center"},
                            {prop: "bdje", label: "标的金额", width: "110", align: "center"},
                        ]
                    },
                    {
                        index: 3, label: '案款到账', align: 'center', chirldren: [
                            {prop: "dzbs", label: "到账笔数", width: "80", align: "center"},
                            {prop: "dazje", label: "到账总金额", width: "90", align: "center",},
                            {prop: "zcbaje", label: "转入本案笔数", width: "100", align: "center",},
                            {prop: "zrbaje", label: "转入本案金额", width: "100", align: "center",},
                        ]
                    },
                    {
                        index: 4, label: '案款支出', align: 'center', chirldren: [
                            {prop: "ffbs", label: "发放笔数", width: "120", align: "center"},
                            {prop: "ffzje", label: "发放总金额", width: "120", align: "center", formatKind: "Je"},
                            {prop: "zctaje", label: "转入他案笔数", width: "120", align: "center", formatKind: "Je"},
                            {prop: "zrtaje", label: "转入他案金额", width: "120", align: "center", formatKind: "Je"},
                        ]
                    },
                    {
                        index: 5, label: '', align: 'center', chirldren: [
                            {prop: "bakclje", label: "本案可处理金额", width: "120", align: "center", formatKind: "Je"},
                            {
                                prop: "basyffje",
                                label: "本案剩余未发放金额",
                                width: "*",
                                align: "center",
                                formatKind: "Je"
                            },
                        ]
                    }
                ],
                data: [{
                    id: "1",
                    index: 1,
                    ah: "(2018)苏民2号",
                    cbbm: "执行局",
                    larq: "20210129",
                    cbr: "张飒",
                    bdje: "2254.251",
                    dzbs: "145",
                    dazje: "2254.251",
                    zcbaje: "123456789",
                    zrbaje: "5254.36",
                    ffbs: '14',
                    ffzje: '25',
                    zctaje: '15552.21',
                    zrtaje: '254.251',
                    bakclje: "2254.251",
                    basyffje: "1254122.2",
                }]
            },

        }
    },
    methods: {
        expandChange(row) {
            let temp = this.expands;
            this.expands = [];
            this.expands.push(row.id);
            console.log("temp:" + temp);
            if (temp.length === 1 && temp[0] === row.id) {// 收起展开行
                this.expands = [];
                console.log("expands:" + this.expands);
            } else {
                // api.viewRequirementModuleOrg({requirementId: row.id})
                //     .then((res) => {
                //         if (res.ret > -1) {
                //             res.data.data.map(x => {
                //                 x.state = this.$t(x.state);
                //             });
                //             this.tableData = res.data.data;
                //         }
                //     })
                this.detailData.data = [];
            }
        }
    }
}
</script>
<style scoped>
</style>

13.6.Element UI表格左右滑动过程中始终显示某些列

只要是使用fixed,默认是靠左,可以设置靠右

<div>
    <el-table
        :data="emps"
        stripe
        border
        style="width: 100%">
        <el-table-column
            type="selection"
            width="55">
        </el-table-column>
        <el-table-column
            prop="name"
            label="姓名"
            fixed
            align="left"
            width="90">
        </el-table-column>
        <el-table-column
            label="合同期限"
            align="left"
            width="100">
            <template slot-scope="scope">
                <el-tag>{{ scope.row.contractTerm }}</el-tag>
                年
            </template>
        </el-table-column>
        <el-table-column
            label="操作"
            fixed="right"
            width="200">
            <template slot-scope="scope">
                <el-button style="padding: 3px" size="mini" type="primary">查看高级资料</el-button>
            </template>
        </el-table-column>
    </el-table>
</div>

13.7.Element UI选择日期范围

要求:选择时开始日期不能大于结束日期,结束日期不能小于开始日期

13.7.1.页面

<template>
	<div>
		<el-form name="searchForm" ref="searchForm" :model="form" size="mini">
			<el-row style="margin-top: 10px">
				退款日期:
				<el-date-picker
					v-model="form.tkrqStartDate"
					type="date"
					placeholder="开始日期"
					value-format="yyyy-MM-dd"
					:picker-options="pickerOptions0">
				</el-date-picker>
				<el-date-picker
					v-model="form.tkrqEndDate"
					type="date"
					placeholder="结束日期"
					value-format="yyyy-MM-dd"
					:picker-options="pickerOptions1">
				</el-date-picker>
			</el-row>
		</el-form>
	</div>
</template>

13.7.2.JS函数

注意需要安装moment

export default {
	name: "Ssfzhzcdzd",
	data() {
		return {
			form: {
				tkrqStartDate: '',
				tkrqEndDate: '',
			},
			pickerOptions0: {
				disabledDate: (time) => {
					if (this.form.tkrqEndDate !== "") {
						return time.getTime() > Date.parse(new Date(this.form.tkrqEndDate));
					}
				}
			},
			pickerOptions1: {
				disabledDate: (time) => {
					return time.getTime() < Date.parse(new Date(this.form.tkrqStartDate));
				}
			},
		}
	}
}

13.7.3.效果

image-20210224103719337

13.8.使用moment格式转换日期

13.8.1.安装moment

npm install moment

13.8.2.vue中引入并使用

<script>
import moment from "moment";

export default {
	name: "Akszqkhztjb",
	mounted() {
		this.date = moment(new Date()).format("YYYY-MM-DD");
	}
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值