webpack

webpack 知识概括

2020-04-06  本文已影响0人  阿畅_

基本使用

几个核心的概念

entry

  module.exports = {
    entry: './src/index.js'
  }

output

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.bundle.js'
  }
} 

loader

// 来自 webpack 官网
const config = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};

注意 如果一个文件同时使用多个 loader 时,loader 执行顺序是,从后往前

插件

// 代码 来自 webpack 官网
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;   

如何配置多文件打包

Vue 版

// 需要在 public 中下 添加新的 HTML 文件
module.exports = {
  pages: {
    index: {
      entry: 'src/pages/index/main.js'
      template: 'public/index.html',
      chunks: [index],
    }
    ...
  }
}

React 版

module.exports = {
  appHtml: resolveApp('public/index.html'),
  appIndexJS: resolveApp('public/index'),
  appNewHtml: resolveApp('public/newIndex.html'),
  appNewIndexJS: resolveApp('public/newIndex'),
  ...
}
entry: {
  index: [
    paths.appIndexJs,
    isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient')
  ].filter(Boolean)
  ...
}
  plugins: [new HtmlWebpackPlugin()]  

抽离 css 文件

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

plugins: [
  new MiniCssExtractPlugin({
    // Options similar to the same options in webpackOptions.output
    // both options are optional
    filename: "[name].css",
    filename: 'css/[name].[contentHash:8].css'
    chunkFilename: "[id].css"
  })
],
module: {
  rules: [
    {
      test: '/\.css$/,
      loader: [
        MiniCssExtractPlugin.loader, // 这里不再用style-css
        'css-loader',
        'postcss-loader'
      ],
      options: {
          publicPath: '', // 抽离指定文件
      }
    }
  ],
},

压缩 js 和 css 文件

const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [new TerserJSPlugin({}),
    new OptimizeCSSAssetsPlugin({})],
  },
};

抽离公众代码

  optimization: {
    splitChunks: {
      chunks: 'all',
      /**
        initial 入口 chunk,对于异步导入的文件不处理
        async 异步 chunk ,只对异步导入的文件处理
        all 全部 chunk
      */
    }
  }
  optimization: {
    cacheGroups: {
      // 第三方模块
      vendor: {
        name: 'vendor', // chunk 名称
        priority: 1, // 权限更高,优先抽离
        test: /node_modules/,
        minSize: 0, // 大小限制
        minChunks: 1 // 最少复用过几次
      },
      // priority 权重
      // minChunks 如果有一次复用,就单独抽离出来
      // 公共的模块
      common: {
        name: 'common', // chunk 名称
        priority: 0, // 权限更高,优先抽离
        minSize: 0, // 大小限制
        minChunks: 2 // 最少复用过几次
      }
    }
  }  

module chunk bundle 的区别

output:{
  filename:"[name].bundle.js"
}

优化打包构建速度

优化 babel-loader 加入缓存

  rules: [
    test: /\.js$/,
    use: ['babel-loader?cacheDirectory], // 开启缓存,
    include: path.resolve(__dirname, 'src'), // 明确范围
    // 排除范围,include 和 exclude 二者选择一个即可
    // exclude: path.resolve(__dirname, 'node_modules')
  ]

IgnorePlugin

new webpack.IgnorePlugin({resourceRegExp, contextRegExp});

new webpack.IgnorePlugin({
  resourceRegExp: /^\.\/locale$/,
  contextRegExp: /moment$/
})

noParse

  module: {
    // 对完整的 react.min.js 文件没有必要采用模块化
    // 忽略对 react.min.js 文件的递归解析处理
    noParse: [/react\.main\.js$/]
  }

IgnorePlugin 和 noParse 的区别

happyPack

  rules: [
    {
      test: /\.js$/,
      // 把对 .js 文件的处理转交给 id 为 babel 的 happyPack 实力
      use: ['happypack/loader?id=babel'],
    }
  ]

  plugins: [
    // happyPack 开启多进程打包  babel 打包配置多线程
    new HappyPack({
      // 用 唯一的标识符 id 来来代表当前的 HappyPack 是用来处理一类特定的文件
      id: 'babel',
      // 如果处理 .js 文件,用法和 Loader 配置中一样
      loaders: ['babel-loader?cacehDirectory']
    })
  ]

ParallelUglifyPlugin

关于开启多进程打包

  1. 项目较大,打包较慢,开始多进程打包能提高速度
  2. 项目较小,打包很快,开始多进程打包会降低速度(因为有进程开销)

自动刷新 (不能用于生产环境)

  watch: true, // 开启监听,默认为 false
  // 注意,开启监听之后,webpack-dev-server 会自动开启刷新浏览器

  // 监听配置
  watchOptions: {
    ignored: /node_modules/, // 忽略哪些
    // 监听变化后会等 多少 毫秒去执行,防止文件更新太快导致重新编译的频率太高
    aggregateTimeout: 300, // 默认 300 ms
    // 判断文件是否发生变化,通过不停的询问系统指定文件是否发生改变
    poll: 1000
  }

热更新 (不能用于生产环境)

  entry: {
    index: [
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/dev-server',
      path.join(srcPath)
    ]
  }

  plugins: [
    new HotModuleReplacementPlugin()
  ]

  devServer: {
    hot: true
  }

  // 开发环境下,增加,开启热更新的代码
  if (module.hot) {
    module.hot.accept([./xxx.js], () => {
      监听分为内修改,才会触发热更新
    })
  }

DLLPlugin 和 DLLReferencePlugin

  const path = require('path')
  const DllPlugin = require('webpack/lib/DllPlugin')
  const { srcPath, distPath } = require('./paths')

  module.exports = {
    mode: 'development',
    // JS 执行入口文件
    entry: {
      // 把 React 相关模块的放到一个单独的动态链接库
      react: ['react', 'react-dom']
    },
    output: {
      // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
      // 也就是 entry 中配置的 react 和 polyfill
      filename: '[name].dll.js',
      // 输出的文件都放到 dist 目录下
      path: distPath,
      // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
      // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
      library: '_dll_[name]',
    },
    plugins: [
      // 接入 DllPlugin
      new DllPlugin({
        // 动态链接库的全局变量名称,需要和 output.library 中保持一致
        // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
        // 例如 react.manifest.json 中就有 "name": "_dll_react"
        name: '_dll_[name]',
        // 描述动态链接库的 manifest.json 文件输出时的文件名称
        path: path.join(distPath, '[name].manifest.json'),
      }),
    ],
  }
<script src="miam.dll.js"></script>

DllReferencePlugin 使用

// 告诉 webpack 使用哪些动态链接库
new DllReferencePlugin({
  // 描述 react 动态链接库的文件内容
  mainfest: require(path.join(distPath, 'main.mainfest.json'))
})

优化产出代码

使用 production 的好处

tree-shaking

ES6 Module 和 Common.js 的区别

所以只有 ES6 Module 才能静态分析,实现 tree-shaking

Scope Hosting (ModuleConcatenationPlugin)

此插件仅适用于由webpack直接处理的 ES6 模块

const Webpack = require('webpack')

resolve: {
  mainFields: ['jsnext:main', 'browser', 'main']
}

plugins: [
  new Webpack.optimize.ModuleConcatenationPlugin()
]

介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面

首先,介绍webpack-dev-server:
webpack-dev-server 主要包含了三个部分:

  1. webpack: 负责编译代码
  2. webpack-dev-middleware: 主要负责构建内存文件系统,把webpack的 OutputFileSystem 替换成 InMemoryFileSystem。同时作为Express的中间件拦截请求,从内存文件系统中把结果拿出来。
  3. express:负责搭建请求路由服务。

其次,介绍工作流程:

  1. 启动dev-server,webpack开始构建,在编译期间会向 entry 文件注入热更新代码;
  2. Client 首次打开后,Server 和 Client 基于Socket建立通讯渠道;
  3. 修改文件,Server 端监听文件发送变动,webpack开始编译,直到编译完成会触发"Done"事件;
  4. Server通过socket 发送消息告知 Client;
  5. Client根据Server的消息(hash值和state状态),通过ajax请求获取 Server 的manifest描述文件;
  6. Client对比当前 modules tree ,再次发请求到 Server 端获取新的JS模块;
  7. Client获取到新的JS模块后,会更新 modules tree并替换掉现有的模块;
  8. 最后调用 module.hot.accept() 完成热更新;
上一篇 下一篇

猜你喜欢

热点阅读