webpack

127 阅读7分钟
  1. Entry 入口文件 指示webpack以哪个文件为起点开始打包,分析构建内依赖图
  2. Output 输出打包后资源bundles文件地址 及命名
  3. Loader 处理那些非JavaScript的文件(webpack自身只理解JavaScript
  4. Pluggins 插件 面向更多强大的功能
  5. Mode
  1. 开发环境 development 能让代码本地调试运行的环境
  2. 生产环境 production 能让代码优化上线运行的环境

结论:

  1. webpack能处理js/json资源,不能处理css/img等其他资源
  2. 生产环境及开发环境将ES模块编译成浏览器能识别等模块化
  3. 生产环境比开发环境多了一个压缩js代码

Entry 入口起点

  1. string --- './src/index.js'

单入口

打包形成一个chunk,输出一个bundle文件 chunk的默认名称为main

  1. array --- ['./src/index.js','./src/Block.js']

多入口

所有文件都会打包成一个chunk,输出去的只有一个bundle文件

---只有在HMR功能中让html生效~

  1. object

多入口

几个文件就打包成几个chunk,输出几个bundle文件

entry: {

index: './src/index.js',

test: './src/test.js'

},

entry: {
  index: ["./src/index.js","./src/block.js"], //所有文件最终形成一个文件,输出出去只有一个bundle文件
  test: "./src/test.js"
},

output 打包后的文件目录

  output: {
    //文件名称(指定名称)
    filename: 'liang[name].[contenthash:10].js',
    //输出的文件目录(所有资源输出的公共目录)
    path: path.resolve(__dirname, 'dist'),
    //所有资源(引入)公共路径前缀 ---'imgs/a.jpg' --- '/imgs/a.jpg' (一般用于生产环境)
    publicPath: '/',
    //打包时自动删除上次生成的包
    clean: true,
    //非入口chunk的命名 如 import 和 optimization打包名称
    chunkFilename:'[name]_chunk.js', 
    //打包后文件的名称默认为main
    library:'[name]',
    //将打包后的变量名添加到哪个环境上 'global' || 'window' ||'commonjs'
    libraryTarget:'window' 
  },

loader

 module: {
    rules: [
      {
        oneOf: [ //以下loader只匹配一个,不能有两个处置处理同一类型的文件,(遇到css less只匹配一个loader)
          {
            test: /.css$/,
            //多个loader用use
            use:['style-loader', 'css-loader'] ,
          },
          {
            test: /.(js|jsx)$/,
            //排除文件node_module下的js文件
            exclude: /(node_modules)/,
            //只检查src下面的js文件
            include: resolve(__dirname,'src'),
            //优先执行'pre' 延迟执行 'post' 不设置顺序执行
            enforce:'pre',
          },
        ]
      },
    ]
  },

resolve

  resolve: {
    //配置解析模块路径别名 
    alias: {
      $css: resolve(__dirname, 'src') // src下的文件引用可以设置为 import '$css/index.less';
    },
  //告诉webpack解析模块是去找哪个目录
    modules: [resolve(__dirname,'./node_modules'),'node_modules']
}

devserver

  devServer: {
    compress: true,//对服务进行gzip 压缩
    port: 3001,
    hot: true,//开启HMR功能 热更新 1.css style-loader   2.js
    proxy:
    //一旦devserver服务器接收到/api/xxx的请求,就会把请求转发到另一个服务器(3000)
    '/api':target: 'http://localhost:3000',
    //发送请求时,请求路径重写,将/api/xxx --- /xxx(去掉/api)
    pathRewrite:{'^/api:''}
  },
    
  浏览器和服务器之间有跨域,同源策略。
  域名 端口 协议
  服务器之间没有跨域,浏览器和代理之间没有跨域问题,代理之间没有跨域

代码分割

1.多入口

2.import

3.optimization

const TerserPlugin = require("terser-webpack-plugin");

optimization: { //node_modules依赖单独打包为一个chunk
    splitChunks: {
      chunks: 'all',
      //以下都是默认值
      minSize: 30 * 1024, //分割的chunk最小为30kb,
      maxSize: 0, // 最大没有限制
      minChunks:1,//要提取的chunk最小被引用一次
      maxAsyncRequests:5,//按需加载时并行加载的最大文件的最大数量
      maxInitialRequests:3, //入口文件最大并行请求数量
      ......
    },
    //将当前模块记录其他模块的hash值单独打包为一个文件 runtime  
    //解决:b文件引入了a文件,修改a文件导致b文件的contenthash值变化,引起缓存失效
    runtimeChunk: {
      name: (entrypoint) => `runtimechunk~${entrypoint.name}~~~~`,
    },
    minimize: true,
    minimizer: [ //匹配压缩的文件
      new TerserPlugin({
        test: /.js(?.*)?$/i,
      }),
    ],
  },

webpack.config.js webpack的配置文件

作用 :指定webpack干哪些活儿 运行指令时会加载里面的配置

所有构建工具都是基于nodejs 平台运行的~模块化默认采用commonjs

使用方法

loader 1.下载 2.使用(配置loader

plugins 1.下载 2.引用 3.使用(配置plugin

插件

html-webpack-plugin 默认创建空的HTML文件,自动引入打包后的输出的所有资源(CSS/JS资源)

webpack-dev-server 安装内置服务器,用来自动化(自动化编译,自动打开浏览器,自动刷新浏览器

mini-css-extract-plugin 将打包后js中的css代码提取出来

css-minimizer-webpack-plugin css代码压缩

loader

css-loader

html-loader 处理html文件中的img图片

postcss-loader postcss-preset-evn 处理css兼容性

eslint-config-airbnb-base eslint-plugin-import eslint eslint-loader 配置eslint

babel babel-loader @babel/core

@babel/preset-env ****处理基本兼容性

core-js 更多兼容性处理---实现按需加载

兼容IE 模拟js的运行环境 让老版本的浏览器用新版本的环境 可配置按需加载

thread-loader 多进程打包

webpack 代码优化

devtool

  1. source-map :外部

错误代码准确信息和源代码的错误位置

  1. inline-source-map:内联

错误代码准确信息和源代码的错误位置

  1. hidden-source-map :外部

错误代码错误原因,但没有错误位置

不能追踪源代码错误,只能提示构建后代码的错误位置

  1. eval-source-map:内联

错误代码准确信息和源代码的错误位置

  1. nosources-source-map : 外部

显示错误代码准确原因 不生成 SourceMap

  1. cheap-source-map :外部

错误代码准确信息和源代码的错误位置,只能精确到行

inline 代码内通过 dataUrl 形式引入 SourceMap

hidden 生成 SourceMap 文件,但不使用

eval eval(...) 形式执行代码,通过 dataUrl 形式引入 SourceMap 速度最快

nosources 不生成 SourceMap

cheap 只需要定位到行信息,不需要列信息

module 展示源代码中的错误位置

1.开发环境

  • 优化打包构建速度
  • 优化代码调试

速度快 eval-cheap-source-map eval速度最快,且只需定到行

调试更友好 cheap-module-source-map 错误原因定位到源代码中,报错时精确到行,

最佳 eval-cheap-module-source-map

2.生产环境性能优化 (内联会让代码体积变大,所以在生产下不要用内联

  • 优化打包构建速度
  • 优化代码运行的性能

nosources-source-map 全部隐藏

hidden-source-map 只隐藏源代码,会提示打包后代码错误信息

source-map none

          /**
     * 语法检查 eslint-loader eslint
     * 注意:只检查自己写的源代码,第三方库不检查
     * 设置检查规则
     * package.json 中 eslintConfig中设置
     * "eslintConfig":{
     *  "extends":"airbnb-base"
     * }
     * airbnb ---> eslint-config-airbnb-base    eslint-plugin-import   eslint
     * **/
      
  {
    test: /.js$/,
    exclude: /node_modules/,
    loader: 'eslint-loader',
    enforce: 'pre',// loader优先执行
    options: {
      //自动修复eslint的错误
      fix: true
    }
  },

缓存

babel缓存

cacheDirectory: true

文件资源缓存

  1. hash:每次webpack构建时会生成唯一的hash值 ,任何一个文件改动,整个项目的构建 hash 值都会改变

filename: 'bundle.[hash:10].js

问题:如果js和css同时使用一个hash值,重新打包会导致所有缓存失效(可能我只改了一个文件却更新了所有文件)

2.chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样,文件的改动只会影响其所在 chunk 的 hash 值

filename: 'chunkhash.js'

问题:css和js值还是一样的 是因为要在js中引入css

3.contenthash:根据文件内容来生成hash值,不同文件的hash值一定不一样,只会影响自身

filename:'[name].[contenthash:10].js',

\

tree shaking :去除无用代码

前提:

1.必须使用ES6模块化

2.开启 production(生产环境 )生产模式,打包比较慢,会开启 tree-shaking 和压缩代码

// 所有文件都有副作用,全都不可 tree-shaking
{
 "sideEffects": true
}
// 没有文件有副作用,全都可以 tree-shaking
{
 "sideEffects": false
}
// 只有这些文件有副作用,所有其他文件都可以 tree-shaking,但会保留这些文件
{
 "sideEffects": [
  "./src/file1.js",
  "./src/file2.js"
 ]
}
{
            //test指定的是规则生效的文件
            test: /.js$/,
            //要使用的loader
            use: [
              //配置babel 兼容老的浏览器
              {
                //指定加载器
                loader: "babel-loader",
                //设置babel
                options: {
                  //设置与定义的环境
                  presets: [
                    [
                      //指定环境的插件
                      "@babel/preset-env",
                      //配置信息
                      {
                        //兼容的目标浏览器
                        targets: {
                          chrome: "60",
                          ie: "11"
                        },
                        //指定corejs的版本
                        corejs: {
                          version: 3
                        }, //比如ie11没有Promise 它会用自己的Promise方法
                        //使用corejs的方式  "usage"表示按需加载
                        useBuiltIns: "usage",
                        modules: false
                      }
                    ]
                  ],
                  cacheDirectory: true //生产环境 开启babel缓存 第二次构建时,会读取之前的缓存
                }
              },
              // 'ts-loader' //加载器从后往前执行 先用ts-loader把ts代码转化为js 然后再用babel转换为老版本的js
            ],
            //要排除的文件
            exclude: /node_modules/
          },

打包

entry: './src/index.js',
entry: {
  index:'./src/index.js',
  test:'./src/test.js'
},
  
filename:'[name].[contenthash:10].js',

  
optimization 
1.可以将node_modules的依赖单独打成一个chunk最终输出
2.自动分析多入口chunk中,有无公共的代码,如果有单独打包成一个chunk

optimization: {
  splitChunks: {
    chunks: 'all'
  }
}
/**
通过js代码,让某个文件单独打包成一个chunk
import 动态导入语法:能将某个文件单独打包
**/

import('./test')
  .then((result) => {
    console.log(result);
  })
  .catch(() => {
    console.log('文件加载失败!');
  })

以上代码在index.js

懒加载&预加载

//懒加载当文件点击时才加载
//正常加载是并行加载,同一个时间加载多个文件
//预加载 Prefetch 会在使用之前提前加载js文件,等其他资源加载完毕,浏览器空闲了,在偷偷加载资源    
const add = () => {
    return import(/*webpackChunkName: 'test',webpackPrefetch:true*/'./test').then(({ test }) => {
      return console.log(test, '00');
    });
  }

多进程打包 thread-loader

进程启动大概为600ms,进程通信也有开销,大项目才适合多教程打包

tnpm i thread-loader -D

const config = {
//...
  {
    test: /.(js|jsx)$/,
    exclude: /(node_modules|bower_components)/,
    use: [
      {
        loader: 'thread-loader', // 开启多进程打包
        options: {
          worker: 3,
        }
      },
      {
        loader: 'babel-loader',
        options: { presets: ['@babel/env', '@babel/preset-react'] },
      }
    ],
  },
};

externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法

例如,从 CDN 引入 jQuery,而不是把它打包:

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>
const config = {
  //...
  externals: {
    jquery: 'jQuery',
  },
};
import $ from 'jquery';

$('.my-element').animate(/* ... */);

我们可以用这样的方法来剥离不需要改动的一些依赖,大大节省打包构建的时间。

1.开发环境性能优化

优化打包构建速度

  • HRM
    1. css style-loader
    2. js
if (module.hot) {
  module.hot.accept();
}

优化代码调试

  • source-map

2 .生产环境性能优化

优化打包构建速度

  • oneOf 找到了就直接用 忽略其他loader(需要两个loader处理的时候需要提取出去不呢直接使用
  • babel 缓存
{
  loader: 'babel-loader',
  options: { 
    presets: ['@babel/env', '@babel/preset-react'],
    cacheDirectory: true  //开启babel js缓存
  },
},
  • 多进程打包(进程启动大概为600ms,进程通信也有开销,大项目才适合多教程打包
{
  test: /.(js|jsx)$/,
  exclude: /(node_modules|bower_components)/,
  use: [
    {
      loader: 'thread-loader', // 开启多进程打包
      options: {
        worker: 3,
      }
    },
  ],
},

优化代码运行的性能

  • 缓存(hash-chunkhash-contenthash)
  • tree shaking 去除无用代码 sideEffect:["./src/file2.js"]
  • code split

单入口 输出一个文件

optimization :node_modules依赖单独打包为一个chunk

多入口 几个入口就输出几个文件

optimization :提取多个入口文件中对node_modules依赖 不会重复打包以依赖

    • import 制定js文件打成一个包
  optimization: { //node_modules依赖单独打包为一个chunk
    splitChunks: {
      chunks: 'all'
    }
  },
  • 懒加载/预加载
    • 懒加载 点击的时候再加载
    • 预加载 等其他js css文件加载之后悄悄加载 用的时候是缓存
  • externals 不打包jquery库 用cdn的方法引用