项目架构
公司项目分为以下几种架构:
- 主项目-扩展项目:扩展项目前端独立,是以 npm 包的形式安装到主项目,后端可以独立编译,但不能独立运行,即后端与主项目共用一套,主项目需要对扩展项目编译出的后端代码进行监听来实现增量编译
- 主项目-子项目:微前端架构,子项目前、后端独立可运行
需求背景
需要解决主项目-扩展项目架构下主项目的后端不能增量编译的问题
技术选型
选型 | 优点 | 缺点 |
---|---|---|
直接在 node_modules 目录下开发 | 主项目已实现对 node_modules 下扩展项目的监听 | 每次 npm install 都要重新新建项目 |
gulp 文件同步 | 有第三方插件 | 需要多运行一个文件同步脚本 |
npm link | 不需要多运行一个脚本 | 需要每次手动重启主项目来让后端代码生效 |
nodemon + npm link | 监听到扩展项目变化自动重启 | 每次 npm install 都要重新 npm link |
解决过程
gulp 文件同步
核心逻辑:监听扩展项目编译出来的后端代码,发现改动时同步到 node_modules 下扩展项目的位置
./config.js
module.exports = {
//同步文件更改到指定目录
syncTo: '../主项目/node_modules/扩展项目/extend'
}
复制代码
./gulpfile.js
const gulp = require('gulp')
const fileSync = require('gulp-file-sync')
const {syncTo} = require('./config')
const mkdirp = require('mkdirp')
const fs = require('fs')
const watch = require('gulp-watch')
const run = require('run-sequence')
gulp.task('sync', function() {
try {
let state = fs.statSync(syncTo)
if (!state.isDirectory()) {
mkdirp(syncTo)
}
} catch (e) {
console.log(e)
mkdirp(syncTo)
}
fileSync('extend', syncTo)
})
gulp.task('watch-extend', function() {
watch([
'./extend/**',
], function() {
run('sync')
})
})
gulp.task('watch', ['watch-extend'])
gulp.task('default', ['sync', 'watch'])
复制代码
nodemon + npm link
- 对扩展项目进行 npm link 操作,然后在主项目中 npm link 扩展项目,可以在主项目的 node_modules 下看到指向扩展项目的软链接
- 主项目使用了 nodemon 来监听 node_modules 文件变化,鉴于每次重启主项目之后扩展项目的代码才会生效的问题,需要解决 nodemon 不能监听软链接下文件变化的问题
核心逻辑:
- 在不影响 nodemon 脚本调用逻辑的情况下,在 nodemon.json 添加
watchSymbolLink
属性,明确监听软链接的文件位置 - 利用 glob 三方库读取 watchSymbolLink 下的文件,当判断读到的文件是软链接文件时(isSymbolicLink),读取真实的文件路径(realpathSync)
- 在监听原来文件的基础上,调用 nodemon 监听另外真实的文件路径
package.json
{
"scripts": {
"dev:server": "node bin/nodemon.js --config nodemon.json ./bin/www",
},
}
复制代码
bin\nodemon.js
const nodemon = require('nodemon')
const nodemonCli = require('nodemon/lib/cli')
const options = nodemonCli.parse(process.argv)
const glob = require('glob')
const fs = require('fs')
const lodash = require('lodash')
const path = require('path')
// "watchSymbolLink": [
// {
// "url": "node_modules/sugo-analytics-extend-*",
// "path": "/extend/app/**/*"
// },
// ],
// 或
// "watchSymbolLink": [
// "node_modules/sugo-analytics-extend-*",
// ],
const { configFile, ...optionsRest } = options
const nodemonJsonPath = path.join(process.cwd(), configFile)
const nodemonJson = require(nodemonJsonPath)
const { watch, watchSymbolLink = [], ...nodemonJsonRest } = nodemonJson
const dirs = []
watchSymbolLink.forEach(item => {
const { url, path: linkPath = '' } = lodash.isObject(item) ? item : { url: item }
const paths = glob
.sync(url)
.filter(a => fs.lstatSync(a).isSymbolicLink())
.map(a => path.join(fs.realpathSync(a), linkPath))
dirs.push(...paths)
})
const opts = {
...optionsRest,
...nodemonJsonRest,
watch: [...watch, ...dirs]
}
// console.log(opts)
nodemon(opts)
nodemon.on('quit', function () {
process.exit()
})
const packageJsonPath = path.join(process.cwd(), 'package.json')
// checks for available update and returns an instance
const pkg = JSON.parse(fs.readFileSync(packageJsonPath))
if (pkg.version.indexOf('0.0.0') !== 0 && options.noUpdateNotifier !== true) {
require('update-notifier')({ pkg }).notify()
}
复制代码
nodemon.json
{
"restartable": "rs",
"ignore": [".git", "node_modules", "node_modules/**/node_modules"],
"verbose": true,
"execMap": {
"js": "node --harmony"
},
"events": {
"restart": "osascript -e 'display notification \"App restarted due to:\n'$FILENAME'\" with title \"nodemon\"'"
},
"watch": ["src/server", "src/common", "config.default.js", "node_modules/sugo-analytics-extend-*/extend/app/**/*", "config.js"],
"watchSymbolLink": [
{
"url": "node_modules/sugo-analytics-extend-*",
"path": "extend/app/**/*"
}
],
"env": {
"NODE_ENV": "development"
},
"delay": "1000",
"ext": "js,json"
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END