zoukankan      html  css  js  c++  java
  • vue学习记录(二)---自定义指令,组件

      1、全局自定义指令

    <body>
    <div id="container">
        <div v-bill="prop">{{msg}}</div>
    </div>
    <script src="./vue.js"></script>
    <script>
        //每个钩子的参数都是 ( el、binding、vnode 和 oldVnode),名称是bill但是使用的时候需要用v-bill进行使用,如果是驼峰命名法,那么就用-进行连接
        Vue.directive('bill', {
            bind(){                                 //全局绑定元素时调用,只调用一次,如果没有调整的话,会一直保存
                console.log('bind', arguments);
            },
            inserted() {                            //被绑定元素插入父节点时调用
                console.log('inserted', arguments);
            },
            update() {                              //所在组件的 VNode 更新时调用
                console.log('update', arguments);
            },
            componentUpdated() {                    //所在组件的 VNode 更新时调用
                console.log('componentUpdate', arguments);
            },
            unbind() {                              //解绑时调用,vue实例销毁时也会被调用即app.$destroy()的时候会调用
                console.log('unbind', arguments);
            }
        });
        let app = new Vue({
            el: '#container',
            data: {
                msg: 'this is test',
                prop: 'red'
            }
        })
    </script>

       2、局部指令

    <body>
    <div id="container">
        <div v-bill="prop">{{msg}}</div>
    </div>
    <script src="./vue.js"></script>
    <script>
        let bill = {
            bind() {
                console.log('bind', arguments);
            },
            inserted() {
                console.log('inserted', arguments);
            },
            update() {
                console.log('update', arguments);
            },
            componentUpdated() {
                console.log('componentUpdated', arguments);
            },
            unbind() {
                console.log('unbind', arguments);
            }
        };
        let app = new Vue({
            el: '#container',
            data: {
                msg: 'this is test',
                prop: 'red'
            },
            directives: {
                bill
            }
        })
    </script>
    </body>

     二、Vue.extend的使用

    <body>
    <div id="container">
        <div @click="check">{{msg}}</div>
        <bill></bill>
    </div>
    <script src="./vue.js"></script>
    <script>
        let Bill = Vue.extend({
            template: '<div><h3>{{count}}</h3></div>',
            data: function() {
                return {
                    url: 'javascript:;',
                    extend: 'this is extend',
                    count: 0
                }
            }
        });
        new Bill().$mount('bill');          //实现数据绑定
        // new Bill().$mount('#bill');     =>   <div id='bill'></div>       //可以用id进行绑定,也可用class进行绑定
        
    
        let app = new Vue({
            el: '#container',
            data: {
                msg: 'this is test',
                prop: 'red'
            },
            methods: {
                check() {
                    console.log(arguments);
                }
            }
        })
    </script>
    </body>
    <body>
    <div id="container"></div>
    <bill></bill>
    <script src="./vue.js"></script>
    <script>
        let Bill = Vue.extend({
            template: '<div><h1>this is title</h1><p>{{msg}}---{{text}}</p><input type="button" value="btn" @click="test"></div>',
            props: ['msg'],
            data: function() {
                return {
                    text: 'this is text'
                }
            },
            methods: {
                test() {
                    console.log('are you ok????');
                }
            }
        });
        new Bill({
            propsData: {msg: 'this is msg'}
        }).$mount('bill');
    </script>
    </body>

     在vue设定el时也可以用类似的方法设置,具体如下例子:

    <body>
    <div id="container">
        <div>{{count}}</div>
        <input type="button" value="btn" @click="change">
    </div>
    <script src="./vue.js"></script>
    <script>
    let app = new Vue({
        data: {
            count: 0
        },
        methods: {
            change() {
                this.count ++;
            }
        }
    });
    app.$mount('#container');
    </script>
    </body>

    三、Vue.set 与 Vue.delete

    在实例的内部调用是this.$set(target, key, value)或者是this.$delete(target, key)进行调用,如果是在外部调用也可以用Vue.set(target, key, value), Vue.delete(target, key)进行调用

    <body>
    <div id="container">
        <ul>
            <li v-for="(v, k) in list">{{v}}</li>
        </ul>
        <input type="button" value="change" @click="change">
        <input type="button" value="delete" @click="del">
    </div>
    <script src="./vue.js"></script>
    <script>
    let app = new Vue({
        el: '#container',
        data: {
            list: ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
        },
        methods: {
            change() {
                let index = this.list.findIndex(value => value === 'ccc');
                // this.list[index] = 'are you ok???';                          //这种方法是没有效果的,不能实现界面的变化
                this.$set(this.list, index, 'are you ok????');
            },
            del() {
                let index = this.list.findIndex(value => value==='bbb');
                index > 0 && this.$delete(this.$data.list, index);
            }
        }
    })
    </script>
    </body>

     四、template的几种写法

       a、写法一:直接书写在构造器中

    <body>
    <div id="container">
        <div>{{msg}}</div>
    </div>
    <script src="./vue.js"></script>
    <script>
    let app = new Vue({
        el: '#container',
        data: {
            msg: 'msg'
        },
        template: '<h4 style="color: red">this is test</h4>'
    })
    </script>
    </body>

    注意:这种写法主要针对模板的代码比较少的情况下,并且会所全部的的el的内容改成模板的内容

      b、利用template标签进行模板的定义与书写

    <body>
    <div id="container">
        <div>{{msg}}---main</div>
        <template id="tp">
            <div>
                <span>{{msg}}</span>
                <span>{{msg}}</span>
                <span>{{msg}}</span>
            </div>
        </template>
    </div>
    <script src="./vue.js"></script>
    <script>
    let app = new Vue({
        el: '#container',
        data: {
            msg: 'msg'
        },
        template: '#tp'
    })
    </script>
    </body>

      c、利用script标签进行模板定义与书写

    <body>
    <script type="x-template" id="tp">
    <div>
        <span>{{msg}}</span>
    </div>
    </script>
    <div id="container">
    
    </div>
    <script src="./vue.js"></script>
    <script>
    let app = new Vue({
        el: '#container',
        data: {
            msg: 'msg'
        },
        template: '#tp'
    })
    </script>

     五、component 组件

    a、父组件传值给子组件

    <body>
    <script type="x-template" id="bill">
        <div>
            <h1>{{getMsg}}</h1>
            <h2>{{title}}</h2>
            <ul>
                <li v-for="(v,k) of list">{{v}}---{{k}}</li>
            </ul>
            <input type="button" value="btn" @click="check">
        </div>
    </script>
    <div id="container">
        <bill :msg="msg" title='this is title'></bill>          <!--进行属性传值,利用标签里的属性往元素内部进行传值-->
    </div>
    <script src="./vue.js"></script>
    <script>
    Vue.component('bill', {
        template: '#bill',
        props: ['msg', 'title'],                                //接收从外部传入的属性值
        data: function() {                                      //内部定义data应该要用函数的形式进行定义
            return {
                list: ['aaa', 'bbb', 'ccc', 'ddd']
            }
        },
        computed: {
            getMsg: function() {
                return `are you ok??? ${this.msg}`;
            }
        },
        methods: {
            check() {
                console.log(arguments, this);                  //this 是指向当前的component组件
            }
        }
    });
    let app = new Vue({
        el: '#container',
        data: {
            msg: 'this is msg'
        }
    })
    </script>
    </body>

     注意: 在父组件传值给子组件后,回为vue中的单向数据流,在子组件使用的时候,不应该去更改传进来的值,而是在data中定义一个新的字段克隆过去后,再进行更改

    <body>
    <template id="bill">
        <div @click="add">{{number}}</div>
    </template>
    <div id="container">
        <bill :num="total" @change="addHandle"></bill>
        <bill :num="total" @change="addHandle"></bill>
        <h2>{{total}}</h2>
    </div>
    <script src="./vue.js"></script>
    <script>
    let bill = {
        props: ['num'],
        template: '#bill',
        data: function() {
            return {
                number: this.num                //把父组件传进来后,克隆给number字段后,再进行修改使用以防止引型的数据的更改造成的问题
            }
        },
        methods: {
            add() {
                this.number ++;
                this.$emit('change', 1);
            }
        }
    
    };
    let app = new Vue({
        el: '#container',
        data: {
            total: 0
        },
        components: {
            bill
        },
        methods: {
            addHandle(num) {
                this.total += num;
            }
        }
    })
    </script>
    </body>

    b、子组件传值给父组件

    <body>
    <script type="x-template" id="bill">
        <li @click="postData">{{val}}</li>
    </script>
    <div id="container">
        <div><input type="text" v-model="msg"><input type="button" value="提交" @click="submitHandle"></div>
        <ul>
            <bill :val='v' :index='k' v-for="(v,k) in list" @delete-event="getPostData">{{v}}</bill>    <!--接收内部的信息,并且调用外部对应的函数-->
        </ul>
    </div>
    <script src="./vue.js"></script>
    <script>
    Vue.component('bill', {
        props: ['val', 'index'],
        template: '#bill',
        methods: {
            postData() {
                this.$emit('delete-event', this.index);             //通过内部的观察者对外发送信息
            }
        }
    });
    let app = new Vue({
        el: '#container',
        data: {
            msg: '',
            list: []
        },
        methods: {
            submitHandle() {
                this.list.push(this.msg);
                this.msg = '';
            },
            getPostData(index) {
                this.$delete(this.list, index);
            }
        }
    })
    </script>
    </body>

     通常来讲,在上面<bill></bill>组件上绑定事件,如<bill @click='check'></bill>要利用组件内this.$emit('click)去触发父组件内的check事件,但是如果在后面加一个修辞符,native,那么就表示触发的是父组件里的check原生方法,如<bill @click.native='check'></bill>

    c、组件中is的使用

    在写组件的时候,为了符合H5的标准,这个时候就可以使用关键字is,比如在table中用tr,ul中只能用li,select 中只能用option的情况一样,如下例子:

    <body>
    <template id="bill">
        <tr>
            <td>{{content}}</td>
        </tr>
    </template>
    <div id="container">
        <table>
            <tbody>
                <tr is="bill" :content="v" v-for="(v,k) of list"></tr>  <!--指定完后,就相当于写了<bill></bill>的标签,效果是一样的-->
            </tbody>
        </table>
    </div>
    <script src="./vue.js"></script>
    <script>
    Vue.component('bill', {
        props: ['content'],
        template: '#bill',
    });
    let app = new Vue ({
        el: '#container',
        data: {
            list: ['aaa', 'bbb', 'ccc']
        }
    })
    </script>
    </body>

     d、ref的用法

     当ref挂在节点上的时候,那么输出的是节点,如果挂在组件上的话,那么输出的是组件对象

    <body>
    <template id="bill">
        <tr>
            <td>{{content}}</td>
        </tr>
    </template>
    <div id="container">
        <h3 ref="one">{{msg}}</h3>
        <table>
            <tbody>
                <tr ref="two" is="bill" :content="v" v-for="(v,k) of list"></tr>  <!--指定完后,就相当于写了<bill></bill>的标签,效果是一样的-->
            </tbody>
        </table>
        <input type="button" value="btn" @click="check">
    </div>
    <script src="./vue.js"></script>
    <script>
    Vue.component('bill', {
        props: ['content'],
        template: '#bill',
    });
    let app = new Vue ({
        el: '#container',
        data: {
            list: ['aaa', 'bbb', 'ccc'],
            msg: 'this is msg'
        },
        methods: {
            check() {
                console.group('以下是两种情况的ref');
                console.log(this.$refs.one);        //输出的是元素节点
                console.log(this.$refs.two);        //输出的是组件
                console.groupEnd()
            }
        }
    })
    </script>
    </body>

     e、组件参数较验证与非props特性

    // props: {
    //     content: [Boolean, Number]      //表示,content接收Boolean或者Number类型的数据
    // },
    props: {
        content: {
            // type: String,               //表示,content的type值为string类型
            type: [String, Number],        //表示, content的type值可以为String 类型也可以为Number类型
            required: false,               //表示,是否必需传入
            default: 'this is default text',    //表示,如果没有传入则使用的默认值
            validator: function(val) {          //验证的方法,对值的合法性进行验证
                return val.length > 4 || val>10000;
            }
        }
    },

    非props特性是指,当绑定属性在组件中时,如果子组件没有进行接收和使用时,会被当作行内属性填充入元素中去

     f、非父子组件之间的传值

    <body>
    <template id="bill">
        <div @click="change">{{text}}</div>
    </template>
    <div id="container">
        <bill :content="one"></bill>
        <bill :content="two"></bill>
    </div>
    <script src="./vue.js"></script>
    <script>
    Vue.prototype.bus = new Vue();          //利用vue里自带的观察者模式进行非组件间的数据传递
    let bill = {
        props: {
            content: String
        },
        template: '#bill',
        data: function() {
            return {
                text: this.content
            }
        },
        methods: {
            change() {
                this.bus.$emit('msg', this.text, this)
            }
        },
        mounted() {
            this.bus.$on('msg', (text, context) => {
                if(context !== this) {
                    this.text = text;
                }
            })
        }
    
    };
    let app = new Vue({
        el: '#container',
        data: {
            one: 'first',
            two: 'second'
        },
        components: {
            bill
        }
    })
    </script>

     g、slot在组件中的使用

    slot可以指定组件元素中的内容,可以实现元素传递给父元素

    <body>
    <template id="bill">
        <div>
            <slot></slot>           <!--如果没有指定slot的情况,会显示全部的没有指定slot的内容-->
            <slot name="header">this is default header content</slot>      <!--会显示slot='header'的内容-->
            <div>{{text}}</div>
            <slot name="footer">this is default footer content</slot>       <!--会显示slot='footer'的内容-->
            <slot name="other">this is default other content</slot>         <!--会显示slot='other'的内容-->
        </div>
    </template>
    <div id="container">
        <bill>
            <a href="javascript:;">这个是没指定slot名称的内容</a>     <!--没有指定slot名称的元素-->
            <h1 slot="header">this is header</h1>                    <!--指定slot名称的元素-->
            <h2 slot="footer">this is footer</h2>
            <template v-slot:other><u>this is u text</u></template>   <!--v-slot只能用在template标签的-->
        </bill>
    </div>
    <script src="./vue.js"></script>
    <script>
    let bill = {
        template: '#bill',
        data: function() {
            return {
                text: 'this is body'
            }
        }
    };
    let app = new Vue({
        el: '#container',
        data: {},
        components: {
            bill
        }
    })
    </script>
    </body>

     h、作用域slot的使用

     slot可以从组件内传递值到组件外进行页面渲染,例子如下:

    <body>
    <template id="bill">
        <div>
            <slot :val="val" :index="key" v-for="(val, key) of list"></slot>
        </div>
    </template>
    <div id="container">
        <bill>
            <template slot-scope="props">          <!--也可以用其他的标签,只是其他的标签后面会被转成实际的标签,而不能像template那样删除掉-->
                <div>{{props.val}}---{{props.index}}</div>
            </template>
        </bill>
    </div>
    <script src="./vue.js"></script>
    <script>
    let bill = {
        template: '#bill',
        data: function() {
            return {
                list: ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']
            }
        }
    };
    let app = new Vue({
        el: '#container',
        data: {},
        components: {
            bill
        }
    })
    </script>
    </body>

     i、作用域slot与通常的slot的合并使用

    <body>
    <template id="bill">
        <div>
            <slot name="header" :msg="msg"></slot>
            <slot :val="val" :index="key" v-for="(val, key) of list" name="body"></slot>
        </div>
    </template>
    <div id="container">
        <bill>
            <template slot-scope="props" slot="body">
                <div>{{props.val}}---{{props.index}}</div>
            </template>
            <template slot-scope="props" slot="header">
                <h1>{{props.msg}}</h1>
            </template>
        </bill>
    </div>
    <script src="./vue.js"></script>
    <script>
    let bill = {
        template: '#bill',
        data: function() {
            return {
                list: ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff'],
                msg: 'this is title'
            }
        }
    };
    let app = new Vue({
        el: '#container',
        data: {},
        components: {
            bill
        }
    })
    </script>
    </body>

     j、动态组件与v-once的使用

    在项目中,如果遇到组件的切换,可以运用vue官方提供的动态组件切换方法

    但是在组件间频繁的切换,组件间的生成与销毁对性能的消耗挺大的,为了节省性能,可以使用v-once进行渲染一次,以节省性能,具体例子如下:

    <body>
    <template id="bill">
        <h1 v-once>{{msg}}</h1>                 <!--v-once可以让component只渲染一次,缓存到内存中-->
    </template>
    <div id="container">
        <component :is="type===true?'bill':'abc'"></component>  <!--is利用v-bind绑定到对应的变量上,动态组件用component-->
        <input type="button" value="change" @click="change">
    </div>
    <script src="./vue.js"></script>
    <script>
    let abc = {
        template: '<u v-once>this is check</u>',
    };
    let bill = {
        template: '#bill',
        data: function() {
            return {
                msg: 'this is title'
            }
        }
    };
    let app = new Vue({
        el: '#container',
        data: {
            type: true
        },
        components: {
            bill,
            abc
        },
        methods: {
            change() {
                this.$data.type=!this.$data.type;
            }
        }
    })
    </script>
    </body>

     六、利用countup.js编写一个vue组件,案例如下:

    <template>
        <div :class="getStyle">
            <slot name="before"></slot>
            <span :id="id"></span>
            <slot name="after"></slot>
        </div>
    </template>
    <script>
        import { CountUp } from 'countup.js'            // 需要安装countup.js  npm i countup --save
        export default {
            name: 'CountTo',
            props: {
                config: {                               // 接收默认配置
                    type: Object,
                    required: false
                },
                getStyle: {                             // 接收样式
                    type: String,
                    default: 'count_to',
                    required: false
                },
                endVal: {                              // 接收终止值
                    type: Number,
                    required: true
                }
            },
            data: function () {
                return {
                    handle: null,                      // 主要函数句柄
                    val: this.endVal,
                    configs: {                         // 默认配置
                        startVal: 0,
                        decimalPlaces: 0,
                        duration: 2,
                        useGrouping: true,
                        useEasing: true,
                        separator: ',',
                        decimal: '2'
                    }
                }
            },
            computed: {
                id: function () {                       // 配置唯一的id
                    return `count_to_${this._uid}`;
                }
            },
            watch: {
                endVal (nVal) {
                    this.handle.update(nVal);
                }
            },
            methods: {
                endCountEvent () {                     // 终止时调用函数
                    this.val = this.endVal;
                    this.$emit('endCount', this.val);
                    this.$root.$emit('endCount', this.val);
                }
            },
            mounted () {
                this.$nextTick(function () {          // 等待DOM元素挂载完成后执行的函数
                    Object.assign(this.configs, this.config);
                    this.handle = new CountUp(this.id, this.val, this.configs);
                    this.handle.start(this.endCountEvent);
                })
            }
        }
    </script>
    <style scoped>
        .count_to{
            font-size: 14px;
            color: #747F9E;
            display: inline-block;
        }
    </style>
  • 相关阅读:
    JavaScript常见注意点(一)
    jspServlet2.5和Servlet3的区别
    jspMVC案例
    jQuery入口函数的写法
    Servlet 简介
    jspMVC设计模式和Servlet2.5入门案例
    display 属性
    JSON简单使用
    Tomcat修改端口号
    php开发环境简单配置
  • 原文地址:https://www.cnblogs.com/rickyctbu/p/11706437.html
Copyright © 2011-2022 走看看