webpack

150 阅读6分钟

webpack 指南

entry

配置入口文件 webpack4中有默认,但也是可扩展的配置

  1. 单入口写法
            const config = { entry: './file.js' };
     相当于 const config = {
              entry: {
                 main: './file.js'
              }
            }
  1. 数组

会创建多个主入口

  1. 对象写法 可重用并且可以与其他配置组合使用。

output

只指定一个输出配置。 值为一个对象包括:

  1. filename:输出文件的文件名
  2. path: 目标输出目录的绝对路径
//单入口时
  const config = { 
            filename: './bundle.js',
            path: '/dist/js'  //绝对路径
          }
//多入口
const config = {
  filename: '[name].js',
  path:path.resolve(__dirname , 'dist')
  }

mode

  1. production
    提供优化,代码压缩,不支持watching,process.env.NODE_ENV 的值默认production
  2. development 优化构建速度和开发体验 process.env.NODE_ENV 的值默认development 支持注释和提示
  3. none 禁用一切优化

loader

loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块 (webpack 自身只理解 JavaScript)

  1. 导出为函数的javascript模块
  2. 链式调用 把上一个loader产生的结果或资源文件放进去

加载相应的资源文件 loader webpack 根据正则表达式,来确定应该查找哪些文件,并将其提供给指定的 loader

{test:/\.css$/,use:['style-loader','css-loader']}

plugin

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。

  1. HtmlWebpackPlugin 生成html文件,并自动引入打包生成的js文件
  2. CleanWebpackPlugin在每次构建前清理 /dist 文件夹
  3. NamedModulesPlugin
  4. HotModuleReplacementPlugin/WebpackHotMiddleware
  5. optimization.splitChunks
optimization:{
    splitChunks:{
      cacheGroups: {
        commons: {
          name: "commons",
          chunks: "initial",
          minChunks: 2
        }
      }
    }
  }
  1. DefinePlugin 定义全局常量
new webpack.DefinePlugin({
         PRODUCTION: JSON.stringify(true),
         VERSION: JSON.stringify("5fa3b9"),
         BROWSER_SUPPORTS_HTML5: true,
         TWO: "1+1",
         "typeof window": JSON.stringify("object")
       })

-----------分割线————————

开发

  1. 开发模式下使用source-map,可以准确定位错误的位置
  2. 观察模式 如果其中一个文件被更新,代码将被重新编译,不必手动运行整个构建。 package.json watch: 'webpack --watch' npm run watch
  3. webpack-dev-server 提供简单的web服务器 并实时加载
  4. webpack-dev-middleware (server.js)

生产环境构建

开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间。

  1. 公共配置
  2. 开发环境配置 webpack-merge
  3. 生产环境配置 webpack-merge

代码分离

  1. 多入口 打包多个js (如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。 这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码。)
  2. 防止重复 4之前CommonsChunkPlugin 4之后 optimization.splitChunks
  3. 动态导入 不在页面一开始就import 需要用到的地方在import (import() 会返回一个 promise,因此它可以和 async 函数一起使用)
  4. bundle analysis
    A. webpack-chart: webpack 数据交互饼图。 B. webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。 C. webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。

懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

  1. React https://reacttraining.com/react-router/web/guides/code-splitting
  2. Vue https://alexjoverm.github.io/2017/07/16/Lazy-load-in-Vue-using-Webpack-s-code-splitting/

缓存

  1. 输出文件名称使用 [name].[chunkhash].js 重新编译时,文件名可能会变也可能不会变 可以使用插件:第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建

渐进式网络应用程序

PWA 在离线(offline)时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的。

  1. 建议服务器,搭建所需的离线环境 npm install http-server
  2. 添加workbox 打包时会生成两个额外文件 service-worker.js,precache....js
new WorkboxPlugin.GenerateSW({
   //这些选项帮助 ServiceWorkers 快速启用
   //不允许遗留任何“旧的” ServiceWorkers
   clientsClaim: true,
   skipWaiting: true
 })
  1. 注册 Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js').then(registration => {
      console.log('SW registered: ', registration);
    }).catch(registrationError => {
      console.log('SW registration failed: ', registrationError);
    });
   });
 }
  1. 打包后 重新运行 如果成功 关闭服务器 应用程序会继续运行

typescript

  1. npm install typescript ts-loader --save
  2. 新建tsconfig.json文件
{
  "complierOptions": {
    "outDir":'./dist/',
    "sourceMap": true,//启用source map
    "noImplicitAny": true,
    "module": 'es6',
    "target": 'es5', //编译到es5
    "jsx": "react", //支持jsx
    "allowJs": true,
  }
}
  1. webpack.config.js 使用ts-loader编译

  2. 使用其他资源 (非代码资源) 需要告诉 TypeScript 如何兼容这些导入类型 新建custom.d.ts文件

declare module "*.svg" {
 const content: any;
 export default content;
}
//通过指定任何以 .svg 结尾的导入,并将模块的 content 定义为 any,将 SVG 声明一个新的模块
//可以通过将类型定义为字符串,来更加显式地将它声明为一个 url

tree shaking

package中设置"sideEffects": false,将不会打包没有用到的导出部分,可以安全地删除文件中未使用的部分。

shimming

一些第三方的库可能会引用一些全局依赖。这些库可能创建一些需要被导出的全局变量。这些“不符合规范的模块”就是 shimming 发挥作用的地方。 当你希望 polyfill 浏览器功能以支持更多用户时。在这种情况下,你可能只想要将这些 polyfills 提供给到需要修补(patch)的浏览器(也就是实现按需加载)。

  1. shimming 全局变量 ProvidePlugin
  2. 当模块运行在 CommonJS 环境下此时的 this 指向的是 module.exports。可以通过使用 imports-loader 覆写 this
module: {
  rules: [
    {
      test: require.resolve('index.js'),
      use: 'imports-loader?this=>window'
    }
  ]
},
  1. 全局exports exports-loader,将一个全局变量作为一个普通的模块来导出。
{
   test: require.resolve('globals.js'),
   use: 'exports-loader?file,parse=helpers.parse'
}

//使用
//import { file, parse } from './globals.js';
  1. 加载polyfills

在单独文件中引入 babel-polyfills whatwg-fetch 打包成单独js文件 依赖于那些需要支持的技术以及浏览器,来确定是否需要引入这些 polyfills

  var modernBrowser = (
    'fetch' in window &&
    'assign' in Object
  );

  if ( !modernBrowser ) {
    var scriptElement = document.createElement('script');

    scriptElement.async = false;
    scriptElement.src = '/polyfills.bundle.js';
    document.head.appendChild(scriptElement);
  }

构建性能

  1. 将loaders应用于最少的必要模块,使用include字段
  2. 尽量减少使用不同的工具
  3. 尽量减少 resolve.modules, resolve.extensions, resolve.mainFiles, resolve.descriptionFiles 中类目的数量,他们会增加文件系统调用的次数。
  4. 使用 DllPlugin 将更改不频繁的代码进行单独编译。
  5. 减少编译的整体大小
    • 使用更小的库
    • optimization.splitChunks
    • 移除不使用的代码
    • 只编译开发中的代码