[TOC] >[success] # 编写自己的工具类进行父子传递 <a href='https://juejin.im/book/5bc844166fb9a05cd676ebca/section/5bc844166fb9a05cf52af65f'>文章来自AresnTalkingData 前端架构师,iView 作者 发布在掘金网小册中内容启发整理</a> 如果你有能力有钱请你购买原作者文找那个,尊敬每一个原作者是我们应该做的,不要做代码行业的伸手党 感谢这些大佬的文章,本内容是根据大佬的文章二次整理,用更通俗的理解让初学者也能看懂 ~~~ 1.准备工作,'iview' 作者喜欢在vue结构目录中创建一个lib文件夹,并且在文件夹中创建一个'utils' 文件专门用来写自己的工具方法,结构目录如下: │ ├── 'lib' //工具包 │ ├── 'tools.js' // 存放和业务无关工具性质的js代码 │ └── 'util.js' //存放和业务相关工具性质的js代码 2.下面将会做这个五个场景父子传递的工具类: 2.1.由一个组件,向上找到最近的指定组件; 2.2.由一个组件,向上找到所有的指定组件; 2.3.由一个组件,向下找到最近的指定组件; 2.4.由一个组件,向下找到所有指定的组件; 2.5.由一个组件,找到指定组件的兄弟组件。 3.注意这次的工具组件传递的查找方法和'dispatch'不同的,这次是找整个组件,而'dispatch' 是吧某个 方法传递 ~~~ >[info] ## 由一个组件,向上找到最近的指定组件 -- findComponentUpward ~~~ 1.思路:编写这个函数时候,需要的形参分析,根据需要我们是要找到某个组件的最近的指定的父组件, 因此某个组件肯定是参数之一,指定的父组件就是参数之二 2.利用'dispatch' 思想我们需要去递归,一直找到当前组件的父组件,知道找到和我们需要匹配的组件 ,因此需要'$parent',和'$options.name' ~~~ >[danger] ##### 在utils 中正式编写 ~~~ 1.context 参数代表当前起点,也就当前组件的'this',componentName 就是目标组件的,可以理解成当前'this' 向上的某个父组件或者有可能是他的爷爷组件 2.逻辑 先获取当前组件的'$parent' 和 'componentName' 父组件的名称,然后去循环如果他有父组件,并且 组件没有名字或者名字不等于目标的名字,我们就继续递归循环查找,直到找到返回整个对象 3.'iview' 作者的解释: 3.1.第一个参数一般都是传入 this,即当前组件的上下文(实例)。 4.提醒自己一点:在写代码的时候一定要对某些条件做判断,例如下面的代码中的 'if(parent)' 就可以减收不必要 的操作 ~~~ ~~~ 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; } export { findComponentUpward }; ~~~ >[danger] ##### 使用篇章 * 创建一个test-a 父组件 ~~~ <!--test-a 组件作为父组件--> <template> <test-b></test-b> </template> <script> import testB from './test-b' export default { name: "test-a", components: { testB }, methods:{ sayHiB(){ console.log('我是A组件的方法,但是现在被B调用了'); } } } </script> <style scoped> </style> ~~~ * 组件B 子组件去使用组件a的方法 ~~~ <template> <div> 组件 B </div> </template> <script> import {findComponentUpward} from '../../lib/utils' export default { name: "test-b", // 发现一个规律类似这种组件调用 最好是在生命周期时候就注册好 // 不要在点击的时候在触发 // 也可以吧这个放回的对象放进B组件的 data中方便调用 mounted () { const comA = findComponentUpward(this, 'test-a'); if (comA) { comA.sayHiB(); // 我是A组件的方法,但是现在被B调用了 } } } </script> <style scoped> </style> ~~~ >[info] ## 由一个组件,向上找到所有的指定组件 -- findComponentsUpward ~~~ 1.findComponentsUpward 场景递归后续研究 做标记 ~~~ >[danger] ##### findComponentsUpward ~~~ // 由一个组件,向上找到所有的指定组件 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 []; } } export { findComponentsUpward }; ~~~ >[info] ## 由一个组件,向下找到最近的指定组件 -- findComponentDownward ~~~ 1.原理就是找到当前组件的所有子组件,然后递归查找看那个子组件符合我们传入的名字 如果相等就是我们需要的组件 2.这里要说明一个数组的循环,for ...in 和 for ...of,in简单粗暴理解循环对象用的k值 ,因此循环数组的时候是脚标,of 是用来循环数组中的内容 ~~~ >[danger] ##### findComponentDownward ~~~ 1.找到当前组件的所有子组件利用'$children',如果子组件中也没有就去子组件的子组件找 ,也就是递归查找,知道找到了 返回对应的子组件 2.这里注意循环数组的循环使用 for ...of ~~~ ~~~ function findComponentDownward (context,componentName){ let childrens = context.$children // 定义一个接受 变量 let children = null; if(childrens.length>0){ 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 } export { findComponentDownward }; ~~~ >[danger] ##### 案例 * 父组件A ~~~ <!--test-a 组件作为父组件--> <template> <test-b></test-b> </template> <script> import testB from './test-b' import {findComponentDownward } from '../../lib/utils' export default { name: "test-a", components: { testB }, mounted(){ // 调用子组件方法 const comB = findComponentDownward(this, 'test-b'); if (comB) { comB.sayHiB(); // 我是B组件的方法,但是现在被A调用了 } } } </script> <style scoped> </style> ~~~ * 子组件B ~~~ <template> <div> 组件 B </div> </template> <script> export default { name: "test-b", methods:{ sayHiB(){ console.log('我是B组件的方法,但是现在被A调用了'); } } } </script> <style scoped> </style> ~~~ >[info] ## 由一个组件,向下找到所有的指定组件 -- findComponentsDownward ~~~ 1.findComponentsDownward 场景递归后续研究 做标记 ~~~ >[danger] ##### findComponentsUpward ~~~ 1.后续理解'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); }, []); } export { findComponentsDownward }; ~~~ >[info] ## 找到指定组件的兄弟组件——findBrothersComponents ~~~ ~~~ >[danger] ##### findBrothersComponents ~~~ 1.这里使用了三个参数,和之前一样钱两个分别是起始组件对象,要找的组件名字, 这里还用了数组方法'findIndex' 用来找到脚标 2.对第三个参数做详细讲解,第三个参数是,是否包含自己,咋一看觉得无法理解, 举个例子,想弹窗这类组件 在一个页面可能会使用多次,但是她们的名字相同,但是 我在对应的兄弟组件肯定是不想包含本身,因此利用了'_uid' 唯一标识做了标记去重 ~~~ ~~~ function findBrothersComponents (context,componentName,exceptMe = true) { // 找到符合的子组件名称数组 let res = context.$parent.$children.filter(item =>{ return item.$options.name === componentName; }) // 找到当前本身组件在数组中的位置 let index = res.findIndex(item =>{ return item._uid === context._uid }) if (exceptMe) res.splice(index, 1); return res; } export { findBrothersComponents }; ~~~ >[danger] ##### 案例说明 * 父组件中同一个组件调用两次 ~~~ <!--test-a 组件作为父组件--> <template> <div> <test-b></test-b> <test-b></test-b> </div> </template> <script> import testB from './test-b' export default { name: "test-a", components: { testB }, } </script> <style scoped> </style> ~~~ * 子组件中兄弟组件默认不包括自己 ~~~ <template> <div> 组件 B </div> </template> <script> import {findBrothersComponents } from '../../lib/utils' export default { name: "test-b", methods:{ sayHiB(){ console.log('我是B组件的方法,但是现在被A调用了'); } }, mounted(){ const comB = findBrothersComponents(this, 'test-b'); if (comB) { console.log(comB); } } } </script> <style scoped> </style> ~~~