前言:关于前端优化始终是没法跟构建工具相分离,下面由我从平常的前端的视角从文件的提取、合并、拆分等几个方面来讲一下 webpack 是如何进行打包配置的。
前端是怎么作到性能优化的? 带着这个问题 咱们从几个方面出发:javascript
详情能够看:前端性能优化 24 条建议;大佬总结的很细致,可是若是从 webpack 出发的话 咱们对以上的建议能够作到几条?css
答:均可以html
以 react 为例前端
webpack: 4.31.0 webpack-cli : 3.3.2
解释一下 为何用的是 webpack4x,目前组内大部分都是用的 webpack4 进行构建,后面也会单独出一份关于 webpack5 的教程。java
如上图所示,当前的项目放置一些静态的文件便可。node
项目开始前的准备插件react
{ "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" } }
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }
合并图片资源
webpack
npm i url-loader -D
module: { rules: [ ... { test: /.(png|jpg|gif|jpeg)$/, use: [ { loader: 'url-loader', options: { limit: 102400, // 100k }, }, ], }, ], },
场景:对于项目中有几个的小图标可使用 url-loader 进行优化处理,它默认会把文件转为 DataURL,若是文件小于 limit,那么 url-loader 会调用file-loader进行处理。当前,图标过多推荐使用iconfont进行加载。git
参考: url-loadergithub
module: { rules: [ ... { test: /.js$/, use: [ { loader: 'babel-loader', exclude: /node_modules/, // 不须要被解析的模块 // include: path.resolve('src') // 不须要被解析的模块 }, ], }, ], },
配置publicPath
output: { path: path.join(__dirname, 'dist'), filename: '[name].js', // 多个入口的状况下 不知道对应的名称、能够用占位符来指定[name] publicPath: 'https://cdn.example.com/assets/', // 配置CDN地址 }, mode: 'production', // 生产环境
配置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
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.js
和src/search/index.js
import { common } from '../common'; ... let result = common(); console.log(result);
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 的抽离,不用在每次文件构建中反复构建。
yarn add hard-source-webpack-plugin -D // or npm i hard-source-webpack-plugin -D
... plugins: [ new HardSourceWebpackPlugin(), ],
第一次构建:
第二次构建:
场景:当项目在本地构建的时候须要的依赖较多,能够增长为模块提供中间缓存的方式进行构建,构建的速度能够达到80%
左右。 详细的配置也能够参考文档
参考: hard-source-webpack-plugin
yarn add mini-css-extract-plugin -D // or npm i mini-css-extract-plugin -D
Hash :和整个项目的构建相关,只要项目文件有修改,整个项目构建的`hash`值就会随之更改 Chunkhash:和`webpack`打包的`chunk`有关,不一样的`entry`会申城不一样的`chunkhash`值 Contenthash : 根据文件内容来定义`hash`,文件内容不变,则`contenthash`不变
"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", }), ], };
场景:
用做版本管理时,若是一个项目须要发布,只须要发布修改过的文件指纹;对于没有修改过的文件,用户在访问的时候,依旧可使用浏览器缓存好的,无需二次加载,加速页面访问。
参考:
uglifyjs-webpack-plugin
// js 压缩 [内置]optimize-css-assets-webpack-plugin
// css 压缩html-webpack-plugin
// html 压缩yarn add html-webpack-plugin optimize-css-assets-webpack-plugin cssnano -D // or npm i html-webpack-plugin optimize-css-assets-webpack-plugin cssnano -D
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 一块儿作一些有意思的事情