导言
用vue 蛮久了才发现自己有很多比较方便的用法都没有用过,还是自己当初在开始学的时候掌握的,是在惭愧。这些在各种组件源码中经常看到,以为都是很高级很高级的用法,其实你会发现这些都是在Vue官方文档中写的很仔细很仔细的,但是我自己从来没有看过。这次算给自己上了一节课了,在刚开始学习的时候可以粗略的过,但是用了一段时间之后,一定要回过来在仔细的阅读官方文档。
js部分
组件递归
组件是可以在它们自己的模板中调用自身的。不过它们只能通过
name
选项来做这件事:
1
2
// 一般编写复杂组件的时候可能会用到,平常业务中可能不太用上,先记下
name: 'unique-name-of-my-component'
sync
1
2
3
4
5
6
7
// 父组件
<message :show.sync="show">添加成功</message>
// 子组件
<div class="message-box" v-if="show">
<slot>消息成功了。。。</slot>
<span class="message-box-close" @click="$emit('update:show', false)">X</span>
</div>
自定义组件的v-model
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
- 普通value ,prop用法。自定义组件的时候使用v-model而不是在父级上再定义 v-if ``` javascript // 父组件
// 子组件
我是来测试了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 自定义model
```javascript
// 父组件
<base-checkbox v-model="loginvue" @change="customizeChange" />
// 子组件 base-check.vue
<template>
<div>
<input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" /></div>
</template>
<script>export
default {
name:
"base-checkbox",
model: {
prop: "checked",
event: "change"
},
props: { // 这个地方别忘记定义喽
checked: Boolean
}
};</script>
监听组件生命周期
实际开发过程中会遇到当子组件某个生命周期完成之后通知父组件,然后在父组件做对应的处理
1
2
3
4
5
6
7
8
9
10
// 传统方式
// 子组件在对应的钩子中发布事件
created(){
this.$emit('done')
}
// 父组件订阅其方发
<list @done="childDone">
// 使用 通过`@hook`监听子组件的生命周期
<list @hook:mounted="listMounted" />
路由组件传参
1
2
3
4
5
6
7
8
{
path: 'notice/:tab/:id',
name: 'user-notice',
component: UserNotice,
props: true, // `route.params` 将会被设置为组件属性。
}
// 组件中
props: ["id", "tab"],
更多参考:https://router.vuejs.org/zh/guide/essentials/passing-props.html
程序化事件监听器
通常页面中有定时器的时候,我们会定义一个timer变量,在页面销毁之前清除定时器。
this.timer
唯一的作用只是为了能够在beforeDestroy
内取到计时器序号,除此之外没有任何用处。
1
2
3
4
5
6
7
8
9
10
export default {
mounted() {
this.timer = setInterval(() => {
console.log(Date.now())
}, 1000)
},
beforeDestroy() {
clearInterval(this.timer)
}
}
如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。
我们可以通过
$on
或$once
监听页面生命周期销毁来解决这个问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
mounted() {
this.creatInterval('hello')
this.creatInterval('world')
},
creatInterval(msg) {
let timer = setInterval(() => {
console.log(msg)
}, 1000)
this.$once('hook:beforeDestroy', function() {
clearInterval(timer)
})
}
}
全局挂载组件
- Message 提示弹框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Message/index.js
import Vue from "vue";
import message from './message';
const MessageConstructor = Vue.extend(message);
const messageInstance = new MessageConstructor();
messageInstance.$mount();
document.body.appendChild(messageInstance.$el)
const Message = (option = {}) = >{
messageInstance.add(option);
}
export default Message;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// message/message.vue
<template>
<div class="wrap">
<div class="message" :class="item.type" v-for="item in notices" :key="item._name">
<div class="content"></div></div>
</div>
</template>
<script>// 默认选项
const DefaultOptions = {
duration: 1500,
type: "info",
content: "这是一条提示信息!"
};
let mid = 0;
export
default {
data() {
return {
notices:
[],
isShow: false
};
},
methods: {
add(notice = {}) {
this.isShow = true;
// name标识 用于移除弹窗
let _name = this.getName();
// 合并选项
notice = Object.assign({
_name
},
DefaultOptions, notice);
this.notices.push(notice);
setTimeout(() = >{
this.removeNotice(_name);
},
notice.duration);
},
getName() {
return "msg_" + mid++;
},
removeNotice(_name) {
this.isShow = false;
let index = this.notices.findIndex(item = >item._name === _name);
this.notices.splice(index, 1);
}
}
};</script>
- confirm 弹框,方法类写在inde.js中,通过实例创建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// comfrim/index.js
import confirm from './confirm';
import Vue from 'vue';
import {
pageScroll
}
from '@/utils/assist';
// 构造子类
let confirmConstructor = Vue.extend(confirm);
// 实例化组件
let confirmInstance = new confirmConstructor({
el: document.createElement('div')
});
// 也可通过此方法
// $mount可以传入选择器字符串,表示挂载到该选择器
// 如果不传入选择器,将渲染为文档之外的的元素,你可以想象成 document.createElement()在内存中生成dom
// confirmInstance.$el获取的是dom元素
// confirmInstance.$mount();
const hashChange = function() {
pageScroll.unlock();
const el = confirmInstance.$el;
el.parentNode && el.parentNode.removeChild(el);
}
confirmConstructor.prototype.closeConfirm = function(stay, callback) {
let stopClose = true;
if (typeof callback === 'function') {
stopClose = callback();
if (stopClose === undefined) stopClose = true;
}
if (!stopClose || stay) return;
pageScroll.unlock();
const el = confirmInstance.$el;
el.parentNode && el.parentNode.removeChild(el);
window.removeEventListener("hashchange", hashChange);
}
const Confirm = (options = {}) = >{
confirmInstance.mes = options.mes || '';
confirmInstance.align = options.align || '';
confirmInstance.title = options.title || '提示';
confirmInstance.opts = options.opts;
window.addEventListener("hashchange", hashChange);
document.body.appendChild(confirmInstance.$el);
pageScroll.lock();
}
export default Confirm;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// confirm/confirm.vue
<template>
<div class="wb-dialog-black-mask">
<div class="wb-confirm">
<div class="wb-confirm-hd">
<strong class="wb-confirm-title" v-html="title"></strong>
</div>
<div class="wb-confirm-bd" :class="align ? 'text-'+align :'' " v-html="mes"></div>
<template v-if="typeof opts == 'function'">
<div class="wb-confirm-ft">
<a href="javascript:;" class="wb-confirm-btn default" @click.stop="closeConfirm(false)">取消</a>
<a href="javascript:;" class="wb-confirm-btn primary" @click.stop="closeConfirm(false, opts)">确定</a></div>
</template>
<template v-else>
<div class="wb-confirm-ft">
<a href="javascript:;" class="wb-confirm-btn" :key="key" v-for="(item, key) in opts" :class="typeof item.color == 'boolean' ? (item.color ? 'primary' : 'default') : ''" :style="{color: item.color}" @click.stop="closeConfirm(item.stay, item.callback)"></a></div>
</template>
</div>
</div>
</template>
<script type="text/babel">export
default {
props:
{
title:
String,
mes: String,
align: String,
opts: {
type: [Array, Function],
default:
() = >{}
}
}
};</script>
css 部分
样式穿透
样式穿透在开发中修改第三方组件样式是很常见,但由于scoped属性的样式隔离,可能需要去除scoped或是另起一个style。这些做法都会带来副作用(组件样式污染、不够优雅),样式穿透在css预处理器中使用才生效。 ```javascript
- less使用 / deep /
- stylus使用 »>
// 你也可以通过 JavaScript 访问到它: created(){ console.log(this.$style.red) }
1
2
3
4
5
6
7
8
### svg loading
> 发现了一个类google的loading动画
```html
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="15" fill="none" stroke-width="2" stroke-miterlimit="10" /></svg>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
.myloader {
position:relative;
margin:0 auto;
width:60px;
height:100%;
}
.myloader:before {
content:"";
display:block;
padding-top:100%;
}
.circular {
animation:rotate 2s linear infinite;
-webkit-animation:rotate 2s linear infinite;
height:auto;
transform-origin:center center;
-webkit-transform-origin:center center;
width:100%;
position:absolute;
top:0;
bottom:0;
left:0;
right:0;
margin:auto;
}
.path {
stroke-dasharray:1,200;
stroke-dashoffset:0;
animation:dash 1.5s ease-in-out infinite,color 6s ease-in-out infinite;
-webkit-animation:dash 1.5s ease-in-out infinite,color 6s ease-in-out infinite;
stroke-linecap:round;
}
@keyframes rotate {
100% {
transform:rotate(360deg);
}
}@keyframes dash {
0% {
stroke-dasharray:1,200;
stroke-dashoffset:0;
}
50% {
stroke-dasharray:89,200;
stroke-dashoffset:-35px;
}
100% {
stroke-dasharray:89,200;
stroke-dashoffset:-124px;
}
}@keyframes color {
100%,0% {
stroke:#d62d20;
}
40% {
stroke:#0057e7;
}
66% {
stroke:#008744;
}
80%,90% {
stroke:#ffa700;
}
}@-webkit-keyframes rotate {
100% {
transform:rotate(360deg);
}
}@-webkit-keyframes dash {
0% {
stroke-dasharray:1,200;
stroke-dashoffset:0;
}
50% {
stroke-dasharray:89,200;
stroke-dashoffset:-35px;
}
100% {
stroke-dasharray:89,200;
stroke-dashoffset:-150px;
}
}@-webkit-keyframes color {
100%,0% {
stroke:#d62d20;
}
40% {
stroke:#0057e7;
}
66% {
stroke:#008744;
}
80%,90% {
stroke:#ffa700;
}
}
参考来源: