Vue 开发基础(下)
组件进阶
组件是对结构的抽象,组件可复用性很强,每个组件都有自己的作用域,区域之间独立工作互不影响,从而降低了代码的耦合度。Vue 还刻有对组件的选项轻松完成合并,让组件的功能变得灵活,使用起来更加方便。
mixins
分发 Vue 组件中科复用功能的方式。其对象可以包含任何组件选项,当前组件使用 mixins 时,将定义的 mixins 对象引入组件中即可使用,mixins 中的所有选项会混入到组件自己的选项中。
<script> // 定义 myMixins 对象 var myMixin = { created() { this.hello() }, methods: { hello() { console.log('hello from mixin!') } } } var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() </script>
经过 mixins 混合之后会发生组件选项重用,为解决这一问题,mixins 提供了相应的合并测策略
<script> var mixin = { data() { return {msg: 'hello'} } } var vm = new Vue({ mixins: [mixin], // 如果该实例中没有数据的建立,则打印的值将会是混入的 mixin 的数据值 data() { return { msg: 'goodbye' } }, created() { console.log((this.$data.msg)) } }) </script>
mixin 中的钩子函数将在组件自己的钩子函数之前调用
<script> var mixin = { created() { console.log('mixin 钩子函数调用') } } var vm = new Vue({ mixins: [mixin], created() { console.log(' 组件钩子函数调用 ') } }) </script>
rend
使用 Vue.render() 实现对虚拟 DOM 的操作。在 Vue 中一般使用 template 来创建 HTML ,但是可编程性不强,而使用 Vue.render() 可以更好地发挥 JavaScript 的编程能力
<body> <div id="app"> <my-component>成功渲染</my-component> </div> </body> <script> Vue.component('myComponent', { // 定义渲染函数 render(createElement) { // 参数 p 等同于 标签 p return createElement('p', { style: { color: 'red', fontsize: '16px', backgroundColor: '#eee' } // 插槽 使得 “成功渲染” 可以显示 },this.$slots.default) } }) var vm = new Vue({ el: '#app' }) </script>
createElement
// wrapper function for providing a more flexible interface // without getting yelled at by flow export function createElement ( context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean ): VNode | Array<VNode> { if (Array.isArray(data) || isPrimitive(data)) { normalizationType = children children = data data = undefined } if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE } return _createElement(context, tag, data, children, normalizationType) }
createElement
方法实际上是对 _createElement
方法的封装,它允许传入的参数更加灵活,在处理这些参数后,调用真正创建 VNode 的函数 _createElement
export function _createElement ( context: Component, tag?: string | Class<Component> | Function | Object, data?: VNodeData, children?: any, normalizationType?: number ): VNode | Array<VNode> { if (isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV !== 'production' && warn( `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` + 'Always create fresh vnode data objects in each render!', context ) return createEmptyVNode() } // object syntax in v-bind if (isDef(data) && isDef(data.is)) { tag = data.is } if (!tag) { // in case of component :is set to falsy value return createEmptyVNode() } // warn against non-primitive key if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.key) && !isPrimitive(data.key) ) { if (!__WEEX__ || !('@binding' in data.key)) { warn( 'Avoid using non-primitive value as key, ' + 'use string/number value instead.', context ) } } // support single function children as default scoped slot if (Array.isArray(children) && typeof children[0] === 'function' ) { data = data || {} data.scopedSlots = { default: children[0] } children.length = 0 } if (normalizationType === ALWAYS_NORMALIZE) { children = normalizeChildren(children) } else if (normalizationType === SIMPLE_NORMALIZE) { children = simpleNormalizeChildren(children) } let vnode, ns if (typeof tag === 'string') { let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) if (config.isReservedTag(tag)) { // platform built-in elements vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { // component vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) } if (Array.isArray(vnode)) { return vnode } else if (isDef(vnode)) { if (isDef(ns)) applyNS(vnode, ns) if (isDef(data)) registerDeepBindings(data) return vnode } else { return createEmptyVNode() } }
_createElement
方法有 5 个参数,context
表示 VNode 的上下文环境,它是 Component
类型;tag
表示标签,它可以是一个字符串,也可以是一个 Component
;data
表示 VNode 的数据,它是一个 VNodeData
类型,可以在 flow/vnode.js
中找到它的定义,这里先不展开说;children
表示当前 VNode 的子节点,它是任意类型的,它接下来需要被规范为标准的 VNode 数组;normalizationType
表示子节点规范的类型,类型不同规范的方法也就不一样,它主要是参考 render
函数是编译生成的还是用户手写的。
<body> <div id="app"> <my-component> <template v-slot:header> <div style="background-color: #ccc; height: 50px"> 导航栏 </div> </template> <template v-slot:content> <div style="background-color: #ddd; height: 50px"> 信息栏 </div> </template> <template v-slot:footer> <div style="background-color: #eee; height: 50px"> 底部信息 </div> </template> </my-component> </div> </body> <script> Vue.component('myComponent', { // 定义渲染函数 render(createElement) { return createElement('div', [ createElement('header', this.$slots.header), createElement('content', this.$slots.content), createElement('footer', this.$slots.footer) ]) } }) var vm = new Vue({ el: '#app' }) </script>