Webpack重要知识点

更新日期: 2019-06-04阅读: 2.5k标签: webpack

Tree Shaking

为了使用tree shaking,需要满足以下条件:

  • 使用ES2015语法(即import和export)
  • 在项目package.json文件中,添加sideEffects入口
  • 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如:UglifyJSPlugin)


将文件标记为无副作用(side-effect-free)

这种方式是通过package.json的sideEffects属性来实现的。

{
  "sodeEffects": false
}

「副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个export或多个export。举例说明,例如polyfill,它影响全局作用域,并且通常不提供export。

注意,任何导入的文件都会受到tree shaking的影响。这意味着,如果在项目中使用类似css-loader并导入CSS文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:

{
  "sideEffects": ['*.css']
}


压缩输出

webpack 4 开始,也可以通过 "mode" 配置选项轻松切换到压缩输出,只需设置为 "production"。

也可以在命令行接口中使用--optimize-minimize标记,来使用UglifyJSPlugin。

Code Splitting

code splitting的必要性

  • 不进行code splitting,打包后单文件提交较大,加载时长较长,影响用户体验
  • 不进行code splitting,经常修改业务代码,重新打包后,浏览器不能进行缓存,导致性能较差,影响用户体验

code splitting的配置

同步代码方式

import _ from 'lodash';

webpack.common.js配置如下:

....
optimization: {
  splitChunks: {
    chunks: 'all'
  }
}
....

配置后,会将公用类库进行打包,生成一个vendors~main.js文件。

异步代码方式

function getComponent() {
  return import('lodash').then(({ default: _ }) => {
    var element = document.createElement('div');
    element.innerhtml = _.join(['Clear', 'love'], '');
    return element;
  })
}

getComponent().then(element => {
  document.body.appendChild(element);
})


SplitChunksPlugin 配置参数详解

魔法注释(magic comment)修改打包动态组件名称

1. 使用@babel/plugin-syntax-dynamic-import支持动态引入插件

在.babelrc中引用该插件

....
plugins: ['@babel/plugin-syntax-dynamic-import']
....

2. 添加魔法注释

function getComponent() {
  return import(/* webpackChunkName:"lodash" */'lodash').then(({ default: _ }) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['Clear', 'love'], '');
    return element;
  })
}

getComponent().then(element => {
  document.body.appendChild(element);
})

关键是注释:webpackChunkName: "lodash".。打包后的文件名为vendors~lodash.js。
若想打包过后的文件名不带vendors~前缀,可以修改webpack.common.js中optimization配置项:

....
optimization: {
  splitChunks: {
    chunks: "all",
    cacheGroups: {
      venders: false,
      default: false
    }
  }
}
....

splitChunks配置详解

  splitChunks: {
      chunks: 'async', // all async initial 是否对异步代码进行的代码分割
      minSize: 30000,  // 引入模块大于30kb才进行代码分割
      maxSize: 0, // 引入模块大于Xkb时,尝试对引入模块二次拆分引入
      minChunks: 1, // 引入模块至被使用X次后才进行代码分割
      maxAsyncRequests: 5, // 
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10, // 优先级 
          filename: 'vendors.js' // 打包文件名称
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true // 是否复用已打包代码
        }
      }
    }
  }


Lazy Loading && Chunk

import 动态引入组件

function getComponent() {
  return import(/* webpackChunkName:"lodash" */'lodash').then(({ default: _ }) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['Clear', 'love'], '');
    return element;
  })
}

document.addEventListener('click', () => {
  /* 当点击时才加载lodash */
  getComponent().then(element => {
    document.body.addChild(element);
  })
})

页面初始化时,不会加载lodash。当点击页面时才加载。
import引入动态组件实现的Lazy Loading,其实跟Webpack没什么关系。import是ES6的语法标准。而Webpack借助babel-profill能识别该语法。

Chunk

每个打包的js文件都是一个chunk


打包分析 && Preloading && Prefetching

借助webpack analyse进行打包分析

在package.json的scripts项中进行配置:

....
scripts: {
  "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js" 
}
....

打包后会生成stats.json,然后上传该文件至webpack/analyse进行分析

其他分析分工具

利用魔法注释实现Preload/Prefetch

document.addEventListener('click', () => {
  import(/* webpackPrefetch: true */ 'lodash').then(() => {
    ....
  })
})

Preload和Prefetch的区别:

  • preloaded chunk与主模块并行加载,而prefetched chunk是主模块加载完后再加载
  • preloaded chunk具有中等优先级,可以立即下载。而prefetched chunk是在浏览器空闲时下载。
  • 浏览器支持程度不同

具体可以参考prefetching/preloading-modules


CSS文件的代码分割

若没有进行css的代码分割,通过import方式引入的样式文件,将会被当作普通的模块打包到.js文件中。
若需要对css进行代码分割,需要借助optimize-css-assets-webpack-plugin插件实现,具体如下:

// webpack.prod.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

....
module: [{
  test: /\.scss$/,
  use: [
    MiniCssExtractPlugin.loader,
    {
      loder: 'css-loader',
      options: { importLoaders: 2 }
    },
    'saas-loader',
    'postcss-loaerd'
  ]
},
{
  test: /\.css$/,
  use: [
    MiniCssExtractPlugin.loader,
    'css-loader',
    'postcss-loader'
  ]
}],
plugins: [
  new MiniCssExtractPlugin({
    filename: '[name].css',  // 入口文件中直接引入css匹配该规则
    chunkFilename: '[name].chunk.css'  // 非入口文件中引入或嵌套引入匹配匹配该规则
  })
]
....

若需要对引入css进行合并、压缩,可以借助optimize-css-assets-webpack-plugin。,具体配置如下:

// webpack.prod.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
....
optimization: {
  minimizer: [new OptimizeCSSAssetsPlugin({})]
}
....

若之前有配置过tree shaking,则需要对以下文件进行修改:

  1. webpack.common.js
optimization: {
  usedExports: true
}
  1. package.json
"sideEffects": [
    "*.css"
  ]


Webpack与浏览器缓存

webpack实现浏览器缓存,主要是借助配置output中的contenthash来实现的。

// webpack.prod.js
....
output: {
  filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].js'
}
....

旧版webpack进行打包时,虽然文件没有进行任何修改,但打包后生成的contenthash还是会改变,这时需要再进行一些配置。

// webpack.common.js
....
optimization: {
  runtimeChunk: {
    name: 'runtime'
  }
}
....


构建性能

常规

保持版本最新

使用最新稳定版本的webpack、node、npm等,较新的版本更够建立更高效的模块树以及提高解析速度。

loaders

将loaders应用于最少数的必要模块中,而不是:

// webpack.common.js
module: {
  rules: [
    {
      test: /\.jsx?$/,
      use: ['babel-loader']
    }
  ]
}

使用include字段仅将loader模块应用在实际需要用其转换的位置:

// webpack.common.js
module: {
  rules: [
    {
      test: /\.jsx?$/,
      include: path.resolve(__dirname, '../src'),
      use: ['babel-loader']
    }
  ]
}

Smaller = false

减少编译的整体大小,以提高构建性能。尽量保持chunks小巧。

  • 使用更小/更少的库
  • 在多页面应用程序中使用CommonsChunksPlugin
  • 在多页面应用程序中以async模式使用CommonsChunksPlugin
  • 移除不需要的代码
  • 只编译你在开发的代码

Worker Pool

thread-loader可以将非常耗性能的loaders转存到worker pool中。<br/>
不要使用太多的 workers ,因为 Node.js 的 runtime 和 loader 有一定的启动开销。最小化 workers 和主进程间的模块传输。进程间通讯(IPC)是非常消耗资源的。

持久化缓存

对于一些性能开销较大的loader之前可以添加cache-loader,启用持久化缓存。

使用package.json中的postinstall清楚缓存目录。

Dlls

使用DllPlugin将更新不频繁的代码进行单独编译。这将改善引用程序的编译速度。即使它增加了构建过程的复杂度。

解析(resolve)

以下几步可以提高解析速度:

  • 尽量减少resolve.modules、resolve.extensions、resolve.mainFiles、resolve.desciriptionsFiles中类目的数量,因为它们会增加文件系统的调用次数。
  • 如果你不使用symlinks,可以设置resolve.symlinks: false
  • 如果你使用自定义解析plugins,并且没有指定context信息,可以设置resolve.cacheWithContext: false

Development

在内存中编译

以下几个实用的工具通过在内存中进行代码的编译和资源的提供,但并不写入磁盘来提高性能:

  • webpack-dev-server
  • webpack-hot-middleware
  • webpack-dev-middleware

Devtool

需要注意在不同的devtool的设置,会导致不同的性能差异。

  • eval具有最好的性能,但不能帮你转义代码
  • 如果你能接受稍微差一些的mapping质量,你可以使用cheap-source-map选择来提高性能
  • 使用eval-source-map配置进行增量编译 

在大多数情况下,cheap-module-eval-source-map是最好的选择

避免在生产环境在才会用到的工具

某些实用工具,plugins和loaders都只能在构建生产环境时才使用。例如,在开发时使用UglifyJsPlugin来压缩和修改代码是没有意义的。以下这些工具在开发中通常被排除在外:

  • UglifyJsPlugin
  • ExtractTextPlugin
  • [hash]/[chunkhash]
  • AggressiveSplittingPlugin
  • AggressiveMergingPlugin
  • ModuleConcatenationPlugin

最小化入口chunk

webpack只会在文件系统中生成已更新的chunk。应当在生成入口chunk时,尽量减少入口chunk的体积,以提高性能。

Production

不要为了非常小的性能增益,牺牲了你应用程序的质量!!请注意,在大多数情况下优化代码质量,比构建性能更重要。

多个编译时

当进行多个编译时,以下工具可以帮助到你:

  • parallel-webpack: 它允许编译工作在woker池中进行。
  • cache-loader: 缓存可以在多个编译之间共享。


工具相关问题

Babel

项目中的preset/plugins数量最小化

TypeScript

  • 在单独的进程中使用fork-ts-checker-webpack-plugin进行类型检查
  • 配置loaders时跳过类型检查
  • 使用ts-loader时,设置happyPackMode: true以及 transpileOnly: true

Saas

node-sass中有个来自Node.js线程池的阻塞线程的bug。当使用thread-loader时,需要设置workParallelJobs: 2


其他

修改CleanWebpackPlugin根路径

在webpack.config.js配置中,需要对plugins中的CleanWebpackPlugin的根路径进行修改,可以通过配置root参数。

....
plugins: [
  new CleanWebpackPlugin(['dist'], {
    root: path.resolve(__dirname, '../')
  })
]
....


链接: https://fly63.com/article/detial/4027

浅谈Webpack打包工具的应用

webpack 在前端工程中随处可见,当前流行的 vue, react, weex 等解决方案都推崇 webpack 作为打包工具。前端工具云集的时代,这是你值得选择的之一。

一步一步webpack,webpack的学习入门

webpack是前端工程构建的一套工具,为什么一个程序称之为一套呢,是因为webpack其实是npm的一个模块,使用起来的话,这期间还需要很多其它模块来进行支持,所以我称之为一套工具。

如何写 Webpack 配置文件

本文从一个小Demo开始,通过不断增加功能来说明webpack的基本配置,只针对新手。webpack基本的配置就可以熟悉了,会引入loader,配置loader选项,会设置alias,会用plugins差不多。

WebPack中Plugins的使用和整理,以及常用的Plugins插件

Plugins是webpack的基础,我们都知道webpage的plugin是基于事件机制工作的,这样最大的好处是易于扩展。讲解如果扩展内置插件和其他插件,以及我们常用的Plugins插件

大多数项目中会用到的webpack小技巧

webpack技巧的总结:进度汇报、压缩、复数文件打包、分离app文件与第三方库文件、资源映射、输出css文件、开发模式、分析包的大小、更小的react项目、更小的Lodash、引入文件夹中所有文件、清除extract-text-webpack-plugin日志。

优化Webpack构建性能的几点建议

Webpack 作为目前最流行的前端构建工具之一,在 vue/react 等 Framework 的生态圈中都占据重要地位。在开发现代 Web 应用的过程中,Webpack 和我们的开发过程和发布过程都息息相关,如何改善 Webpack 构建打包的性能也关系到我们开发和发布部署的效率。

Webpack 4正式发布了!

新版 Webpack 中我们所做的每一个更新目的都在于此,为了当大家在使用 Webpack 的时候敏捷连续毫无顿挫感。 webpack 4 进行构建性能测试,得出的结果非常有趣。结果很惊人,构建时间降低了 60%-98%!

Webpack 4.0.0不再支持 Node.js 4

Webpack 是一个现代 JavaScript 应用程序的模块打包器 (module bundler) 。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块

我当初为什么写webpack_Tobias Koppers

Tobias Koppers是一位自由软件开发者,家住德国纽伦堡。他因写出webpack这个已有数百万开发者使用的开源软件而名噪一时。他目前专注于JavaScript和开源项目。以下是我对他个人的专访,希望对大家有所启发。

webpack项目轻松混用css module

本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。比如antd-mobilenpm包的引入。在不做特殊处理的前提下,样式文件将会被转译成css module。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!