webpack 基础与项目优化实践总结

前言:本文基于weboack4.x,主要涉及webpack4 基本概念、基本配置和实际项目打包优化。关于概念方面参考官网,经常使用配置来自于网络资源,在文末有相关参考连接,实践部分基于本身的项目进行优化配置。javascript

webpack 四大概念

entry

定义编译打包入口文件。类型:字符串(单入口)、对象(多入口)css

entry: './src/index.js'
// 等同于
entry: {
    main: './src/index.js'
}

output

一、filename:
[name]: 对应着entry中对象的key
[id]: 内部的chumk id
[hash]: 每次打包编译的惟一hash,改动会影响整个项目的打包,缓存失效
[chunkhash]:对应着每一个入口文件计算而来的hash,惟一,文件之间互不影响
[contenthash]: contenthash = (moduleId + content) 生成的hash。同一文件中,修改某个module影响其余module。例如,js代码中引入css文件,修改js文件形成css文件的hash改变 html

二、path: 是配置输出文件存放在本地的目录,字符串类型,是绝对路径puclicPath: 对构建出的资源进行异步加载(图片,文件) 时候的路径前缀, 能够看做静态文件托管在cdn
三、chunkFilename: 决定了非入口(non-entry) chunk 文件的名称,如按需加载、异步加载vue

参考:从零搭建webpack4 之output输出java

Module

配置Loader

rules 数组,包含多个处理文件的 loader 配置react

  1. 条件匹配: 经过test、include、exclude三个配置来命中Loader要应用的规则文件。
  2. 应用规则: 对选中后的文件经过use配置项来应用loader,能够应用一个loader或者按照从后往前的顺序应用一组loader。同时还能够分别给loader传入参数。
  3. 重置顺序: 一组loader的执行顺序默认是从有道左执行,经过exforce选项可让其中一个loader的执行顺序放到最前或者是最后。

loader配置属性

test:类型正则、字符串、数组。匹配文件
use:类型字符串、对象、数组【loader字符串或对象】。对选中的文件应用loader。webpack

对象中包含:loader、options:loader的具体配置参数、enforce: 改变该loader的执行的顺序【pre/post】

include: 指定须要处理的文件。类型:通常为路径、能够是数组类型。
exclude:排除不须要处理的文件。类型:通常为路径、能够是数组类型。
noParse: 排除对没有采用模块化的文件解析和处理,相似:jQuery。 类型:RegExp, [RegExp], function其中一个。
parser:能够更细粒度的配置哪些模块语法【AMD、CommonJS、ES6等】是否须要解析git

Plugin

Plugin 是用来扩展Webpack 功能的,经过在构建流程里注入钩子实现,它为Webpack 带
来了很大的灵活性。主要在打包的某个阶段执行该插件,对文件进行处理,相似于钩子函数github

tree shaking

触发treeshking条件:
1.须要代码是es module规范的而且使用解构赋值的方式引入,
2.开启optimization.usedExports:true来标记使用和未使用的模块,web

  1. 使用压缩的插件进行删除未使用代码。

webpack4的mode设置为production,默认开启optimization.usedExports和使用代码压缩

注:
一、tree shaking 不能做用于有反作用side-effect的代码
若是全部代码没有反作用,在package.json 中添加

sideEffects: false

若是存在反作用代码/模块,

sideEffects: [
".src/some-side-effectful-file.js",
"*.css"
]
"side effect(反作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局做用域,而且一般不提供 export。

二、不要意外地将 ES 模块编译成 CommonJS 模块。若是你使用 Babel 的时候,采用了 babel-preset-env 或者 babel-preset-es2015,请检查这些预置的设置。默认状况下,它们会将 ES 的导入和导出转换为 CommonJS 的 require 和 module.exports,能够经过设置.babelrc中 { modules: false } 选项来禁用它

参考:webpack tree shaking 的三个要点

HMR (Hot Module replacement)模块热替换

devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''}
},
context: ['/api', '/online'], //匹配多个路径,同时代理到同一个站点
target: 'http://localhost:3000'
},
    hot: true,
    hotOnly: true
}

code spiliting

把代码分离到不一样的 bundle 中,能够按需加载或并行加载这些文件

实现方式:
一、入口配置:entry 入口使用多个入口文件 =》 存在重复引用的模块
二、抽取公有代码:使用 SplitChunksPlugin 抽取公有代码,取代CommonsChunkplugin

webpack.config.js 配置:
```

  optimizition: {
    splitChunks: {
        chunks: 'all'        
    }
 }
```

三、动态加载 :动态加载一些代码 =》 ECMAScript 提案 的 import() 语法
方式1 + 方式2 须要配合使用,才能达到代码抽离的效果

prefetch && preload

prefetch(预取):未来某些导航下可能须要的资源
preload(预加载):当前导航下可能须要资源

import(/* webpackPrefetch: true */ 'LoginModel');

/ webpackPrefetch: true /:把主加载流程加载完毕,在空闲时在加载其余,等再点击其余时,只须要从缓存中读取便可,性能更好。推荐使用,提升代码利用率。把一些交互后才能用到的代码写到异步组件里,经过懒加载的形式,去把这块的代码逻辑加载进来,性能提高,页面访问速度更快。
/ webpackPreload: true /: 和主加载流程一块儿并行加载。

lazy loading

import() 异步加载加载的模块,开启代码分割后,会被单独打包在一个文件中
路由懒加载

const Login = () => import('./components/login')

模块异步加载

import(/* webpackChunkName: "vendor"*/ './page/vendor.js').then(({default: _}) => {// todo})

公共代码提取

mini-css-extract-plugin 用于提取公共css,取代 webpack 3 的 extract-text-webpack-plugin
注:通常适用于生产环境,在开发环境会致使HMR功能缺失;在开发环境,使用style-loader
使用方式:

const MiniCssExtractPlugin=require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 代码压缩
// modules 中
// css,scss,sass,less
{
test:/\.(sa|sc|c)ss$/,
use: [
process.env.NODE_ENV === 'development' ? 'style-loader': MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
optimization: {
minimizer: [new OptimizeCssAssetsPlugin({})]
},


//plugins中
new MiniCssExtractPlugin({
filename: "[name].css"
})

DllPlugin & DllReferencePlugin

防止第三方包屡次编译打包。 第一次打包时,把第三方模块单独打包生成一个文件 vendors.dll.js,以后在打包时就能够直接从 vendors.dll.js 中引入以前打包好的第三方模块
实现过程:
一、编写一个用于生成动态连接库的配置文件
二、运行生成动态连接库和对应的.mainfest.json映射文件
三、在webpack.config.js中使用动态连接库。这样第二次编译打包会从json文件中找相应的模块

webpack.dll.config.js

module.exports = {
    entry: {
        react: ['react'] //react模块打包到一个动态链接库
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].dll.js', //输出动态链接库的文件名称
        library: '_dll_[name]' //全局变量名称
    },
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]', //和output.library中一致,值就是输出的manifest.json中的 name值
            path: path.join(__dirname, 'dist', '[name].manifest.json')
        })
    ]
}

webpack.config.js

plugins: [
  new webpack.DllReferencePlugin({
  manifest: require(path.join(__dirname, 'dist', 'react.manifest.json')),
})
],

提升webpack的构建(打包/build)速度

  1. 多入口状况下,使用SplitChunk来提取公共代码
  2. 经过externals配置来提取经常使用库
  3. 利用DllPlugin和DllReferencePlugin预编译资源模块 经过DllPlugin来对那些咱们引用可是绝对不会修改的npm包来进行预编译,再经过DllReferencePlugin将预编译的模块加载进来。
  4. 使用Happypack 实现多线程加速编译
  5. 使用webpack-uglify-parallel来提高uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提高压缩速度
  6. 使用Tree-shaking和Scope Hoisting来剔除多余代码

模式分离

webpack 做为模块化打包工具, 经常使用来对项目进行打包进行本地调试和发布到线上,因此不管是本身在项目配置使用webpack仍是使用开发框架的脚手架进行开发,都须要区分开发和生产环境。在webpack4 的配置项中添加了 mode属性,能够用来区分二者模式。
下面是开发和生产模式下一些默认配置和区别:

development 模式下,默认开启了NamedChunksPlugin 和NamedModulesPlugin方便调试,提供了更完整的错误信息,更快的从新编译的速度。
production 模式下,因为提供了splitChunks和minimize,代码就会自动分割、压缩、优化,同时 webpack 也会自动帮你 Scope hoisting 和 Tree-shaking

优化划分

体积优化

工具 - 打包文件可视化

使用 webpack-bundle-analyzer, 可直观的看出打包后每一个模块所占比例和大小
使用方法:

  1. 安装
    npm i -D webpack-bundle-analyzer

2.在 package.json -> script 中添加启动命令
"analyz": "cross-env NODE_ENV=prodution npm_config_report=true npm run build"

3.在 webpack.pro.conf.js -> plugin 添加如下代码,能够改变启动时的端口等配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new BundleAnalyzerPlugin({analyzerPort: 8089})

关于使用方法详细可参考:webpack实践-webpack-bundle-analyzer使用

具体方案

路由懒加载

import xxxx from '@componets/xxx' => const xxx = () => require('@componets/xxx')

模块化按需加载

对于一些相似antd、element-ui、eCharts等库,能够按需引入,没有必要全局引入,具体方法见官方文档
对于loadash等API依赖工具,结合lodash-webpack-plugin和babel-plugin-lodash,实现按需引入,把须要的API一次性引入,并挂载在全局上

// 从lodash 中统一引入你须要的方法
import _ from 'lodash'

export default {
cloneDeep: _.cloneDeep,
debounce: _.debounce,
throttle: _.throttle,
size: _.size,
pick: _.pick,
isEmpty: _.isEmpty
}

// 注入到全局
import _ from '@helper/lodash.js'
Vue.prototype.$_ = _

// vue 组件内运用
this.$_.debounce()

外部模块使用CDN

对于相似Jquery等大而使用较少的库,能够在index.html使用cdn引入;若是顾虑到可能形成外部攻击等问题,能够下载成为本地资源,再引入

参考:webpack 打包优化之体积篇

速度优化

实现方法

缩短路径,减少文件搜索范围

一、缩小文件搜索范围或者指定特定的文件夹位置
二、排除不须要进行处理的文件
......

使用并发压缩插件

不使用默认的 UglifyJs,使用并行压缩工具 webpack-parallel-uglify-plugin
......

具体参考:webpack 打包优化之速度篇

实践

vue 项目

我是在我以前开发一大型项目使用webpack进行项目优化
注:此项目使用的webpack版本为2.x,不是最新版本4.x,因为项目比较复杂,升级带来的潜在问题可能比较多,时间精力有限,暂时未升级
项目技术和库:vue全家桶 + vue-cli + webpack + jQuery + element-UI + eCharts ...
按照上文中的经常使用配置,就打包速度和体积进行了优化,从而致使页面加载速度获得必定提高
图片上传失败,显示不出来 【手动捂脸】

最后

因为本文参考了许多相关文章,并加以本身的理解和实战,若有不妥之处,请多包涵并指出,谢谢

参考:
webpack实践——webpack-bundle-analyzer 的使用
从基础到实战 手摸手带你掌握新版Webpack4.0详解 教你看文档
webpack 4.0 基础到实战配置github
webpack 官方文档
关于webpack的面试题总结

相关文章
相关标签/搜索