手把手教你配置 Webpack,并优化

在日常的开发过程当中,若是没有手动地配置或者优化打包后尺寸,那么用户打开网站时,首屏加载会很慢,几秒后才出现内容,大大增长了用户的等待时间。css

为了解决这个问题,咱们须要从打包这个环节进行优化。常见的优化打包工具webpack,咱们从流行的ReactVue库着手,尝试着优化它们。html


首先,使用create-react-app脚手架建立一个React应用。react

若是没有,首先须要从全局中安装脚手架,命令以下:webpack

npm install create-react-app -g
复制代码

接着新建一个文件夹,并在终端编写命令为:web

create-react-app webpack-optimiation-react
复制代码

模板建立完毕以后,它目录结构以下:npm

.
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── serviceWorker.js
│   └── setupTests.js
├── tree.txt
└── yarn.lock
复制代码

模板文件建好后,咱们在不一样的库中添加相同的代码,而后使用router使他们可以正常运转。json

// src/Home.js
import React from 'react';
export default () => <h1>Home</>; 复制代码
// src/About.js
import React from 'react';
export default () => <h1>About</>; 复制代码
// src/Concat.js
import React from 'react';
export default () => <h1>Concat</h1>;
复制代码

而后使用npm包管理器,添加react-router-dom后端

npm install react-router-dom -D
复制代码

以上的代码写好后,在src/index.js中添加:跨域

import React, { lazy, Suspense } from 'react';
import { Switch, BrowserRouter as Router, Link, Route } from 'react-router-dom';

const Home = lazy(() => import('./Home'));
const Concat = lazy(() => import('./Concat'));
const About = lazy(() => import('./About'));

const NavBar = () => (
  <div> <Link to='/'>Home</Link> <Link to='/about'>About</Link> <Link to='/concat'>Concat</Link> </div>
);

function App() {
  return (
    <Router className='App'>
      <>
        <NavBar />
        <Suspense fallback={<div>loading...</div>}>
          <Switch>
            <Route path='/' exact component={Home} />
            <Route path='/about' component={About} />
            <Route path='/concat' component={Concat} />
          </Switch>
        </Suspense>
      </>
    </Router>
  );
}

export default App;
复制代码

编写完成以后,使用npm run start,浏览器会自动打开,并显示:浏览器

react

OK,运行正常。

咱们打包试试,在终端输入命令:

npm run build
复制代码

这时就会出现一个build文件夹,这就是打包后的结果,能够给后端部署了。

build以后的文件大小竟然有600k之多,为了查看具体那些包体积大,就须要配置webpack

analysis

可是create-react-app并不暴露webpack配置文件,须要输入命令才能看到webapck配置文件:

npm run eject
复制代码

它的结构目录以下:

.
├── config
│   ├── env.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── modules.js
│   ├── paths.js
│   ├── pnpTs.js
│   ├── webpack.config.js
│   └── webpackDevServer.config.js
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── serviceWorker.js
│   └── setupTests.js
├── tree.txt
└── yarn.lock
复制代码

为了解决上面打包600kb的问题,首先须要分析,那些地方打包后的尺寸过大。咱们须要webpack-bundle-analyzer这个插件才能可视化地看到那些包尺寸较大。

安装方法:

npm install webpack-bundle-analyzer -D
复制代码

而后在config/webpack.config.js写入代码:

首先导入这个包,而后再添加插件:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins:[
    ...
     isEnvProduction && new  BundleAnalyzerPlugin(),
     // isEnvProduction这个变量是指,在是否在生产环境
    ...
]
复制代码

可视化分析出来的结果以下:

webpack_analysis

从上图咱们能够发现,打包后最大的包是react-dom

那么咱们就着手优化它吧,从两个方面考虑:

  1. 减小服务器端的压力方法

  2. 减小尺寸的方法

减小服务器压力的方法有:

  • 使用AggressiveSplittingPlugin插件

  • 使用prefetch&preload方法

  • 使用gzip方法

减小尺寸的方法有:

  • 使用ModuleConcatenationPlugin插件

  • 使用uglifyjs插件

  • 使用exterals选项


AggressiveSplittingPlugin

那么如今试着用AggressiveSplittingPlugin优化打包后的代码吧。

直接在config/webpack.config.jsplugins中添加代码:

plugins: [
  new webpack.optimize.AggressiveSplittingPlugin({
    minSize: 3000,
    maxSize: 5000,
    chunkOverhead: 0,
    entryChunkMultiplicator: 1
  })
];
复制代码

build文件夹中出现了很是多的小文件。这个插件是的超过必定体积会分割文件。有利于减小服务器的请求压力。AggressiveSplittingPlugin能够将bundle拆分红更小的chunk,直到各个chunk的大小达到option设置的 maxSize。它经过目录结构将模块组织在一块儿。

它记录了在webpack Records里的分离点,并尝试按照它开始的方式还原分离。这确保了在更改应用程序后,旧的分离点(和 chunk)是可再使用的,由于它们可能早已在客户端的缓存中。所以强烈推荐使用Records


preload&prefetch

第二项是预加载的能力,和预请求能力。咱们先来看看使用preload会怎么样?

个人html-webpack-plugin版本是4.0.0-beta.11,为了正确安装这个 preload 插件,必需要将它的版本设置为3.0.0-beta.3,否则它会报如下的错误:

Plugin could not be registered at 'html-webpack-plugin-before-html-processing'. Hook was not found.
复制代码

使用npm管理器安装这个版本:

npm install preload-webpack-plugin@3.0.0-beta.3
复制代码

接着在webpack.config.js中添加:

const PreloadWebpackPlugin = require('preload-webpack-plugin');
// ...
plugins: [
  new HtmlWebpackPlugin(
    Object.assign(
      {},
      {
        inject: true,
        template: paths.appHtml
      },
      isEnvProduction
        ? {
            minify: {
              removeComments: true,
              collapseWhitespace: true,
              removeRedundantAttributes: true,
              useShortDoctype: true,
              removeEmptyAttributes: true,
              removeStyleLinkTypeAttributes: true,
              keepClosingSlash: true,
              minifyJS: true,
              minifyCSS: true,
              minifyURLs: true
            }
          }
        : undefined
    )
  ),
  // ...
  new PreloadWebpackPlugin()
];
// ...
复制代码

打开调试台,若是的你节点出现了这样的样子,那么说明你成功了。

preload

后面有一个ref=preload就是预加载。

preload有能作什么?

  • 能够预先加载文件或者资源。
  • 不会阻塞页面加载。

举个例子,若是一个网页中使用了许多的字体文件,那么用户在浏览的时候就会等待字体加载,出现白屏的现象。

若是使用了preload的话,那么浏览器不会每次请求一个页面,到指定页面中从新加载,而是预先加载字体文件,到指定页面中不用再次加载。


如今,咱们尝试使用prefetch,是什么结果。

config/webpack.config.js修改:

// ...
plugins: [
  // ...
  new PreloadWebpackPlugin({
    rel: 'prefetch'
  })
  // ...
];
// ...
复制代码

这时,preload就变成了prefetch,以下图:

prefetch

若是你看到了ref=prefetch的话,那么表明预处理成功。

它们的共同点是?

  1. 异步加载资源,不会阻塞网页渲染
  2. 下载并不执行文件
  3. 可以提早请求文件
  4. 没有同域名限制

prefetch 和 preload 的区别是什么?

  1. preload会首先优先加载,而且会占用HTTP并发数,也就是刚进入页面就会请求。而prefetch会浏览器出空闲期时,再请求文件。
  2. preload能够跨域请求,prefetch不会。

Gzip

gzip可以为咱们减小存储空间,以及减小传输的时间。

咱们须要安装一下插件:

npm install -D compression-webpack-plugin
复制代码

config/webpack.config.js中添加:

//...
plugins: [
  //...
  new CompressionWebpackPlugin({
    filename: '[path].gz[query]',
    algorithm: 'gzip',
    test: /\.(js|css)/,
    threshold: 1024,
    minRatio: 0.8
  })
  //...
];
//...
复制代码

build完成以后,打开浏览器。若是你看到这样的header的话,那么表明开启gzip成功。

gzip


如今,咱们开始减小尺寸的优化。

使用ModuleConcatenationPlugin能够提升代码的执行速度和预编译:

config/webpack.config.js中添加:

// ...
plugins: [
  // ...
  new webpack.optimize.ModuleConcatenationPlugin()
];
// ...
复制代码

build后,尺寸比之前集减小接近10kb,并且网页的打开速度也提升很多。

相比以前打开网页的速度,提升了50ms之多。再加上懒加载,并不须要提早加载全部页面,因此首屏渲染速度提升了许多。


Uglify

接着,咱们上一个大杀器,UglifyJS插件,它最大程度压缩和丑化代码。

安装它的命令行:

npm install -D uglifyjs-webpack-plugin
复制代码

导入到config/webpack.config.js中,并使用:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

 // ...
optimization: {
  minimizer:{
    new UglifyJsPlugin(),
  }
}
// ...
复制代码

能够看见,压缩后的代码有多180kb

uglify

Externals

externals这个webpack选项是在打包过程当中剔除掉。这样就有效地减小了依赖,使用第三方CDN减小HTTP压力和请求的压力。

使用externals只须要在webpack.config.js中添加一下的代码:

// ...
externals:{
      'react': 'React',
      'react-dom': 'ReactDOM',
      'react-router-dom': 'ReactRouterDOM'
}
// ...
复制代码

接着在index.html中添加CDN

<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdn.bootcss.com/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
复制代码
相关文章
相关标签/搜索