vue组件略佳操作

button组件

亮点:prop接收参数


使⽤组件


 

mixins

如果你的项⽬⾜够复杂,或需要多⼈协同开发时,在 app.vue ⾥会 写⾮常多的代码,多到结构复杂难以维护。这时可以使⽤ Vue.js 的 混合 mixins,将不同的逻辑分开到不同的 js ⽂件⾥。
user.js

export default { 
data () { return { userInfo: null } },

methods: { 

getUserInfo(){ 
        $.ajax('/user/info', (data) => { this.userInfo = data; }); } },

 mounted () {

 this.getUserInfo();

 } }

然后在 app.vue 中混合: app.vue:

跟⽤户信息相关的逻辑,都可以在 user.js ⾥维护

$on 与 $emit

$emit 会在当前组件实例上触发⾃定义事件,并传递⼀些参数给监 听器的回调,⼀般来说,都是在⽗级调⽤这个组件时,使⽤ @on 的 ⽅式来监听⾃定义事件的,⽐如在⼦组件中触发事件:$on 监听了⾃⼰触发的⾃定义事件 test,因为有时不确定何时会触 发事件,⼀般会在 mounted 或 created 钩⼦中来监听。
子组件

 methods: {

 handleEmitEvent() {

 this.$emit('test', 'Hello Vue.js')

 },

 },

父组件

 mounted() {

 this.$on('test', (text) => {

 window.alert(text)

 })

 },

⾃⾏实现 dispatch 和 broadcast ⽅法

*思路:
在⼦组件调⽤ dispatch ⽅法,向上级指定的组件实例(最近 的)上触发⾃定义事件,并传递数据,且该上级组件已预先通 过 $on 监听了这个事件; 
相反,在⽗组件调⽤ broadcast ⽅法,向下级指定的组件实例 (最近的)上触发⾃定义事件,并传递数据,且该下级组件已 预先通过 $on 监听了这个事件。*
该⽅法可能在很多组件中都会使⽤,复⽤起⻅,我们封装在混合(mixins)⾥。那它的使⽤样例可能是这样的:
有 A.vue 和 B.vue 两个组件,其中 B 是 A 的⼦组件,中间可能跨多级,在 A 中向 B 通信:





// B.vue 

export default { 

 name: 'componentB', 

 created () { 

 this.$on('on-message', this.showMessage);

 },

 methods: {

 showMessage (text) { window.alert(text); 

 } } }

在独⽴组件 (库)⾥,每个组件的 name 值应当是唯⼀的,name 主要⽤于递归 组件

emitter.js
function broadcast(componentName, eventName, params) {

this.$children.forEach(child => { 

 const name = child.$options.name; 

 if (name ===componentName) { 

 child.$emit.apply(child, [eventName].concat(params)); 

 } else {

 broadcast.apply(child, [componentName, eventName].concat([params])); 

 } })

 }
 
export default { 

 methods: { 

 dispatch(componentName, eventName, params) { 

 let parent = this.$parent || this.$root;

 let name = parent.$options.name; 

 while (parent && (!name || name !== componentName)) { 

 parent = parent.$parent; if (parent) { 

 name = parent.$options.name; 

 } }

 if (parent) { 

 parent.$emit.apply(parent, [eventName].concat(params)); 

 } },

 broadcast(componentName, eventName, params) {

 broadcast.call(this, componentName, eventName, params); 

 }

} };

同理,如果是 B 向 A 通信,在 B 中调⽤ dispatch ⽅法,在 A 中使 ⽤ $on 监听事件即可。


因为是⽤作 mixins 导⼊,所以在 methods ⾥定义的 dispatch 和 broadcast ⽅法会被混合到组件⾥,⾃然就可以⽤ this.dispatch 和 this.broadcast 来使⽤。 

这两个⽅法都接收了三个参数,第⼀个是组件的 name 值,⽤于向上 或向下递归遍历来寻找对应的组件,第⼆个和第三个就是上⽂分析的 ⾃定义事件名称和要传递的数据。 可以看到,在 dispatch ⾥,通过 while 语句,不断向上遍历更新当 前组件(即上下⽂为当前调⽤该⽅法的组件)的⽗组件实例(变量 parent 即为⽗组件实例),直到匹配到定义的 componentName 与 某个上级组件的 name 选项⼀致时,结束循环,并在找到的组件实例 上,调⽤ $emit ⽅法来触发⾃定义事件 eventName。broadcast ⽅法与之类似,只不过是向下遍历寻找。


*相⽐ Vue.js 1.x,有以下不同: 
需要额外传⼊组件的 name 作为第⼀个参数; 
⽆冒泡机制;
第三个参数传递的数据,只能是⼀个(较多时可以传⼊⼀个对 象),⽽ Vue.js 1.x 可以传⼊多个参数,当然,你对 emitter.js 稍作修改,也能⽀持传⼊多个参数,只是⼀般场景 传⼊⼀个对象⾜以*

组件的通信找到任意组件实例 ——findComponents 系列⽅法

是组件通信的终极⽅案。通过递归、遍历,找到指定组件的 name 选项 匹配的组件实例并返回。 findComponents 系列⽅法最终都是返回组件的实例,进⽽可以读 取或调⽤该组件的数据和⽅法
*它适⽤于以下场景: 
由⼀个组件,向上找到最近的指定组件;
由⼀个组件,向上找到所有的指定组件; 
由⼀个组件,向下找到最近的指定组件; 
由⼀个组件,向下找到所有指定的组件;
由⼀个组件,找到指定组件的兄弟组件。*

utils/assits.js

// 由⼀个组件,向上找到最近的指定组件
//context  上下文   
//componentName  组件名称
 function findComponentUpward (context, componentName) { 

 let parent = context.$parent; 

 let name = parent.$options.name; 

 while (parent && (!name || [componentName].indexOf(name) < 0)) { 

 parent = parent.$parent; 

 if (parent) name = parent.$options.name; 

 }

 return parent; 

}


 // 由⼀个组件,向上找到所有的指定组件

 function findComponentsUpward(context, componentName) {

 let parents = []

 const parent = context.$parent

 if (parent) {

 if (parent.$options.name === componentName) parents.push(parent)

 return parents.concat(findComponentsUpward(parent, componentName))

 } else {

 return []

 }

 },
 
  // 由⼀个组件,向下找到最近的指定组件

 function findComponentDownward(context, componentName) {
//context.$children 得到的是当前组件的全部⼦组件
 const childrens = context.$children

 let children = null

 if (childrens.length) {

 for (const child of childrens) {

 const name = child.$options.name

 if (name === componentName) {

 children = child

 break

 } else {

 children = findComponentDownward(child, componentName)

 if (children) break

 }

 }

 }

 return children

 },
 
 
 // 由⼀个组件,向下找到所有指定的组件
//使⽤ reduce 做累加器,为数组中的每一个元素依次执行回调函数,并 ⽤递归将找到的组件合并为⼀个数组并返回,
 function findComponentsDownward(context, componentName) {

 return context.$children.reduce((components, child) => {

 if (child.$options.name === componentName) components.push(child)

 const foundChilds = findComponentsDownward(child, componentName)

 return components.concat(foundChilds)

 }, [])

 },
 
 
 
 // 由⼀个组件,找到指定组件的兄弟组件

 function findBrothersComponents(context, componentName, exceptMe = true) {
//⽗组件的 全部⼦组件,这⾥⾯当前包含了本身
//exceptMe  true是不包括自己
 let res = context.$parent.$children.filter((item) => {

 return item.$options.name === componentName

 })
// Vue.js 在渲染组件时,都会给每个组件加⼀个内置的属 性 _uid,这个 _uid 是不会重复的,借此我们可以从⼀系列兄弟组 件中把⾃⼰排除掉
 let index = res.findIndex((item) => item._uid === context._uid)

 if (exceptMe) res.splice(index, 1)

 return res

 },
export { findComponentUpward,findComponentsUpward,findComponentDownward,findComponentsDownward,findBrothersComponents };

使用(A 是 B 的⽗组件)








组合多选框组件—— CheckboxGroup & Checkbox

checkbox.vue
updateModel⽅法在 Checkbox ⾥的 mounted 初始化时调⽤。这 个⽅法的作⽤就是在 CheckboxGroup ⾥通过 findComponentsDownward ⽅法找到所有的 Checkbox,然后把 CheckboxGroup 的 value,赋值给 Checkbox 的 model,并根 据 Checkbox 的 label,设置⼀次当前 Checkbox 的选中状态。 这样⽆论是由内⽽外选择,或由外向内修改数据,都是双向绑定的, ⽽且⽀持动态增加 Checkbox 的数量。





checkbox-group.vue





Vue的构造器extend 与⼿ 动挂载$mount

Vue 的$mount()为手动挂载,在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作、判断等),之后要手动挂载上。new Vue时,el和$mount并没有本质上的不同。

创建⼀个 Vue 实例时,都会有⼀个选项 el,来指定 实例的根节点,如果不写 el 选项,那组件就处于未挂载状 态。Vue.extend 的作⽤,就是基于 Vue 构造器,创建⼀个“⼦ 类”,它的参数跟 new Vue 的基本⼀样,但 data 要跟组件⼀样, 是个函数,再配合 $mount ,就可以让组件渲染,并且挂载到任意 指定的节点上,⽐如 body

import Vue from 'vue'

//创建了⼀个构造器,这个过程就可以解决异步获取 template 模板的问题
const AlertComponent = Vue.extend({

 template: '
{{ message }}
', data() { return { message: 'Hello, Aresn' } }, })

⼿动渲染组件,并把它挂载到 body 下:

const component = new AlertComponent().$mount();
//$mount ⽅法对组件进⾏了⼿动渲染,但它仅 仅是被渲染好了,并没有挂载到节点上,也就显示不了组件。此时的 component 已经是⼀个标准的 Vue 组件实例,因此它的 $el 属性 也可以被访问:
document.body.appendChild(component.$el);

$mount 也有⼀些快捷的挂载⽅式,以下两种都是可以的:

 在 $mount ⾥写参数来指定挂载的节点

 new AlertComponent().$mount('#app'); 

 不⽤ $mount,直接在创建实例时指定 el 选项

 new AlertComponent({ el: '#app' });

实现同样的效果,除了⽤ extend 外,也可以直接创建 Vue 实例, 并且⽤⼀个 Render 函数来渲染⼀个 .vue ⽂件

import Vue from 'vue'

import Notification from './notification.vue'

const props = {} // 这⾥可以传⼊⼀些组件的 props 选 项

const Instance = new Vue({

 render(h) {

 return h(Notification, { props: props })

 },

})

const component = Instance.$mount()

document.body.appendChild(component.$el)

渲染后, 操作 Render 的 Notification 实例

const notification = Instance.$children[0];
//因为 Instance 下只 Render 了 Notification ⼀个⼦组件,所以可以 ⽤ $children[0] 访问到。

⽤ $mount ⼿动渲染的组件,如果要销毁, 也要⽤ $destroy 来⼿动销毁实例,必要时,也可以⽤ removeChild 把节点从 DOM 中移除。

动态渲染 .vue ⽂件的组件—— Display

⼀个常规的 .vue ⽂件⼀般都会包含 3个部分: