Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

迁移babel7+tree-shaking #67

Open
Realwate opened this issue Apr 9, 2019 · 1 comment
Open

迁移babel7+tree-shaking #67

Realwate opened this issue Apr 9, 2019 · 1 comment

Comments

@Realwate
Copy link
Contributor

Realwate commented Apr 9, 2019

babel7主要变化

废弃 babel-preset-stage-x

babel团队认为,非标准的特性是不稳定的(通过stage来推进),按照stage来划分需要花很多精力来维护。

对应 stage < 3plugin 也修改了命名,从@babel/plugin-transform-* 改为 @babel/plugin-proposal-*
proposal 表示这只是一个提案中的非标准特性,随时可能变化,使用前要考虑清楚。

废弃 babel-preset-es20xx

使用 babel-preset-env
不需要自己决定去用哪些 preset ,而是由 env 自动的根据要支持的环境启用相应的 preset

使用 Scoped Packages

更加清晰,易于区分 official package 和 community package

如:
babel-core -> @babel/core
babel-preset-env -> @babel/preset-env

迁移步骤

# 会修改 babelrc 和 package.json
npx babel-upgrade --write
// babelrc before
{
  "presets": [
    "react",
    "es2015",
    "stage-0"
  ],
  "plugins": [
    "transform-decorators-legacy",
    "react-hot-loader/babel"
  ]
}
// after
{
  "presets": [
    "@babel/preset-react",
    "@babel/preset-env"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "react-hot-loader/babel",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-json-strings",
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    "@babel/plugin-proposal-optional-chaining",
    [
      "@babel/plugin-proposal-pipeline-operator",
      {
        "proposal": "minimal"
      }
    ],
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-do-expressions",
    "@babel/plugin-proposal-function-bind"
  ]
}

这样就很快完成了 babel7 的升级,但是还不够。

  1. 因为我们之前是直接引用 preset-stage-0,升级后实际上很多 proposal-plugin 是没用到的(扫下每个插件的文档)
  2. babel7config-file 也有一些变化,简单来说,它提供了两种配置 babel.config.js.babelrc
    babel.config.js 作为整个项目通用的配置,可作用于 node_modules。而 .babelrc 只作为所在 package 的配置,不会影响到其他package
    这意味着我们项目根目录下配置的 .babelrcnode_modules 是不起作用的,而我们很多库都依赖 babel 的转译。

解决:项目根目录新建个 babel.config.js ,同时删掉没用到的 proposal-plugin (package.json中也对应删除),结果如下。

// babel.config.js
module.exports = (api) => {
  api.cache(true)
  return {
    'presets': [
      '@babel/preset-react',
      '@babel/preset-env'
    ],
    'plugins': [
      [
        '@babel/plugin-proposal-decorators',
        {
          'legacy': true
        }
      ],
      'react-hot-loader/babel',
      '@babel/plugin-syntax-dynamic-import',
      [
        '@babel/plugin-proposal-class-properties',
        { 'loose': true }
      ],
      '@babel/plugin-proposal-function-bind'
    ]
  }
}

babel-transform-runtime

babel 在转换语法的时候会生成一些helper(如 class 的 classCallCheck 等),@babel/plugin-transform-runtime 这个插件会将所有生成 helper 的地方,统一从@babel/runtime引入,减少了冗余代码。

babel.config.js 中添加 @babel/plugin-transform-runtime,然后 install

npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime

因为我们的编译包括 node_modules,要忽略 @babel/runtime 本身的处理。

node_modules下的包可能使用了多种模块格式,而 babel-plugin-transform-runtime 默认是添加 import 语句,wabpack中同一模块下,import 语句不能和 module.exports 同时使用 (babel文档 webpack相关issue)
(PS: 原来可以是因为 babel 默认会将所有的模块转化成 cjs ,再传给 webpack。但是这样无法做 tree-shaking,下文会提到)

所以添加配置。

'sourceType': 'unambiguous', // 自动推断编译的模块类型
'ignore': [/@babel[/\\]runtime/], // 忽略 @babel/runtime处理

polyfill

polyfill 部分变化不大,继续走 script 引入,不经过 webpack 打包。

tree-shaking

webpackproduction mode 会自动进行 tree-shaking 优化,消除未使用到的模块代码,原理是通过 es6 module 的静态分析。

需要配置 babel 不转换我们的 module,给 env 加个配置,让 webpack 来处理模块。

['@babel/preset-env', {
    'modules': false
}]

我们的代码中(业务代码、库)可能会混用 importmodules.exports ,这在 webapck 中是不允许的。issue

如下代码会报错

// enum.js 混用了 import 和 modules.exports
import { i18next } from 'gm-i18n'
let enum = {
  a, b
}
modules.exports = enum

// 引用模块
import { a } from 'enum'
// 提示
// export 'a' was not found
// console 输出
// enum.js:347 Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

解决:统一只使用es6模块。也可以配置babel把模块先转换成cjs格式,但是这样webpack就不能做tree-shaking,不推荐

一般而言,tree-shaking 对业务代码没多大作用,主要是针对 node_modules 下的库。webpack4 新增了 sideEffects 选项,进一步优化了 tree-shaking 的结果。可以看下Webpack 中的 sideEffects 到底该怎么用?
简单来说,如果一个模块的export没有被任何文件用到 -> 去掉这个模块代码不会产生任何影响(反例是polyfill)-> 那就可以设置 sideEffects:false

对于我们自己发布的库,统一使用 es6 module,并且在 package.json 中加上 sideEffects: false(也可以设置为具体的文件),这样跟着业务代码一起打包,就能通过 tree-shaking 去除没用到的模块代码(例如我们的图标库 gm-svg,针对不同的项目去掉没用到的图标)。

最终...

最后的 babel.config.js 文件内容如下

module.exports = (api) => {
  api.cache(true)
  return {
    'sourceType': 'unambiguous', // 自动推断编译的模块类型(cjs,es6)
    'ignore': [/@babel[/\\]runtime/], // 忽略 @babel/runtime
    'presets': [
      '@babel/preset-react',
      ['@babel/preset-env', {
        'modules': false
      }]
    ],
    'plugins': [
      [
        '@babel/plugin-proposal-decorators',
        {
          'legacy': true
        }
      ],
      'react-hot-loader/babel',
      '@babel/plugin-syntax-dynamic-import',
      [
        '@babel/plugin-proposal-class-properties',
        { 'loose': true }
      ],
      '@babel/plugin-proposal-function-bind',
      '@babel/plugin-transform-runtime'
    ]
  }
}

package.json 新增的依赖如下

  "dependencies": {
    "@babel/runtime": "^7.4.3"
    },
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/plugin-proposal-class-properties": "^7.0.0",
    "@babel/plugin-proposal-decorators": "^7.0.0",
    "@babel/plugin-proposal-function-bind": "^7.0.0",
    "@babel/plugin-syntax-dynamic-import": "^7.0.0",
    "@babel/plugin-transform-runtime": "^7.4.3",
    "@babel/preset-env": "^7.0.0",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.0",
    }
@liyatang
Copy link
Contributor

学到了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants