組件化,解決了一組能夠複用的功能,咱們可以使用通常的開源的公共組件,也能夠針對咱們特殊業務場景,沉澱出符合本身業務的業務組件;
工程化,解決了可控和規範性的功能,咱們可以使用開源的一些腳手架好比vue-cli、create-react-app等,或者公司內部本身沉澱的內部腳手架解決方案;
可是誰來解決散落在各個模塊和工程中的邏輯?怎樣去避免硬代碼編程,減小邏輯的後期維護和成本等等,也是一個須要考慮的點。前端
首先能夠從一個客觀角度去分析這份代碼,review這份代碼,能夠看出不少問題,好比:vue
當時最初寫這份代碼還作過簡單的分類,有點邏輯管理的淺顯意識。可是咱們能夠看看咱們本身真實用於生產的公司的項目,多人維護,協同開發、業務增加等,到最後已經徹底不可控,邏輯動都不敢動,只敢打補丁,愈來愈臃腫。下面就是我以前針對咱們內部項目一小塊作的一塊分析,這些都真實存在幾乎全部人的代碼裏,是咱們存在的痛點。react
基於以前對代碼的分析,堆積了不少問題,說明這塊確實是咱們的痛點。那麼這些痛點歸根究底是咱們作了什麼致使?前端對業務到底作了哪些方面的東西?git
以上,幾乎囊括了前端在業務領域,所須要作的全部事情,也是咱們的全部的邏輯。github
咱們須要這些邏輯的堆砌去完成咱們須要的東西,其實觀察每一小塊業務代碼,都是由一條條最簡單的邏輯規則,一步步流轉到最後咱們所須要的結果的,就跟咱們作的思惟腦圖同樣,一個流程節點都是一個小邏輯。一個業務的開始,到一個業務的結束,都是由每一個最小的邏輯點組成的。ajax
so,咱們能不能站在一個全局的角度去看整個業務,能不能把每一個流程節點打碎成一個最小的原子,全部的業務邏輯,都是從最小的原子一個一個組裝起來的,這樣,咱們就能更專一於最小的邏輯。咱們所作的任何業務都是由原子拼起來。這樣就能夠從基礎去hold住任何邏輯,無論複雜和簡單。vue-cli
咱們也能夠參考,在Java或者其餘後端語言裏,設計最初是最理想。它們都但願,個人世界就和現實世界同樣,都是由最小的顆粒去組裝我想要的設計的世界。因此一個class表明了一類事情,一個function表明了一件事。不管大家上面怎麼玩,我都能支持大家去組裝大家要的世界,大家要作的任何複雜的事。因此,邏輯處理其實也是這樣的,把任何邏輯打成最小顆粒,經過拼接,組裝,去支撐上層的任何業務邏輯。npm
如此以後,設想以下場景:編程
原子邏輯:對象的基類,管理全部注入原子後端
組合邏輯:繼承原子,組合,輸出
對外接口:解析配置,調用原子和組合類管理、拋出生產結果
思路圖以下:
// 原子管理類,管理全部原子邏輯
class Atom {
/*
* 注入原子邏輯,以屬性的方式管理
* objArr: 原子邏輯數組
* */
setBasics(objArr) {
objArr.forEach(x => {
this[x.name] = x.assembly
})
}
/*
* 生產組裝類所須要的原子
* param
* useBasics:組裝類,所須要繼承的原子
* 支持type: String - 指定一個、Array - 指定多個、無(undefined)- 全部
*
* return
* output:生產出的原子邏輯
* */
machiningBasics(useBasics) {
let output = {}
if (useBasics) {
if (Array.isArray(useBasics)) {
useBasics.forEach(x => {
Object.assign(output, this[x])
})
} else {
Object.assign(output, this[useBasics])
}
} else {
Object.keys(this).forEach(x => {
Object.assign(output, this[x])
})
}
return output
}
}
複製代碼
基類,做爲最底層的基礎模塊,管理全部原子,供上層業務邏輯繼承和調用,去組裝本身的業務邏輯。該類內部拋出2個方法以下:
setBasics:做爲對原子邏輯的注入。能夠持續去豐富底層的原子邏輯(後期是否支持動態注入,再考慮);
machiningBasics:提供給組裝類繼承原子的邏輯,輸出所須要的底層基礎,供上游拼裝
// 因ES6不支持私有屬性,因此將私有屬性放到外層
/*
* 生產組裝對象,並注入指定做用域
* param -
*
* return
* Temporary:組裝對象
*
* */
function makeObject() {
function Temporary(assembly) {
for (let key in assembly) {
this[key] = assembly[key].bind(this)
}
}
return Temporary
}
/*
* 組裝中是否透傳原子方法
* param
* Temporary:組裝對象
* config: 組裝的配置
*
* return
* output:輸出最終邏輯
* */
function isThrough(Temporary, config) {
// 根據配置,實例化對象
let temp = new Temporary(config.assembly)
let output = {}
for (let key in temp) {
// 是否開啓配置
if (config.through === false) {
// 是不是自身屬性
if (temp.hasOwnProperty(key)) {
output[key] = temp[key]
}
} else {
output[key] = temp[key]
}
}
return output
}
// 組裝類,管理組裝和輸出。
class Package {
/*
* 注入組裝配置
* param
* config:組裝配置
* prototype:組裝所依賴的原子屬性
*
* return 生產完成的對象
* */
setPackage(config, prototype) {
let temp = makeObject(config)
temp.prototype = prototype
return isThrough(temp, config)
}
}
export default Package
複製代碼
組裝類,經過一系列的原子邏輯組裝成一條條所須要的業務邏輯。總體步驟爲:生產出組裝的對象,經過原型繼承裝配原子,對外暴露組裝結果。就跟工廠同樣,生產目標,生產原料,生產產物。組裝類對內部拋出一個方法:
setPackage:根據提供的配置文件以及所需繼承的原子,組裝出一類業務邏輯。
import Atom from './atom/index'
import Package from './package/index'
// 實例化原子和組裝類
const _atom = new Atom()
const _package = new Package()
// 生產原子緩存
let _globalCache = {}
/*
* 對外暴露,注入配置依賴,生產組裝
* param
* param: 配置參數
* */
export const injection = function (param) {
_atom.setBasics(param.atom)
param.package.forEach(x => {
let prototype = _atom.machiningBasics(x.extends)
// 緩存組裝
_globalCache[x.name] = _package.setPackage(x, prototype)
})
}
/*
* 對外暴露,獲取生產完成的組裝對象
* param
* param:獲取的目標
* type:String - 指定一個、Array - 指定多個、 無(undefined) - 所有
*
* return
* output:生產結束的對象
* */
export const getMateriel = function (param) {
let output = {}
if (param) {
if (Array.isArray(param)) {
return param.forEach(x => {
output[x] = _globalCache[x]
})
} else {
output = _globalCache[param]
}
} else {
output = _globalCache
}
return output
}
複製代碼
對外的入口,主要功能爲解析配置,組裝配置,輸出組裝結果供使用3大功能。
injection:標準對外入口,進行邏輯管理的初始化,該方法將全部的原子邏輯注入到原子類裏,再經過組裝配置,從原子類獲取到每一個組裝對象所須要繼承的原子供組裝使用,最後將組裝好的邏輯全局存到一個全局的緩存裏。
getMateriel:對外輸出生產完成的組裝邏輯,暴露出組裝結束的結果,可獲取全部組裝結果,也可單獨或者批量獲取結果
/*
* injection方法注入對象的格式
* atom: 全部的原子邏輯
* package: 組裝原子的邏輯
*/
{
atom: ['原子邏輯1', '原子邏輯2'],
package: ['組裝邏輯1', '組裝邏輯2']
}
複製代碼
/*
* 該格式爲原子邏輯的標準格式
* name: 原子類的名稱
* assembly: 原子的方法存放的對象
*/
export default {
name: '原子的名稱',
assembly: {
// 原子邏輯所對外提供的方法
sendRequest() {
// do something
}
}
}
複製代碼
/*
* 該格式爲組裝邏輯的標準格式
* name: 組裝類的名稱
* extends: 組裝類須要繼承的原子
* through: 是否透傳原子類內部的信息
* assembly: 原子的方法存放的對象
*/
export default {
name: '組裝類名稱',
extends: '繼承原子', // 支持字符串(單原子)、無(默認繼承全部原子)、數組(指定多個原子)
assembly: {
// 組裝邏輯對外產出的方法,可直接this.來調用繼承原子的方法
getAtom1Promise() {
// do something...
}
}
}
複製代碼
--src
|-atom // 存放原子邏輯的地方
|-package // 存放組裝邏輯的地方
|-index.js // 入口文件
atom1.js
// atom1.js
export default {
name: 'atom1',
assembly: {
sendRequest() {
return new Promise((res, rej) => {
setTimeout(function () {
res([1, 2, 3])
}, 3000)
})
}
}
}
複製代碼
atom2.js
// atom2.js
export default {
name: 'atom2',
assembly: {
judgeArray(data) {
return Array.isArray(data)
}
}
}
複製代碼
package1.js
// package1.js
export default {
name: 'package1',
extends: 'atom1',
assembly: {
getAtom1Promise() {
this.sendRequest()
.then(x => {
console.warn('使用成功', x)
})
}
}
}
複製代碼
package2.js
// package2.js
export default {
name: 'package2',
through: false,
assembly: {
packageLogin() {
this.sendRequest()
.then(x => {
console.warn('判斷是不是數組:', this.judgeArray(x))
})
}
}
}
複製代碼
index.js
import {injection, getMateriel} from '@fines/factory-js'
import atom1 from './atom/atom1'
import atom2 from './atom/atom2'
import package1 from './package/package1'
import package2 from './package/package2'
injection({
atom: [atom1, atom2],
package: [package1, package2]
})
console.warn('組裝成功:', getMateriel())
// 測試package1方法
getMateriel('package1').getAtom1Promise()
// 測試package2方法
getMateriel('package2').packageLogin()
複製代碼
連接:傳送門 感受有參考意義能夠點個star,內部正在使用踩坑中
連接:傳送門有問題,有意見,你就說😆
連接:傳送門
PS:可直接 npm run start 直接跑起來測試
@fines/factory-js
npm i @fines/factory-js
fines做爲一個新的註冊的組織,這裏將寫一些更美好的東西,之後全部能變得更美好的代碼都將發佈到這個包下面(更重要一些包名已經沒法使用,可是組織能夠無限制)
之前在邏輯管理領域作過相關的摸索和思考,以下:
在以前的摸索基礎上,更深刻的思考,才最終產出這個邏輯的解決方案,僅供你們參考,後面仍將持續完善該方案。
社區有人說,這不是你前端作的事,不是你的活,作這個幹啥?聽完這句話,總感受有點彆扭。
在我看來,咱們每一個人都是一個架構師,不斷地在架構本身的代碼。不停的去認知世界的樣子,認知自我。咱們都不是最完美的,有好也有壞。去發現自身痛點,對痛點進行分析,進行思考,找出最終的根源,而後再去思考如何去解決這個痛點,嘗試,摸索,失敗,階段性勝利,再繼續。就這樣一路走來,堅信終有收穫。共勉!