Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
樣式功能來(lái)看,整體不是很復(fù)雜,alert 組件主要包括了主題色,title,關(guān)閉按鈕,關(guān)閉事件,居中,加粗等
<template>
<!-- 顯示隱藏有動(dòng)畫效果 -->
<!-- 開發(fā)沒用過,不是很理解為什么使用v-show判斷顯示 -->
<transition name="d-alert-fade">
<div
class="d-alert"
:class="[typeClass, center ? 'is-center' : '', 'is-' + effect]"
v-show="visible"
role="alert"
>
<!-- 左側(cè)圖標(biāo) -->
<i
class="d-alert__icon"
:class="[iconClass, isBigIcon]"
v-if="showIcon"
></i>
<!-- title 和 描述 -->
<div class="d-alert__content">
<span
class="d-alert__title"
:class="[isBoldTitle]"
v-if="title || $slots.title"
>
<slot name="title">{{ title }}</slot>
</span>
<p v-if="$slots.default && !description" class="d-alert__description">
<slot></slot>
</p>
<p v-if="description && !$slots.default" class="d-alert__description">
{{ description }}
</p>
<i
class="d-alert__closebtn"
:class="{
'is-customed': closeText !=='',
'd-icon-close': closeText===''
}"
v-show="closable"
@click="close"
>{{ closeText }}</i
>
</div>
</div>
</transition>
</template>
使用 role 屬性告訴輔助設(shè)備(如屏幕閱讀器)這個(gè)元素所扮演的角色。本質(zhì)上是增強(qiáng)語(yǔ)義性,當(dāng)現(xiàn)有的 HTML標(biāo)簽不能充分表達(dá)語(yǔ)義性的時(shí)候,就可以借助 role 來(lái)說明。
這里不是很理解為什么 title 和 description 使用了屬性和 slot 判斷,有清楚的朋友可以幫忙解答
setup(props, { emit, slots }) {
// 接受的屬性轉(zhuǎn)為響應(yīng)式
const { description, type }=toRefs(props)
// 使用 v-show 顯示隱藏
const visible=ref(true)
// 關(guān)閉事件
const close=()=> {
visible.value=false
emit('close')
}
const typeClass=computed(()=> {
return `d-alert--${type.value}`
})
const iconClass=computed(()=> {
return TYPE_CLASSES_MAP[type.value] || 'd-icon-info'
})
const isBigIcon=computed(()=> {
return description.value || slots.default ? 'is-big' : ''
})
const isBoldTitle=computed(()=> {
return description.value || slots.default ? 'is-bold' : ''
})
return {
close,
visible,
typeClass,
iconClass,
isBigIcon,
isBoldTitle
}
}
組件介紹到這里就結(jié)束了,比較簡(jiǎn)單。為了湊字呢,這里在介紹下 transition 組件
大部分朋友都了解這是設(shè)置組件動(dòng)畫的內(nèi)置動(dòng)畫組件。通常有三種使用方式:
我們通常使用的方法,css 配置 enter 和 leave
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition name="fade">
<p v-if="show">我是測(cè)試</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
<template>
<div class="app">
<button @click="show=!show">Toggle show</button>
<transition name="bounce">
<p v-if="show">我是測(cè)試</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
}
}
</script>
<style>
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
// reverse 很關(guān)鍵
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
監(jiān)聽 transition 組件的內(nèi)置方法,js 控制動(dòng)畫
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@before-leave="beforeLeave"
@leave="leave"
css="false"
>
<p v-if="show">hello</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: true
}
},
methods: {
beforeEnter(el) {
el.style.opacity=0
el.style.transition='opacity 0.5s ease'
},
enter(el) {
this.$el.offsetHeight
el.style.opacity=1
},
beforeLeave(el) {
el.style.opacity=1
},
leave(el) {
el.style.transition='opacity 0.5s ease'
el.style.opacity=0
}
}
}
</script>
如果形參不指定 done ,則表明用戶不手動(dòng)控制動(dòng)畫的結(jié)束,而轉(zhuǎn)由節(jié)點(diǎn)的 transition 或者 animationEnd 來(lái)標(biāo)識(shí)動(dòng)畫結(jié)束,開始回調(diào) afterEnter。
鉤子函數(shù)的形參的個(gè)數(shù)大于1,表示形參中有 done, 也就是說用戶必須手動(dòng)控制動(dòng)畫何時(shí)結(jié)束。所以一旦你配置了 done 形參,則轉(zhuǎn)由你告訴框架,動(dòng)畫何時(shí)結(jié)束。需要在合適的時(shí)機(jī)調(diào)用 done,否則 afterEnter 接口就沒法被調(diào)用了。
實(shí)例
<template>
<div class="app">
<button @click="show=!show">
Toggle render
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
</template>
編譯生成的 render 函數(shù)(不是使用的模板組件)
import {
createVNode as _createVNode,
openBlock as _openBlock,
createBlock as _createBlock,
createCommentVNode as _createCommentVNode,
Transition as _Transition,
withCtx as _withCtx,
} from "vue";
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (
// 收集動(dòng)態(tài)節(jié)點(diǎn) 如 v-if v-for
_openBlock(),
_createBlock("template", null, [
_createVNode("div", { class: "app" }, [
_createVNode(
"button",
{
onClick: ($event)=> (_ctx.show=!_ctx.show),
},
" Toggle render ",
8 /* PROPS */,
["onClick"]
),
_createVNode(
_Transition,
{ name: "fade" },
{
// transition 只有一個(gè)子節(jié)點(diǎn),默認(rèn)插槽。多個(gè)子節(jié)點(diǎn)報(bào)錯(cuò)
default: _withCtx(()=> [
_ctx.show
? (_openBlock(), _createBlock("p", { key: 0 }, "hello"))
: _createCommentVNode("v-if", true),
]),
_: 1,
}
),
]),
])
);
}
那么如何在組建創(chuàng)建和銷毀的時(shí)候執(zhí)行事件呢?————?jiǎng)?chuàng)建鉤子函數(shù) transition 組件返回的是處理過的第一個(gè)子節(jié)點(diǎn)
trantion 組件定義
const Transition=(props, { slots })=>
//esolveTransitionProps 函數(shù)主要作用是,在我們給 Transition 傳遞的 Props 基礎(chǔ)上做一層封裝,然后返回一個(gè)新的 Props 對(duì)象,由于它包含了所有的 Props 處理
h(BaseTransition, resolveTransitionProps(props), slots);
const BaseTransition={
name: `BaseTransition`,
props: {
mode: String,
appear: Boolean,
persisted: Boolean,
// enter
onBeforeEnter: TransitionHookValidator,
onEnter: TransitionHookValidator,
onAfterEnter: TransitionHookValidator,
onEnterCancelled: TransitionHookValidator,
// leave
onBeforeLeave: TransitionHookValidator,
onLeave: TransitionHookValidator,
onAfterLeave: TransitionHookValidator,
onLeaveCancelled: TransitionHookValidator,
// appear
onBeforeAppear: TransitionHookValidator,
onAppear: TransitionHookValidator,
onAfterAppear: TransitionHookValidator,
onAppearCancelled: TransitionHookValidator,
},
setup(props, { slots }) {
const instance=getCurrentInstance();
const state=useTransitionState();
let prevTransitionKey;
return ()=> {
const children=slots.default && getTransitionRawChildren(slots.default(), true);
if (!children || !children.length) {
return;
}
// Transition 組件只允許一個(gè)子元素節(jié)點(diǎn),多個(gè)報(bào)警告,提示使用 TransitionGroup 組件
if (process.env.NODE_ENV !=="production" && children.length > 1) {
warn(
"<transition> can only be used on a single element or component. Use " +
"<transition-group> for lists."
);
}
// 不需要追蹤響應(yīng)式,所以改成原始值,提升性能
const rawProps=toRaw(props);
const { mode }=rawProps;
// 檢查 mode 是否合法
if (
process.env.NODE_ENV !=="production" &&
mode &&
!["in-out", "out-in", "default"].includes(mode)
) {
warn(`invalid <transition> mode: ${mode}`);
}
// 獲取第一個(gè)子元素節(jié)點(diǎn)
const child=children[0];
if (state.isLeaving) {
return emptyPlaceholder(child);
}
// 處理 <transition><keep-alive/></transition> 的情況
const innerChild=getKeepAliveChild(child);
if (!innerChild) {
return emptyPlaceholder(child);
}
const enterHooks=resolveTransitionHooks(
innerChild,
rawProps,
state,
instance
);
setTransitionHooks(innerChild, enterHooks);
const oldChild=instance.subTree;
const oldInnerChild=oldChild && getKeepAliveChild(oldChild);
let transitionKeyChanged=false;
const { getTransitionKey }=innerChild.type;
if (getTransitionKey) {
const key=getTransitionKey();
if (prevTransitionKey===undefined) {
prevTransitionKey=key;
} else if (key !==prevTransitionKey) {
prevTransitionKey=key;
transitionKeyChanged=true;
}
}
if (
oldInnerChild &&
oldInnerChild.type !==Comment &&
(!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)
) {
const leavingHooks=resolveTransitionHooks(
oldInnerChild,
rawProps,
state,
instance
);
// 更新舊樹的鉤子函數(shù)
setTransitionHooks(oldInnerChild, leavingHooks);
// 在兩個(gè)視圖之間切換
if (mode==="out-in") {
state.isLeaving=true;
// 返回空的占位符節(jié)點(diǎn),當(dāng)離開過渡結(jié)束后,重新渲染組件
leavingHooks.afterLeave=()=> {
state.isLeaving=false;
instance.update();
};
return emptyPlaceholder(child);
} else if (mode==="in-out") {
leavingHooks.delayLeave=(el, earlyRemove, delayedLeave)=> {
const leavingVNodesCache=getLeavingNodesForType(
state,
oldInnerChild
);
leavingVNodesCache[String(oldInnerChild.key)]=oldInnerChild;
// early removal callback
el._leaveCb=()=> {
earlyRemove();
el._leaveCb=undefined;
delete enterHooks.delayedLeave;
};
enterHooks.delayedLeave=delayedLeave;
};
}
}
return child;
};
},
};
在渲染的過程中,Transition 組件還會(huì)通過 resolveTransitionHooks 去定義組件創(chuàng)建和刪除階段的鉤子函數(shù)對(duì)象,然后再通過 setTransitionHooks 函數(shù)去把這個(gè)鉤子函數(shù)對(duì)象設(shè)置到 vnode.transition 上。
hooks定義
const hooks={
mode,
persisted,
beforeEnter(el) {
let hook=onBeforeEnter;
if (!state.isMounted) {
if (appear) {
hook=onBeforeAppear || onBeforeEnter;
} else {
return;
}
}
if (el._leaveCb) {
el._leaveCb(true /* cancelled */);
}
const leavingVNode=leavingVNodesCache[key];
if (
leavingVNode &&
isSameVNodeType(vnode, leavingVNode) &&
leavingVNode.el._leaveCb
) {
leavingVNode.el._leaveCb();
}
callHook(hook, [el]);
},
enter(el) {
let hook=onEnter;
let afterHook=onAfterEnter;
let cancelHook=onEnterCancelled;
if (!state.isMounted) {
if (appear) {
hook=onAppear || onEnter;
afterHook=onAfterAppear || onAfterEnter;
cancelHook=onAppearCancelled || onEnterCancelled;
} else {
return;
}
}
let called=false;
const done=(el._enterCb=(cancelled)=> {
if (called) return;
called=true;
if (cancelled) {
callHook(cancelHook, [el]);
} else {
callHook(afterHook, [el]);
}
if (hooks.delayedLeave) {
hooks.delayedLeave();
}
el._enterCb=undefined;
});
if (hook) {
hook(el, done);
if (hook.length <=1) {
done();
}
} else {
done();
}
},
leave(el, remove) {
const key=String(vnode.key);
if (el._enterCb) {
el._enterCb(true /* cancelled */);
}
if (state.isUnmounting) {
return remove();
}
callHook(onBeforeLeave, [el]);
let called=false;
const done=(el._leaveCb=(cancelled)=> {
if (called) return;
called=true;
remove();
if (cancelled) {
callHook(onLeaveCancelled, [el]);
} else {
callHook(onAfterLeave, [el]);
}
el._leaveCb=undefined;
if (leavingVNodesCache[key]===vnode) {
delete leavingVNodesCache[key];
}
});
leavingVNodesCache[key]=vnode;
if (onLeave) {
onLeave(el, done);
if (onLeave.length <=1) {
done();
}
} else {
done();
}
},
clone(vnode) {
return resolveTransitionHooks(vnode, props, state, instance);
},
};
鉤子函數(shù)對(duì)象定義了 4 個(gè)鉤子函數(shù),分別是 beforeEnter,enter,leave 和 clone。在節(jié)點(diǎn) patch 階段的 mountElement 函數(shù)中,在插入節(jié)點(diǎn)前且存在過度會(huì)執(zhí)行 vnode.transition 中的 beforeEnter 函數(shù)
//beforeEnter 鉤子函數(shù)主要做的事情就是根據(jù) appear 的值和 DOM 是否掛載,來(lái)執(zhí)行 onBeforeEnter 函數(shù)或者是 onBeforeAppear 函數(shù)。appear 是否節(jié)點(diǎn)現(xiàn)實(shí)的時(shí)候執(zhí)行動(dòng)畫
beforeEnter(el) {
let hook=onBeforeEnter
if (!state.isMounted) {
if (appear) {
hook=onBeforeAppear || onBeforeEnter
}
else {
return
}
}
if (el._leaveCb) {
el._leaveCb(true /* cancelled */)
}
const leavingVNode=leavingVNodesCache[key]
if (leavingVNode &&
isSameVNodeType(vnode, leavingVNode) &&
leavingVNode.el._leaveCb) {
leavingVNode.el._leaveCb()
}
callHook(hook, [el])
}
resolveTransitionProps 函數(shù)
function resolveTransitionProps(rawProps) {
let {
name="v",
type,
css=true,
duration,
enterFromClass=`${name}-enter-from`,
enterActiveClass=`${name}-enter-active`,
enterToClass=`${name}-enter-to`,
appearFromClass=enterFromClass,
appearActiveClass=enterActiveClass,
appearToClass=enterToClass,
leaveFromClass=`${name}-leave-from`,
leaveActiveClass=`${name}-leave-active`,
leaveToClass=`${name}-leave-to`,
}=rawProps;
const baseProps={};
for (const key in rawProps) {
if (!(key in DOMTransitionPropsValidators)) {
baseProps[key]=rawProps[key];
}
}
if (!css) {
return baseProps;
}
const durations=normalizeDuration(duration);
const enterDuration=durations && durations[0];
const leaveDuration=durations && durations[1];
const {
onBeforeEnter,
onEnter,
onEnterCancelled,
onLeave,
onLeaveCancelled,
onBeforeAppear=onBeforeEnter,
onAppear=onEnter,
onAppearCancelled=onEnterCancelled,
}=baseProps;
const finishEnter=(el, isAppear, done)=> {
removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
done && done();
};
const finishLeave=(el, done)=> {
removeTransitionClass(el, leaveToClass);
removeTransitionClass(el, leaveActiveClass);
done && done();
};
const makeEnterHook=(isAppear)=> {
return (el, done)=> {
const hook=isAppear ? onAppear : onEnter;
const resolve=()=> finishEnter(el, isAppear, done);
hook && hook(el, resolve);
nextFrame(()=> {
removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass);
addTransitionClass(el, isAppear ? appearToClass : enterToClass);
if (!(hook && hook.length > 1)) {
if (enterDuration) {
setTimeout(resolve, enterDuration);
} else {
whenTransitionEnds(el, type, resolve);
}
}
});
};
};
return extend(baseProps, {
onBeforeEnter(el) {
onBeforeEnter && onBeforeEnter(el);
addTransitionClass(el, enterActiveClass);
addTransitionClass(el, enterFromClass);
},
onBeforeAppear(el) {
onBeforeAppear && onBeforeAppear(el);
addTransitionClass(el, appearActiveClass);
addTransitionClass(el, appearFromClass);
},
onEnter: makeEnterHook(false),
onAppear: makeEnterHook(true),
onLeave(el, done) {
const resolve=()=> finishLeave(el, done);
addTransitionClass(el, leaveActiveClass);
addTransitionClass(el, leaveFromClass);
nextFrame(()=> {
removeTransitionClass(el, leaveFromClass);
addTransitionClass(el, leaveToClass);
if (!(onLeave && onLeave.length > 1)) {
if (leaveDuration) {
setTimeout(resolve, leaveDuration);
} else {
whenTransitionEnds(el, type, resolve);
}
}
});
onLeave && onLeave(el, resolve);
},
onEnterCancelled(el) {
finishEnter(el, false);
onEnterCancelled && onEnterCancelled(el);
},
onAppearCancelled(el) {
finishEnter(el, true);
onAppearCancelled && onAppearCancelled(el);
},
onLeaveCancelled(el) {
finishLeave(el);
onLeaveCancelled && onLeaveCancelled(el);
},
});
}
我們來(lái)看 onBeforeEnter 函數(shù),它的內(nèi)部執(zhí)行了基礎(chǔ) props 傳入的 onBeforeEnter 鉤子函數(shù),并且給 DOM 元素 el 添加了 enterActiveClass 和 enterFromClass 樣式。
其中,props 傳入的 onBeforeEnter 函數(shù)就是我們寫 Transition 組件時(shí)添加的 beforeEnter 鉤子函數(shù)。enterActiveClass 默認(rèn)值是 v-enter-active,enterFromClass 默認(rèn)值是 v-enter-from,如果給 Transition 組件傳入了 name 的 prop,比如 fade,那么 enterActiveClass 的值就是 fade-enter-active,enterFromClass 的值就是 fade-enter-from。(onBeforeAppear 和 onBeforeEnter 的邏輯類似,就不贅述了,它是在我們給 Transition 組件傳入 appear 的 Prop,且首次掛載的時(shí)候執(zhí)行的。執(zhí)行完 beforeEnter 鉤子函數(shù),接著插入元素到頁(yè)面,然后會(huì)執(zhí)行 vnode.transition 中的 enter 鉤子函數(shù),上面的 hooks 中)
在 enter 函數(shù)內(nèi)部,首先執(zhí)行基礎(chǔ) props 傳入的 onEnter 鉤子函數(shù),然后在下一幀給 DOM 元素 el 移除了 enterFromClass,同時(shí)添加了 enterToClass 樣式(動(dòng)畫也就是所謂的樣式交替改變)
Transition 組件允許我們傳入 enterDuration 這個(gè) prop,它會(huì)指定進(jìn)入過渡的動(dòng)畫時(shí)長(zhǎng),當(dāng)然如果你不指定,Vue.js 內(nèi)部會(huì)監(jiān)聽動(dòng)畫結(jié)束事件,然后在動(dòng)畫結(jié)束后,執(zhí)行 finishEnter 函數(shù)
來(lái)看它的實(shí)現(xiàn)
const finishEnter=(el, isAppear, done)=> {
removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
done && done();
};
其實(shí)就是給 DOM 元素移除 enterToClass 以及 enterActiveClass,同時(shí)執(zhí)行 done 函數(shù),進(jìn)而執(zhí)行 onAfterEnter 鉤子函數(shù)
leave 鉤子主要功能和 enter 相反。小伙伴們可自行查閱。
以上就是對(duì) alert 組件的學(xué)習(xí)。如有不對(duì)歡迎指正。
么是JavaScript
JavaScript是一種基于對(duì)象和事件驅(qū)動(dòng)的、并具有安全性能的腳本語(yǔ)言,已經(jīng)被廣泛用于Web應(yīng)用開發(fā),常用來(lái)為網(wǎng)頁(yè)添加各式各樣的動(dòng)態(tài)功能,為用戶提供更流暢美觀的瀏覽效果。通常JavaScript腳本是通過嵌入在HTML中來(lái)實(shí)現(xiàn)自身的功能的。
JavaScript特點(diǎn)
是一種解釋性腳本語(yǔ)言(代碼不進(jìn)行預(yù)編譯)。
主要用來(lái)向HTML(標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言下的一個(gè)應(yīng)用)頁(yè)面添加交互行為。
可以直接嵌入HTML頁(yè)面,但寫成單獨(dú)的js文件有利于結(jié)構(gòu)和行為的分離。
跨平臺(tái)特性,在絕大多數(shù)瀏覽器的支持下,可以在多種平臺(tái)下運(yùn)行(如Windows、Linux、Mac、Android、iOS等)。
JavaScript組成
JavaScript日常用途
1、嵌入動(dòng)態(tài)文本于HTML頁(yè)面。
2、對(duì)瀏覽器事件做出響應(yīng)。
3、讀寫HTML元素。
4、在數(shù)據(jù)被提交到服務(wù)器之前驗(yàn)證數(shù)據(jù)。
5、檢測(cè)訪客的瀏覽器信息。
6、控制cookies,包括創(chuàng)建和修改等。
7、基于Node.js技術(shù)進(jìn)行服務(wù)器端編程。
JavaScript的基本結(jié)構(gòu)
<script type="text/javascript"> <!— JavaScript 語(yǔ)句; —> </script >
示例:
…… <title>初學(xué)JavaScript</title> </head> <body> <script type="text/javascript"> document.write("初學(xué)JavaScript"); document.write("<h1>Hello,JavaScript</h1>"); </script> </body> </html>
<script>…</script>可以包含在文檔中的任何地方,只要保證這些代碼在被使用前已讀取并加載到內(nèi)存即可
JavaScript的執(zhí)行原理
網(wǎng)頁(yè)中引用JavaScript的方式
1、使用<script>標(biāo)簽
2、外部JS文件
<script src="export.js" type="text/javascript"></script>
3.直接在HTML標(biāo)簽中
<input name="btn" type="button" value="彈出消息框" onclick="javascript:alert('歡迎你');"/>
JavaScript核心語(yǔ)法:
1. 變量
①先聲明變量再賦值
var width; width=5; var - 用于聲明變量的關(guān)鍵字 width - 變量名
②同時(shí)聲明和賦值變量
var catName="皮皮"; var x, y, z=10;
③不聲明直接賦值【一般不使用】
width=5;
變量可以不經(jīng)聲明而直接使用,但這種方法很容易出錯(cuò),也很難查找排錯(cuò),不推薦使用。
2. 數(shù)據(jù)類型
①undefined:示例:var width;
變量width沒有初始值,將被賦予值undefined
②null:表示一個(gè)空值,與undefined值相等
③number:
var iNum=23; //整數(shù)
var iNum=23.0; //浮點(diǎn)數(shù)
④Boolean:true和false 但是JS會(huì)把他們解析成1;0
⑤String:一組被引號(hào)(單引號(hào)或雙引號(hào))括起來(lái)的文本 var string1="This is a string";
3. typeof運(yùn)算符
typeof檢測(cè)變量的返回值;typeof運(yùn)算符返回值如下:
①undefined:變量被聲明后,但未被賦值.
②string:用單引號(hào)或雙引號(hào)來(lái)聲明的字符串。
③boolean:true或false。
④number:整數(shù)或浮點(diǎn)數(shù)。
⑤object:javascript中的對(duì)象、數(shù)組和null。
rometheus發(fā)出告警時(shí)分為兩部分。首先,Prometheus按告警規(guī)則(rule_files配置塊)向Alertmanager發(fā)送告警(即告警規(guī)則是在Prometheus上定義的)。然后,由Alertmanager來(lái)管理這些告警,包括去重(Deduplicating)、分組(Grouping)、靜音(silencing)、抑制(inhibition)、聚合(aggregation ),最終將面要發(fā)出的告警通過電子郵件、webhook等方式將告警通知路由(route)給對(duì)應(yīng)的聯(lián)系人。
分組:就是將具有相同性質(zhì)的告警先分類,然后當(dāng)作單個(gè)通知發(fā)送出來(lái)。比如A和B兩臺(tái)主機(jī)的磁盤(CPU/內(nèi)存)使用率都在告警,則磁盤的告警就可以合并在一個(gè)通知中發(fā)送出來(lái)。可以想像某個(gè)服務(wù)部署了100個(gè)節(jié)點(diǎn),在一次升版后因?yàn)閎ug,日志中均報(bào)同樣一類錯(cuò)誤,如果不合并這類通知,那就是告警風(fēng)暴。
抑制:就是某些告警觸發(fā)后,則抑制(禁止)另一些告警。比如收到一條告警提示集群故障無(wú)法訪問,那么在該集群上的所有其他警告應(yīng)該被抑制。
靜音(默):將某些在預(yù)期內(nèi)的告警設(shè)置為靜默(即不發(fā)送告警)。靜默是基于配置匹配規(guī)則。Alertmanager會(huì)檢查從Prometheus推送過來(lái)的告警事件,看這些告警事件是否與配置的靜默規(guī)則能匹配上,如果匹配則不發(fā)送任何通知。配置靜默方法是在Alertmanager的Web界面中,也可以使用amtool工具。
總之:Alertmanager制定這一系列規(guī)則目的只有一個(gè),就是提高告警質(zhì)量。
配置Alertmanager來(lái)做告警通知主要分三個(gè)步驟:
一、安裝并配置Alertmanager
# tar -xvf alertmanager-0.20.0.linux-amd64.tar.gz
mv alertmanager-0.20.0.linux-amd64 /usr/local/alertmanager
# cat alertmanager.yml
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.mxhichina.com:465'
smtp_from: 'noreply@demo.com'
smtp_auth_username: 'noreply@demo.com'
smtp_auth_password: '1235698'
smtp_require_tls: false
templates:
- '/usr/local/alertmanager/template/default.tmpl'
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 2m
repeat_interval: 10m
receiver: 'email'
# continue default is false
continue: true
receivers:
- name: 'email'
email_configs:
- to: 'cookingit222@163.com,itcooking222@163.com'
send_resolved: true
html: '{{ template "default.html" . }}'
headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
alertmanager配置簡(jiǎn)要說明:
global:全局配置,主要配置告警方式,如郵件、webhook等。
route:Prometheus的告警先是到達(dá)alertmanager的根路由(route),alertmanager的根路由不能包含任何匹配項(xiàng),因?yàn)楦酚墒撬懈婢娜肟邳c(diǎn)。另外,根路由需要配置一個(gè)接收器(receiver),用來(lái)處理那些沒有匹配到任何子路由的告警(如果沒有配置子路由,則全部由根路由發(fā)送告警),即缺省接收器。告警進(jìn)入到根route后開始遍歷子route節(jié)點(diǎn),如果匹配到,則將告警發(fā)送到該子route定義的receiver中,然后就停止匹配了。因?yàn)樵趓oute中continue默認(rèn)為false,如果continue為true,則告警會(huì)繼續(xù)進(jìn)行后續(xù)子route匹配。如果當(dāng)前告警仍匹配不到任何的子route,則該告警將從其上一級(jí)(匹配)route或者根route發(fā)出(按最后匹配到的規(guī)則發(fā)出郵件)。
group_by:用于分組聚合,對(duì)告警通知按標(biāo)簽(label)進(jìn)行分組,將具有相同標(biāo)簽或相同告警名稱(alertname)的告警通知聚合在一個(gè)組,然后作為一個(gè)通知發(fā)送。如果想完全禁用聚合,可以設(shè)置為group_by: [...]
group_wait: 當(dāng)一個(gè)新的告警組被創(chuàng)建時(shí),需要等待'group_wait'后才發(fā)送初始通知。這樣可以確保在發(fā)送等待前能聚合更多具有相同標(biāo)簽的告警,最后合并為一個(gè)通知發(fā)送。
group_interval: 當(dāng)?shù)谝淮胃婢ㄖl(fā)出后,在新的評(píng)估周期內(nèi)又收到了該分組最新的告警,則需等待'group_interval'時(shí)間后,開始發(fā)送為該組觸發(fā)的新告警,可以簡(jiǎn)單理解為,group就相當(dāng)于一個(gè)通道(channel)。
repeat_interval: 告警通知成功發(fā)送后,若問題一直未恢復(fù),需再次重復(fù)發(fā)送的間隔。
查看你的告警路由樹,將alertmanager.yml配置文件復(fù)制到對(duì)話框,然后點(diǎn)擊"Draw Routing Tree"
https://www.prometheus.io/webtools/alerting/routing-tree-editor/
修改好配置文件后,可以使用amtool工具檢查配置
# ./amtool check-config alertmanager.yml
Checking 'alertmanager.yml' SUCCESS
# cat >/usr/lib/systemd/system/alertmanager.service <<EOF
[Unit]
Description=alertmanager
[Service]
ExecStart=/usr/local/alertmanager/alertmanager --config.file=/usr/local/alertmanager/alertmanager.yml --storage.path=/usr/local/alertmanager/data --web.listen-address=:9093 --data.retention=120h
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
# systemctl enable alertmanager
# systemctl restart alertmanager
alertmanager默認(rèn)運(yùn)行端口是:9093
alertmanager也可以同prometheus一樣熱加載配置
1)向alertmanager進(jìn)程發(fā)送SIGHUP信號(hào),如:kill -SIGHUP alertmanager_pid
2)curl -X POST http://prometheus_ip:9093/-/reload
二、修改prometheus的配置,關(guān)聯(lián)Alertmanager服務(wù),同時(shí)添加對(duì)alertmanager的監(jiān)控
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- monitor01:9093
rule_files:
- "rules/*_rules.yml"
- "rules/*_alerts.yml"
......
- job_name: 'alertmanager'
static_configs:
- targets: ['localhost:9093']
labels:
app: alertmanager
三、修改prometheus的配置,添加記錄規(guī)則和告警規(guī)則。
# cat node_rules.yml
groups:
- name: node_rules
#interval: 15s
rules:
- record: instance:node_cpu_usage
expr: 100 - avg(irate(node_cpu_seconds_total{mode="idle"}[1m])) by (nodename) * 100
labels:
metric_type: CPU_monitor
- record: instance:node_1m_load
expr: node_load1
labels:
metric_type: load1m_monitor
- record: instance:node_mem_usage
expr: 100 - (node_memory_MemAvailable_bytes)/(node_memory_MemTotal_bytes) * 100
labels:
metric_type: Memory_monitor
- record: instance:node_root_partition_predit
expr: round(predict_linear(node_filesystem_free_bytes{device="rootfs",mountpoint="/"}[2h],12*3600)/(1024*1024*1024), 1)
labels:
metric_type: root_partition_monitor
# cat node_alerts.yml
groups:
- name: node_alerts
rules:
- alert: cpu_usage_over_threshold
expr: instance:node_cpu_usage > 80
for: 1m
labels:
severity: warning
annotations:
summary: 主機(jī) {{ $labels.nodename }} 的 CPU使用率持續(xù)1分鐘超出閾值,當(dāng)前為 {{humanize $value}} %
- alert: system_1m_load_over_threshold
expr: instance:node_1m_load > 20
for: 1m
labels:
severity: warning
annotations:
summary: 主機(jī) {{ $labels.nodename }} 的 1分負(fù)載超出閾值,當(dāng)前為 {{humanize $value}}
- alert: mem_usage_over_threshold
expr: instance:node_mem_usage > 80
for: 1m
annotations:
summary: 主機(jī) {{ $labels.nodename }} 的 內(nèi)存 使用率持續(xù)1分鐘超出閾值,當(dāng)前為 {{humanize $value}} %
- alert: root_partition_usage_over_threshold
expr: instance:node_root_partition_predit < 60
for: 1m
annotations:
summary: 主機(jī) {{ $labels.nodename }} 的 根分區(qū) 預(yù)計(jì)在12小時(shí)使用將達(dá)到 {{humanize $value}}GB,超出當(dāng)前可用空間,請(qǐng)及時(shí)擴(kuò)容!
for 表示告警持續(xù)的時(shí)長(zhǎng),若持續(xù)時(shí)長(zhǎng)小于該時(shí)間就不發(fā)給alertmanager了,大于該時(shí)間再發(fā)。for的值不要小于prometheus中的scrape_interval,例如scrape_interval為30s,for為15s,如果觸發(fā)告警規(guī)則,則再經(jīng)過for時(shí)長(zhǎng)后也一定會(huì)告警,這是因?yàn)樽钚碌亩攘恐笜?biāo)還沒有拉取,在15s時(shí)仍會(huì)用原來(lái)值進(jìn)行計(jì)算。另外,要注意的是只有在第一次觸發(fā)告警時(shí)才會(huì)等待(for)時(shí)長(zhǎng)。
例如:10:43:00 觸發(fā)了集群A中的h1告警;10:43:10又觸發(fā)了集群A中的h2告警。則在10:44:10后發(fā)生一條告警是只包含h1的郵件,在10:44:20時(shí)會(huì)收到h1和h2聚合后的告警郵件,若h1和h2持續(xù)未恢復(fù),則在repeat_interval后仍以聚合方式發(fā)送告警。
注:h1和h2告警名相同,只是在不同主機(jī)上。
完成上述配置后,主機(jī)CPU、負(fù)載、內(nèi)存、磁盤超出閾值時(shí)就發(fā)觸發(fā)郵件告警。
上述配置僅是對(duì)主機(jī)資源做了監(jiān)控,并且告警只發(fā)到了缺省聯(lián)系人組。設(shè)想一下,在實(shí)際生產(chǎn)環(huán)境中,可能會(huì)按產(chǎn)品線或業(yè)務(wù)功能進(jìn)行分組來(lái)研發(fā),不同的服務(wù)出現(xiàn)告警時(shí)只發(fā)送通知到對(duì)應(yīng)的聯(lián)系人組,其他不相關(guān)的組不需要接告警收通知,這就需要我們修改告警路由規(guī)則,而Alertmanager的核心和最復(fù)雜的地方就在告警路由規(guī)則的設(shè)置上。
Prometheus的告警規(guī)則在推送給Alertmanager前有的三種狀態(tài):
1、沒有觸發(fā)告警閾值時(shí),狀態(tài)為:inactive
2、觸發(fā)了告警閾值但未滿足告警持續(xù)時(shí)間(for)時(shí),狀態(tài)為:pending,如果不配置for或者指定for的值為0,則將跳過pending狀態(tài)。
3、已觸發(fā)告警閾值并達(dá)到告警持續(xù)時(shí)間,開始將告警事件推送到Alertmanager,此時(shí)狀態(tài)為:firing。
配置告警靜默(silence),用于在預(yù)期內(nèi)的維護(hù)升級(jí)等操作。
當(dāng)我們?cè)趯?duì)系統(tǒng)進(jìn)行維護(hù)升級(jí)時(shí),通常不希望觸發(fā)告警通知;另外,當(dāng)上游服務(wù)出現(xiàn)異常,想讓下游的服務(wù)不觸發(fā)告警,Prometheus將用于這種配置稱為silence(靜默)。silence用于設(shè)定一個(gè)(維護(hù))時(shí)間段,如1小時(shí),也可提前手動(dòng)觸發(fā)silence過期,表示維護(hù)結(jié)束,恢復(fù)對(duì)應(yīng)服務(wù)的告警通知功能。
設(shè)置silence的方式有以下2種:
1、登錄到alertmanager的控制臺(tái)操作
2、通過amtool命令進(jìn)行操作
配置告警模板
Alertmanager的通知模板是基于Go模板系統(tǒng),詳細(xì)可參考官網(wǎng)。
https://golang.org/pkg/text/template/
https://prometheus.io/docs/alerting/latest/notifications/#alert
https://prometheus.io/docs/prometheus/latest/configuration/template_reference/
模板配置步驟:
1、修改alertmanager.yml,配置模板地址,然后在每個(gè)receiver引用模板
templates:
- '/usr/local/alertmanager/template/default.tmpl'
...
...
html: '{{ template "default.html" . }}'
headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }
2、配置模板
default.tmpl
{{ define "default.html" }}
{{- if gt (len .Alerts.Firing) 0 -}}
[{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]
{{ range $i, $alert :=.Alerts }}
<pre>
告警節(jié)點(diǎn):{{ index $alert.Labels "nodename" }}
告警服務(wù):{{ index $alert.Labels "alertname" }}
報(bào)警詳情:{{ index $alert.Annotations "summary" }}
開始時(shí)間:{{ $alert.StartsAt }}
</pre>
{{ end }}
{{ end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
[{{ .Status | toUpper }}:{{ .Alerts.Resolved | len }}]
{{ range $i, $alert :=.Alerts }}
<pre>
恢復(fù)節(jié)點(diǎn):{{ index $alert.Labels "nodename" }}
恢復(fù)服務(wù):{{ index $alert.Labels "alertname" }}
狀 態(tài):{{ index $alert.Status }}
開始時(shí)間:{{ $alert.StartsAt }}
恢復(fù)時(shí)間:{{ $alert.EndsAt }}
</pre>
{{ end }}
{{ end }}
{{- end }}
注:告警模板如果配置有問題,會(huì)導(dǎo)致郵件發(fā)送失敗,注意觀察日志。
以下是靜默設(shè)置步驟:
1、登錄到alertmanager控制臺(tái)(http://IP:9093/#/alerts),點(diǎn)擊上方菜單欄中Alerts,可看到當(dāng)前有2個(gè)告警。
2、點(diǎn)擊右上角"New Silence",創(chuàng)建Silence任務(wù),具體設(shè)置如下,匹配規(guī)則支持正則。
注:可按instance或job_name等方式來(lái)進(jìn)行正則匹配。
3、Silence創(chuàng)建成功后,可以看到處于Active狀態(tài)的Silence
總之,多實(shí)踐。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。