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>
原文地址:https://www.cnblogs.com/rickyctbu/p/11706437.html