Webpack

100 阅读6分钟

webpack

  • 是一种前端资源构建工具,一个静态模块打包器。前端所有的资源文件(js/json/img/less/...)都会作为模块处理
  • 它会根据模块的依赖关系进行静态分析。打包生成对应的静态资源。

5大核心

  • Entry:webpack以哪个文件作为入口起点开始打包,分析构建内部依赖图
  • Output: webpack打包后的资源,bundle输出到哪里去,以及如何命名
  • Loader: Loader让webpack处理非js文件
  • Plugins: 插件可以用于执行范围更广的任务。从打包优化,压缩一直到重新定义环境中的变量等
  • mode:development和production。开发模式比较简单。生成模式要各种优化

初体验

  • npm init:初始化一个包描述文件

  • npm i webpack webpack-cli -g 下载

  • 创建入口文件

  • 在终端执行打包命令:webpack ./src/index.js -o ./bundle mode=development

  • webpack 5.0现在默认生成main.js文件

  • asset列:指构建后输出的资源文件,名称由filename的配置决定

  • emitted列:指文件被输出

  • (name:main):指chunk的name为main

  • 如果是生产环境的话,打包之后的文件会被极限压缩

打包css文件

  • 配置文件

const {resolve} = require("path")

module.exports={
  entry:'./src/index.js',
  //出口是个对象,调用path 的resolve方法来设置绝对路径
  output:{
    filename:"built.js",
    path:resolve(__dirname,'build')
  },
  //module配置对应的loader
  module:{
    rules:[
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ]
  },
  mode:'development'
}
  • 下载对应的loader:css和style。 style先执行cssloader,讲css文件变成commonjs模块加载js中,里面内容是样式字符串。

再执行style,创建style标签。将js中的样式资源插入。

  • 打包成功

plugins的使用

  • 下载

  • 引入

  • 安装

  • HtmlWebpackPlugin:1.如果没有指定的html模板,会自动生成一个空白文件,同时引入打包之后的js文件 2.如果指定了模块,会生成对应的html结构,同时引入打包之后的js文件

图片资源的引入

  • 下载url-loader和file-loader
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            //限制图片大小。如果小于8-12kb就用base64去解析
            options: {
              limit: 8 * 1024,
            },
          },
        ],
  • 但是无法处理html文件中通过img引入的图片(只能处理通过css引入的图片:背景图)
  • 解决:引入npm install --save-dev html-loader
      {
        test:/\.html$/,
        loader:'html-loader'
      }
  • 同时,url-loader里面的esMudle需要改成false
  • 注意,当引入这个模块的时候。只会解析scr的图片模块,不会再解析js文件了!!!

处理字体图标

  • 下载字体图片
  • 在入口文件导入 import "./font/iconfont.css"
  • 导入需要的字体文件
  • 设置字体的类名(2个)
  • 配置loader,因为处理使用fileloder,所以不需要重新下载
  • 注意,此时需要排除。css。js,。html和各种图片文件。不然会报错。写法也有区别,是exclude来做
      {
        exclude:/\.(css|js|html|png|jpg)$/i,
        loader:'file-loader',
        options:{
          name:"[hash:10].[ext]"
        }
      },

devServer

  • 目前最新版本的devServer只支持webpack-cli 3.3.12才能运行
  • 下载
  • 配置devServer
    //自动化编译,打开浏览器,刷新浏览器
    //只会在内存中编译打包,不会有任何输出
  devServer:{
    //项目构建后路径
    contentBase:resolve(__dirname,"build"),
    //开启gzip压缩
    compress:true,
    port:5000,
    //执行的时候自动打开浏览器
    open:true
  },
  • 执行npx webpack-dev-server

打包分类

  • 将生成的js文件放在这个目录下面
  • 别的css(已经集合在js中不用另外生成),其他文件和图片资源要指定路径要这样写
      {
        exclude:/\.(css|js|html|png|jpg)$/i,
        loader:'file-loader',
        options:{
          name:"[hash:10].[ext]",
          //media放在build文件下面,其她资源文件会放到build/media下面
          outputPath:'media'
        }
      },

生产模式

  • 因为,webpack都是把css文件压入到js中,所以会导致js文件过大,而且,只有js运行了才会创建css,会导致闪屏。
  • 需要在生产模式中,将css文件单独抽离出来。
  • 下载一个新插件:
  • 引入插件,注册插件
  • 这个插件用法放在loader里面。同时注释掉style.loader
      {
        test:/\.css$/,
        use:[MiniCssExtractPlugin.loader,'css-loader']
      }
    ]
  • 如果想要css放在特定目录,就修改插件下面的属性
//文件名字为build.css,放在build的css文件下面(css文件没有的话会自动创建)
    new MiniCssExtractPlugin({
      filename:"css/build.css"
    })

css的兼容性处理

  • 不同浏览器的css样式有些事不同的。
  • 用postcss-loader和postcss-preset-env(下载)
  • 再webpack.config.js的同级目录下面创建文件:postcss.config.js
  • 配置文件(postcss.config.js)
module.exports={
  plugins:[
    require('postcss-preset-env')
  ]
}
  • 再package.json里面写上浏览器的list
  "browserslist":{
    "development":[
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production":[
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  }

再webpack.config.js里面加上这个loader

      {
        test:/\.css$/,
        use:[MiniCssExtractPlugin.loader,'css-loader','postcss-loader']
      },
  • 注意:这里生产的css文件默认是看生产环境的。和mode为开发环境无关
  • 如果是要生产环境的话。修改node的环境变量为开发模式

配置babel

  • 让es6语法转化为es5语法,让ie浏览器能够识别
  • 下载3个依赖包。npm i babel-loader @babel/core @babel/preset-env -D
  • 配置loader
      {
        test:/\.js$/,
        //不去修改第三方的库
        exclude:/node_modules/,
        loader:"babel-loader",
        //预设:指示babel做什么样的兼容性处理
        options:{
          presets:['@babel/preset-env']
        }
      }
  • 但是会有一个问题,就是这些只能识别最基本的es6语法,无法识别高级的比如promise

  • 下载npm i @babel/polyfill -D 这个包直接定义了所有的高级语法

  • 再入口的js文件引入即可

  • 但是这样的js文件会很多有几百kb

  • 最终解决方法,按需加载

  • 下载core/js npm i core/js -D

  • 进行配置,注意要注释掉@babel/polyfill

  • 修改预设

          presets:[
            [
              '@babel/preset-env',
              {
                //按需加载
                useBuiltIns:'usage',
                //指定版本
                corejs:{
                  version:3
                },
                //指定兼容性做到哪个版本
                targets:{
                  chrome:"60",
                  firefox:"60",
                  ie:"9",
                  safari:"10",
                  edge:"17"
                }
              }
            ]
          ]

js和html压缩

  • 在生产环境下会自动压缩js代码,因为生产环境会自动调用很多plugins。包括uglyfy。所以只有将mode改成production就可以了
  • html,需要在htmlwebpackplugin配置2个参数为true
    new HtmlWebpackPlugin({
      template:"./src/index.html",
      minify:{
        //移除空格
        collapseWhitespace:true,
        //移除注释
        removeComments:true
      }
    }),

对于js文件需要同时执行eslint和babel

  • 要先进行eslint
  • 给eslint加上enforce:'pre'

开发模式下的HMR

  • 需要:我们只想在更新1个js或者1个css代码模块时,就只重新构建这个模块,而不重新构建所有的模块
  • 开启dev的HMR:hot module replacement。

source-map

  • 一种 提供源代码到构建后代码映射的技术(如果构建后代码出错了,可以通过映射追踪到源代码) -
  • 生成一个map文件
  • inline-source-map 内联式(不生成map文件,代码集合在js文件中)
  • hidden-source-map 外联式(生成map文件)
  • eval-source-map 内敛(每一个文件都生成对应的source-map,都在eval函数中一一对应)

在vue和react的脚手架中,都是用eval-source-map

开发优化

oneOf

  • 一个文件会被所有的loader,都会检查是否命中,这样速度会慢
  • 因此,我们可以设计这个一个东西:如果一个文件被一个loader命中,那么他不会被后面的loader进行检查
  • oneOf出来了,同时由于js需要babel和eslint去处理,那么把eslint放在最前面,每个文件都需要被eslint的loader所检查。
    rules: [
      {
        //这里放eslint的loader文件
      },
      {
        //把所有的loader都复制进去
        oneOf:[
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          }
        ]
      }
    ]

其他知识

生产环境

放到服务器上的环境,展示给用户看的

缓存

相当于开发环境的HRM(基于devServer),修改其中一个js文件,只更新这个js文件,别的不变的js代码都直接拿缓存。而且最多的也是js代码,css和其他代码都很少。所以对babel进行处理,因为babel会对所有的js代码进行编译处理。

  • babel缓存

  • 用hash值来做缓存(和协商缓存差不多)
  • 原理:每次webpack生成的文件名都带有哈希值,如果重新构建哈希值不同的话,就会重新请求,如果文件没变,重新请求就会向缓存里请求数据。
  • 问题:js或者css变,都会变化

treeShaking

  • 就是把没有用到的js代码不打包

但是会对一些没有引用的css代码进行省略,这时候去package.json里面配置sideeffect

  • 代表不去检查css文件

code_split

  • 可以提供多入口文件
  • 可以将依赖单独作为一个chunk,而且如果多入口文件同时依赖这个包的话,会自动分析,单独打包,不会打包成多份!只会打包为1分

懒加载!

  • 前提是会分隔代码
  • 预加载(兼容性比较差)

pwa(兼容性较差)

  • pwa:渐进式网络开发应用程序(离线可访问)
  • 下载插件
  • 注册插件
  • 在入口文件index.js写执行代码

多进程打包

  • 优点:多个进程进行打包
  • 缺点:进程启动会消耗时间和进程通信也会有消耗,因此小项目不用这个
  • 而且只在babel里面用这个插件
  • 下载插件
  • 在babel里面注册
  • 进程数量可以调整(最多为cpu核数-1)

extrenals

  • 将一些包通过cdn引入而不去打包

dll

  • 将node——module的包单独打包,单独引入
  • 与external区别是,dll第一次打包后面就不用再次打包了,加快了构建速度