How to write code more elegantly when developing drag and drop visualization projects recently

How can I reduce GIT conflicts and duplicate code import files in a multi-party project

// Inject the file
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
Copy the code

Those of you who are developing the front end know that when we use require to import some resources, the path can’t be a dynamic variable, it has to be a template string or an addition. Actually, this is not a problem with the framework you use, it’s a problem with webpack, it can’t dynamically resolve a variable. If you use a template string or a plus sign, it’s because it imports resources from this folder

Zhihu address: www.zhihu.com/question/42…

How can I be more elegant when encapsulating classes

// For example, when we encapsulate a class, we often need to use chain calls
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
  }
}

// The usage mode
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')
Copy the code

If you think about it a little bit more elegantly, passing the same arguments every time

Well, let’s do it gracefully

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()
Copy the code

Of course, another solution is to return a subclass in retune and pass in comName when instantiating the subclass

Code sample

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)
Copy the code

Of course this code isn’t elegant enough, but if you’re familiar with the back end, or the front end of Typescript, you already know about function overloading, so there are more elegant ways to override functions to make your code more elegant

What is function overloading

When a function or method has the same name but different argument lists, such functions or methods with the same name and different arguments are called overloaded functions or methods.

There is no such thing as overloading in JavaScript, but you can tell by arguments.length

function heavyLoad() {
  if (arguments.length === 1) {
    console.log('One parameter')}if (arguments.length === 2) {
    console.log('Two arguments')}// ...
}
Copy the code

This way we can implement function overloading, but this does not meet our needs

Classic example (from Nuggets article)

Let’s start with a requirement, we have a Users object, the Users object values property has some names, a name has two parts

[last-name] [first-name] [last-name] [last-name] [last-name

const users = {
  values: ["Dean Edwards"."Alex Russell"."Dean Tom"]}// We will add a method to the Users object
// Return the whole users.values when no arguments are worn
// When passing a parameter, we match first-name with the parameter when the element is returned
// When two arguments are passed, both first-name and last-name are matched when the element is returned

function addMethod(object, name, callback) {
  // Add the original object[name] method to the object
  const old = object[name]
  // Redefine the object[name] method
  object[name] = function () {
    // If the function needs the same number of arguments as it actually passed, it calls fn directly
    if (callback.length === arguments.length) {
      return callback.apply(this.arguments)}else if (typeof old === 'function') {
      // If not, determine if old is a function
      return old.apply(this.arguments)}}}The addMethod function takes three arguments
// First: the method object to bind
// Second: binding method name
// Third: the method that needs to be bound

// The addMethod function not only determines the length of the parameter, but also determines the length attribute of the function definition (which defines several parameters).
// Next we use the addMethod method

// Do not pass parameters
function find0() {
  // TODO
  return console.log('Return all')}// Pass a parameter
function find1(arg1) {
  // TODO
  return console.log('Return matched')}// Pass two arguments
function find2(arg1, arg2) {
  // TODO
  return console.log('Return head and tail')
}

addMethod(users, 'find', find0)
addMethod(users, 'find', find1)
addMethod(users, 'find', find2)
Copy the code

So that’s how you can overload functions

Every time addMethod is called, it overwrites the previous method, but old stores the previous method, and when the parameter is passed in, it goes back to old to find the method that was overwritten last time. The scope is actually preserved using closures

So let’s do it this way

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 -- I didn't pass comName')}addComStyle(name, path) {
    console.log(this.comName)
    console.log('addComStyle - I didn't pass 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(a)const list = map.addCom('test')
list.addComData(1.2)
const list1 = map.addCom('jen')
map.getComList()
Copy the code

In summary, the second method is better, and the third method produces a closure every time it is executed

About Code Splitting

If there is too much code, split mixins or separate them into common JS files to keep the code clean and unified

About VUE communication

  1. Parent-child communication: the parent passes data to the child through props and the child passes data to the parent through events (emit). Can also communicate via parent/child chain (EMIT); Can also communicate via parent/child chain (EMIT); Communication is also possible through parent/child chains (parent/children); Ref can also access component instances; Dojo.provide/injectAPI; The children); Ref can also access component instances; Provide/inject API; The children); Ref can also access component instances; Dojo.provide/injectAPI; attrs/$listeners
  2. Brother communication: Bus; Vuex
  3. Cross-level communication: Bus; Vuex; Dojo.provide/inject API, attrs/attrs/attrs/listeners

The importance of a unified approach

In real projects, where requirement changes are quite common, what about reducing global changes due to requirement changes?

A couple of suggestions

  1. Global unification of variables
  2. Function independence
  3. Single component function

In fact, the summary is that

High cohesion, low coupling reduced because other factors cause you to rewrite your code, or even refactor it, to make your code extensible

For the API layer multi-interface caching implementation, see next time. Consider the typescript version

About Subsequent updates

I can only say that I have been very busy recently, and there are a lot of things to learn. I may update some more practical contents in the future, such as how to use VUEX in the project, realize unified configuration, etc., or the analysis of some principles, etc. Please look forward to !!!!!