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

GladstoneLeif 发布于2月前
0 条问题

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

webpack 四大概念

entry

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

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

output

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

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

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

Module

配置Loader

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

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

loader配置属性

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

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

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

Plugin

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

tree shaking

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

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

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

注:
1、tree shaking 不能作用于有副作用side-effect的代码
如果所有代码没有副作用,在package.json 中添加

 sideEffects: false

如果存在副作用代码/模块,

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

2、不要意外地将 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 中,可以按需加载或并行加载这些文件

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

 webpack.config.js 配置:
```

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

3、动态加载 :动态加载一些代码 =》 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 中引入之前打包好的第三方模块
实现过程:
1、编写一个用于生成动态链接库的配置文件
2、运行生成动态链接库和对应的.mainfest.json映射文件
3、在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 打包优化之体积篇

速度优化

实现方法

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

1、缩小文件搜索范围或者指定特定的文件夹位置
2、排除不需要进行处理的文件
,,,,

使用并发压缩插件

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

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

实践

vue 项目

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

最后

由于本文参考了许多相关文章,并加以自己的理解和实战,如有不妥之处,请多包涵并指出,谢谢

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

查看原文: webpack 基础与实践(项目优化)总结

  • orangeladybug
  • smalltiger
  • blackfrog
  • ticklishmouse
  • redgorilla
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。