转载地址:https://www.jianshu.com/p/189755f9ce43
1. 后编译介绍
目前大部分的前端项目开发都是使用es6+的代码并且使用babel进行编译,而传统的对代码包的引入都是引入一个被babel编译好的文件入口,这样带来了一个缺点,那就是项目中无用的代码也会被引入到最终打包的文件中。
后编译的思想是不会在包发布的时候进行编译,而是会在使用这个包的前端项目构建的时候进行编译。
下面就来看一下cube-ui对更好的使用后编译作出的探索。
2. 后编译遇到的问题
2.1 后编译和普通编译的兼容
由于webpack的开发团队也考虑到直接引入一个编译好的代码包带来的一些问题,所以在webpack2.x之后引入了一个属性。
module.exports = { // ... resolve: { mainFields: ["browser", "module", "main"] } // ... }
在引入一个第三方包的时候,会根据mainFields的队列头到队列尾的顺序检测package.json文件中是否有这些字段值。
上面的示例代码是target值为webworker时的默认配置,当target为其他值时,mainFields的默认值为:
mainFields: ["module", "main"]
在使用cube-ui时我们如何配置使用后编译或者时普通编译呢?
后编译时直接引入代码包时就会默认引入ES2015 module的代码,对于普通编译需要在webpack.base.conf.js中配置别名解析。
module.exports = { // ... resolve: { alias: { // ... "cube-ui": "cube-ui/lib" } } // ... }
这样在引入时就可以引入到编译后的代码。
2.2 引入路径
在做到上面的几步后,就可以实现代码包的后编译,但是这里有一个引入路径的问题。比如我们要引入cube-ui的switch组件,我们需要这样引入:
import Switch from 'cube-ui/src/modules/switch'
这样的引入方式对于用户来说肯定不是很友好。
通过cube-ui团队开发的webpack插件webpack-transform-modules-plugin可以通过下面的方式进行组件的引入。
import {Switch} from 'cube-ui'
在引入这个插件的同时需要在package.json中引入下面的代码:
{ // ... "transformModules": { "cube-ui": { "transform": "cube-ui/src/modules/${member}", "kebabCase": true } }, // ... }
通过这段配置来解析cube-ui的引入路径。
同时还要在webpack.base.conf.js中添加webpack-transform-modules-plugin插件。
var PostCompilePlugin = require('webpack-post-compile-plugin') var TransformModulesPlugin = require('webpack-transform-modules-plugin') module.exports = { // ... plugins: [ // ... new PostCompilePlugin(), new TransformModulesPlugin() ] // ... }
2.3 嵌套后编译
如果我们要使用后编译还要在webpack配置的rules中加入include配置,比如要后编译代码包A和B,就要写下面的代码:
module.exports = { // ... module: { rules: [ // ... { test: /.js$/, loader: 'babel-loader', // 注意这里的 include // 除了 src 还包含了额外的 node_modules 下的两个包 include: [ resolve('src'), resolve('node_modules/A'), resolve('node_modules/B') ] }, // ... ] }, // ... }
如果在后编译的生态中,会出现下面的情况:
这里借用cube-ui团队的一张嵌套后编译的图。
当A为后编译,A依赖的包C和D也为后编译时,需要在应用的webpack配置中增加rules.include的配置:
module.exports = { // ... module: { rules: [ // ... { test: /.js$/, loader: 'babel-loader', // 注意这里的 include // 除了 src 还包含了额外的 node_modules 下的两个包 include: [ resolve('src'), resolve('node_modules/A'), resolve('node_modules/B'), resolve('node_modules/C'), resolve('node_modules/D') ] }, // ... ] }, // ... }
cube-ui团队为了解决这种情况开发了webpack插件webpack-post-compile-plugin,通过这个插件可以通过获取package.json文件中的compileDependencies属性来获取当前包依赖的后编译包,并且将这些包添加到webpack配置的rules.include中。
webpack-post-compile-plugin插件的核心代码如下:
PostCompilePlugin.prototype.apply = function (compiler) { var that = this compiler.plugin(['before-run', 'watch-run'], function (compiler, callback) { // ... var dependencies = that._collectCompileDependencies(compiler) if (dependencies.length) { var rules = compiler.options.module.rules rules && rules.forEach(function (rule) { if (rule.include) { if (!Array.isArray(rule.include)) { rule.include = [rule.include] } rule.include = rule.include.concat(dependencies) } }) } callback() }) }
就是在webpack编译过程中的before-run和watch-run的两个钩子中进行依赖的收集。
3. 使用后编译的其他问题
由于cube-ui使用的是stylus进行样式的编写,如果采用后编译需要在前端工程中配置stylus的解析。
在vue-loader的配置中添加下面的配置。
const stylusOptions = { 'resolve url': true } return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus',stylusOptions), styl: generateLoaders('stylus',stylusOptions) }
总结
后编译的优点是能对依赖包中的代码进行依赖分析,从而让公共的依赖被提取出来。由于后编译是在前端应用构建时一起构建,所以babel转换的API只有一份,不会冗余。
缺点是包的babel配置要与应用的babel配置兼容,依赖包不能使用alias和DefinePlugin。编译时间会变长。
后编译是一种生态,如果你编写的代码包需要使用后编译,那么就要遵守后编译的配置规定。
作者:imamba
链接:https://www.jianshu.com/p/189755f9ce43
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
最新评论