组件定义
SFC 单文件组件
SFC
字符串模板或 ES6 模板字面量
Vue.component('my-btn', { template: `
`, data() { return { count: 0, }; }, methods: { handleClick() { this.count++;
console.log('clicked', this.count); }, }, });
渲染函数
Vue.component('my-btn', { data() { return { count: 0, }; }, methods: {
handleClick() { this.count++; console.log('clicked', this.count); }, },
render(h) { return h( 'button', { attrs: { class: 'btn-primary', }, on: { click:
this.handleClick, }, }, this.$slots.default ); }, });
JSX 语法糖
Vue.component('my-btn', { data() { return { text: 'Click me', }; }, methods: {
handleClick() { console.log('clicked'); }, }, render() { return (
); }, });
Vue 类组件装饰器
参考
- https://vuejs.org/v2/guide/single-file-components.html
- https://vuejs.org/v2/guide/render-function.html
- https://medium.com/js-dojo/7-ways-to-define-a-component-template-in-vuejs-c04e0c72900d
- https://codewithhugo.com/writing-multiple-vue-components-in-a-single-file/
组件通信
Prop属性和事件
vue组件基本上遵循单向数据流原则,即数据通过props向下传递子组件,子组件向上触发事件。
props数据只读,因此无法子组件无法改变props。 当props变化时,子组件会自动重新渲染。(props是响应式数据源)
子组件只能向父组件触发事件,父组件接收事件,改变映射至子组件的props数据。
参考
https://vuejs.org/v2/guide/components-props.html
https://alligator.io/vuejs/component-communication/
https://www.smashingmagazine.com/2017/08/creating-custom-inputs-vue-js/
https://vegibit.com/vue-sibling-component-communication/
https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87
https://gambardella.info/2017/09/13/vue-js-communication-part-2-parent-child-components/
组件事件处理
参考
- https://vuejs.org/v2/guide/components-custom-events.html
- https://itnext.io/leveraging-vue-events-to-reduce-prop-declarations-e38f5dce2aaf
- https://alligator.io/vuejs/component-event-hooks/
- https://alligator.io/vuejs/global-event-bus/
- https://medium.com/@jesusgalvan/vue-js-event-bus-promises-f83e73a81d72
组件条件渲染
指令
v-if
Render only if v-if condition is true
v-if
和 v-else
Render only if v-if condition is true
Render only if v-if condition is false
v-else-if
Render only if `type` is equal to `A`
Render only if `type` is equal to `B`
Render only if `type` is equal to `C`
Render if `type` is not `A` or `B` or `C`
v-show
Always rendered, but it should be visible only if `v-show` conditions is true
如果想条件渲染多个元素,可以在模板template
元素上使用条件指令。
注意模板元素仅仅是隐形包装,不会渲染成DOM。
All the elements
will be rendered into DOM
except `template` element
渲染函数或JSX语法糖
如果你在vue应用中使用渲染函数或jsx
, 可以直接使用 if-else
和switch-case
语句以及三元和逻辑运算符。
if-else
语句
export default {
data() {
return {
isTruthy: true,
};
},
render(h) {
if (this.isTruthy) {
return Render value is true
;
} else {
return Render value is false
;
}
},
};
switch case
语句
import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';
export default {
data() {
return {
type: 'error',
};
},
render(h) {
switch (this.type) {
case 'info':
return ;
case 'warning':
return ;
case 'error':
return ;
default:
return ;
}
},
};
或者你可以使用map
对象简化switch-case
import Info from './Info';
import Warning from './Warning';
import Error from './Error';
import Success from './Success';
const COMPONENT_MAP = {
info: Info,
warning: Warning,
error: Error,
success: Success,
};
export default {
data() {
return {
type: 'error',
};
},
render(h) {
const Comp = COMPONENT_MAP[this.type || 'success'];
return ;
},
};
三元
运算符
export default {
data() {
return {
isTruthy: true,
};
},
render(h) {
return (
{this.isTruthy ? (
Render value is true
) : (
Render value is false
)}
);
},
};
逻辑运算符
export default {
data() {
return {
isLoading: true,
};
},
render(h) {
return {this.isLoading && Loading ...
};
},
};
参考
https://vuejs.org/v2/guide/conditional.html
https://dzone.com/articles/difference-between-v-if-and-v-show-with-a-video
动态组件
带有is属性组件
上例中,如果有不同组件渲染进入component
组件,渲染过的组件将会销毁。
如果你想要component
标签内部实例不被销毁,可以在外面再包一层keep-alive
:
参考
https://vuejs.org/v2/guide/components.html#Dynamic-Components
https://vuejs.org/v2/guide/components-dynamic-async.html
https://medium.com/scrumpy/dynamic-component-templates-with-vue-js-d9236ab183bb
组件组合
第三方库
Proppy-组件函数式组合方案
基础组合
参考
https://vuejs.org/v2/guide/#Composing-with-Components
扩展
当你想扩展单文件组件时可以使用扩展:
参考
https://vuejs.org/v2/api/#extends
https://medium.com/js-dojo/extending-vuejs-components-42fefefc688b
混入
// closableMixin.js
export default {
props: {
isOpen: {
default: true,
},
},
data: function() {
return {
shown: this.isOpen,
};
},
methods: {
hide: function() {
this.shown = false;
},
show: function() {
this.shown = true;
},
toggle: function() {
this.shown = !this.shown;
},
},
};
{{ text }}
参考
https://vuejs.org/v2/guide/mixins.html
http://www.qcode.in/practical-use-of-components-and-mixins-in-vue-js/
默认插槽
/* VBtn */
Login
参考
https://vuejs.org/v2/guide/components-slots.html#Slot-Content
https://alligator.io/vuejs/component-slots/
https://alligator.io/web-components/composing-slots-named-slots/
https://alligator.io/vuejs/vue-abstract-components/
命名插槽
基础布局文件
/* base-layout */
应用文件
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
参考
https://vuejs.org/v2/guide/components-slots.html#Named-Slots
https://medium.com/@fdietz/vue-js-component-composition-with-slots-eda311579218
域插槽
-
{{ todo.text }}
✓ {{ todo.text }}
参考
https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots
https://medium.com/js-dojo/getting-your-head-around-vue-js-scoped-slots-281bf82a1e4e
https://medium.com/corebuild-software/understanding-scoped-slots-in-vue-js-db5315a42391
https://alligator.io/vuejs/scoped-component-slots/
https://adamwathan.me/the-trick-to-understanding-scoped-slots-in-vuejs/
https://pineco.de/power-scoped-slots-vue/
https://medium.com/@tkwebdev/building-a-list-keyboard-control-component-with-vue-js-and-scoped-slots-c74db4fcf84f
渲染属性
大部分场景可以用域插槽替代渲染属性,但有些场景渲染属性更有用。
使用SFC
使用JSX
const Mouse = {
name: 'Mouse',
props: {
render: {
type: Function,
required: true,
},
},
data() {
return {
x: 0,
y: 0,
};
},
methods: {
handleMouseMove(event) {
this.x = event.clientX;
this.y = event.clientY;
},
},
render(h) {
return (
{this.$props.render(this)}
);
},
};
export default Mouse;
参考
https://vuejs.org/v2/guide/render-function.html
https://medium.com/@dillonchanis/leveraging-render-props-in-vue-7eb9a19c262d
https://medium.com/js-dojo/use-a-vue-js-render-prop-98880bc44e05
https://medium.com/js-dojo/using-react-style-callback-props-with-vue-pros-and-cons-e0ee7455695b
传递 Props 和监听器
有时需要向子组件传递属性和监听器,但不需要声明全部属性。
你可以在子组件中绑定$attrs
和$listeners
,设置inheritAttrs
为false
,
外部div和子组件都会接收到设置的attributes。
传递Props
{{title}}
父组件
参考
https://zendev.com/2018/05/31/transparent-wrapper-components-in-vue.html
高阶组件
参考
- https://medium.com/bethink-pl/higher-order-components-in-vue-js-a79951ac9176
- https://medium.com/bethink-pl/do-we-need-higher-order-components-in-vue-js-87c0aa608f48
- https://medium.com/tldr-tech/higher-order-components-in-vue-js-38b500c6d49f
依赖注入
Vue支持 Provide和Inject机制为所有子代组件提供对象。
不管组件层次有多深,只要都在共同父组件下,机制都可以发挥作用。
注意provide
和inject
绑定都不是响应式的,除非传递可观察对象。
根据上例中组件层次,为了从父组件中获取数据,你应该将数据对象作为props
传递给
子组件和孙组件。但是如果父组件提供
数据对象,孙组件仅定义注入
父组件提供的数据对象可以直接获取数据。
参考
- https://vuejs.org/v2/api/#provide-inject
- https://vuejs.org/v2/guide/components-edge-cases.html#Dependency-Injection
- https://alligator.io/vuejs/component-communication/#provide--inject
- https://blog.kloud.com.au/2017/03/22/dependency-injection-in-vuejs-app-with-typescript/
Provide和Inject API
提示: 可以使用vue属性装饰器的
@Provide
和@Inject
。
主题提供者
带主题按钮
Themed Button
错误处理
errorCaptured 钩子函数
错误边界
抛出错误
参考
- https://medium.com/@dillonchanis/handling-errors-in-vue-with-error-boundaries-91f6ead0093b
- https://jsfiddle.net/Linusborg/z84wspcg/
效率建议
创建时执行观察
// don't
created() {
this.fetchUserList();
},
watch: {
searchText: 'fetchUserList',
}
// do
watch: {
searchText: {
handler: 'fetchUserList',
immediate: true,
}
}
译者注
- 原文链接
- vue 技术内幕
- 原文有删减,因译者水平有限,如有错误,欢迎留言指正交流