Vue
name
声明name选项的必要性:
- 可被
$options.name
返回,允许组件模板递归地调用自身 - 便于调试。更友好的警告信息与 vue-devtools 语义的组件结构
router-view
的include/exclude值为有效的组件name
v-model
v-model
能够在表单元素上快速创建数据双向绑定(语法糖),不同表单元素代理对应input、change事件;
如果用于组件(component)上,则默认添加名为 value 的 prop 值传递,以及监听内部 input事件;
自定义model
允许在子元素内对model自定义prop与事件通信;
export default {
model: {prop: 'userProfile', event: 'submit' },
props: ['userProfile'],
methods: {
onSubmit(form) {
this.$emit('submit', form);
}
}
}
指令修饰符
- v-model.trim
- v-model.number
- v-model.lazy
computed
计算属性(由多个属性值固定计算出的属性值)
get/set控制
set(item) {
this.data.push(item);
},
get() { return this.data.map(item => item.value) }
v-for/if
当v-for
同时引用v-if
,如:
<div v-if="expression" v-for="item in list">...</div>
这会产生分歧:判断条件后决定是否跳过整个列表循环 or 循环列表时判断条件控制显示的列表项
最终实际结果取决于v-if
与v-for
的执行优先级,事实上vue确定v-for
优先级高于v-if
;但依旧会提示不可同时使用的警告信息;
解决
嵌套
<ul v-if="expression"><li v-for="item in list"></li></ul>
<template v-if="expression"><div v-for="item in list"></div></template>
<template v-for="item in list"><li v-if="expression"></li></template>
计算属性
<div v-for="item in filterList"></div>
<script>
export default {
computed: {
filterList(){
if(expression) return [];
return list.filter(expression);
}
}
}
</script>
循环前判断
<div v-for="item in list || []"></div>
attrs/listeners
attrs
父组件传递的但未被子组件prop接受的attr集合,可在内部组件上v-bind="$attrs"
实现prop穿透传递;
<template>
<div><input v-model="value" /></div>
</template>
<script>
export default {
inheritAttrs: false,
// inherinheritAttrs默认值true,即父组件传递的但未被子组件prop接受的attr会作为标签属性添加至子组件根元素上;
// false即这一默认行为失效;配合inheritAttrs: false实现更干净的穿透;
computed: {
value(){
return this.$attrs.value;
}
}
}
</script>
listeners
父组件v-on
监听的事件集合,可在内部组件上v-on="$listeners"
实现穿透监听;
$listeners实现“监听子组件下某原生元素事件特性”
<template>
<div><input v-bind="$attrs" v-on="$listeners" /></div>
</template>
watch
手动添加监听
// 监听子组件的状态
vm.$watch('$refs.elColorPicker.showPicker', function (newVal, oldVal) { ... })
provide/inject
父组件 provide
// 值为一个对象
provide: {
title: '标题',
model: { ... }
}
// 值为返回一个对象的函数
provide() {
return {
title: this.title,
claculate: this.claculate,
getList: this.list
}
}
子组件inject
// 注入
inject: ['title', 'model'];
inject: {
mainTitle: { from: 'title', default: '' },
model: { default: () => ({}) },
};
// 注入方法
inject: ['claculate', 'getList'],
computed: {
list(){ return this.getList() };
}
provide/inject的绑定值不会做响应式处理;可传入对象类型的值,借助property
响应;
或传入Function类型的值,借助computed
计算属性实现响应式;
事件总线
-
创建事件总线
Vue.property.$EventBus = new Vue()
-
监听事件总线
mounted(){ this.$EventBus.$on('add-todo', this.addTodo); }, beforeDestroy() { this.$EventBus.$off('add-todo', this.addTodo); }
-
发射事件总线的事件
this.$EventBus.$emit('delete-todo', options);
动态组件
component(元组件)作为Vue内置组件,通过设置is
决定哪个具体组件被渲染
<component
:is="currentPanelComponent"
ref="currentPanel"
v-model="editContent"
:panelInfo="currentPanel"
:viewType="0"
></component>
<script>
const panleList = [{id: 'xxx', name: 'xxx', component: panel1}]
export default {
computed: {
currentPanel(){
return panleList.find((item) => item.id === this.id) || {};
},
currentPanelComponent(){
return activatedPanel.component;
}
}
}
</script>
keep-alive
配合keep-alive缓存组件状态,避免重复渲染的不必要性能损失
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
插槽
实现组件的动态内容(VNode合成组件)
this.$slot
插槽内容
<comp>
<div>defalut</div>
<div #footer>footer</div>
// 等同于 v-slot
</comp>
// comp.vue
<div>
<h2><slot></slot></h2>
<footer><slot name="footer"></slot></footer>
</div>
解构传值
<comp>
<div>defalut</div>
<div #footer>footer</div>
// 等同于 v-slot
</comp>
// comp.vue
<div>
<h2><slot></slot></h2>
<footer><slot name="footer"></slot></footer>
</div>
异步组件
组件在被需要时异步加载所需资源并构造,声明一个函数作为异步组件引用
() => import('./my-async-component')
// 返回一个带有更多配置的异步组件
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
Component
Vue组件是可复用的Vue实例,Vue.component
创建Vue组件(component实例)方法
全局注册
// 定义一个名为 button-counter 的新全局组件
Vue.component('button-counter', {
// 没有el外,其它选项与Vue实例相同
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me 8 times.</button>'
});
在Vue根实例作为组件被使用
<div id="components-demo">
<button-counter></button-counter>
</div>
<script>
new Vue({ el: '#components-demo' })
</script>
局部注册
new Vue({
el: '#app',
components: {
'button-counter': ButtonCounter,
'component-b': ComponentB
}
})
自定义指令
自定义指令提供了一系列钩子支持对普通DOM元素进行底层操作,包括bind、inserted、update、componentUpdated、unbind;
// 指令
const burySpot = {};
burySpot.install = (Vue) => {
// bind 和 update简写
Vue.directive('bury-spot', (el, binding) => {
el.dataset.buryspot = binding.value;
el.addEventListener(binding.arg || 'click', burySpotHandle);
});
};
function burySpotHandle(e) {
// 提交埋点数据
request.post({ url: e.currentTarget.dataset.buryspot });
}
export default burySpot;
// 注册
import Vue from 'vue';
import burySpot from './bury-spot';
Vue.use(burySpot);
// 使用
<button type="button" v-bury-spot="https://wwwi.baidu.com"></button>
参数: el为DOM元素
binding:
value: 指令值, 支持任何js表达式,
oldValue: update/componentUpdated时可用,
arg: 指令参数值,
modifiers: 修饰符对象, 属性值为布尔值
常见场景:埋点、图片默认图/懒加载、权限控制
过渡动画
css过渡
.v-enter-active,v-leace-active{
transition: opacity 0.3s;
}
.v-enter, .v-leave-to{
opacity: 0;
}
css动画
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
多内容过渡
多元素过渡需添加key,多组件过渡使用动态组件;
render函数
template选项的替换项(template编译后在render中被使用,本质上基于render),内部创建虚拟节点;
render: function (createElement) {
// render内创建一个VNode
return createElement('h1', this.blogTitle)
}
createElement
相比模板语法,以更接近编译器的形式去建立DOM树
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中 attribute 对应的数据对象。可选。
{
// (详情见下一面代码)
},
// {String | Array}
// 子级虚拟节点组成的数组 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
'文本节点内容',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML attribute
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM property
domProps: {
innerHTML: 'baz'
},
// 事件监听器在 `on` 内,
// 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
// 需要在处理函数中手动检查 keyCode。
on: {
click: this.clickHandler
},
// 仅用于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽的格式为
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层 property
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
}
拓展:函数式组件
JSX
render函数支持返回jsx(类似模板语法)
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})