Webpack 2中的摇树ES6模块

by Jake Wiesler

杰克·威斯勒(Jake Wiesler)

Webpack 2中的摇树ES6模块 (Tree-shaking ES6 Modules in webpack 2)

Webpack 2 was just released from beta last week. It brings with it a variety of anticipated features, including native support for ES6 modules.

Webpack 2上周刚刚从Beta发布。 它带来了各种预期的功能,包括对ES6模块的本地支持。

Instead of using the var module = require('module') syntax, webpack 2 supports ES6 imports and exports. This opens the door for code optimizations like tree-shaking.

Webpack 2支持ES6 importsexports ,而不是使用var module = require('module')语法。 这为诸如tree-shaking的代码优化打开了大门。

什么是摇树? (What is tree-shaking?)

Popularized by Rich Harris’ Rollup.js module bundler, tree-shaking is the ability to only include code in your bundle that is being used.

摇树是由Rich Harris的Rollup.js模块捆绑程序流行的功能,它可以仅将代码包含在正在使用的捆绑程序中

When I first played around with Rollup, I was amazed at how well it worked with ES6 modules. The development experience just felt…right. I could create separate modules written in “future JavaScript,” then include them anywhere in my code. Any code that goes unused doesn’t make it into my bundle. Genius!

当我第一次使用Rollup时,我惊讶于它与ES6模块的配合情况。 开发经验只是感觉...正确。 我可以创建用“未来JavaScript”编写的单独模块,然后将其包含在代码中的任何位置。 任何未使用的代码都不会放入我的捆绑包中。 天才!

它解决什么问题? (What problem does it solve?)

If you’re writing JavaScript in 2017 and understand (see: JavaScript fatigue) the various tools around, your development experience probably feels pretty fluid. This is important, but what’s also important is user experience. A lot of these modern tools ends up bloating web applications with massive JavaScript files, resulting in slower performance.

如果您在2017年编写JavaScript并了解 (请参阅: JavaScript疲劳 )各种工具,那么您的开发经验可能会感觉很流畅。 这很重要,但是用户体验也很重要。 许多此类现代工具最终都使大量JavaScript文件使Web应用程序膨胀,从而导致性能降低。

What I love about Rollup is that it takes a stab at this issue, and brings a solution to the forefront of the JavaScript community. Now big names like webpack are attempting to iterate on it.

我喜欢Rollup的原因是它在此问题上颇受关注,并将解决方案带到了JavaScript社区的最前沿。 现在,像webpack这样的大牌正试图对其进行迭代。

Tree-shaking may not be “the solution to end all solutions” but it is an important piece in the larger pie.

摇摇欲坠也许不是“解决所有问题的解决方案”,但它是更大蛋糕中的重要组成部分。

一个简单的例子 (A simple example)

Before you get started I want to provide you with a trivial example of tree-shaking. Your application is made up of 2 files, index.js and module.js.

在开始之前,我想向您提供一个摇摇欲坠的简单例子。 您的应用程序由2个文件index.jsmodule.js

Inside of module.js you export 2 named arrow functions:

module.js内部,您导出2个命名箭头功能:

// module.js 

export const sayHello = name => `Hello ${name}!`; 

export const sayBye = name => `Bye ${name}!`

Only sayHello is imported into index.js file:

sayHello导入到index.js文件中:

// index.js 

import { sayHello } from './module'; 

sayHello('World');

sayBye is exported but never imported. Anywhere. Therefore, due to tree-shaking, it won't be included in your bundle:

sayBye已导出但从未导入。 任何地方。 因此,由于摇树,它不会包含在您的捆绑包中:

// bundle.js 

const sayHello = name => `Hello ${name}!`; 

sayHello('World');

Depending on the bundler used, the output file above may look different. It’s just a simplified version, but you get the idea.

根据使用的捆绑程序,上面的输出文件可能看起来有所不同。 它只是一个简化的版本,但是您明白了。

Recently I read an article written by Roman Liutikov, and he made a great analogy in order to visualize the concept of tree-shaking:

最近,我读了Roman Liutikov撰写的一篇文章,他做了一个很好的类比,以可视化摇树的概念:

“If you wonder why it’s called tree-shaking: think of your application as a dependency graph, this is a tree, and each export is a branch. So if you shake the tree, the dead branches will fall.” — Roman Liutikov

“如果您想知道为什么将其称为摇树:将您的应用程序视为依赖关系图,这是一棵树,每个导出都是一个分支。 因此,如果您摇动树,死去的树枝就会掉下来。” —罗马·柳蒂科夫

在webpack 2中摇树 (Tree-shaking in webpack 2)

Unfortunately for those of us using webpack, tree-shaking is “behind a switch,” if you will. Unlike Rollup, some configuration needs to be done before you can get the functionality you’re looking for. The “behind a switch” part might confuse some people. I’ll explain.

对于我们这些使用webpack的人来说,不幸的是,如果您愿意的话,摇晃是“开关之后的”。 与汇总不同,要获得所需的功能,需要先进行一些配置。 “切换后”部分可能会使某些人感到困惑。 我会解释。

步骤1:专案设定 (Step 1: Project setup)

I’m going to assume that you understand webpack basics and can find your way around a basic webpack configuration file.

我将假设您了解webpack的基础知识,并且可以找到有关基本webpack配置文件的方法。

Let’s start by creating a new directory:

让我们从创建一个新目录开始:

mkdir webpack-tree-shaking && cd webpack-tree-shaking

Once inside, let’s initialize a new npm project:

进入内部后,让我们初始化一个新的npm项目:

npm init -y

The -y option generates the package.json quickly without requiring you to answer a bunch of questions.

-y选项可快速生成package.json ,而无需您回答很多问题。

Next, let’s install a few project dependencies:

接下来,让我们安装一些项目依赖项:

npm i --save-dev webpack@beta html-webpack-plugin

The command above will install the latest beta version of webpack 2 locally in your project as well as a useful plugin named html-webpack-plugin. The latter is not necessary for the goal of this walkthrough but will make things a bit quicker.

上面的命令将在您的项目中本地安装webpack 2的最新beta版本以及一个名为html-webpack-plugin的有用html-webpack-plugin 。 对于本演练的目标而言,后者不是必需的,但会使操作更快一些。

Note: The command npm i --save-dev webpack@beta is still recommended by the webpack team at the time of writing. webpack@beta will eventually be phased out in favor of the webpack latest command. Check out the How To Download? section of webpack’s latest release post for more details.

注意 :在撰写本文时,webpack团队仍建议使用命令npm i --save-dev webpack@betawebpack@beta最终将被淘汰,而使用webpack最新命令。 查看如何下载? 有关更多详细信息,请参见webpack最新版本的“发布”部分。

Open up package.json and make sure they've been installed as devDependencies.

打开package.json并确保已将它们安装为devDependencies

步骤2:建立JS档案 (Step 2: Create JS files)

In order to see tree-shaking in action you need to have some JavaScript to play around with. In your project’s root, create a src folder with 2 files inside:

为了了解摇摇欲坠的实际效果,您需要使用一些JavaScript。 在项目的根目录中,创建一个src文件夹,其中包含2个文件:

mkdir src && cd src 

touch index.js 

touch module.js

Note: The touch command creates a new file through the terminal.

注意: touch命令通过终端创建一个新文件。

Copy the code below into the correct files:

将以下代码复制到正确的文件中:

// module.js 

export const sayHello = name => `Hello ${name}!`; 

export const sayBye = name => `Bye ${name}!`;
// index.js 

import { sayHello } from './module'; 

const element = document.createElement('h1'); 

element.innerHTML = sayHello('World'); 

document.body.appendChild(element);

If you’ve gotten this far, your folder structure should look like this:

如果到此为止,您的文件夹结构应如下所示:

/ 
| - node_modules/ 
| - src/ 
|    | - index.js 
|    | - module.js 
| - package.json
步骤3:从CLI进行Webpack (Step 3: Webpack from the CLI)

Since you have no configuration file created for your project, the only way to get webpack to do any work at the moment is through the webpack CLI. Let’s perform a quick test.

由于没有为项目创建配置文件,因此使Webpack进行任何工作的唯一方法是通过webpack CLI。 让我们进行快速测试。

In your terminal, run the following command in your project’s root:

在终端中,在项目的根目录中运行以下命令:

node_modules/.bin/webpack

After running this command, you should see output like this:

运行此命令后,您应该看到如下输出:

No configuration file found and no output filename configured via CLI option. A configuration file could be named 'webpack.config.js' in the current directory. Use --help to display the CLI options.

The command doesn’t do anything, and the webpack CLI confirms this. You haven’t given webpack any information about what files you want to bundle up. You could provide this information via the command line or a configuration file. Let’s choose the former just to test that everything is working:

该命令不执行任何操作,并且webpack CLI确认了这一点。 您尚未向webpack提供有关要捆绑哪些文件的任何信息。 您可以通过命令行配置文件提供此信息。 让我们选择前者只是为了测试一切正常:

node_modules/.bin/webpack src/index.js dist/bundle.js

What you’ve done now is pass webpack an entry file and an output file via the CLI. This information tells webpack, "go to src/index.js and bundle up all the necessary code into dist/bundle.js". And it does just that. You'll notice that you now have a dist directory containing bundle.js.

现在,您要做的就是通过CLI向webpack传递一个entry文件和一个output文件。 该信息告诉webpack,“转到src/index.js并将所有必需的代码捆绑到dist/bundle.js ”。 它就是这样做的。 您会注意到,您现在有了一个包含bundle.jsdist目录。

Open it up and check it out. There’s some extra javascript in the bundle necessary for webpack to do its thing, but at the bottom of the file you should see your own code as well.

打开它并签出。 捆绑包中还包含一些额外的javascript,以使webpack能够完成其工作,但是在文件的底部,您也应该看到自己的代码。

步骤4:创建Webpack配置文件 (Step 4: Create a webpack configuration file)

Webpack can handle a lot of things. I’ve spent a good chunk of my free time diving into this bundler and I still have barely scratched the surface. Once you’ve move passed trivial examples its best to leave the CLI behind and create a configuration file to handle the heavy lifting.

Webpack可以处理很多事情。 我将大量的业余时间花在了这个打捆机上,但我仍然勉强抓过表面。 一旦通过了琐碎的示例,最好将CLI留在后面,并创建一个配置文件来处理繁重的工作。

In your project’s root, create a webpack.config.js file:

在项目的根目录中,创建一个webpack.config.js文件:

touch webpack.config.js

This file can be as complicated as you make it. We’ll keep it light for the sake of this post:

该文件可以像您创建的那样复杂。 为了这篇文章,我们将其保持清淡:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: 'dist'
  },
  plugins: [
    new HtmlWebpackPlugin({ title: 'Tree-shaking' })
  ]
}

This file provides webpack with the same information you gave to the CLI earlier. You’ve defined index.js as your entry file and bundle.js as your output file. You've also added your html-webpack-plugin which will generate an html file in your dist directory. Convenient.

该文件为webpack提供了您之前提供给CLI的相同信息。 您已经将index.js定义为entry文件,并将bundle.jsoutput文件。 您还添加了html-webpack-plugin ,它将在dist目录中生成一个html文件。 方便。

Go ahead and test this to make sure it’s still working. Remove your dist directory, and in the command line type:

继续进行测试,以确保它仍在工作。 删除dist目录,然后在命令行中输入:

webpack

If everything went smoothly, you can open up dist/index.html and see "Hello World!".

如果一切顺利,则可以打开dist/index.html并查看“ Hello World!”。

Note: The use of a configuration file gives us the convenience of typing webpack instead of node_modules/.bin/webpack. Small wins.

注意:使用配置文件为我们提供了键入webpack而不是node_modules/.bin/webpack 。 小胜。

步骤5:Babel (Step 5: Babel)

I mentioned earlier that webpack 2 brings native support for ES6 modules. This is all true, but it doesn’t change the fact that ES6 is not fully supported across all browsers. Because of this, you’re required to transform your ES6 code into readily acceptable JavaScript using a tool like Babel. In conjunction with webpack, Babel gives us the ability to write your “future JavaScript” without worrying about the implications of unsupported browsers.

前面我提到过,webpack 2为ES6模块带来了本机支持。 确实如此,但这并没有改变所有浏览器均未完全支持ES6的事实。 正因为如此,你需要使用类似的工具来改变 ES6代码转换成易于接受JavaScript 巴贝尔 。 结合webpack,Babel使我们能够编写“未来JavaScript”,而不必担心不受支持的浏览器的影响。

Let’s go ahead and install Babel in your project:

让我们继续在您的项目中安装Babel:

npm i --save-dev babel-core babel-loader babel-preset-es2015

Take note of the babel-preset-es2015 package. This little guy is the reason I sat down to write all of this up.

请注意babel-preset-es2015软件包。 这个小家伙是我坐下来写下所有这些的原因。

第6步: babel-loader (Step 6: babel-loader)

Webpack can be configured to transform specific files into modules via loaders. Once they are transformed, they are added to a dependency graph. Webpack uses the graph to resolve dependencies and includes only what is needed into the final bundle. This is the basis for how webpack works.

Webpack可以配置为通过加载程序将特定文件转换为模块。 转换后,将它们添加到依赖图。 Webpack使用该图来解决依赖关系,并且仅将最终捆绑包中需要的内容包括在内。 这是webpack工作原理的基础。

You can now configure webpack to use babel-loader to transform all of your .js files:

现在,您可以将webpack配置为使用babel-loader转换所有.js文件:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: { filename: 'bundle.js', path: 'dist' },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: { 
          presets: [ 
            'es2015' 
          ] 
        }
      }
    ]
  },
  plugins: [ 
    new HtmlWebpackPlugin({ title: 'Tree-shaking' }) 
  ]
};

The module property provides a set of instructions for webpack. It says, "take any files ending in .js and transform them using babel-loader, but don't transform any files inside of node_modules!"

module属性为Webpack提供了一组说明。 它说:“获取任何以.js结尾的文件,并使用babel-loader对其进行转换,但不要在node_modules内部转换任何文件!”

We’re also passing the babel-preset-es2015 package as an option to babel-loader. This just tells babel-loader how to transform the JavaScript.

我们还将babel-preset-es2015软件包作为babel-loader的选项传递。 这只是告诉babel-loader 如何转换JavaScript。

Run webpack again to make sure everything is good. Yes? Great! What we've done is bundled up your JavaScript files while compiling them down to JavaScript thats readily supported across browsers.

再次运行webpack以确保一切正常。 是? 大! 我们所做的是将您JavaScript文件捆绑在一起,同时将它们编译为跨浏览器容易支持JavaScript。

潜在的问题 (The underlying problem)

The package babel-preset-es2015 contains another package named babel-plugin-transform-es2015-modules-commonjs that turns all of your ES6 modules into CommonJS modules. This isn't ideal, and here's why.

软件包babel-preset-es2015包含另一个名为babel-plugin-transform-es2015-modules-commonjs ,它将您的所有ES6模块转换为CommonJS模块。 这不是理想的,这就是原因。

Javascript bundlers such as webpack and Rollup can only perform tree-shaking on modules that have a static structure. If a module is static, then the bundler can determine its structure at build time, safely removing code that isn’t being imported anywhere.

诸如webpack和Rollup之类的Javascript捆绑程序只能在具有静态结构的模块上执行树状摇动。 如果模块是静态的,则捆绑程序可以在构建时确定其结构,从而安全地删除未导入到任何地方的代码。

CommonJS modules do not have a static structure. Because of this, webpack won’t be able to tree-shake unused code from the final bundle. Luckily, Babel has alleviated this issue by providing developers with an option that you can pass to your presets array along with babel-preset-es2015:

CommonJS模块没有静态结构。 因此,webpack将无法从最终捆绑包中摇晃未使用的代码。 幸运的是,Babel为开发人员提供了一个选项,您可以将其与babel-preset-es2015一起传递给您的presets数组,从而缓解了此问题:

options: { presets: [ [ 'es2015', { modules: false } ] ] }

According to Babel’s documentation:

根据Babel的文档

“modules - Enable transformation of ES6 module syntax to another module type (Enabled by default to "commonjs"). Can be false to not transform modules, or one of ["amd", "umd", "systemjs", "commonjs"]".

“modules -启用将ES6模块语法转换为其他模块类型的功能(默认启用为“ commonjs”)。 对于不转换模块或["amd", "umd", "systemjs", "commonjs"] “”之一,可以为false

Slide that extra bit of code into your configuration and you’ll be cooking with peanut oil.

将多余的代码滑入您的配置中,即可使用花生油烹饪。

The final state of webpack.config.js looks like this:

webpack.config.js的最终状态如下所示:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: { filename: 'bundle.js', path: 'dist' },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: { 
          presets: [ 
            [ 'es2015', { modules: false } ] 
          ] 
        }
      }
    ]
  },
  plugins: [ new HtmlWebpackPlugin({ title: 'Tree-shaking' }) ]
};

大结局 (The Grand Finale)

Run webpack again and pop open your bundle.js file. You won't notice any difference. Before you go crazy, know this! It's ok. We've been running webpack in development mode this whole time. Webpack knows that you have unused exports in your code. Even though it's included in the final bundle, sayBye will never make it to production.

再次运行webpack并弹出打开bundle.js文件。 您不会发现任何差异。 在发疯之前,请知道这一点! 没关系。 我们一直在以开发模式运行webpack。 Webpack知道您的代码中有未使用的导出。 即使它已包含在最终捆绑包中, sayBye也永远不会投入生产。

If you still don’t believe me, run webpack -p in your terminal. The -p option stands for production. Webpack will perform a few extra performance optimizations, including minification, removing any unused code along the way.

如果您仍然不相信我, webpack -p在终端中运行webpack -p-p选项代表生产 。 Webpack将执行一些额外的性能优化,包括最小化,删除所有未使用的代码。

Open up bundle.js. Since it's minified, go ahead and search for Hello. It should be there. Search for Bye. It shouldn't.

打开bundle.js 。 由于已缩小,请继续搜索Hello 。 它应该在那里。 搜索Bye不应该

Voila! You now have a working implementation of tree-shaking in webpack 2!

瞧! 现在,您在webpack 2中有了一个有效的摇树实现!

For the curious, I’ve been slowly iterating over my own lightweight webpack configuration in a GitHub Repo:

出于好奇,我一直在GitHub Repo中缓慢地遍历我自己的轻量级Webpack配置:

jake-wies/webpack-hotplatewebpack-hotplate - A webpack boilerplate for personal projectsgithub.com

jake-wies / webpack- hotplate webpack-hotplate-用于个人项目的 webpack 样板 github.com

It’s not meant to be overly verbose and bloated. It’s focused on being an approachable boilerplate with walkthroughs at every turn. If you’re interested, check it out!

这并不意味着过于冗长和肿。 它的重点是成为一个平易近人的样板,每步都有演练。 如果您有兴趣,请查看!

If you have any questions, feel free to reach out on Twitter!

如果您有任何疑问,请随时联系Twitter

翻译自: https://www.freecodecamp.org/news/tree-shaking-es6-modules-in-webpack-2-1add6672f31b/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值