修剪AST树减少webapck打包尺寸

背景

公司每年都有不同的H5活动(vue2)上线、下线,这些代码都在存在于同一个仓库中。

虽然过期的活动代码,增加了无效的编译时间和打包内容,但是我们并不想删除它们,毕竟代码即资产。

所以,我们希望实现一种方案,既能保留所有活动路由,又能将过期活动内容从包中剔除。

webpack loader

loader 用于对模块的源代码进行转换。我们可以编写一个 loader 来参与 webpack 打包过程,进而修改源代码。一个最简单的 loader 如下——它把接收到的源码原封不动的返回了:

module.exports = function doNothingLoader(content) {
  return content
}

修剪路由AST

使用自定义 loader,我们就可以在 webpack 打包时修改源码而不会产生副作用。具体的思路是:通过this.resourcePath识别源代码是否为路由文件,若是,则解析代码,并将其中的component字段删除,以达到保留路由而删除内容的目的。

router.js源码

import router from "@/router";
const routes = [{
  name: 'test',
  path: '/promotion/test',
  component: () => import(/* webpackChunkName: 'promotion/pages/test' */'./pages/test/index'),
  meta: {
    title: '测试'
  }
}]
router.addRoutes(routes);

修剪后(删除了 component 字段)

import router from "@/router";
const routes = [{
  name: 'test',
  path: '/promotion/test',

  meta: {
    title: '测试'
  }
}]
router.addRoutes(routes);

代码实现

通过 babel 插件,可以修改 babel 转换后的 AST 树。复制任何有效的JS代码到astexplorer,能够非常清晰的看到其AST结构。

我们要做的事就变得很简单了:如果是路由文件,则遍历其 AST,找到component字段并删除。

const babel = require('babel-core'); // v6.26.0
const isWin = /^win/.test(process.platform)
const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)
const routerRegx = /projects\/test\/(.*)router\.js/
const isInvalidRoute = () => null // 根据项目实际情况实现 isInvalidRoute

const pruneInvalidRoute = {
  visitor: {
    ObjectExpression(path) {
      const pathNode = (path.node.properties || []).find(p => p.key.name === 'path') 
      if (pathNode) {
        const routeVal = pathNode.value.value || ''
        if (isInvalidRoute(routeVal) {
          const compIndex = properties.findIndex(p => p.key.name === 'component')
          if (compIndex >= 0) {
            properties.splice(compIndex, 1)
            return
          }
        }
      }
    }
  }
}

module.exports = function pruneInvalidRouteLoader(content) {
  const filePath = normalizePath(this.resourcePath)
  const matched = routerRegx.test(filePath)
  if (!matched) return content
  const result = babel.transformFileSync(filePath, {plugins: [pruneInvalidRoute]})
  return result.code
}

唯一要注意的是,由于 babel 的版本差异,其接口略有不同,具体可查看版本对应文档。本文所用的babel-core版本为:6.26.0。

posted @ 2021-12-01 18:29  Liaofy  阅读(315)  评论(0编辑  收藏  举报