最近项目总结

525 阅读1分钟

最近在开发拖拽可视化项目 如何更优雅的书写代码

在多人协同的项目中如何减少GIT冲突以及减少重复性代码导入文件

// 注入文件
const moduleFiles = require.context('./src/route', true, /\.js$/)
const modules = moduleFiles.keys().reduce((modules, path) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = moduleFiles(path)
  module[moduleName] = value.default
  return modules
}, {})
export default modules

开发前端的同学的都知道,我们用require 引入一些资源的时候,这个路径不能是一个动态变量,需要通过模版字符串或者通过加法的方式才能引入,其实这个不是你用的框架问题,这是webpack打包的问题,他不能动态解析一个变量,如果你通过模版字符串或者加号的方式,是因为它会引入这个文件夹的资源

知乎地址:www.zhihu.com/question/42…

在封装类的时候,如何更加优雅

// 例如我们在封装类的时候,经常会需要用到链式调用
class Map {
  constructor() {
    this.mapList = []
  }
  addCom(comName){
    this.mapList.push({
      comName,
      styleList: [],
      dataList: []
    })
    return this
  }
  addComData(comName, comDataName, path) {
    for (const com of this.mapList) {
      if (com.comName === comName) {
        com.dataList.push({
          name: comDataName,
          path
        })

      }
    }
    return new Add()
  }
  addStyleCom(comName, comStyleName, path) {
    for (const com of this.mapList) {
      if (com.comName === comName) {
        com.dataList.push({
          name: comStyleName,
          path
        })
      }
    }
    return this
  }
  getComList() {
    console.log(this.comList)
    return this.comList
  }
}

// 使用方式
const map = new Map()
map.addCom('test')
  .addComData('test', 'testData', './src/components/test/data.vue')
	.addStyleCom('test', 'testStyle1', './src/components/test/style1.vue')
	.addStyleCom('test', 'testStyle2', './src/components/test/style2.vue')

仔细一想会不会发现以上代码有点不够优雅, 因为每一次都要传同样的参数

那我们就整的优雅一点

class Map {
  constructor() {
    this.comList = []
  }
  addCom(comName) {
    this.comList.push({
      comName,
      comDataList: [],
      comStyleList: []
    })
    return {
      addComData: this.addComData.bind(this, comName),
      addComStyle: this.addComStyle.bind(this, comName)
    }
  }
  addComData(comName, name, path) {
    for (const com of this.comList) {
      if (com.comName === comName) {
        com.comDataList.push({
          name,
          path
        })
      }
    }
    return {
      addComStyle: this.addComStyle.bind(this, comName)
    }
  }
  addComStyle(comName, name, path) {
    for (const com of this.comList) {
      if (com.comName === comName) {
        com.comStyleList.push({
          name,
          path
        })
      }
    }
    return {
      addComStyle: this.addComStyle.bind(this, comName)
    }
  }
  getComList() {
    console.log(this.comList)
    return this.comList
  }
}

const map = new Map()

map.addCom('test')
  .addComData('testComData', './src/components/test/data.vue')
  .addComStyle('testComStyle1', './src/components/test/style1.vue')
  .addComStyle('testComStyle2', './src/components/test/style2.vue')

map.addCom('test1')
  .addComData('testComData', './src/components/test/data.vue')
  .addComStyle('testComStyle1', './src/components/test/style1.vue')
  .addComStyle('testComStyle2', './src/components/test/style2.vue')

map.getComList()

当然还有另外一种解决方法就是在retune的时候返回一个子类,在实例化子类的时候把comName传入即可

代码示例

class Add {
  constructor(comName, comList) {
    this.comName = comName
    this.comList = comList
  }
  _addComData(name, path) {
    for (const com of this.comList) {
      if (com.comName === this.comName) {
        com.comDataList.push({
          name,
          path
        })
      }
    }
     return this
  }
  _ddComStyle(name, path) {
    for (const com of this.comList) {
      if (com.comName === this.comName) {
        com.comStyleList.push({
          name,
          path
        })
      }
    }
    return this
  }
}

class Map {
  constructor() {
    this.comList = []
  }
  addCom(comName) {
    this.comList.push({
      comName,
      comDataList: [],
      comStyleList: []
    })
    return new Add(comName, this.comList)
  }
  getComList() {
    return this.comList
  }
}

const map = new Map()
map
  .addCom('test')
  ._addComData('data.vue', '/xxx/xxx')
  ._ddComStyle('style1.vue', '/xxx/xxx')
  ._ddComStyle('style2.vue', '/xxx/xxx')

map
  .addCom('jen')
  ._addComData('data.vue', '/xxx/xxx')
  ._ddComStyle('style1.vue', '/xxx/xxx')
  ._ddComStyle('style2.vue', '/xxx/xxx')


const mapList = map.getComList()
console.log(mapList)

当然以上代码你觉得还不够优雅,如果熟悉后端的同学,或者前端同学熟悉Typescript的话,那肯定知道函数的重载,所以还有更优雅的方案,去实现函数的重载从而实现更优雅的代码

什么是函数的重载

函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。

在JavaScript中其实没有重载的概念,但是我们可以根据arguments.length去判断

function heavyLoad() {
  if (arguments.length === 1) {
    console.log('一个参数')
  }
  if (arguments.length === 2) {
    console.log('两个参数')
  }
  // ...
}

这种方式我们可以实现函数的重载,但是这个不满足我们的需求

经典例子(来自掘金文章)

我们先看一个需求,我们有一个users对象,users对象values属性存在一些名字,一个名字由两部分组成

空格左边的是first-name,空格右边是last-name,解构如下

const users = {
  values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
}
// 我们要在users对象中添加一个方法
// 当不穿任何参数当时候 返回整个users.values
// 当传一个参数当时候,就把first-name 跟这个参数匹配当元素返回
// 当传两个参数当时候,则把first-name last-name都匹配当元素返回

function addMethod(object, name, callback) {
  // 先把原来object[name] 方法添加保存到object中
  const old = object[name]
  // 重新定义 object[name]方法
  object[name] = function () {
    // 如果函数需要的参数 和 实际传入的参数个数相同,就直接调用fn
    if (callback.length === arguments.length) {
      return callback.apply(this, arguments)
    } else if (typeof old === 'function') {
      // 如果不相同,判断old是不是函数 如果是就调用
      return old.apply(this, arguments)
    }
  }
}

// addMethod 函数接受三个参数
// 第一个:要绑定的方法对象
// 第二个:绑定的方法名称
// 第三个:需要绑定的方法

// 这个addMethod 函数不仅仅是判断参数的长度,还判断了函数定义的length属性(定义了几个形参)
// 接下来我们用这个addMethod 方法

// 不传参数
function find0() {
  // TODO
  return console.log('返回全部')
}

// 传一个参数
function find1(arg1) {
  // TODO
  return console.log('返回匹配的')
}

// 传两个参数
function find2(arg1, arg2) {
  // TODO
  return console.log('返回头和尾')
}

addMethod(users, 'find', find0)
addMethod(users, 'find', find1)
addMethod(users, 'find', find2)

以上就可以实现函数的重载,原理就是

addMethod 每一次调用addMethod 方法的时候,他会覆盖原来之前的addMethod方法,但这时候old存的是上一次的方法,但传入参数不满足f n.length 的时候它回去old里面去找上一次被覆盖的方法,其实利用闭包保存了作用域

接下来我们来用这种方式实现

class Map {
  constructor() {
    this.comMap = {
      comList: []
    }
  }
  _init(object) {
    Map.addMethod(object, 'addComData', this.addComData)
    Map.addMethod(object, 'addComData', this.addComDataByComName)
    Map.addMethod(object, 'addComStyle', this.addComStyle)
    Map.addMethod(object, 'addComStyle', this.addComStyleByComName)
  }
  addCom(comName) {
    const name = comName
    return (function (that) {
      const com = {
        comName: name,
        comDataList: [],
        comStyleList: []
      }
      that.comMap.comList.push(com)
      that._init(com)
      return com
    })(this)
  }
  static addMethod(object, fnName, callback) {
    const old = object[fnName]
    object[fnName] = function () {
      if (callback.length === arguments.length) {
        return callback.apply(this, arguments)
      } else {
        return old.apply(this, arguments)
      }
    }
  }
  addComData(name, path) {
    console.log(this.comName)
    console.log('addComData -- 我没传comName')
  }
  addComStyle(name, path) {
    console.log(this.comName)
    console.log('addComStyle - 我没传comName')
  }
  addComDataByComName(comName, name, path) {
    // TODO
    console.log('addComDataByComName')
  }
  addComStyleByComName(comName, name, path) {
    // TODO
    console.log('addComStyleByComName')
  }
  getComList() {
    console.log(this.comMap)
  }
}

const map = new Map()

const list = map.addCom('test')
list.addComData(1, 2)
const list1 = map.addCom('jen')
map.getComList()

image.png

总结,其实还是第二种方式比较好,第三种每执行一次都会产生闭包

关于代码拆分

如果充分代码比较多,拆分mixins 或者 抽离成公共js文件吧,保持代码整洁,全局统一处理

关于vue 通信

  1. 父子通信: 父向子传递数据是通过 props,子向父是通过 events(emit);通过父链/子链也可以通信(emit);通过父链 / 子链也可以通信(parent / children);ref也可以访问组件实例;provide/injectAPIchildren);ref 也可以访问组件实例;provide / inject API;attrs/$listeners
  2. 兄弟通信: Bus;Vuex
  3. 跨级通信: Bus;Vuex;provide / inject API、attrs/attrs/listeners

全局统一处理的重要性

在实际项目中,需求变更是十分常见的,如果减少因为需求变更导致的全局修改呢?

有几点建议

  1. 变量全局统一
  2. 函数功能独立
  3. 组件功能单一

其实总结一句话就是

高内聚,低耦合 减少因为其他因素导致自己需要重写代码,甚至重构,保证代码可扩展性

关于API层多接口缓存实现,下期再说吧,考虑typescript版本的

关于后续更新

只能说最近一直都很忙,要学习的东西特别多,可能接下来会更新一些更加实际的内容吧,例如VUEX在项目中如何使用,实现统一配置,等等吧,或者一些原理的解析,等等,敬请期待!!!!!