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>
注意:在2.6以后的版本中,slot-scope已经被弃用,接收用v-slot:default = “自己起的变量名”(表示slot的名字,如果没有用default来表示)
在2.6版本以后
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
<current-user #default="{ user }"> {{ user.firstName }} </current-user>
但是注意以下的写法是无效的
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
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>