Open Hilshire opened 5 years ago
想把不同项目可以共用的组件抽取出来,发到公司的npm镜像上。能发包的组件,和项目里的业务组件,要求本身就不一样——不如说正确的发包本身就是一种要求。
抽象的方式是建立一个项目提供一个基本的壳然后一点点放进去。没什么太大意义但是玩一玩还不错。
按照Publishing NPM package with Rollup, Babel, Flow, Jest and ESLint 进行了配置然后发现测试代码没有被编译。测试代码:
let a = 1;
new Promise((resolve, reject) => {
setTimeout(() => resolve(a), 0);
})
顺便一体如果没用到 a 这边变量,上面的赋值语句会被处理掉。
然后发现这篇文章已经有一点过时了,babel 现在把包放在 @babel 域下,所以安装的包也发生了变化。 babel-core -> @babel/core babel-preset-env -> @babel/preset-env 修改之后顺利编译,但是Promise没编译,可能要手动加垫片。不过好麻烦,先放着吧。 最后的结果:
var a = 1;
new Promise(function (resolve, reject) {
setTimeout(function () {
return resolve(a);
}, 0);
});
//# sourceMappingURL=index.esm.js.map
稍微有点规模的 rollup 配置一般都能见到这两个东西,那么它们是什么呢? 其实官网上就有介绍 @babel/plugin-transform-runtime 它干了两件事:
Babel uses very small helpers for common functions such as _extend. By default this will be added to every file that requires it...This is where the @babel/plugin-transform-runtime plugin comes in: all of the helpers will reference the module @babel/runtime to avoid duplication across your compiled output. The runtime will be compiled into your build.
@babel/polyfill
和 提供的内建函数污染全局作用域
Another purpose of this transformer is to create a sandboxed environment for your code. If you use @babel/polyfill and the built-ins it provides such as Promise, Set and Map, those will pollute the global scope...he transformer will alias these built-ins to core-js so you can use them seamlessly without having to require the polyfill.
不过看上面的东西就要搞懂做了什么事很难的,所以找了另一篇文章: 一口(很长的)气了解 babel 这样就差不多了 还想了解就再看这个: 你真的会用 babel 吗? 羡慕大佬的博客
要注意 @babel/plugin-transform-runtime
是运行时使用的依赖,对应的@babel/runtime
需要放在 dependencies
而不是 devDependencies
中(这一条存疑,希望有人能指教一下)
做好之后 Promise 会变成从 corejs 中引入的模块
import _Promise from '@babel/runtime-corejs2/core-js/promise';
之后发现这样配出来的 rollup 是不能编译 requrie 的。
emm... 我选择webpack
项目使用的是 iview@2.5.3
,由这个项目抽出来的业务组件也必然依耐 iview
如果按照通常的方式,让项目依赖 iview
, 会导致如果要使用组件库,就要安装 iview
. 如果版本相同还好说,版本不同就会单独安装一个不同版本的 iview
,这显然是不可接受的。做成这样的话就无法对外提供服务了。
不过 npm
管理依赖的方式很全面。除了开发常见的 dependencies
和 devDependencies
之外,还有 peerDependencies
dependencies: 指定项目运行时所依赖的模块 devDependencies: 指定项目开发时所需要的模块 peerDependencies:指定当前模块所在的宿主环境所需要的模块及其版本 谈谈npm依赖管理
更全面的包管理可以看这里:你需要知道的几类npm依赖包管理
可以看一下 html-webpack-plugin
的 package.json
,可以看到它就是用 peerDependencies
指定了插件依赖 webpack
的。安装这个插件不会安装 webpack
,但是如果你的项目中没有 webpack
会导致报错。
看一下 package.json
的文档,大部分人都会惊讶 package.json
提供了这么多选项。
0.0.1
版本完成之后,理所当然的要发到公司的 npm
镜像上。然而公司的 npm
镜像功能一直奉欠,这让我十分难受。我那个组件库名字叫 canon
,这确实是一个很容易想到的名字, npm
上也确实有一个包叫 canon
。
一般来说,这样的包是发不上去的,我是这么想的。然而实际上发布成功了,我上镜像上一查,就找到了那个同名的包。镜像把我的推送合并到那个包里去了。
如果 canon
的作者看到,必然会满脸问号吧。
于是给自己的包加了个域。之前的 gitlab
群组取名非常随意,唤作: frostmourne
。于是,公司 npm
镜像的域里面多了一个 frostmourne
scope,里面有一个叫 canon
的项目。
嗯, Lich king 想不到还是个音乐爱好者哩。
关于 scope
相关在这里可以查到: npm - 参考手册
rollup
在使用 webpack
之后,项目变得顺风顺水起来。然而,当我把包发到 npm
镜像之后,有一个问题等待着我。 export default
的对象没有被引入,引入的是一个空对象。
// canon/src/index.js
export default {
version: '0.0.1',
install,
...
}
// test-canon-pkg/src/main.js
import canon from '@frostmourne/canon';
console.log(canon); // {}
在网上搜索了一下,得到了这个解释:https://stackoverflow.com/questions/44541561/webpack-output-is-empty-object
于是按照上面的修改配置,成功解决问题。
...
output: {
path: path.resolve(__dirname, '..', 'dist'),
filename: 'index.js',
library: 'canon',
libraryTarget: 'umd',
},
...
之前一直在想为什么写轮子的时候偏向 rollup
呢?原因很简单,它从一开始的出发点就是这个!而 webpack
的出发点是 web app
,这导致两者的侧重点不同。
比如对 webpack
来说 code split
是最重要的功能之一,以至于在 webpack4
直接从插件集成到配置里。而对 rollup
,把代码打包成单个文件时合适的。这些细节会一点点体现出来。
这样一来,这个项目应该用 rollup
打包。我还得把它请回来。
不过也不会 remove webpack
。和众多组件库一样,canon
有自己的介绍页。这一部分需要 webpack
。
工具本身并不重要,重要的是在合适的场合使用合适的“锤子”。
然后被拒了。这pr确实比较水,也算是意料之中。不过即使只是提一个pr也是有门槛的。
pr是一个工作流的问题。这里有两篇不错的文章,而我不打算再出篇幅说这个。其中的 git
操作本身就是开发应该掌握的知识,每一步的原因也很明了。和常见的工作流相比,多了一步 fork
和 pr
本身而已。而解释其后工作流相关内容,已超出我的能力。我对工作流并无深刻的见解,恐怕不能写的比这两篇文章更好。所在团队的项目也不太可能基于同样的工作流。
不过其它部门也确实有使用 pr
的项目,我至今才知道其中的意义,而项目的建立者时候并没有向其它人科普的打算。对于老师,也要有学生才行吧。
GIT团队合作探讨之二--Pull Request 3.3 创建 Pull Request
项目中独立于业务的公共组件单独提取成了一个 npm package,之后有适合做成公共组件的就在 canon 里迭代。业务组件与项目分开,其它项目可以方便的引用。
同时,团队成员在写组件时要考虑之后可能会放到 canon 之中,组件变得更加纯粹。
而 canon 本身的发展也可以促进团队的进步,如何写好一个组件库是一个很讲究的事情。关于canon,也许写在另一个commet里比较好。
花了两天把项目代码按照公司的 eslint
规范犁了一边...想死。码农真是名不虚传啊。如果不是 eslint 可以帮你修复大部分的格式问题,感觉要升仙了。
于是目前在着手把 webpack2
迁移到 webpack4
。
遇到的第一个问题,怎么用 npm
把 依赖升级到最新版本。
看了下文档,没找到答案不过看到很有趣的事情。
As of npm@2.6.1, the npm update will only inspect top-level packages. Prior versions of npm would also recursively inspect all dependencies. To get the old behavior, use npm --depth 9999 update.
不知道升级最外层的时候外层依赖问题是怎么解决的。
然后文档没有给我答案,不过看到那个 latest
想起来了该怎么做。就是一遍遍敲 latest
手有点疼。而且会给我安装 beta
版本,略不安。
目前是这样:
dev
分支进行开发lint
分支处理 eslint
问题,这一块已经结束了webpack
分支升级 webpack
可以想见之后解决冲突抓耳捞腮的样子了
会更新
babel
是因为babel-loader
也更新了
.babelrc
也没啥,把 @babel
加进去,删除了不需要的配置。
然后加上 @babel/plugin-proposal-class-properties
, @babel/plugin-syntax-dynamic-import
。这是因为项目中有相关语法,没有的话不需要加。
本来到这里应该差不多了,然而一跑发现报错
You may need an appropriate loader to handle this file type.
从错误信息来看应该是不能解析 css 部分了。增加 VueLoaderPlugin
后解决。参考了这篇文章:
ac undefined A We - Config from Scratch For Vue
删除了 minimize
选项,新版本的 css-loader
已经不支持了,不知道现在是怎么个压缩法。
之后应该会再加上 extract-loader
,不过当务之急是让项目能正常跑起来。
加个 TODO
吧
升级过程中最难解决的问题,大致如下:
Module parse failed: 'import' and 'export' may only appear at the top level. You may need an appropriate loader to handle this file type.
之前也说过项目中有动态导入,也加了对应的 babel
插件,但是依然报错。结果这居然是一个 npm
bug.
详见:Parsing of import()
fails in 4.29.0 (Compilation issue, related to dynamic import)
改用 yran
装了下依赖就解决了。
最难解决问题的有力竞争者
按照上面说的升级 extract-text-webpack-plugin
,没有解决。
升级了所有 webpack
相关的插件,没有解决。
最后发现 dev-server
里就用到了 plugin
。这时候,我们开始理解 webapck
编译 API
这里贴一张图,可以感受一下 show html-webpack-plugin-after-emit error
vue-cli 2
中的代码改成了这样:
compiler.hooks.compilation.tap('compilation', function (compilation) {
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
但是还是不行。首先打出来的包就不一样,而且hooks没有这些值。到此我已经画了一天时间,和计划的时间有出入了。我必须寻求简单一点的方法。
于是决定用 vue-cli
搞一个样板项目然后把 src
塞进去,希望这次可以顺利
无法解决中间件的问题,我尝试使用 webpack-dev-server
,然后就...成功了。
但是在尝试使用 webpack
打包时犯了难。webpack
执行,然后没有结果。没有 output
,没有结束,程序就挂在那儿。我倒是可以挂机一晚上看它是不是真的在打包,不过最终没有选择这么做。
写了一个最简单的 production config
,除了 mode
和 css loader
外什么都没有,结果成功了。
最终的数据,打包时间:66057ms -> 113081ms
???
因为 product 为空了,之前配置的优化项也没了,比如某 dll
,又比如某parallel-uglify
插件,不过时间加了一倍还是有点问题吧。
不过体积倒是小了不少,12.1M -> 8.77M
不过还是有一种花费这么长时间干了什么的感觉...
5.16 更新:重新打包了一次时间大致差不多,看来每次打包时间波动也是有的。总算有动力继续下去了。
webpack.prod.conf.js
之前用的是 prod mode
的默认配置,打包出来的结果和之前并不相同。使用原先的 webpack.prod.conf.js
后,报错。
原因是 utils
里的 generateLoaders
使用了 extract-text-webpack-plugin
.
Since webpack v4 the extract-text-webpack-plugin should not be used for css. Use mini-css-extract-plugin instead.
vue-loader
里有了新的方案:
npm install -D mini-css-extract-plugin
// webpack.config.js
var MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 其它选项...
module: {
rules: [
// ... 忽略其它规则
{
test: /\.css$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
// ... 忽略 vue-loader 插件
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
删除 htmlWebpackPlugin
里的 chunksSortMode: 'dependency'
, webpack4
里不再需要了。
https://github.com/jantimon/html-webpack-plugin/issues/1055#issuecomment-424605059
搞定了——虽然这么说,其实开发体验变化不是很大。当然,打包出来的体积实实在在变小了。另外,从 webpack-bundle-analyzer
,打出来的包有相当程度的优化。考虑到项目是从 webpack2 迁移过来,也是理所当然吧。
这个项目稍微有点历史,带来的结果就是它没有前后端完全分离。每次打包之后需要手动复制粘贴到后端的目录里面去,非常麻烦了。所以想写一个脚本处理这个事情,希望能做到这样:
打包时自动更新后端代码,然后把打出来的文件复制到后端仓库的地址里,然后 push
旧版本的 vue-cli
的结构还是比较简单,build
就是跑一个 js
脚本,它会删除静态文件,并通过 ora
, chalk
提供更友好的终端。既然有了这么一个位置,我就不客气的那来用了。
个人对 node
不是很熟悉,再次捡起来 error-first
写法着实恶心到我了。询问组里某大神,才知道 node
现在提供了 util.promisify(original). 为什么不改 api
风格呢... 这是绝对的痛点吧。
为了这一块的功能,用到了以下 pkg
这些是原先就有的,同时又加上了
本以为是个简单的工作,结果出人意料的麻烦,似乎又能在上面折腾不少时间
其实并不是我做的,而是和另一位大佬商议了一下,他顺手就做了。
我不是很清楚 vue-cli3 是什么样,如过是用 vue-cli2 的项目的话,违反 eslint 规则会造成编译失败,并在客户端上显示一个友好的错误提示。其中,提示显示在客户端的设置在这里:
module.exports = {
//...
devServer: {
overlay: {
warnings: true,
errors: true
}
}
};
这种做法还不错,但是在开发中频繁触发 eslint 的感觉不是很好,尤其很多错误是可以用 --fix 自动处理掉的。一种观点是 eslint 不应该阻碍开发,有很多人在反思如何更好的使用 eslint. 有一种实践是这样:
当然, vue-cli 不可能默认使用它的开发团队都配置了 git hook;另一方面,想使用 eslint hook 团队已经具备了工程化意识,并不需要 vue-cli 去做额外的事情了。
一开始的时候,我打算简单的更新一下 git hook Git Hooks
而我的同事比我走的深一点。他加了两个依赖: husky lint-staged
其中,husky 是一种可以友好的处理 hook 处理的工具, lint-staged 可以只检查提交的内容。这已经是一套非常成熟的方案了。实际上,当我着手做这件事的时候,深感我司的前端已经远远落后于大部队了。
有的朋友可能会觉得奇怪:明明标题是重构,怎么看内容就一点一点往工程化偏移了?
这里面的原因有二:
所以,一段时间之后,工作中心一点点偏向了工程化。当然重构一直有在做,只是更多的会在项目整体上做一些调整,这是可以直接起效的。
工程化有一个很大的不同:普通的业务由产品给你提出需求,你实现他的功能就好。而产品的需求往往很具体。而工程化方面需求需要自己想,做了 hook, CI 都要自己寻找什么样的方案适合你的团队。对我而言这是另一个意义上的挑战。
参考了这些: 用 GitLab CI 进行持续集成 Docker搭建自己的Gitlab CI Runner 劈荆斩棘:Gitlab 部署 CI 持续集成
虽然不知道是否正确,我把 runner 理解为 CI 服务器。runner 的安装大致分为以下步骤:
在register的时候会让你选择 runner 的类型,很多实例都是 docker
。其实如果不熟悉的话(比如我),选择 shell
会更合适
其中 runner 需要和 gitlab 版本对应。我司的 gitlab 版本为 GitLab Community 8.17.4,已经大幅落后,使用官网上最新的 runner 会报错。启动之后会在 CI 页面上出现对应的 runner。
测试用流水线如下:
# 定义 stages
stages:
- build
- test
# 定义 job
job1:
tags: [bd_tag]
stage: test
script:
- echo "I am job1"
- echo "I am in test stage"
# 定义 job
job2:
tags: [bd_tag]
stage: build
script:
- echo "I am job2"
- echo "I am in build stage"
注意其中的 tags,需要和上图的 runner
对应。准确来说,需要和已有的 runner
的 tag
对应,如果没有找到对应的 runner
会因此而 pending
然而,这样简单的测试依然报错了
有时真会让人觉得不如想办法装一个 jenkins
算了(哭笑)
待补充...
项目重新开始优化打包等问题了,webpack 相关单独写 issue 记录了 #24
刚进公司写的项目竟然迭代了两年,是该重构一下升个级了。开个贴记录一下吧。
eslint-plugin-html -> eslint-plugin-vue
V2版本的 vue-cli 使用的还是 eslint-plugin-html,之后才升级成 eslint-plugin-vue 升级了 eslint 版本之后,较低版本的 eslint-plugin-html 报错了
详见 eslint-plugin-html error: It seems that eslint is not loaded. Compatibility with ESLint 4