webpack学习笔记

122 阅读3分钟

webpack简介

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。主要是用来将前端资源打包、压缩、优化。

在项目中使用node生成package.json文件

在项目文件目录下输入,随后默认回车,即会在根目录下生成名为package.json的文件

npm init

如果不设置,默认入口是index.js文件

package.png

入口文件的作用

在入口文件中,有许多资源的引入,webpack会将这些引入的资源形成一个依赖,继而形成一个代码块chunk,再对chunk进行处理,比如将less编译成css,es6转化成浏览器可以识别的语法,将chunk处理的这一步称为打包。将打包的东西输出出去称为bundle

//引入js资源
import $ from 'jquery'
//引入样式资源
import './index.less'
//引入图片等资源等等

01.png

webpack五个核心概念

Entry

入口(Entry)指示Webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。 默认为index.js。

Output

输出(Outpuut)指示Webpack打包后的资源bundles输出到哪里去,以及如何命名。

Loader

Loader让Webpack能够去处理那些非Javascript文件(webpack自身只理解JavaScript)

Plugins

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

Mode

模式(Mode)指示Webpack使用相应模式的配置

QQ截图20211103112808.png

webpack初体验

1.新建一个文件夹,命令行输入npm init 创建package.json文件夹

npm init

2.下载两个webpack相关的包,全局安装以及本地依赖

npm i webpack webpack-cli -g
npm i webpack webpack-cli -D

安装完成后出现package-lock.json文件以及node_modules文件夹

3.使用webpack打包js文件输出

新建一个index.js作为入口文件,随意写一段js代码

function add (x, y) {
  return x + y
}

console.log(add(2, 3))

运行指令

webpack ./src/index.js -o ./build/built --mode=development
/*
webpack会以index.js为入口文件输出到build文件夹下面的build.js文件中,整体打包环境是开发环境

生成环境将development改为production
*/

使用node .\build\built\main.js可以打印出结果 5

node .\build\built\main.js   //打印出5

如果打包非js或json文件则会报错

webpack ./src/index.css -o ./build/built  --mode=development

//报错信息[webpack-cli] Unknown command or entry './src/index.css'
//[webpack-cli] Run 'webpack --help' to see available commands and options

生产环境和开发环境能够将ES6编译成浏览器能够识别的语法

4.使用webpack打包样式资源

由于webpack只能打包js和json文件,因为打包样式需要借助Loader,在webpack.config.js中配置,webpack基于node环境,因此遵循common.js语法

新建webpack.config.js文件

//拼接绝对路劲的办法
const {resolve} = require('path');

module.exports = {
  //入口起点
  entry: './src/index.js',

  //输出
  output: {
    //输出文件名
    filename: 'built.js',
    //输出路劲
    path: resolve(__dirname, 'build'),

  },

  //loader的配置
  module: {
    rules: [
      //详细loader配置
      {
        //匹配哪些文件,为正则表达式
        test: /\.css$/,
        //使用哪些loader
        use: [
          //use数组执行顺序从右向左执行,
          //创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          //将css文件变成commonjs模块加载到js中,里面内容是样式字符串
          'css-loader',
        ]
      }
    ],
  },
  //plugins配置
  plugins: [
      
  ],
  //模式,开发或生产模式
  mode: 'development'
}

下载style-loader 和 css-loader包

npm i style-loader css-loader -D

使用打包命令

webpack

下面是项目结构

QQ截图20211104101009.png

刚开始webpack版本下错了踩了很多坑,当你发现项目报错怎么也解接不了的时候可以考虑对照官网重新下一遍webpack版本

5.打包htlm资源

此时需要用到html-webpack-plugin插件,命令行中输入

npm i html-webpack-plugin -D

在页面开头引入,通过new方法在plugin数组中创建

const HtmlWebpackPlugin = require('html-webpack-plugin');

  plugins: [
    //默认创建一个空的html,自动引入打包输出所有资源
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],

6.打包图片资源

webpack官网中提出webpack5内置Asset Modeles

//module中配置
module: {
    rules: [
        {
            test: /\.(png|svg|jpg|jpeg|gif)$/i,
            type: 'asset/resource'
        }
    ]

}
//src下的index.js入口文件中引入图片,如果使用Asset Modeles会报错
import './index.css';

import Man from '../asset/card-2.jpg'

function component() {
  const ele = document.createElement('div');
  const man = new Image();
  man.src = Man;
  ele.appendChild(man)
  return ele
}
document.body.appendChild(component())

如果在url中或标签中引入图片可以直接编译

7.其他资源打包

如果遇到xml或csv资源,下载对应的loader即可

npm install --save-dev csv-loader xml-loader
module: {
 rules: [
  {
   test: /\.(csv|tsv)$/i,
   use: ['csv-loader'],
  },
  {
   test: /\.xml$/i,
   use: ['xml-loader']
  }
 ]
}

devserve

开发服务器devServe: 用来自动化(自动编译,自动打开浏览器,自动刷新浏览器) 启动指令: npx webpack-dev-server

安装webpack-dev-server

npm install webpack-dev-server --save-dev

也可以在package.json中的script中配置使用指令

  "scripts": {
    "start": "webpack serve --open"
  },

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

  devServer: {
    static: resolve(__dirname, 'build'),
    port: 1823,   //服务的端口号
    // 开启gzip压缩
    compress: true,
    // 自动打开浏览器
    open: true
  }

一些相关的plugin

mini-css-extract-plugin

用于提取css为单独的文件,而不是以嵌入的方式 下载插件

npm i mini-css-extract-plugin -D

webpack.config.js中配置

//去除'style-loader'
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 'style-loader',
          miniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
   plugins: [
    new miniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],

css-minimizer-webpack-plugin

用于压缩和优化css代码

npm install css-minimizer-webpack-plugin --save-dev
  optimization: {
    minimizer: [
      // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
      // `...`,
      new CssMinimizerPlugin(),
    ],
  },

Eslink相关

配置Eslint

配置eslint需要下载eslint-webpack-plugin,使用airbnb规范要下载eslint-config-airbnb-base

npm i eslint-webpack-plugin eslint-config-airbnb-base -D

webpack.config.js中配置

  plugins: [
    /*
    eslint只检查用户写的代码,第三方的库是不检查的,
    使用exclude可以排除免于检查的文件
    设置检查规则,在package.json中的eslintConfig中设置
    使用airbnb ----》 eslint-config-airbnb-base   eslint-plugin-import eslint
*/
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new ESLintPlugin({
      fix: true, //ESlint自动修复特性
      extensions: ['js', 'json', 'coffee'], //指定需要检查的扩展名。
      exclude: '/node_modules/'
    })
  ],

package.json中配置

  "eslintConfig": {
    "extends": "airbnb-base"
  }

sourcemap

sourcemap是一种提供源代码到构建后代码的映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误位置。 webpack.config中配置

devtool: 'source-map',

会生成一个map文件

QQ截图20211112151151.png

此外还有其他可选值, source-map: 外部生成文件 inline-source-map: built.js中生成,只生产一个内联source-map hidden-source-map:外部生成, 没有错误信息位置 eval-source-map: 内联,每一个文件都生成对应的source-map,都在eval nosources-source-map 外部,隐藏源代码 cheap-source-map: 外部,定位位置只能精确到行 cheap-module-source-map: 外部

缓存

babel缓存

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env'],
          //开启babel缓存第二次构建时会读取之前的缓存
          cacheDirectory: true
        }
      }
    }
  ]
}

文件资源缓存

在项目根目录下新建一个server服务器代码

/*
  服务器代码
  node server.js启动服务器
*/

const express = require('express');

const app = express();

app.use(express.static('build', {maxAge: 1000 *3600}))

app.listen(3000)

浏览器输入localhost:3000可以查看效果

QQ截图20211112163332.png 可以看到浏览器去请求资源了并且我们设置的过期时间也生效了 但是要是代码有修改重新打包的话因为文件名没改变,因为他还是会去重新请求缓存的文件,这时可以给文件名加一个hash值; 改变webpack.config中输出的文件名

  output: {
    filename: 'js/built[hash:10].js',
    path: resolve(__dirname, 'build')
  },
    new miniCssExtractPlugin({
      filename: 'css/built[hash:10].css'
    })

每次打包都是产生不同的hash值因为当打包编译时会去请求不同的文件

QQ截图20211112163850.png 优化: 使用contenthash值,只有当文件改变了值才会变,避免了当一个文件改变但全部请求的局面

代码分割

通过一次打包多个文件可以提高处理效率

通过入口文件

webpack.config配置

module.exports = {
  entry: {
    main: './src/js/index.js',
    test: './src/js/print.js'
  },
}

设置splitChunks

  //可以将node_modules中的代码单独打包成一个chunk输出
 optimization: {
   splitChunks: {
     chunks: 'all'
   }
 },

通过js代码将文件单独打包

在主入口js文件中写入

import(/* webpackChunkName: 'test' */'./print').then((result) => {
 console.log(result)
})

这时就会将print.js打包成名为test的chunk。路由懒加载也是这种写法

项目中的路由懒加载写法

component: () =>
       import( /* webpackChunkName: "views" */ '@/views/suspectedInfringement/index')

externals

防止将某些npm包打包到bundle中,增大项目体积,例如通过cdn引入的方式去引入jQuery

webpack.config文件

 externals: {
   jquery: 'jQuery',
 },

index.html文件

<script
 src="https://code.jquery.com/jquery-3.1.0.js"
 integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
 crossorigin="anonymous"
></script>

webpack 配置详解

entry入口

/*
entry入口格式:
1.string 
2.array
3.object

*/
module.exports = {
 //入口起点
 //entry: './src/index.js',  //单入口,打包形成一个chunk,输出一个bundle文件
 //entry: ['./src/index.js', './src/index.js']  //多入口但打包一个chunk文件,会将后面的文件打包到第一个index.js中。HMR热更新中应用
 entry: {
     inedx : './src/index.js',  //['./src/index.js', './src/index.js']也可以写成这种形式,会将后面的打包到第一个文件中
     add: './src/add.js'
 }  //多入口,会打包生成两个chunk文件,文件名为对象的key
 output: {
     file: '[name].js',  //此时chunk名称默认为main
     path: resolve(__dirname, 'build')
 }
 
}

output

module.exports = { output: { //文件名称(指定名称+目录) file: 'js/[name].js', //此时chunk名称默认为main //输出文件目录(将来所有资源输出的公共目录) path: resolve(__dirname, 'build'), //适用于生产环境,将'img/a.jpg'--->'/img/a.jpg'可以去服务器根路劲下找 public: '/', chunkFilename: 'js/[name]_chunk.js' //非入口chunk的名称,例如 import('./add.js')这里面的add打包成单独文件 library: '[name]' //将打包的文件暴露出去给外部引用 libraryTarget: 'window' //将暴露的文件添加到window对象下 } }

module

module.exports = {
  module: {
  //解析模块的规则
      resolve: {
      //配置解析模块的路劲别名
         alias:  {
             $css: resolve(__dirname, 'src/css') //这时import'../css/a.css'可以替换成import'$css/a.css'  vue-cli自带@作为src目录
         },
         //配置省略文件路劲的后缀名
         extensions: ['.js','.json'] 
         //告诉webpack解析模块是去找哪个目录
         modules: ['node_module']
      }
  },
}

devserver

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
  devServer: {
  //运行代码的目录
    static: resolve(__dirname, 'build'),
  //启动Gzip压缩
    compress: true
    //指定端口号
    port: 5553,
    //域名
    host: 'localhost',
    //自动打开浏览器
    open: true,
    //热更新HMR
    hot: true,
    //不要显示启动服务器的日志信息
    clientLogLevel: 'none',
    //除了基本的启动信息,其他信息都不打印
    quiet; true,
    //解决开发环境跨域问题
    proxy: {
       //一旦devserver(5000)服务器接受到/api/xxx的请求,就会把请求转发到3000服务器
       '/api/: {
               target: 'http: //localhost: 3000',
               //发送请求时,请求路劲重写,将/api/xxx变成xxx
               pathRewrite: {
                   '^/api': ''
               }
       }
    }
  }
}

optimization

module.exports = {
    optimization: {
        splitChunks: {
            chunks: 'all',
            
            //以下为默认值
            minSize: 30 * 1024  //分割的chunk最小为30kb
            maxSize:   //最大没有限制
            minChunks: 1 //要提取的chunk最少被引用一次
            maxAsyncRequests: 5, //按需加载时并行加载文件最大数量
            maxInitialRequests: 3  //入口js文件最大并行请求数量
            automaticNameDelimiter: '~' //名称连接符,
            name: true  //可以使用命名规则
            cacheGroups: {
                vedors: {
                    test: /[\\/]node_modules/,
                    //优先级
                    priority: -10
                },
                default: {
                    //要提取的chunk最少被引用二次,会覆盖上诉的值
                   minChunks: 2,
                   //优先级
                   priority: -20,
                   //如果当前要打包的模块和之前已经被提取的模块是同一个就会复用
                   reuseExistingChunk: true,
                   将当前模块的记录其他模块的hash单独打包成一个文件runtime,不然依赖的文件改变了但是文件中记录的文件名不变会出现问题
                   runtimeChunk: {
                       name: entrypoint => 'runtime-${entrypoint.name}' 
                   },
                   
                   
                }
            }
        }
    }
}