前言:对于前端优化始终是无奈跟构建工具相拆散,上面由我从日常的前端的视角从文件的提取、合并、拆分等几个方面来说一下 webpack 是如何进行打包配置的。
写在后面:

前端是怎么做到性能优化的? 带着这个问题 咱们从几个方面登程:

  • 缩小 HTTP 申请
  • 动态资源应用 CDN
  • 将 CSS 放在文件头部,JavaScript 文件放在底部
  • 应用字体图标 iconfont 代替图片图标
  • 善用缓存,不反复加载雷同的资源
  • 应用服务端渲染
  • ...

详情能够看:前端性能优化 24 条倡议;大佬总结的很粗疏,然而如果从 webpack 登程的话 咱们对以上的倡议能够做到几条?

答:都能够


我的项目筹备

以 react 为例

webpack: 4.31.0webpack-cli : 3.3.2

解释一下 为什么用的是 webpack4x,目前组内大部分都是用的 webpack4 进行构建,前面也会独自出一份对于 webpack5 的教程。

目录构造

如上图所示,以后的我的项目搁置一些动态的文件即可。

package.json

我的项目开始前的筹备插件

{  "name": "webpack-demo",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1",    "dev": "webpack-dev-server --open --config webpack.dev.js", // 本地静止    "build": "webpack --config webpack.prod.js"  // 打包优化  },  "keywords": [],  "author": "",  "license": "ISC",  "devDependencies": {    "@babel/core": "^7.13.10",    "@babel/preset-env": "^7.13.10",    "@babel/preset-react": "^7.13.13",    "babel-loader": "^8.2.2",    "html-webpack-plugin": "^3.2.0",    "react": "^17.0.2",    "react-dom": "^17.0.2",    "webpack": "^4.31.0",    "webpack-cli": "^3.3.2"  },  "dependencies": {    "webpack-dev-server": "^3.11.2"  }}
.babelrc
{  "presets": ["@babel/preset-env", "@babel/preset-react"]}

缩小 http 申请

合并图片资源

   npm i url-loader -D
webpack.prod.js
 module: {    rules: [      ...      {        test: /.(png|jpg|gif|jpeg)$/,        use: [          {            loader: 'url-loader',            options: {              limit: 102400, // 100k            },          },        ],      },    ],  },

场景:对于我的项目中有几个的小图标能够应用 url-loader 进行优化解决,它默认会把文件转为 DataURL,如果文件小于 limit,那么 url-loader 会调用file-loader进行解决。以后,图标过多举荐应用iconfont进行加载。

参考: url-loader


缩小文件的搜寻范畴

webpack.prod.js
 module: {    rules: [      ...      {        test: /.js$/,        use: [          {            loader: 'babel-loader',            exclude: /node_modules/,  // 不须要被解析的模块           // include: path.resolve('src')  // 不须要被解析的模块          },        ],      },    ],  },

动态资源应用 CDN

配置publicPath

webpack.prod.js
output: {    path: path.join(__dirname, 'dist'),    filename: '[name].js', // 多个入口的状况下 不晓得对应的名称、能够用占位符来指定[name]    publicPath: 'https://cdn.example.com/assets/', // 配置CDN地址  },mode: 'production', // 生产环境

将 CSS 放在文件头部,JavaScript 文件放在底部

配置H5的rem适配计划为例:

npm i px2rem-loader raw-loader@0.5.1 lib-flexible  -D

新建 src/meta.html 增加公共的 mate 标签

<!-- 公共的 meta信息--><meta charset="UTF-8" /><meta name="viewport"  content="width=device-width,initial-scale=1,maximum-scale=1, minimum-scale=1,user-scalable=no,viewport-fit=cover" /><meta http-equiv="X-UA-Compatible" content="ie=edge" />

src/meta.html 配置:

<!DOCTYPE html><html lang="en"><head>  ${require('raw-loader!./meta.html')}  <title>Document</title>  <script>${ require('raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js') }</script></head><body>  <div id="root"></div></body></html>

场景:应用raw-loader能够动静的配置模板的占位符,达到渲染的地位。须要留神的是raw-loader@0.5.1比较稳定,其余版本有些许问题。

参考: raw-loader


抽离资源文件

npm i html-webpack-externals-plugin -D
webpack.prod.js
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');...plugins:[     new HtmlWebpackExternalsPlugin({      // 提取公共资源      externals: [        {          module: 'react',          entry: 'https://unpkg.com/react@16/umd/react.production.min.js',          global: 'React',  // 全局注入        },        {          module: 'react-dom',          entry: 'https://unpkg.com/react-dom@16/umd/react-dom.production.min.js',          global: 'ReactDOM', // 全局注入        },        {          module: 'google-roboto',          entry: {            path: '//at.alicdn.com/t/font_460072_qm96unh8hja.css',            type: 'css',          },        },      ],      files: ['index.html']    })]

优化后果:

优化前:

优化后:

场景:对于我的项目中存在很多的插件或者 UI 组件库等、能够在 HTML 插入外链的 CDN 形式进行打包优化,如果有加载程序渲染的限度也能够应用raw-loader进行打包设置。

参考: html-webpack-externals-plugin


拆散公共办法

应用 webpack 内置的SplitChunksPlugin,它的弱小之处是能够在我的项目中拆散公共办法的引入次数。

例子: 在 src/ 创立common/index.js

export function common() {  return '我是公共的JavaScript';}

别离在:src/index/index.jssrc/search/index.js

import { common } from '../common';...let result = common();console.log(result);
webpack.prod.js
plugins: [    new HtmlWebpackPlugin({      template: path.join(__dirname, 'src/index.html'),      chunks: ['commons', 'index'],  // 引入以后的名称 commons    }),  ],... optimization: {    splitChunks: {      minSize: 0, // 引入的模块的大小,设置为0 有引入就会打包成模块      cacheGroups: {        commons: {          minChunks: 1, // 起码引入的次数          name: 'commons',  // 命名chunks_name          chunks: 'all',        },      },    },  },

场景: 能够依据我的项目引入的次数进行公共办法 chunk 的抽离,不必在每次文件构建中重复构建。

参考: SplitChunksPlugin


善用缓存构建

yarn add  hard-source-webpack-plugin -D// ornpm i  hard-source-webpack-plugin -D
webpack.prod.js
...plugins: [     new HardSourceWebpackPlugin(),  ],

第一次构建:

第二次构建:

场景:当我的项目在本地构建的时候须要的依赖较多,能够减少为模块提供两头缓存的形式进行构建,构建的速度能够达到80%左右。 具体的配置也能够参考文档

参考: hard-source-webpack-plugin


文件指纹

yarn add mini-css-extract-plugin -D// ornpm i  mini-css-extract-plugin -D
Hash :和整个我的项目的构建相干,只有我的项目文件有批改,整个我的项目构建的`hash`值就会随之更改Chunkhash:和`webpack`打包的`chunk`无关,不同的`entry`会申城不同的`chunkhash`值Contenthash : 依据文件内容来定义`hash`,文件内容不变,则`contenthash`不变
webpack.prod.js
"use strict";const path = require("path");const MiniCssExtractplugin = require("mini-css-extract-plugin"); // 提取css独自一个文件module.exports = {  entry: {    // 入口文件能够用对象的模式来写    index: "./src/index.js",    search: "./src/search.js",  },  output: {    path: path.join(__dirname, "dist"),    filename: "[name]_[chunkhash:8].js", // chunkhash 8位的长度  },  mode: "production",  module: {    rules: [      {        test: /.js$/,        use: "babel-loader",        exclude: /node_modules/,      },      {        test: /.css$/, // 配置css的后缀名        exclude: /node_modules/,        use: [MiniCssExtractplugin.loader, "css-loader"], //tips:执行的程序是右到左的      },      {        test: /.less$/, // 配置less的后缀名        exclude: /node_modules/,        use: [MiniCssExtractplugin.loader, "css-loader", "less-loader"], //tips:执行的程序是右到左的      },      {        test: /.(png|jpg|gif|jpeg)$/,        exclude: /node_modules/,        use: [          {            loader: "file-loader",            options: {              name: "[name]_[hash:8].[ext]",            },          },        ],      },      {        test: /.(woff|woff2|eot|ttf|otf)$/,        exclude: /node_modules/,        use: [          {            loader: "file-loader",            options: {              name: "[name]_[hash:8].[ext]",            },          },        ],      },    ],  },  plugins: [    new MiniCssExtractplugin({      filename: "[name]_[contenthash:8].css",    }),  ],};

场景:

用作版本治理时,如果一个我的项目须要公布,只须要公布批改过的文件指纹;对于没有批改过的文件,用户在拜访的时候,仍旧能够应用浏览器缓存好的,无需二次加载,减速页面拜访。

参考:

MiniCssExtractPlugin


代码压缩

  1. uglifyjs-webpack-plugin // js 压缩 [内置]
  2. optimize-css-assets-webpack-plugin // css 压缩
  3. html-webpack-plugin // html 压缩
yarn add html-webpack-plugin optimize-css-assets-webpack-plugin cssnano -D// ornpm i html-webpack-plugin optimize-css-assets-webpack-plugin cssnano  -D
webpack.prod.js
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');plugins:[    ...    new OptimizeCssAssetsWebpackPlugin({      assetNameRegExp: /.css$/g, // 匹配的正则的名称后缀、跟loader配置统一      cssProcessor: require('cssnano'), // 用于最小化的css处理器,默认是cssnano    }),    new HtmlWebpackPlugin({      template: path.join(__dirname, 'src/search.html'),      filename: 'search.html',      chunks: ['search'],      /**       * inject : true || 'head' || 'body' || false       * body : 所有javascript资源将被搁置在body元素的底部。       * head : 把脚本搁置在head元素中.       * true : script标签位于html文件的 body 底部 [默认]       * false: 不插入生成的 js 文件,只是单纯的生成一个 html 文件       *  */      inject: true,      minify: {        collapseWhitespace: true, // 清理html中的空格、换行符。 默认值:false        minifyCSS: true, // 压缩html内的款式。默认值:false        minifyJS: true, // 压缩html内的js。 默认值:false        removeComments: false, // 清理html中的正文。 默认值:false      },    }),]

参考:

optimize-css-assets-webpack-plugin
cssnano
html-webpack-plugin

Tips:_对于具体的html-webpack-plugin的minify能够具体的参考_


写在最初

在目前的前端的性能优化中,构建工具必不可少,怎么做能力使以后的我的项目更快、性能更好是前端业界的一个老生畅谈的问题、只有纯熟的把握构建工具的配置能力在性能渲染独领风骚。

参考文献

https://segmentfault.com/a/11...
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...
https://juejin.cn/post/684490...

其余

插播一条招聘信息,LeapFE 招聘前端工程师

如果你对 用户体验、交互操作流程及用户需要 "有一些" 谋求如果你对 web 、小程序 、Electron 技术 "有一些" 意识如果你 很善于前端新技术的学习和分享 欢送退出好将来,欢送退出 LeapFE 一起做一些有意思的事件