最近在开发拖拽可视化项目 如何更优雅的书写代码
在多人协同的项目中如何减少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()
总结,其实还是第二种方式比较好,第三种每执行一次都会产生闭包
关于代码拆分
如果充分代码比较多,拆分mixins 或者 抽离成公共js文件吧,保持代码整洁,全局统一处理
关于vue 通信
- 父子通信: 父向子传递数据是通过 props,子向父是通过 events(parent / attrs/$listeners
- 兄弟通信: Bus;Vuex
- 跨级通信: Bus;Vuex;provide / inject API、listeners
全局统一处理的重要性
在实际项目中,需求变更是十分常见的,如果减少因为需求变更导致的全局修改呢?
有几点建议
- 变量全局统一
- 函数功能独立
- 组件功能单一
其实总结一句话就是
高内聚,低耦合 减少因为其他因素导致自己需要重写代码,甚至重构,保证代码可扩展性
关于API层多接口缓存实现,下期再说吧,考虑typescript版本的
关于后续更新
只能说最近一直都很忙,要学习的东西特别多,可能接下来会更新一些更加实际的内容吧,例如VUEX在项目中如何使用,实现统一配置,等等吧,或者一些原理的解析,等等,敬请期待!!!!!