出处:《哲玄课堂-大前端全栈实践》
前言
首先,让我们回顾下elpis
的主要目的,elpis的设计初衷是为了复用80%的功能,并将提供20%功能的可定制化能力,那么在完成elpis基础之后,为了这个目的就需要将 elpis 抽离成一个npm
包的形式可以提供给其他开发者使用
Elpis 本地测试以及业务抽离
本地调试
首先我们 elpis 的主体功能已经开发完成,那么就需要对自己开发的包进行本地安装和调试。我们在开发一个包的时候是不希望功能还没做完就发布到npm
仓库的,这个时候就需要用到 npm link
,也叫软链接
。
软链接简单说就是为开发的模块(待发布的npm包)创造一个全局链接,在主项目里链接这个依赖的模块,进行调试。
npm link
是一个用于开发时直接将本地包链接为依赖项的一个命令行工具。通常用于发布npm
包之前本地测试使用。
使用软链接
我们首先需要对在elpis目录下创建一个全局链接,然后在demo
项目下使用这个连接安装这个elpis
包,那么后续在本地对elpis
进行的改动都会实时反应在引入elpis
的demo
项目上
创建全局链接
arduino
// 进入到elpis包目录下
cd elpis
// 创建全局链接
npm link
使用这个全局链接
arduino
// 接下来切换到demo项目目录下,安装elpis即可
cd demo
// 安装elpis
npm link elpis
业务抽离以及功能提取
elpis-core 服务端解析引擎改造
- 首先我们需要对外开放 elpis-core 启动方法
javascript
const { serverStart } = require('elpis')
// 启动 Elpis 服务
const app = serverStart({})
- 修改 elpis-core 中各种loader的加载方式 现有的功能是会对 elpis 的自身的 app 文件夹下的loader进行加载 如下图所示
ini

那么要作为一个npm包发布的话 需要对使用方的 app 文件夹下的loader进行加载,那么就需要对elpis-core 的loader解析进行改造, 以middlewareLoader为例
javascript
module.exports = app => {
const middlewares = {}
// 获取 elpis/app/middlewares 下面的路径
const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}..${sep}app${sep}middleware`)
const elpisFileList = glob.sync(path.resolve(elpisMiddlewarePath, `.${sep}**${sep}**.js`))
elpisFileList.forEach(file => handleFile(file))
// 获取 业务/app/middlewares 下面的路径
const busniessMiddlewarePath = path.resolve(app.busniessDir, `.${sep}middleware`)
// 获取 middlewares 下面的所有文件名数组
const busniessFileList = glob.sync(path.resolve(busniessMiddlewarePath, `.${sep}**${sep}**.js`)) //glob.sync return.resolve all arr of filenames
busniessFileList.forEach(file => handleFile(file))
// 将文件挂载到app.middleware上
function handleFile(file) {
//获取所有的文件名
let fileName = path.resolve(file)
//截取路径
fileName = fileName.substring(
fileName.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length,
fileName.lastIndexOf('.')
)
//将名字改为驼峰式命名
fileName = fileName.replace(/[_-][a-z]/gi, s => s.substring(1).toUpperCase())
//将middlewares挂在到app对象上
let tempMiddleware = middlewares
const fileNames = fileName.split(`${sep}`) //将文件名拆分成数组
for (let i = 0, len = fileNames.length; i < len; i++) {
if (i === len - 1) {
//说明找到了对应的js文件
tempMiddleware[fileNames[i]] = require(path.resolve(file))(app) //path.resolve 获取相对路径下面的绝对路径 这个函数就是相当于引入这个${middleware}的具体js文件,因为每一个middleware都是一个函数接受koa实例,所以将app传入
} else {
//当前目录
if (!tempMiddleware[fileNames[i]]) {
//tempMiddleware里面没有fileNames这个属性
tempMiddleware[fileNames[i]] = {} //tempMiddleWare = { customModule = {} }
}
tempMiddleware = tempMiddleware[fileNames[i]] // tempMiddleware指向tempMiddleware[fileNames[i]]
}
}
}
app.middlewares = middlewares
}
webpack config 基础改造
- 首先我们需要对外开放前端工程的构建方法
scss
const { frontendBuild } = require('elpis')
frontendBuild(process.env.NODE_ENV)
- 修改loader以及webpack运行所需的第三方包的引入方式 之前
elpis
的webpack是在我们当前node的工作目录下的依赖文件下去查找webpack运行所需要的loader
以及第三包,在我们开发elpis
的时候者显然是没问题的,但是当我们将其发布为一个npm
包时,这显然是不合理的,因为我们不能要求使用者在使用elpis
的时候去自行安装这些第三方包 因此我们需要使用require.resolve()
方法去elpis
自身解析这些所需的包,这样才是符合要求的,以下是修改实例
css
// 依赖模块解析配置
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader', // 当前node的工作目录下的依赖文件下去查找
options: {
cacheDirectory: true // 启用缓存
}
},
include: [path.resolve(process.cwd(), './app/pages')]
},
...
}
}
css
// 依赖模块解析配置
module: {
rules: [
{
test: /\.js$/,
use: {
loader: require.resolve('babel-loader'), // elpis 自身查找
options: {
cacheDirectory: true // 启用缓存
}
},
include: [path.resolve(__dirname, '../../pages')]
},
...
}
}
发布npm包
- 确保elpis中的业务代码已经全部抽离
- 去除掉elpis中无用的注释以及调试代码
- 将elpis中大部分devDependencies移到dependencies中,并将elpis中一些启动命令清除
- 在README.md中编写elpis的使用文档
- 发布npm
bash
解除软连接的使用
npm unlink elpis
查看当前镜像源
# npm config get
如何存在镜像源则必须清空
# npm config set registry
再次确认镜像源
# npm config get
登录 npm
# npm login
查看 npm 账号是否正确
# npm whoami
发布
# npm publish