vue入门

vue.js

一、Vue.js简介

1.vue.js是什么

  • 是一个构建用户界面的框架
  • 是一个轻量级的MVVM(Model-View-ViewModel)框架,angular、react也是MVVM框架其实就是所谓的数据双向绑定
  • 数据驱动+组件化的前端开发(核心思想)
  • 通过简单的API实现响应式的数据绑定组合的视图组件

参考:官网

2.vue的特点

  • 简单、易学、更轻量
  • 指令以v-xxx开头
  • 风格:HTML代码+JSON数据,再创建一个vue实例
  • 由个人维护:尤雨溪(华人)

二、起步

1.下载核心库vue.js

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

vue2.0与vue1.0,最大的变化就是引入了Viryual DOM,页面更新效率更高,速度更快

2.vue的实现

    js:
        new Vue({
            el:'#app01',
            data:{
                msg:'hello world',
                name:'Tom'
            }
        })
    html:
        <div id="app01">
            {{name}},{{msg}}
        </div>

3.配置vue-devtools

//配置是否允许vue-devtools检查代码,方便调试;开发环境设置为true,生产环境需设置为false
Vue.config.devtools = false;
//阻止vue启动时生成消息
Vue.config.productionTip = false;

三、常用指令

1.什么是指令

用来扩展html标签的功能

2.vue中常用的指令

  • v-model
    双向数据绑定,一般用于表单元素
  • v-for
    对数组或对象进行循环操作
    注:注意:key的使用,可以提供效率
  • v-on
    用来绑定事件,用法: v-on:事件=’函数’(比如v-on:click=’show’)
  • v-show/v-if
    用来显示或隐藏元素,v-show是通过display实现,v-if是每次删除后重新创建

四、事件

1.事件简写

v-on:click=”” 简写方式 @click=”“

2.事件对象$event

#原生js获取事件对象
<div id="app01">
    <button onclick="show(event)">click me</button>
</div>
<script type="text/javascript">
    function show(e){
        console.log(e.target.innerText)
    }
</script>
#vue.js获取事件对象
<div id="app01">
    <!-- <button v-on:click="show(event)">click me</button> -->
    <button v-on:click="show($event)">click me</button>
</div>
<script type="text/javascript">
    var app01 = new Vue({
        el:'#app01',
        methods:{
            show(e){
                console.log(e)
            }
        }
    });
</script>

3.事件冒泡

阻止事件冒泡:
3.1. 原生js方法,依赖事件对象

    <div onclick="func1(event)" style="height: 400px;width: 400px;background: blue">
        <div onclick="func2(event)" style="height: 200px;width: 200px;background: yellow">
            <button onclick="func3(event)">click</button>
        </div>
    </div>
    <script type="text/javascript">
        function func1(e){
            console.log(111);
        };
        function func2(e){
            console.log(222);      
        };
        function func3(e){
            console.log(333);
            e.stopPropagation();    //通过事件对象阻止事件传播
        }
    </script>
3.2. vue.js方法,更简洁,不依赖事件对象
    <div id="app01">
        <div @click="write">
            <p @click="read">
                <!-- <button v-on:click="show($event)">click me</button> -->
                <!-- 使用v-on:click.stop=""加后缀的方法-->
                <button v-on:click.stop="show($event)">click me</button>
            </p>
        </div>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            methods:{
                write(){
                    console.log(111);
                },
                read(){
                    console.log(222);
                },
                show(e){
                    console.log(333);
                }
            }
        });
    </script>

4.事件默认行为

阻止默认行为
4.1. 原生js方法

    <div>
        <a href="https://www.baidu.com" onclick="func4(event)">百度</a>
    </div>
    <script>
        function func4(e){
            console.log('func4');
            e.preventDefault();     //阻止默认事件发生
        }
    </script>

4.2. vue.js方法

    <div id="app01">
        <!--使用.prevent阻止默认事件发生-->
        <a href="https://www.baidu.com" @click.prevent="test">百度</a>
        <a href="https://www.baidu.com" @click="test">百度</a>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            methods:{
                test(){
                    console.log('prevent default')
                }
            }
        });
    </script>

5.键盘事件

回车:@keydown.13或者@keydown.enter
上:@keydown.38或者@keydown.up

    <div id="app01">
        <!-- 键盘事件:@keydown,@keypress,@keyup -->
        测试keycode:<input type="text" @keydown="show($event)"><br>
        按up触发事件,其余不触发<input type="text" @keydown.up="print"><br>
        按up触发事件,其余不触发<input type="text" @keydown.38="print"><br>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            methods:{
                show(e){
                    console.log(e.keyCode)
                },
                print(){
                    console.log('up')
                }
            }
        });
    </script>

自定义键盘事件(举例,json格式)
Vue.config.keyCodes={left:37,up:[37,38]}

6.事件修饰符

.stop
.prevent
.capture
.self
.once
    <!-- 举例.once -->
    <div id="app01">
        <button @click="print">xx</button>      <!-- 可以触发多次 -->
        <button @click.once="print">xx</button>     <!-- 只能触发一次 -->
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            methods:{
                print(){
                    console.log('1111')
                }
            }
        });
    </script>

五、属性

1.属性的绑定和简写

v-bind用于属性的绑定,语法->v-bind:属性=”“,简写方式->:属性=”“

2.class和style属性

  • class属性
    <style type="text/css">
        .aa{
            color: red;
            font-size: 20px;
        }
        .bb{
            background-color: blue;
        }
    </style>
    <div id="app01">
        <!-- 变量形式 -->
        <p :class="s1">hello,vue!</p>
        <!-- 数组形式 -->
        <p :class="[s1,s2]">hello,vue!</p>
        <!-- json形式 -->
        <p :class="{aa:true,bb:false}">hello,vue!</p>
        <p :class="{aa:num>5,bb:false}">hello,vue!</p>
        <p :class="s">hello,vue!</p>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                s1:'aa',
                s2:'bb',
                num:10,
                s:{aa:true,bb:false}
            }
        });
    </script>
  • style属性
    <div id="app01">
        <!-- 变量形式 -->
        <p :style="s1">hello,vue!</p>
        <!-- 数组形式 -->
        <p :style="[s1,s2]">hello,vue!</p>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                s1:{color:'red',fontSize:'20px'},
                s2:{backgroundColor:'blue'}
            }
        });
    </script>

六、模板

### 1.通过{{}}进行数据绑定
### 2.数据绑定方式
+ 双向绑定,使用v-mode

    <div id="app01">
        <input type="text" v-model="msg">
        <p>{{msg}}</p>
        <!-- v-once表示数据只绑定一次不会被修改 -->
        <p v-once>{{msg}}</p>
        <!-- v-pre表示不编译,直接显示结果还是{{msg}} -->
        <p v-pre>{{msg}}</p>    
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                msg:'hello,vue',
            }
        });
    </script>
  • 单向绑定
    • {{}},v-text,v-html;v-text与v-html区别在于v-text用于文本,v-html用于html标签
    <div id="app01">
        <p>{{msg}}</p>
        <p v-text="msg"></p>
        <p v-html="msg"></p>
        <p v-text="message"></p>
        <p v-html="message"></p>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                msg:'hello,vue',
                message:'<span style="color:red;">hello,vue<span>'
            }
        });
    </script>

七、过滤器

1.简介

用来过滤模型数据,在显示之前进行数据处理和筛选;
语法:{{ data | filter1(参数) | filter2(参数)}}

2.关于内置过滤器

  • vue 1.0 中内置了很多过滤器
  • vue 2.0 中已删除所有内置过滤器
    解决办法:
    a.使用第三方库,如loadash、date-fns日期格式化,accounting.js货币格式化等(引入相关的js)
    b.使用自定义过滤器

3.自定义过滤器

分类:全局过滤器、局部过滤器

  • 自定义全局过滤器
    <div id="app01">
        <!-- 定义全局过滤器 -->
        <h3>{{month|addZero}}</h3>  <!-- 不带参数过滤器 -->
        <h3>{{pi|leftTwo(2)}}</h3>  <!-- 带参数过滤器 -->
    </div>
    <script type="text/javascript">
        Vue.filter('addZero',function(data){
            return data<10?'0'+data:data
        });
        Vue.filter('leftTwo',function(data,n){
            return data.toFixed(n);
        });
        var app01 = new Vue({
            el:'#app01',
            data:{
                month:8,
                pi:3.1415
            }
        });
    </script>
  • 局部过滤器
    <div id="app01">
        <h3>{{currenttime|date}}</h3>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                currenttime:Date.now()
            },
            //局部过滤器
            filters:{
                date:function(data){
                    var d = new Date(data);
                    return d.getFullYear()+'-'+d.getMonth()+'-'+d.getDate()+' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()
                }
            }
        });
    </script>

八、发送AJAX请求

1. 简介

vue本身不支持发送ajax请求,需要使用vue-resource(vue1.0)、axios(vue2.0官方推荐)等插件实现

2.使用axios发送AJAX请求

  • 安装并引入axios
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  • 基本用法(github搜索axios查看api)
    a. axios.get().then().catch()
     // url自带参数
    axios.get('/user?ID=12345')
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });
    // url和参数分开
    axios.get('/user', {
        params: {
          ID: 12345
        }
      })
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });
b. axios.post().then().catch()
    axios.post('/user', {
        firstName: 'Fred',
        lastName: 'Flintstone'
      })
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });
 c. axios().then().catch()
    //post
    axios({
      method: 'post',
      url: '/user/12345',
      data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
      }
    });
    //get
    axios('/user/12345');

九、Vue生命周期

vue从创建到销毁的过程,称为生命周期,公有8个阶段

    <div id="app01">
        <p>{{msg}}</p>
        <button @click="update">更新组件</button>
        <button @click="destroy">销毁组件</button>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                msg:'welcome to vue'
            },
            methods:{
                update(){
                    this.msg="hello vue"
                },
                destroy(){
                    // this.$destroy();
                    app01.$destroy();
                }
            },
            beforeCreate(){
                alert('组件实例刚刚创建,还未进行数据观测和事件配置');
            },
            created(){
                alert('实例已创建完成,并且已进行数据观测和事件配置');
            },
            beforeMount(){
                alert('模板编译之前,还未挂载');
            },
            mounted(){
                alert('模板编译之后,已挂载,此时才会渲染页面,才能看到页面上数据的展示');
            },
            beforeUpdate(){
                alert('组件更新之前');
            },
            updated(){
                alert('组件更新之后');
            },
            beforeDestroy(){
                alert('组件销毁之前');
            },
            destroyed(){
                alert('组件销毁之后');
            }
        });
    </script>

vue生命周期

十、计算属性(computed)

1. 基本用法

计算属性也是用来存储数据,但具有以下几个特点:
+ 数据可以进行逻辑梳理操作,对计算属性中的数据进行监视

    <div id="app01">
        <!-- 1.基本用法 -->
        <p>{{msg}}</p>
        <p>{{msg2}}</p>
        <!-- 2.对数据处理再显示 -->
        <p>{{msg.split('').reverse().join('')}}</p> <!-- 这样写也可以但是逻辑处理和页面展示混在一起不怎么好 -->
        <p>{{reverseMsg}}</p>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{  //普通属性
                msg:'welcome to vue!'
            },
            computed:{  //计算属性
                msg2:function(){    //该函数必须有返回值,用来获取属性,称为get函数
                    return 'hell vue!'
                },
                reverseMsg(){
                    //可以包含逻辑处理,同时依赖msg
                    return this.msg.split('').reverse().join('');
                }
            }
        });
    </script>

2. computed和methods的区别

将计算属性的get函数定义成一个方法也能实现类似的功能
区别:
计算属性是基于它的相关依赖更新的,没有相关依赖也行,但是那样就没啥意义,没必要使用计算属性;计算属性是缓存的,只要相关依赖没有改变,多次访问计算属性得到的值时之前缓存的结果,不会多次执行

    <div id="app01">
        <p>comput:{{comput}}</p>
        <p>method:{{method()}}</p>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{  //普通属性
                msg:'welcome to vue!'
            },
            computed:{  //计算属性
                comput(){
                    this.msg.split();
                    return new Date();
                }
            },
            methods:{
                method(){
                    this.msg.split();
                    return new Date();
                }
            }
        });
    </script>
    //打开浏览器调试窗口,调用app01.comput日期值不改变,改变app01.msg值,再调用app01.comput发现日期改变;无论是否改变app01.msg值,调用app01.method(),日期值都改变

3. get和set

计算属性由get和set俩部分组成,分别用来获取和设置计算属性,默认只有get,如果需要set,要自己添加

    <div id="app01">
        <p>fullname:{{fullName}}</p>
        <button @click="change">改名</button>
    </div>
    <script type="text/javascript">
        var app01 = new Vue({
            el:'#app01',
            data:{
                firstName:'xu',
                lastName:'haixiang'
            },
            /*
            computed:{  //默认只有set方法
                fullName:function(){
                    return this.firstName+this.lastName
                }
            },
            */
            computed:{
                fullName:{
                    get:function(){
                        return this.firstName+this.lastName
                    },
                    set:function(newValue){
                        var names = newValue.split(' ');
                        this.firstName = names[0];
                        this.lastName = names[1];
                    }
                }
            },
            methods:{
                change:function(){
                    this.fullName="zhang yanyan";
                }
            }
        });
    </script>

十一、vue实例的属性和方法

1.常用属性

vm.$el
vm.$data
vm.$options
vm.$refs
<div id="app01">
        <p>{{msg}}</p>
        <h3 ref="hello">hello</h3>  <!-- 定义ref属性元素 -->
        <h3 ref="world">vue</h3>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app01',
            data:{
                msg:'hello vue!'
            },
            name:'vue',     //自定义属性
            show:function(){        //自定义属性
                console.log('show');
            }
        });
        /*
        属性
        */

        //vm.属性名(vm.msg)获取data中的属性

        //vm.$el获取vue实例关联的元素
        //console.log(vm.$el);//DOM对象
        //vm.$el.style.color='red';

        //vm.$data     //获取数据对象
        //console.log(vm.$data);
        //console.log(vm.$data.msg);

        //vm.$options  //获取自定义属性
        // console.log(vm.$options)
        // console.log(vm.$options.name)
        // console.log(vm.$options.show())

        //vm.$refs //获取所有添加ref属性的元素
        // console.log(vm.$refs);  //DOM对象
        // console.log(vm.$refs.hello);
    </script>

2. 方法

vm.$mount()
vm.$destroy()
vm.$nextTick()
    <div id="app01">
        <p ref="title">hello:{{name}}</p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app01',
            data:{
                name:'Tom'
            },
        });

        /*
        方法
        */

        //vm.$mount()  //手动挂载vue实例
        /*
        vm.$mount('#app01');
        new Vue({   //这样写也行
            data:{
                msg:'hello vue'
            }
        }).$mount('#app01');
        */

        //vm.$destroy();       //销毁实例

        //vm.$nextTick(callback)   //传入回调函数,在DOM更新完成后再执行回调函数;一般在修改数据之后使用该方法,以便获取更新后的DOM
        /*
        vm.name='汤姆';       //修改数据
        //DOM还没更新完,vue实现响应式并不是数据发生改变之后DOM立即变化,需要一定的策略进行DOM更新,需要时间
        console.log(vm.$refs.title.textContent);   //打印hello:Tom,原因代码顺序执行太快,DOM还没更新完
        vm.$nextTick(function(){
            //回调函数里,DOM更新完成,再执行此代码
            console.log(vm.$refs.title.textContent);
        });
        */
    </script>
vm.$set()
vm.$delete()
    <div id="app01">
        <button @click="doAdd()">添加属性</button>
        <button @click="doDelete()">删除属性</button>
        <hr>
        <p>{{user.name}}</p>
        <p>{{user.age}}</p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app01',
            data:{
                user:{
                    name:'Tom',
                }
            },
            methods:{
                doAdd(){
                    /*
                    this.user.age=10;   //通过普通方式为对象添加属性时vue无法实时监测到
                    console.log(this.user); //虽然vue无法检测到,但是查看data中的user已经设置了age
                    */
                    vm.$set(this.user,'age',18);   //实例方法
                    // Vue.set(this.user,'age',18); //全局方法
                    console.log(this.user);
                },
                doDelete(){
                    vm.$delete(this.user,'age');       //实例方法
                    // Vue.delete(this.user,'age');     //全局方法
                    console.log(this.user);
                }
            }
        });
    </script>
vm.$watch()
    <div id="app01">
        <input type="text" v-model="name">
        <p>{{name}}</p>
        <hr>        
        <input type="text" v-model="age">
        <p>{{age}}</p>
        <hr>
        <input type="text" v-model="user.height">
        <p>{{user.height}}</p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el:'#app01',
            data:{
                name:'Tom',
                age:20,
                user:{
                    sex:'male',
                    height:180
                }
            },
            watch:{ //方式一:使用vue实例提供的watch选项(更常用)
                age:function(newValue,oldValue){    //针对字符串
                    console.log('age被修改了,原值:'+oldValue+',新值:'+newValue);
                },
                user:{      //针对对象,需要配置深度修改,该情况下newValue、oldValue值一直,因为引用没变
                    handler:function(newValue,oldValue){
                        console.log('user被修改了,原值:'+oldValue+',新值:'+newValue);
                    },
                    deep:true
                }
            }
        });
        /*
        //方式二:使用vue实例提供的$watch()方法
        vm.$watch('name',function(newValue,oldValue){  //针对字符串
            console.log('name被修改了,原值:'+oldValue+',新值:'+newValue);
        });
        vm.$watch('user',function(newValue,oldValue){  //针对对象,需要配置深度修改,该情况下newValue、oldValue值一直,因为引用没变
            console.log('user.name被修改了,原值:'+oldValue.height+',新值:'+newValue.height);    
        },{deep:true});
        */
    </script>

十二、自定义指令

分类:全局指令,局部指令

1. 自定义全局指令

  • 语法:Vue.directive(指令id,指令的定义对象);对象中的钩子函数bind、inserted、update、componentUpdated、unbind
    <div id="app01">
        <h3 v-hello>{{msg}}</h3>
        <button @click="change">更新数据</button>
    </div>
    <script type="text/javascript">
        /*
        自定义全局指令
        注:使用指令时,必须在指令前加v-,比如v-hello
        */
        Vue.directive('hello',{
            bind(){     //常用
                alert('指令第一次绑定到元素上调用,只调用一次,课执行初始化操作');
            },
            inserted(){
                alert('被绑定元素插入到DOM时调用');
            },
            update(){   //常用
                alert('被绑定元素所在模板更新时调用');
            },
            componentUpdated(){
                alert('被绑定元素所在模板完成一次更新周期时调用');
            },
            unbind(){alert('指令与模板解绑时调用')}
        });
        var vm = new Vue({
            el:'#app01',
            data:{
                msg:'hello vue',
            },
            methods:{
                change(){
                    this.msg="welcome to vue";
                }
            }
        });
    </script>
  • 钩子函数的参数
    <div id="app01">
        <h3 v-hello>{{msg}}</h3>
        <!-- 自定义指令可以指定字符串形式的指令表达式、值、参数、修饰符,如下分别对应username、xhx、confirm、(start,end) -->
        <h4 v-hello:confirm.start.end="username">{{msg}}</h4>
    </div>
    <script type="text/javascript">
        /*
        钩子函数的参数(所有钩子函数一样)
        */
        Vue.directive('hello',{
            bind(el,binding){       
                // console.log(el); //指令所绑定的元素的DOM对象
                // el.style.color='red';

                console.log(binding);   //指令对象
                console.log(binding.name);  //指令名
                console.log(binding.expression);    //指令表达式
                console.log(binding.value);     //值
                console.log(binding.arg);   //参数
                console.log(binding.modifiers);     //修饰符   对象
            }
        });
        var vm = new Vue({
            el:'#app01',
            data:{
                msg:'hello vue',
                username:'xhx'
            },
            methods:{
                change(){
                    this.msg="welcome to vue";
                }
            }
        });
    </script>
  • 指令定义简单写法
//传入一个简单函数,在bind以及update钩子函数中都调用
Vue.directive('hello',function(){})

2. 定义局部指令

    new Vue({
        el:'#app01',
        directives:{
            focus1:function(el){el.focus()},    //简单写法
            focus2:{    //复杂写法
                bind(){},
                inserted(el){el.focus()},
                update(){}
            }
        }
    });

3.自定义指令练习

移动页面中的元素(onmousedown,onmousemove,onmouseup)

    <style>
        #app01 div{
            height: 100px;
            width:100px;
            position:absolute;
        }
        #app01 .hello{
            background-color: blue;
            left: 0px;
            top: 0px;
        }
        #app01 .vue{
            background-color: red;
            top: 0px;
            right: 0px;
        }
    </style>
    <div id="app01">
        <div class="hello" v-drag>hello</div>
        <div class="vue" v-drag>vue</div>
    </div>
    <script type="text/javascript">
        Vue.directive('drag',function(el){
            el.onmousedown=function(e){
                //获取鼠标点击处分别于div左边和上边的距离:鼠标位置-div位置
                var disX=e.clientX-el.offsetLeft;
                var disY=e.clientY-el.offsetTop;
                // console.log(disX,disY);

                //包含在onmousedown里面,表示点击后才移动
                /*
                el.onmousemove=function(e){
                    //获取鼠标移动后div的位置(div左上角位置)
                    var l=e.clientX-disX;
                    var t=e.clientY-disY;

                    el.style.left=l+'px';
                    el.style.top=t+'px';
                };
                //停止移动
                el.onmouseup=function(e){
                    el.onmousemove=null;
                    el.onmouseup=null;
                }
                */
                //未防止鼠标移出div,使用document.onmousemove
                document.onmousemove=function(e){
                    //获取鼠标移动后div的位置(div左上角位置)
                    var l=e.clientX-disX;
                    var t=e.clientY-disY;

                    el.style.left=l+'px';
                    el.style.top=t+'px';
                };
                //停止移动
                document.onmouseup=function(e){
                    document.onmousemove=null;
                    document.onmouseup=null;
                }               
            }
        });     
        var vm = new Vue({
            el:'#app01',
        });
    </script>

十三、过渡(动画)

1. 简介

vue在插入、更新或者移除dom元素时,提供多种不同方式的应用过渡效果
本质上还是使用css3动画:transition,animation

2. 基本用法

使用transition组件,将要执行动画的元素包含在该组件内
<transition>运动的元素</transition>
过渡的css类名6个:
v-enter //进入的初始状态
v-enter-active //过渡的过程当中
v-enter-to //进入最终需要变成的状态
v-leavte //离开的初始状态
v-leave-active //过渡的过程当中
v-leave-to //离开最终需要变成的状态

    <style>
        p{
            height: 200px;
            width: 200px;
            background-color: red;
            line-height: 200px;
            text-align: center;
        }
        .v-enter-active,.v-leave-active{
            transition:all 2s ease;
        }
        .v-enter-active{
            opacity: 1;
            width: 200px;
            height: 200px;
        }
        .v-leave-active{
            opacity: 0;
            width: 50px;
            height: 50px;
        }
        .v-enter{   /*需要放在最下面*/
            opacity: 0;
            width: 0px;
            height: 0px;
        }
    </style>
    <div id="app01">
        <button @click="flag=!flag">click</button>
        <!-- 切换没有动画效果 -->
        <!-- <p v-show="flag">hell vue</p>   -->

        <transition>
        <!-- 当给transiton设置name属性时,上面的v-enter需要写成fade-enter、v-enter-active写成fade-enter-active等 -->
        <!-- <transition name="fade"> -->
            <p v-show="flag">hell vue</p>
        </transition>
    </div>
    <script type="text/javascript"> 
        var vm = new Vue({
            el:'#app01',
            data:{
                flag:false,
            }
        });
    </script>

3. 钩子函数

钩子函数都可以传入参数el(元素对象)
beforeEnter
enter
afterEnter
enterCancelled
beforeLeave
leave
afterLeave
leaveCancelled

    <style>
        p{
            height: 200px;
            width: 200px;
            background-color: red;
            line-height: 200px;
            text-align: center;
        }
        .fade-enter-active,.fade-leave-active{
            transition:all 2s ease;
        }
        .fade-enter-active{
            opacity: 1;
            width: 200px;
            height: 200px;
        }
        .fade-leave-active{
            opacity: 0;
            width: 50px;
            height: 50px;
        }
        .fade-enter{    /*需要放在最下面*/
            opacity: 0;
            width: 0px;
            height: 0px;
        }
    </style>
    <div id="app01">
        <button @click="flag=!flag">click</button>

        <transition name="fade"
            @before-enter="beforeEnter"
            @enter="enter"
            @after-enter="afterEnter"
            @enter-cancelled="enterCancelled"

            @before-leave="beforeLeave"
            @leave="leave"
            @after-leave="afterLeave"
            @leave-cancelled="leaveCancelled"
        >
            <p v-show="flag">hell vue</p>
        </transition>
    </div>
    <script type="text/javascript"> 
        var vm = new Vue({
            el:'#app01',
            data:{
                flag:false,
            },
            methods:{
                beforeEnter(el){
                    // alert('动画进入之前');
                },
                enter(el){
                    // alert('动画进入');
                },
                afterEnter(el){
                    // alert('动画进入之后');
                    el.style.background='blue';
                },
                enterCancelled(el){
                    // alert('动画进入之后取消');
                },
                beforeLeave(el){
                    // alert('动画离开之前');
                },
                leave(el){
                    // alert('动画离开');
                },
                afterLeave(el){
                    // alert('动画离开之后');
                    el.style.background='red';
                },
                leaveCancelled(el){
                    // alert('动画离开之后取消');
                }
            }
        });
    </script>   

4. 自定义过渡类名

enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class

它们优先级高于普通的类名,结合第三方css动画库,如Animated.css
Animated.css下载地址

    <link rel="stylesheet" type="text/css" href="css/animate.css">
    <style>
        p{
            height: 200px;
            width: 200px;
            background-color: red;
            line-height: 200px;
            text-align: center;
            margin: 0 auto;
        }
    </style>
    <div id="app01">
        <button @click="flag=!flag">click</button>

        <transition enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
            <p v-show="flag">hell vue</p>
        </transition>
    </div>
    <script type="text/javascript"> 
        var vm = new Vue({
            el:'#app01',
            data:{
                flag:false,
            },
        });
    </script>   

5. 多元素动画

transition-group子元素必须绑定key
<transition-group></transition-group>

    <link rel="stylesheet" type="text/css" href="css/animate.css">
    <style>
        p{
            height: 200px;
            width: 200px;
            background-color: red;
            line-height: 200px;
            text-align: center;
            margin: 50px auto;
        }
    </style>
    <div id="app01">
        <button @click="flag=!flag">click</button>

        <transition-group enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
            <p v-show="flag" :key='1'>hello vue</p>     //必须绑定key
            <p v-show="flag" :key='2'>welcome to vue</p>    //必须绑定key
        </transition-group>
    </div>
    <script type="text/javascript"> 
        var vm = new Vue({
            el:'#app01',
            data:{
                flag:false,
            },
        });
    </script>

示例

    <link rel="stylesheet" type="text/css" href="css/animate.css">
    <style>
        p{
            height: 100px;
            width: 100px;
            background-color: red;
            line-height: 100px;
            text-align: center;
            margin: 10px auto;
        }
    </style>
    <div id="app01">
        <input type="text" v-model='name'>
        <transition-group enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
            <p v-for="(v,k) in arr2" :key="k">{{v}}</p>
        </transition-group>
    </div>
    <script type="text/javascript"> 
        var vm = new Vue({
            el:'#app01',
            data:{
                flag:false,
                arr1:['xhx','zyy','xzt','xyh','zaz'],
                name:''
            },
            computed:{
                arr2:function(){
                    var arr=[];

                    this.arr1.forEach(val => {  //箭头函数不会改变this
                        if(val.includes(this.name)){
                            arr.push(val);
                        }
                    });
                    /*
                    this.arr1.forEach(function(val){
                        if(val.includes(this.name)){
                            arr.push(val);
                        }
                    });
                    */
                    return arr;     
                }
            }
        });
    </script>   

十四、组件

1. 什么是组件

组件(component)是vue.js最强大的功能之一,组件可以扩展html元素,封装可重用的代码,组件是自定义元素(对象)

2. 组件的定义方式

  • 方式一:先创建组件构造器,然后由组件构造器创建组件
    <!-- 1.创建构造器(Vue.extend) 2.注册组件 3.vue实例 -->
    <div id="app01">
        <my-hello></my-hello>
    </div>
    <script type="text/javascript">
        var MyHello = Vue.extend({
            template:'<h3>hello</h3>'
        });
        Vue.component('my-hello',MyHello)
        new Vue({
            el:'#app01',
        })
    </script>
  • 方式二:直接创建组件
    <div id="app02">
        <my-world></my-world>
    </div>      
    <script type="text/javascript">
        Vue.component('my-world',{
            template:'<h4>world</h4>'
        });
        new Vue({
            el:'#app02',
        });
    </script>

3. 组件的类型

  • 全局组件
    <div id="app03">
        <my-component></my-component>
    </div>
    <script type="text/javascript">
        Vue.component('my-component',{
            template:'<h3>全局组件</h3>'
        });
        var app03 = new Vue({
            el:'#app03'
        });
    </script>
  • 局部组件
    <div id="app04">
        <my-component></my-component>
    </div>
    <script type="text/javascript">
        var child = {template:'<h4>局部组件</h4>'};
        var app02 = new Vue({
            el:'#app04',
            components:{
                'my-component':child
            }
        });
    </script>

4. 引用模板

将组件的内容放到模板中并引用,通常组件比较复杂时可以利用template标签;注意事项:template中有且只能有一个根元素

    <div id="app05">
        <my-test></my-test> 
    </div>
    <template id="wbs">
        <!--这样写不行,组件的模板必须只能包含一个根元素
        <h3>{{msg}}</h3>
        <ul>
            <li v-for="val in arr">{{val}}</li>
        </ul>
        -->
        <div>
            <h3>{{msg}}</h3>
            <ul>
                <li v-for="val in arr">{{val}}</li>
            </ul>
        </div>
    </template>
    <script type="text/javascript">
        var app05 = new Vue({
            el:'#app05',
            components:{
                'my-test':{
                    // template:'<h3>{{msg}}</h3>',
                    template:'#wbs',
                    // data:{msg:'welcome to test!'}    组件中的data必须为function
                    data:function(){
                        return {
                            msg:'welcome to test!',
                            arr:['Tom','Jack','David']
                        }
                    }
                    // data(){
                    //  return {msg:'welcome to test!'}
                    // }    //这种写法也可以
                }
            }
        })
    </script>

5.动态组件

<component v-bind:is="">组件,多个组件使用同一个挂载点,然后动态的在他们之间切换

    <div id="app06">
        <button v-on:click="flag='my-hello'">显示hello组件</button>
        <button @click="flag='my-world'">显示world组件</button>
        <div>
            <!-- <component v-bind:is="flag"></component> -->
            <component :is="flag"></component>
            <!-- 使用<keep-alive>组件缓存非活动组件,可以保留状态避免重新渲染,默认每次切换都会销毁非活动组件并重新创建-->
            <keep-alive>    //
                <component :is="flag"></component>
            </keep-alive>
        </div>
    </div>
    <script type="text/javascript">
        var app06 = new Vue({
            el:'#app06',
            data:{
                flag:'my-hello'
            },
            components:{
                'my-hello':{
                    template:'<h3>show hello:{{x}}</h3>',
                    data(){
                        return {
                            x:Math.random()
                        }
                    }
                },
                'my-world':{
                    template:'<h3>show world:{{y}}</h3>',
                    data:function(){
                        return {y:Math.random()}
                    }
                }
            }
        })
    </script>

6. 组件间数据传递

6.1父子组件

一个组件内部定义另一个组件;子组件只能在父组件里使用;默认情况下子组件无法访问父组件中的东西

    <div id="app07">
        <h3>app07</h3>
        <my-hello></my-hello>
        <!-- //子组件(局部组件)不能定义在根组件中 -->
        <!-- <my-world></my-world>   -->
    </div>
    <template id='hello-app07'>
        <div>
            <h3>我是父组件hello</h3>
            <h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
            <hr>
            <!-- 子组件只能定义在父组件中 -->
            <my-world></my-world>   
        </div>
    </template>
    <template id="world-app07">
        <div>
            <h5>我是子组件world</h5>
            <hr>
            <hr>
            <!-- 报错,访问不了 -->
            <!-- <h5>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.id}}</h5>  -->
        </div>
    </template>
    <script type="text/javascript">
        var app07 = new Vue({   //根组件
            el:'#app07',
            components:{
                'my-hello':{    //父组件
                    template:'#hello-app07',
                    data(){
                        return {
                            msg:'hi,Tom',
                            name:'Tom',
                            age:30,
                            user:{id:101,username:'test'}
                        }
                    },
                    components:{
                        'my-world':{    //子组件
                            template:'#world-app07',
                        }
                    }
                }
            }
        })
    </script>
6.2 子组件访问父组件的数据

a.在调用子组件时,绑定想要获取的父组件的数据
b.在子组件内部,使用props选项获取数据,即接受来自父组件的数据
总结:父组件通过props向下传递数据给子组件
注:组件中的数据共有三种方式:data,props,computed

    <div id="app08">
        <h3>app08</h3>
        <my-hello></my-hello>
    </div>
    <template id='hello-app08'>
        <div>
            <h3>我是父组件hello</h3>
            <h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
            <hr>
            <!-- 子组件只能定义在父组件中 -->
            <my-world :message="msg" :name="name" :age="age" :user="user"></my-world>               
        </div>
    </template>
    <template id="world-app08">
        <div>
            <h5>我是子组件world</h5>
            <h5>访问父组件的数据:{{message}},{{name}},{{age}},{{user.id}}</h5>
            <hr>    
            <hr>    
        </div>
    </template>
    <script type="text/javascript">
        var app08 = new Vue({   //根组件
            el:'#app08',
            components:{
                'my-hello':{    //父组件
                    template:'#hello-app08',
                    data(){
                        return {
                            msg:'hi,Tom',
                            name:'Tom',
                            age:30,
                            user:{id:101,username:'test'}
                        }
                    },
                    components:{
                        'my-world':{    //子组件
                            template:'#world-app08',
                            // props:['message','name','age','user']    //简单的字符串数组
                            props:{     //也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值
                                message:String,
                                name:{
                                    type:String,
                                    required:true
                                },
                                age:{
                                    type:Number,
                                    default:18,
                                    validator:function(value){
                                        return value>=0;
                                    }
                                },
                                // user:Object
                                user:{
                                    type:Object,
                                    // default:{id:102,username:'lalala'}   //报错,对象这么写有问题,对象或数组的默认值必须由函数返回
                                    default:function(){
                                        return {id:102,username:'lalala'};
                                    };
                                }
                            }
                        }
                    }
                }
            }
        })
    </script>
6.3父组件访问子组件中的数据

a.在子组件中使用vm.$emit(事件名,数据)事件名自定义
b.父组件在使用子组件的地方监听子组件触发的事件,并在父组件中定义方法,用来获取数据
总结:子组件通过event给父组件发消息,实际上就是子组件吧自己的数据发送到父组件

    <div id="app09">
        <h3>app09</h3>
        <my-hello></my-hello>
    </div>
    <template id='hello-app09'>
        <div>
            <h3>我是父组件hello</h3>
            <h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
            <h4>访问子组件中的数据:{{sex}},{{height}}</h4>
            <hr>
            <!-- 子组件只能定义在父组件中 -->
            <my-world :message="msg" :name="name" :age="age" :user="user" @event="getData"></my-world>              
        </div>
    </template>
    <template id="world-app09">
        <div>
            <h5>我是子组件world</h5>
            <h5>访问父组件的数据:{{message}},{{name}},{{age}},{{user.id}}</h5>
            <h5>访问自己的数据:{{sex}},{{height}}</h5>
            <button @click="send">将子组件的数据向上传递给父组件</button>
            <hr>    
            <hr>    
        </div>
    </template>
    <script type="text/javascript">
        var app09 = new Vue({   //根组件
            el:'#app09',
            components:{
                'my-hello':{    //父组件
                    methods:{
                        getData(sex,height){
                            this.sex=sex;   //需要先初始化
                            this.height=height;     //需要先初始化
                        }
                    },
                    template:'#hello-app09',
                    data(){
                        return {
                            msg:'hi,Tom',
                            name:'Tom',
                            age:30,
                            user:{id:101,username:'test'},
                            sex:'',     //初始化sex
                            height:'',  //初始化height
                        }
                    },
                    components:{
                        'my-world':{    //子组件
                            methods:{
                                send(){
                                    // console.log(this)    //this代表的是当前这个子组件
                                    this.$emit('event',this.sex,this.height);	//使用$emit()触发一个事件,发送数据,事件名随便取,这里我们叫event
                                }
                            },
                            data(){
                                return {
                                    sex:'male',
                                    height:180
                                }
                            },
                            template:'#world-app09',
                            // props:['message','name','age','user']    //简单的字符串数组
                            props:{     //也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值
                                message:String,
                                name:{
                                    type:String,
                                    required:true
                                },
                                age:{
                                    type:Number,
                                    default:18,
                                    validator:function(value){
                                        return value>=0;
                                    }
                                },
                                // user:Object
                                user:{
                                    type:Object,
                                    // default:{id:102,username:'lalala'}   //报错,对象这么写有问题,对象或数组的默认值必须由函数返回
                                    default:function(){
                                        return {id:102,username:'lalala'}
                                    }
                                }
                            }
                        }
                    }
                }
            }
        })
    </script>

7. 单向数据流

props是单向绑定的,当父组件的属性发生变化时,将传导给子组件,但是不会反过来,而且不允许子组件直接修改父组件中的数据,会报错
解决方式:
方式一:如果子组件想把它作为局部数据来用,可以将数据存入一个变量中再操作,不影响父组件中的数据
方式二:如果子组件想修改数据并同步到父组件,俩个方法:
a.使用.sync(1.0版本支持,2.0版本中不支持,2.3版本又开始支持)
需要显式地触发一个更新事件
b.可以将父组件中的数据包装成对象,然后在子组件中修改对象的属性(因为对象是引用类型,指向同一个内存空间) 更常用,推荐

    <div id="app10">
        <h2>app10</h2>
        <h2>父组件:{{name}}</h2>
        <input v-model='name'>      <!-- 可以任意修改数据并影响子组件 -->
        <my-hello :name='name'></my-hello>  
        <hr>
        <hr>
    </div>
    <template id="hello-app10">
        <div>
            <h3>子组件:{{name}}</h3>
            <button @click='change'>修改数据</button>   <!-- 修改数据会报错 -->
        </div>
    </template>
    <script type="text/javascript">
        var app10 = new Vue({
            el:'#app10',
            data:{
                name:'xhx',
            },
            components:{
                'my-hello':{
                    template:'#hello-app10',
                    props:['name'],
                    methods:{
                        change(){
                            this.name='zyy';
                        }
                    }
                }
            }
        });
    </script>
7.1 如果子组件想把它作为局部数据来用,可以将数据存入一个变量中再操作,不影响父组件中的数据
    <div id="app11">
        <h2>app11</h2>
        <h2>父组件:{{name}}</h2>
        <input v-model='name'>      <!-- 可以任意修改数据并影响子组件 -->
        <my-hello :name='name'></my-hello>
        <hr>    
        <hr>    
    </div>
    <template id="hello-app11">
        <div>
            <h3>子组件:{{username}}</h3>
            <button @click='change'>修改数据</button>
        </div>
    </template>
    <script type="text/javascript">
        var app11 = new Vue({
            el:'#app11',
            data:{
                name:'xhx',
            },
            components:{
                'my-hello':{
                    template:'#hello-app11',
                    props:['name'],
                    data(){
                        return{
                            username:this.name  //将数据存入一个新的变量中再操作
                        }
                    },
                    methods:{
                        change(){
                            this.username='zyy';
                        }
                    }
                }
            }
        });
    </script>
7.2使用.sync使得子组件可以修改父组件中的数据,需要2个步骤:a.绑定props时加上.sync,b.修改时使用$emit方法修改
    <div id="app12">
        <h2>app12</h2>
        <h2>父组件:{{name}}</h2>
        <input v-model='name'>      <!-- 可以任意修改数据并影响子组件 -->
        <!-- <my-hello :name='name'></my-hello>  -->
        <my-hello :name.sync='name'></my-hello>     <!-- 加入.sync -->
        <hr>
        <hr>
    </div>
    <template id="hello-app12">
        <div>
            <h3>子组件:{{name}}</h3>
            <button @click='change'>修改数据</button>
        </div>
    </template>
    <script type="text/javascript">
        var app12 = new Vue({
            el:'#app12',
            data:{
                name:'xhx',
            },
            components:{
                'my-hello':{
                    template:'#hello-app12',
                    props:['name'],
                    methods:{
                        change(){
                            // this.name='zyy';
                            this.$emit('update:name','zyy')        //不能直接赋值了需要这样固定格式写法,第一个参数'update:修改的变量名',第二个参数变量新的值
                        }
                    }
                }
            }
        });
    </script>
7.3 通过将父组件中的数据包装成对象,把对象传给子组件,在子组件中修改对象的属性来实现在子组件中修改父组件的数据(更常用)
    <div id="app13">
        <h2>app13</h2>
        <h3>父组件:{{name}}</h3>
        <input v-model='name'>
        <input v-model='user.sex'>
        <h3>父组件:{{user.age}},{{user.sex}}</h3>
        <my-hello :name='name' :user='user'></my-hello> 
        <hr>
        <hr>
    </div>
    <template id="hello-app13">
        <div>
            <h3>子组件:{{name}}</h3>
            <h3>子组件:{{user.age}},{{user.sex}}</h3>
            <button @click='change'>修改数据</button>
        </div>
    </template>
    <script type="text/javascript">
        var app13 = new Vue({
            el:'#app13',
            data:{
                name:'xhx',
                user:{sex:'male',age:29},   //将父组件中的数据包装成对象
            },
            components:{
                'my-hello':{
                    template:'#hello-app13',
                    props:['name','user'],
                    methods:{
                        change(){
                            this.user.age=18;   //在子组件中修改对象的属性
                            this.user.sex='fmale';
                        }
                    }
                }
            }
        });
    </script>

8. 非父子组件间通信

可以通过一个空的vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件

    <div id="app14">
        <h2>app14</h2>
        <my-a></my-a>
        <my-b></my-b>
        <my-c></my-c>
        <hr>
        <hr>
    </div>
    <template id="a">
        <div>
            <h3>A组件:{{name}}</h3>
            <button @click='send'>发送数据给C</button>   <!-- 触发事件 -->
        </div>
    </template>
    <template id="b">
        <div>
            <h3>B组件:{{age}}</h3>
            <button  @click='send'>发送数据给C</button>      <!-- 触发事件 -->
        </div>
    </template>
    <template id="c">
        <div>
            <h3>C组件:{{name}},{{age}},{{sex}}</h3>
        </div>
    </template> 
    <script type="text/javascript">
        //定义一个空的vue实例,触发以及侦听事件的时候都通过这个实例来实现
        var Event = new Vue();
        var A = {
            template:'#a',
            data(){
                return {
                    name:'Tom'
                }
            },
            methods:{
                send(){
                    Event.$emit('data-a',this.name);   //通过空vue实例Event,触发事件,data-a为事件名,随便定义
                }
            }   
        };
        var B = {
            template:'#b',
            data(){
                return {
                    age:18
                }
            },
            methods:{
                send(){
                    Event.$emit('data-b',this.age) //触发事件
                }
            }
        };
        var C = {
            template:'#c',
            data:function(){
                return {
                    name:'',
                    age:'',
                    sex:'male'
                }
            },
            mounted(){
                Event.$on('data-a',name => {   //通过空的vue实例Event监听事件,事件名为上面定义的触发的事件名    
                    console.log(this);      //this指向的是C
                    this.name = name;
                });
                // Event.$on('data-b',function(age){
                //  console.log(this);  //this指向的是Event
                // })
                Event.$on('data-b',age => {
                    this.age = age;
                });
            }
        };
        var app14 = new Vue({
            el:'#app14',
            components:{
                'my-a':A,
                'my-b':B,
                'my-c':C
            }
        });
    </script>

9. slot内容分发

作用用来获取组件中原来的内容
+ 不带名字的slot

    <div id="app15">
        <h2>app15</h2>
        <!-- <my-hi>原来内容</my-hi> -->
        <my-hi></my-hi>
        <hr>
        <hr>
    </div>
    <template id="my-hi-app15">
        <div>
            <h3>welcome to app15!</h3>
            <slot>如果没有原内容则显示该内容</slot>  
        </div>
    </template>
    <script type="text/javascript">
        var app15 = new Vue({
            el:'#app15',
            components:{
                'my-hi':{
                    template:'#my-hi-app15'
                }
            }
        });
    </script>
  • 带有名字的slot
    <div id="app16">
        <h2>app16</h2>
        <my-hi>
            <ol slot='s1'>
                <li>111</li>
                <li>222</li>
                <li>333</li>
            </ol>
            <ul slot='s2'>
                <li>aaa</li>
                <li>bbb</li>
                <li>ccc</li>
            </ul>
            <ul type="circle">
                <li>square</li>
                <li>circle</li>
                <li>disc</li>
            </ul>
        </my-hi>
        <hr>
        <hr>
    </div>
    <template id="my-hi-app16">
        <div>
            <slot name='s2'></slot> 
            <h3>welcome to app15!</h3>
            <slot name='s1'></slot>
            <hr>
            <slot></slot>
        </div>
    </template>
    <script type="text/javascript">
        var app16 = new Vue({
            el:'#app16',
            components:{
                'my-hi':{
                    template:'#my-hi-app16'
                }
            }
        });
    </script>   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值