webpack 笔记

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 1.1 webpack 五个核心概念1.1.1 Entry入口,指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图

1 简介

1.1 webpack 五个核心概念

1.1.1 Entry

入口,指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图

1.1.2 Output

输出,指示webpack打包后的资源bundles输出到哪里去,以及怎么命名

1.1.3 Loader

加载器,可以让webpack能够去处理非js文件(webpack自身之理解javascript)

1.1.4 Plugins

插件,可以用于执行更广范围的任务,插件的范围包括从打包优化和压缩一直到重新定义环境变量等

1.1.5 Mode

模式,指示webpack使用相应模式的配置

  • development:能让代码本地调试运行的环境
  • production:能让代码优化上线的运行环境

2 webpack 初体验

2.1 创建一个webpack项目

  1. 创建一个目录,初始化包管理 npm init
  2. 运行命令npm install webpack webpack-cli -D
  3. 新建src文件夹用于存放代码,新建build文件夹用于存放打包好的文件
  4. 编辑代码
  5. 运行命令(开发环境) webpack ./src/index.js -o ./build/build.js --mode=development

默认只能处理js/json文件

2.2 打包css样式资源

  1. 新建css文件style.css
  2. 在index.js中引入 import 'style.css'
  3. 创建webpack.config.js文件来配置webpack(使用commentJS)
const {
  resolve
} = require('path');
module.exports = {
  mode: 'development', // 模式,不能同时存在
  // mode: 'production'
  entry: './src/index.js', // 入口文件
  output: {
    filename: 'built.js', // 输出文件名
    path: resolve(__dirname, 'build') // 输出路径
  },
  module: { // loader配置
    rules: [{
      test: /\.css$/,// css结尾的文件
      use: [// 执行顺序:从右到左,从下到上,依次执行
        'style-loader',// 创建爱你style标签,将js中的样式资源插入,添加到head
        'css-loader',// 将css文件变成commentjs模块加载js中,内容是字符串
      ]
    }]
  },
  plugins: [ // 插件配置
  ],
}
复制代码
  1. 安装所需要的loader
  2. 运行命令 webpack

使用less、scss等预编译语法需要下载对应的loader,配置方式同上,再添加一个rule对象

2.3 打包HTML文件

  1. 安装打包html的插件 npm install html-webpack-plugin-D
  2. 在webpack.config.js中引入并添加配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
···
plugins: [ // 插件配置
  // html-webpack-plugin
  // 功能: 创建一个空的html文件引入打包之后的所有资源
  new HtmlWebpackPlugin({
    // 赋值目标文件并引入打包的资源
    template: './src/index.html'
  })
],
复制代码

在创建html模板的时候不要引入资源

2.4 打包图片文件

图片添加略过,只给出配置方式

module: { // loader配置
  rules: [{
    test: /\.(jpg|png|gif|jpeg)$/,
        // 只有一个loader的时候可以直接loader+loader名
    loader: 'url-loader',// 下载时需要下载url-loader和file-loader
    options: {
      /**
       * 限制大小,小于限制就会被base64处理
       * 优点减少请求数量,减轻服务器压力
       * 缺点图片会更大,文件请求速度会变慢
       */
      limit: 200 * 1024
    }
  }]
},
复制代码

如果html模板中添加了图片,添加一个额外的loader来解析

{
  test: /\.html$/,
  loader: 'html-withimg-loader' // 处理html中的图片
}
复制代码

但是,因为url-loader默认使用es6模块化解析,而html-loader引入图片是commentjs

解析时会出现[object Module]

解决办法:关闭ur-loader的ES6解析,在url-loader的options中添加 esModule: false

2.5 打包其他资源

{
  exclude: /\.(css|html|jpg|png|gif|jpeg)$/,
  loader: 'file-loader'
}
复制代码

除了这匹配到的后缀文件之外,其他的文件用file-loader打包

2.6 使用devServer

开发服务器:用来自动编译,热更新等

在内存中编译打包,不会有任何输出

运行:webpack-dev-server

  1. 运行npm install webpack-dev-server -D安装
  2. webpack.config.js中添加配置
devServer: {
    contentBase: resolve(__dirname, 'build'),// 项目构建后的路径
  compress: true,// 启动gzip压缩
  port: 3000,// 端口号
  open: true,// 打开默认浏览器
}
复制代码
  1. 运行webpack-dev-server

3 开发环境配置

const {
  resolve
} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  // mode: 'production'
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
      ]
    },{
      test: /\.(jpg|png|gif|jpeg)$/,
      loader: 'url-loader',
      options: {
        limit: 200 * 1024,
        esModule: false
      }
    },{
      test: /\.html$/,
      loader: 'html-loader'
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
  }
}
复制代码

4 生产环境搭建

4.1 提取css成单独文件

  1. 安装插件 npm install mini-css-extract-plugin -D
  2. 修改webpack.config.js配置文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
···
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        // 'style-loader',
        MiniCssExtractPlugin.loader,// 替换style-loader,提取css成单独文件
        'css-loader', 
      ]
    },
    ···]
  },
  plugins: [ 
    ···
    new MiniCssExtractPlugin({
      filename: 'css/style.css'
    })
  ],
复制代码
  1. 运行打包命令

4.2 css兼容性处理

使用到postcss

  1. 运行 npm install postcss-loader postcss-preset-env -D
  2. 修改webpack.config.js的css相关部分配置
{
  test: /\.css$/, 
  use: [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        ident: 'postcss',
        plugins: () => [
          require('postcss-preset-env')()
        ]
      }
    }
  ]
}
复制代码
  1. 在package.json中添加浏览器版本控制
"browerslist": {
  "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safair version"
  ],
  "production": [
     ">0.2%",
     "not dead",
     "not op_mini all"
  ]
}
复制代码
  1. 运行打包

4.3 压缩css

使用插件 optimize-css-assets-webpack-plugin

  1. 运行npm install optimize-css-assets-webpack-plugin -D
  2. webpack.config.js中引入
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
···
plugins: [
  ···
  new OptimizeCssAssetsWebpackPlugin()
]
复制代码

4.4 js语法检查

使用eslint,使用airbnb规则检查js语法

  1. 运行 npm install eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import -D 安装所需的依赖和插件
  2. 在package.json中设置eslintConfig,继承airbnb
"eslintConfig": {
  "extends": "airbnb-base"
}
复制代码
  1. webpack.config.js中添加rules
{
    test: /\.js$/,
    exclude: /node_modules/,// 只检查自己的代码,不检查第三方库中的代码
    loader: 'eslint-loader',
    options: {
        fix: true// 自动修复eslint错误
    }
}
复制代码
  1. 使用 // eslint-disable-next-line控制下一行不进行检查

4.5 js兼容性处理

使用babel-loader进行兼容性处理

  1. 运行 npm install babel-loader @babel/core @babel/preset-env @babel/polyfill -D
  2. 添加webpack.config.js的rules
{
  test: /\.js$/,
     exclude: /node_modules/,
  loader: 'babel-loader',
  options: {
    presets: ['@babel/preset-env']
  }
},
复制代码

@babel/polyfill属于高级语法支持,在需要兼容的js文件中引入即可

import '@babel/polyfill'

@babel/polyfill属于暴力支持,回事打包文件变的非常大,所以一般采用按需加载的方式:core.js

  1. 运行 npm install corejs -D
  2. 修改之前的兼容性配置规则
{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  options: {
    presets: [
      [
        '@babel/preset-env',
        {
          useBuiltIns: 'usage',// 按需加载
          corejs: {
            version: 3
          },
          targets: {// 制定兼容浏览器版本
            chrome: '60',
            firefox: '50',
            ie: '9',
            safari: '10'
          }
        }
      ]
    ]
  }
},
复制代码

4.6 HTML和js压缩

4.6.1 js压缩

将模式调整为production即可实现js代码压缩

4.6.2 HTML压缩

在html的插件中添加设置

new HtmlWebpackPlugin({
  // 复制目标文件并引入打包的资源
  template: './src/index.html',
  minify: {
    // 移除空格
    collapseWhitespace: true,
    // 移除注释
    removeComments: true
  }
}),
复制代码

5 生产环境配置

const {
  resolve
} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: { // loader配置
    rules: [{
        test: /\.css$/, 
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }, {
        test: /\.(jpg|png|gif|jpeg)$/,
        loader: 'url-loader',
        options: {
          limit: 200 * 1024,
          esModule: false
        }
      }, {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
                enforce: 'pre',// 优先执行
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      }, {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: {
                  version: 3
                },
                targets: {
                  chrome: '60',
                  firefox: '50',
                  ie: '9',
                  safari: '10'
                }
              }
            ]
          ]
        }
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/style.css'
    }),
    new OptimizeCssAssetsWebpackPlugin()
  ],
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
  },
  // mode: 'development',
  mode: 'production',
}
复制代码

babel和eslint可能会产生冲突使用enforce: 'pre'让eslint先执行

6 性能优化篇

6.1 开发环境性能优化

6.1.1 优化打包构建速度

6.1.1.1HMR——hot module replacement(模块热替换)

只会重新打包这一个模块,不会重新打包

配置devServer

devServer: {
  contentBase: resolve(__dirname, 'build'),
  compress: true, // 启动gzip压缩
  port: 3000, // 端口号
  open: true, // 打开默认浏览器
  hot: true,// 开启热更新
},
复制代码

样式文件热更新基于'style-loader';

js文件默认不能使用HMR功能;

HTML文件默认不能使用HMR功能,同时会导致问题,HTML文件不能热更新,解决办法:修改entry入口,将html文件引入(不需要做HMR)

6.1.1.2 oneOf减少loader检索

每个文件在检索的的时候会每个rules都验证一遍,将所有的rules都放到oneOf中就可以验证到所在的规则不再继续验证

这样出现了问题,js可能会有多个loader,解决办法:oneOf中保留一条rule,其他的取出来与oneOf平级

6.1.2 优化代码调试

source-map:提供语言代码构建后代码映射技术(构建代码出错,追踪源代码错误)

添加devtool

devtool: 'source-map',
复制代码

[inline- |hidden- | eval-][nosources-][cheap-[module-]]source-map分别对应:

  • inline-内联:(汇集在一起)错误代码准确信息和源代码错误位置
  • hidden-外部:代码错误原因,没有错误位置,不能追踪代码错误
  • eval-内联:(分别跟每个文件对应)错误代码准确信息和源代码错误位置(位置信息是哈希值)
  • nosources-外部:错误代码准确信息,没有源代码错误信息
  • cheap-外部:错误代码准确信息和源代码错误位置(只精确到行,提示整行错误)
  • cheap-module-外部:错误代码准确信息和源代码错误位置
  • source-map`外部:错误代码准确信息和源代码错误位置:

模式使用

  • 开发环境:速度快,开发更友好
  • 速度快eval>inline>cheap>···
  • eval-cheap-source-map
  • eval-source-map
  • 调试更友好
  • source-map
  • cheap-module-source-map
  • cheap-source-map
  • -->eval-source-map / eval-cheap-module-source-map
  • 生产环境:源代码要不要隐藏
  • 内联会让代码体积变大,使用外部方式
  • source-map
  • ···
  • 隐藏代码
  • nosource-source-map// 全部隐藏
  • hidden-source-map// 隐藏源代码
  • -->source-map / cheap-module-source-map

6.2生产环境性能优化

缓存机制

babel缓存

修改js打包配置,开启缓存

{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'babel-loader',
  options: {
    presets: [
      [
        '@babel/preset-env',
        {
          useBuiltIns: 'usage', // 按需加载
          corejs: {
            version: 3
          },
          targets: { // 制定兼容浏览器版本
            chrome: '60',
            firefox: '50',
            ie: '9',
            safari: '10'
          }
        }
      ]
    ],
    cacheDirectory: true// 开启缓存
  }
},
复制代码

强缓存模式,所有的数据都要缓存,从新打包之后不会试用新的文件

文件资源缓存
hash

这里修改输出文件名,每次到打包之后的文件都不一样,这就不加绝了强制缓存带来的不刷新了(取10位hash值)

output: {
  filename: 'js/built[hash:10].js', // 输出文件名
  path: resolve(__dirname, 'build') // 输出路径
},
复制代码

所有的js和css文件共用一个哈希值,重新打包会导致所有的缓存失效

chunkhash

根据chunk生成的hash值,如果打包源来自同一个chunk,那么hash值相同

此时,js和css的hash值还是相同,因为css是在js中引入的,同属于一个chunk

contenthash

根据文件内容生成hash,不同文件hash值一定不一样

filename: 'js/built[contenthash:10].js'
复制代码

如果单独提取了css,css命名修改为

new MiniCssExtractPlugin({
    filename: 'css/style-[contenthash:10].css'
})
复制代码

tree shaking

去除无用代码

  1. 必须使用ES6模块化
  2. 开启production环境

打包是会自动摇去没有用的叶子

在package.json中配置"sideEffects": false所有代码都没有副作用

问题:可能会把css文件干掉,解决"sideEffects": ["*.css"]

code split

文件分割

  • 多入口文件分割
  • 添加webpack.config.js配置项,将node中的代码单独打包一个chunks,自动分析多入口文件中有没有公共文件,如果有会单独打包成一个chunk
optimization: {
  splitChunks: {
    chunks: 'all'
  }
},
复制代码
  • 通过js代码,让某个文件单独打包成一个chunk

懒加载、预加载

懒加载
document.getElementById('btn').onclick = function() {
  import(/* webpackChunkName: 'test'*/'./js/test.js')
        .then(({mul}) => {
      console.log(mul(4, 5));
    })
}
复制代码

将引入放在事件的回调函数中,事件触发时才加载js文件

配合eslint语法检查是有问题的,在.eslintrc文件汇总设置 "allowImportExportEverywher": true,允许在任何地方import

预加载

将引入改为

import(/* webpackChunkName: 'test', webpackPrefetch: true */'./js/test.js')
复制代码

预加载会提前将js文件加载

区别
  • 懒加载:文件需要才加载
  • 预加载:使用之前加载,浏览器空闲再加载(兼容性较差)
  • 正常加载:并行加载

PWA

渐进式网络开发应用程序(离线可访问)

workbox-->workbox-webpack-plugin

  1. 运行 npm install workbox-webpack-plugin -D
  2. 修改webpack.config.js配置
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
···
plugin: [
    ···
    new WorkboxWebpackPlugin.GenerateSW({
    clientsClaim: true,
    skipWaiting: true
  })
]
复制代码
  1. 帮助serviceworker快速启动;删除旧的serviceworker
  2. 在入口文件中注册serviceworker,处理兼容
if('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('./service-worker.js')
          .then(() => {
            console.log('sw注册成功')
        }).catch(() => {
            console.log('sw注册失败')
        })
    })
}
复制代码
  1. eslint不识别window,navigator等全局变量,修改package.json配置
"eslintConfig": {
  "extends": "airbnb-base",
  "env": {
    "browser": true// 支持浏览器全局变量
  }
}
复制代码
  1. 构建代码

多进程打包

消耗时间长的才需要多进程打包,多进程开始需要时间,进程通信也需要时间

  1. 运行npm install thread-loader -D
  2. 修改webpack.config.js中babel所在的rule
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
    {
      loader: 'thread-loader',// 多进程打包
      options: {
        workers: 2,// 使用两个进程
      }
    },
    {
      loader: 'babel-loader',
      ···
    }
  ]
},
复制代码

externals

忽略某些包,比如CDN引入的

在webpack.config.js第一级目录下添加

externals: {
  // 包名: npm库名
  jquery: 'jQuery'
}
复制代码

dll

  1. 新建一个配置文件,例如 webpack.dll.js
const {resolve} = require('path');
const webpack = require('webpack');
module.exports = {
  entry: {
    // [name]: 要打包的库名
    jquery: ['jquery']
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]'// 打包库里暴露的内容叫什么名
  },
  plugins: [
    // 打包生成一个映射关系
    new webpack.DllPlugin({
      name: '[name]_[hash]',// 映射库暴露的内容名称
      path: resolve(__dirname, 'dll/manifast.json')// 输出文件路径
    })
  ],
  mode: 'production'
}
复制代码
  1. 运行命令 webpack --config webpack.dll.js
  2. 运行 npm install add-asset-html-webpack-plugin -D
  3. 修改webpack.config.js配置
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
···
plugins: [
    ···
    // 告诉webpack那些文件不需要打包,以及映射关系
  new webpack.DllReferencePlugin({
    manifest: resolve(__dirname, 'dll/manifest')
  }),
    // 将文件打包输出,并在html自动引入该文件
  new AddAssetHtmlWebpackPlugin({
    filepath: resolve(__dirname, 'dll/')
  })
]


相关文章
|
6月前
|
域名解析 JavaScript 前端开发
TypeScript笔记(3)—— 使用WebPack工具
TypeScript笔记(3)—— 使用WebPack工具
56 0
|
JavaScript 测试技术 Shell
[笔记]vue从入门到入坟《五》vue-cli构建vue webpack项目
[笔记]vue从入门到入坟《五》vue-cli构建vue webpack项目
|
前端开发
前端学习笔记202307学习笔记第五十七天-模拟面试笔记webpack-webpack能打包成es6语法吗
前端学习笔记202307学习笔记第五十七天-模拟面试笔记webpack-webpack能打包成es6语法吗
53 0
|
前端开发
前端学习笔记202303学习笔记第三天-Vue3.0-webpack插件作用
前端学习笔记202303学习笔记第三天-Vue3.0-webpack插件作用
57 0
|
前端开发
前端学习笔记202303学习笔记第三天-Vue3.0-webpack-devServer节点
前端学习笔记202303学习笔记第三天-Vue3.0-webpack-devServer节点
53 0
前端学习笔记202303学习笔记第三天-Vue3.0-webpack-devServer节点
|
前端开发
前端学习笔记202303学习笔记第二天-webpack基本使用
前端学习笔记202303学习笔记第二天-webpack基本使用
60 0
前端学习笔记202303学习笔记第二天-webpack基本使用
|
前端开发 JavaScript 测试技术
Webpack | 青训营笔记
Webpack | 青训营笔记
50 0
|
前端开发
前端学习笔记202303学习笔记第三天-Vue3.0-webpack访问自动打包生成的文件1
前端学习笔记202303学习笔记第三天-Vue3.0-webpack访问自动打包生成的文件1
76 0
|
前端开发
前端学习笔记202303学习笔记第四天-Vue3.0-webpack自动清理webpack中dist目录
前端学习笔记202303学习笔记第四天-Vue3.0-webpack自动清理webpack中dist目录
51 0
|
前端开发
前端学习笔记202303学习笔记第四天-Vue3.0-webpack了解企业级项目打包发布
前端学习笔记202303学习笔记第四天-Vue3.0-webpack了解企业级项目打包发布
66 0