本文首发于 Array_Huang的技术博客——实用至上
,非经做者赞成,请勿转载。
原文地址: http://www.javashuo.com/article/p-xwrwoszw-bz.html
若是您对本系列文章感兴趣,欢迎关注订阅这里:https://segmentfault.com/blog/array_huang
本文介绍如何在多项目间共用同一套基础设施,又或是某种层次的框架。javascript
一个完整的网站,不可能只包含一个jQuery,或是某个MVVM框架,其中一定包含了许多解决方案,例如:如何上传?如何兼容IE?如何跨域?如何使用本地存储?如何作用户信息反馈?又或者具体到如何选择日期?等等等等……这里面一定包含了UI框架、JS框架、各类小工具库,不管是第三方的仍是本身团队研发的。而以上所述的种种,就构成了一套完整的解决方案,也称基础设施。css
基础设施有个重要的特征,那就是与业务逻辑无关,不管是OA仍是CMS又或是CRM,只要总体产品形态相似,咱们就可使用同一套基础设施。html
框架这个概念很泛,泛得让人心生困惑,但抽象出来讲,框架就是一套定义代码在哪里写、怎么写的规则。不能说咱们要怎么去用框架,反却是框架控制咱们怎么去填代码。前端
本系列前面的十来篇文章,分开来看是不一样的点,但若是全部文章合起来,并连同示例项目(Array-Huang/webpack-seed),实际上阐述的就是一套完整的多页应用框架(或称架构)。这套框架规定了整个应用的方方面面,举几个例子:java
固然,这只是个人框架,我但愿大家能够看懂了,而后根据本身的需求来调整,变成大家的框架。甚至说,我本身在作不一样类型的项目时,总体架构也都会有很多的变化。jquery
数月前,我找同事要了一个他本身写的地区选择器,拉回来一看遍地都是ESLint的报错(他负责的项目没有用ESLint,比较随意),我这人有强迫症的怎么看得过眼,卷起袖子就开始改,改好也就正常使用了。过了一段时间,来了新需求,同事在他那改好了地区选择器又发了一份给我,我一看头都大了,又是满地报错,这不是又要我再改一遍吗?当时我就懵了,只好按着他的思路,对个人版本作了修改。今后,也确立了咱们公司会有两份外观功能都一致,可是实现却不同的地区选择器。webpack
很坑爹是吧?git
上面说的是组件级的,下面咱们来讲架构级别的。github
我在公司主要负责的项目有两个,在个人不懈努力下,已经作到跟个人脚手架项目Array-Huang/webpack-seed大致上同构了。但维持同构显然是要付出代价的,我在脚手架项目试验过的改进,小至改个目录路径,大至引入个plugin啊loader啊什么的,都要分别在公司的两个项目里各作一遍,超烦哒(嫌弃脸web
试想只是两个项目就已经这样了,若是是三个、四个,甚至六个、七个呢?堪忧啊堪忧啊!
不知道大家有没有这样子的经验:接到新项目时,灵机一动“这不就是个人XX项目吗?”,而后赶忙搬出XX项目的源码,而后删掉业务逻辑,保留可复用的基础设施。
也许你会说,这不已经比从零开始要好多了吗?整体上来讲,是吧,但还不够好:
以上这些问题,你每建立一个新项目都要经历一遍,我问你怕了没有。
是的没错,脚手架自己就算是一整套基础设施了,但依然有下列问题:
在以前的文章里,我使用的一直都是Array-Huang/webpack-seed这个脚手架项目做为示例,而为了实践多项目共用基础设施,我对该项目的架构作了较大幅度的调整,升级为2.0.0版本。为免你们看前面的文章时发现示例项目货不对板,感到困惑,我新开了一个repo来存放调整后的脚手架:Array-Huang/webpack-seed-v2(https://github.com/Array-Huang/webpack-seed-v2
),而且,我在两个项目的README里我都注明了相应的内容,你们可不要混淆了哈。
下面就以从Array-Huang/webpack-seed到Array-Huang/webpack-seed-v2的改造过程来介绍如何实现多项目共用基础设施。
改造思路其实很简单,就是把预想中多个项目都能用得上的部分从现有项目里抽离出来。
抽离的说法是针对原项目的,若是单纯从文件系统的角度来讲,只不过是移动了某些文件和目录。
移动到哪里了呢?天然是移动到与项目目录同级的地方,这样就方便多个项目引用这个核心了。
若是你跟我同样,在原项目中定义了大量路径和alias的话,移动这些文件/目录就只是个改变量的活了:
选自webpack-seed/webpack-config/base/dir-vars.config.js
:
var path = require('path'); var moduleExports = {}; // 源文件目录 moduleExports.staticRootDir = path.resolve(__dirname, '../../'); // 项目根目录 moduleExports.srcRootDir = path.resolve(moduleExports.staticRootDir, './src'); // 项目业务代码根目录 moduleExports.vendorDir = path.resolve(moduleExports.staticRootDir, './vendor'); // 存放全部不能用npm管理的第三方库 moduleExports.dllDir = path.resolve(moduleExports.srcRootDir, './dll'); // 存放由各类不常改变的js/css打包而来的dll moduleExports.pagesDir = path.resolve(moduleExports.srcRootDir, './pages'); // 存放各个页面独有的部分,如入口文件、只有该页面使用到的css、模板文件等 moduleExports.publicDir = path.resolve(moduleExports.srcRootDir, './public-resource'); // 存放各个页面使用到的公共资源 moduleExports.logicDir = path.resolve(moduleExports.publicDir, './logic'); // 存放公用的业务逻辑 moduleExports.libsDir = path.resolve(moduleExports.publicDir, './libs'); // 与业务逻辑无关的库均可以放到这里 moduleExports.configDir = path.resolve(moduleExports.publicDir, './config'); // 存放各类配置文件 moduleExports.componentsDir = path.resolve(moduleExports.publicDir, './components'); // 存放组件,能够是纯HTML,也能够包含js/css/image等,看本身须要 moduleExports.layoutDir = path.resolve(moduleExports.publicDir, './layout'); // 存放UI布局,组织各个组件拼起来,因应须要能够有不一样的布局套路 // 生成文件目录 moduleExports.buildDir = path.resolve(moduleExports.staticRootDir, './build'); // 存放编译后生成的全部代码、资源(图片、字体等,虽然只是简单的从源目录迁移过来) module.exports = moduleExports;
选自webpack-seed/webpack-config/resolve.config.js
:
var path = require('path'); var dirVars = require('./base/dir-vars.config.js'); module.exports = { // 模块别名的配置,为了使用方便,通常来讲全部模块都是要配置一下别名的 alias: { /* 各类目录 */ iconfontDir: path.resolve(dirVars.publicDir, 'iconfont/'), configDir: dirVars.configDir, /* vendor */ /* bootstrap 相关 */ metisMenu: path.resolve(dirVars.vendorDir, 'metisMenu/'), /* libs */ withoutJqueryModule: path.resolve(dirVars.libsDir, 'without-jquery.module'), routerModule: path.resolve(dirVars.libsDir, 'router.module'), libs: path.resolve(dirVars.libsDir, 'libs.module'), /* less */ lessDir: path.resolve(dirVars.publicDir, 'less'), /* components */ /* layout */ layout: path.resolve(dirVars.layoutDir, 'layout/html'), 'layout-without-nav': path.resolve(dirVars.layoutDir, 'layout-without-nav/html'), /* logic */ cm: path.resolve(dirVars.logicDir, 'common.module'), cp: path.resolve(dirVars.logicDir, 'common.page'), /* config */ configModule: path.resolve(dirVars.configDir, 'common.config'), bootstrapConfig: path.resolve(dirVars.configDir, 'bootstrap.config'), }, // 当require的模块找不到时,尝试添加这些后缀后进行寻找 extentions: ['', 'js'], };
抽离的方法很简单,那么关键就看究竟是哪些部分能够抽离、须要抽离了,这一点看我抽离后的成果就比较清晰了:
先来看根目录:
├─ core # 抽离出来的基础设施,或称“核心” ├─ example-admin-1 # 示例项目1,被抽离后剩下的 ├─ example-admin-2 # 示例项目2,嗯,简单起见,直接复制了example-admin-1,不过仍是要作一点调整的,好比说配置 ├─ npm-scripts # 没想到npm-scripts也能公用吧? ├─ vendor # 没法在npm上找到的第三方库 ├─ .eslintrc # ESLint的配置文件 ├─ package.json # 全部的npm库依赖建议都写到这里,不建议写到具体项目的package.json里
再来看看core
目录
├─ _webpack.dev.config.js # 整理好公用的开发环境webpack配置,以备继承 ├─ _webpack.product.config.js # 整理好公用的生产环境webpack配置,以备继承 ├─ webpack-dll.config.js # 用来编译Dll文件用的webpack配置文件 ├─ manifest.json # Dll文件的资源目录 ├─ package.json # 没有什么实质内容,我这里就放了个编译Dll用的npm script ├─components # 各类UI组件 │ ├─footer │ ├─header │ ├─side-menu │ └─top-nav ├─config # 公共配置,有些是提供给具体项目的配置来继承的,有些自己就有用(好比说“核心”部分自己须要的配置) ├─dll # 以前的文章里就说过,我建议把各类第三方库(包括npm库也包括非npm库)都打包成Dll来加速webpack编译过程,这部分明显就属于基础设施了 ├─iconfont # 字体图标能不能公用,这点我也是比较犹豫的,看项目实际须要吧,不折腾的话仍是推荐公用 ├─layout # 布局,既然是同类型项目,布局确定是基本同样的 │ ├─layout │ └─layout-without-nav ├─less # 样式基础,在我这项目里就是针对bootstrap的SB-Admin主题作了修改 │ ├─base-dir │ └─components-dir ├─libs # 本身团队研发的一些公共的方法/库,又或是针对第三方库的适配器(好比说对alert库封装一层,后面要更换库的时候就方便了) ├─npm-scripts # 与根目录下的npm-scripts目录不同,这里的不是用来公用的,而是“核心”使用到的script,好比我在这里就放了编译dll的npm script └─webpack-config # 公用的webpack配置,尤为是关系到“核心”部分的配置,好比说各第三方库的alias。这里的配置是用来给具体项目来继承的,老实说我如今继承的方法也比较复杂,回头看看有没有更简单的方法。 ├─base ├─inherit └─vendor
最后总结一下,是哪些资源被抽离出来了:
上传上来之后发现图被压小了,请到这里看原图
https://segmentfault.com/a/1190000006843916
https://segmentfault.com/a/1190000006863968
https://segmentfault.com/a/1190000006871991
https://segmentfault.com/a/1190000006887523
https://segmentfault.com/a/1190000006897458
https://segmentfault.com/a/1190000006907701
https://segmentfault.com/a/1190000006952432
https://segmentfault.com/a/1190000006992218
https://segmentfault.com/a/1190000007030775
https://segmentfault.com/a/1190000007043716
https://segmentfault.com/a/1190000007104372
https://segmentfault.com/a/1190000007126268
https://segmentfault.com/a/1190000007159115
https://segmentfault.com/a/1190000007301770
本文首发于 Array_Huang的技术博客——实用至上
,非经做者赞成,请勿转载。
原文地址: http://www.javashuo.com/article/p-xwrwoszw-bz.html
若是您对本系列文章感兴趣,欢迎关注订阅这里:https://segmentfault.com/blog/array_huang