乐趣区

Webpack入门到精通1

前言

什么是 webpack 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器 (module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack 有哪些功能(代码转换 文件优化 代码分割 模块合并 自动刷新 代码校验 自动发布)
首先学习 webpack 需要有简单的 node 基础,打开 node 官方网站进行安装 node, http://nodejs.cn/ 下载最新版 node 包并进行安装。

学习目标:

  1. webpack 常见配置 webpack 高级配置
  2. webpack 优化策略
  3. AST 抽象语法树
  4. webpack 中的 Tapable
  5. 掌握 webpack 流程 手写 webpack
  6. 手写 webpack 中常见的 loader
  7. 手写 webpack 中常见的 plugin
  • 定义好学习目标让我们开启 webpack 的新旅程。(本文学习主要针对 webpack4.0 进行学习讲解)

webpck—> 基础搭建与使用:

安装完毕在终端 快速创建 node 项目 执行命令 npm init -y 生成 packge.json
在当前目录安装本地 webpack
终端执行命令:
npm i webpack webpack-cli -d
i 表示 install ,d 表示当前是开发环境安装完成会产生 node_modules 文件
webpack 可以进行 0 配置 并且 webpack 是打包工具(默认是 js 模块 通过入口进行打包输出打包后 js 结果)。
创建 src 目录 –> 创建 index.js -> 输出:console.log(‘hello webpack’);
npx 语法进行把 index.js 进行打包
终端执行命令:
npx webpack
我们发现当前目录生成了一个 dist 目录并且创建了一个 main.js(如图:)

执行顺序:(默认找 node_modules—>bin 文件 –> webpack 文件)

这里我们明白了安装 webpack 必须安装他的依赖 webpack-cli
webpack 打包默认支持 js 模块化 -> 类似于 common.js

webpack: 两种默两种模式如果没有创建 webpack.config.js 配置文件指定 mode (production/development)生成模式或开发模式,打包运行会直接默认生产模式打包并且进行压缩。
这里说一下 webpack 配置文件的默认名称有两种(webpack.config.js / webpackfile.js 一般情况下我们会选择前一种)

如何手动配置 webpack 呢?其实比较简单

(1)创建 webpack.config.js 配置文件 由于 webpack 是 node.js 的框架所以配置文件中要采用 node 语法来进行编辑。

const path = require("path"); //webpack 内部方法 path 组件
module.exports = {
  mode: "development", // 打包模式 development 开发模式
  entry: "./src/index.js", // 入口文件指定
  output: {
    // 出口文件配置 配置出口打包路径
    filename: "build.js", // 打包后的文件名称
    path: path.resolve(__dirname, "build") //resolve 绝对路径引入
  }
};

我们分析一下 build.js 打包出的结果, 默认下是一个匿名函数 并且接收两个参数 接收一个对象,Key : value (key: 是当前模块的路径 value:是一个执行函数)

接收到 modules 里 先定义一个缓存对象 installedModules 先定一个缓存目的是如果我当前模块加载完成没有必要再进行加载
webpack_require 实现了一个 require 方法因为浏览器无法直接执行 node 的 require 方法 (详解如图)

执行__webpack_require__ 发现接收了一个入口模块



终端运行:npx webpack , 发现我们打包当前目录产生了文件夹 build 目录
分析了一下打包文件是不是感觉 webpack 源码没有想象的那么难 继续我们 webpack 的探索之旅。
如何更改 webpack 配置文件名称呢其实很简单重命名 webpack.config.js (webpack.test.js)
执行命令:
npx webpack –config webpack.test.js 发现可以执行 webpack 打包。
这样打包我发现命令很长所以我们利用 packge.json 来配置打包脚本在 scripts–> 添加 build.

"scripts": {
 "test": "echo \"Error: no test specified\"&& exit 1",
 "build": "webpack --config webpack.config.js"
  }

终端运行 npm run build 发现执行打包结果一样.
(2)webpack 其他配 –> 置插件的使用不会生成文件会生成内存中的打包
安装 webpck 内置服务 webpack-dev-server 好处是
终端执行命令: npm i webpack-dev-server -d -save
安装完成可以执行 npx webpack-dev-server 按提示打开 http://localhost:8080/
如何配置开发服务运行目录可以在配置文件中添加在 webpack.config.js 添加 devServer

const path = require("path");
module.exports = {
  mode: "development", // 打包模式
  entry: "./src/index.js", // 入口文件指定
  output: {
    // 出口文件配置 配置出口打包路径
    filename: "build.js", // 打包的文件名称
    path: path.resolve(__dirname, "build") //resolve 绝对路径引入
  },
  devServer: {
    // 开发服务器配置
    contentBase: "./build", // 指向打包目录
    port: 3000, // 服务端口号
    progress: true, // 打包进度
    open: true, // 是否打开浏览器
    compress: false // 是否压缩
  }
};

在 packge.json 中添加 start 启动服务脚本

"scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1",
    "build": "webpack --config webpack.config.js",
    "start":"webpack-dev-server"
  }

运行 npm run start 发现没有自动创建 index.html 不能直观看到我们代码在浏览器的执行。
在 src 目录下创建 html 模板 index.html 并安装 html-webpack-plugin 插件
终端运行: npm i -d html-webpack-plugin
在 webpack.config.js 下添加插件配置 plugins

const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "development", // 打包模式
  entry: "./src/index.js", // 入口文件指定
  output: {
    // 出口文件配置 配置出口打包路径
    filename: "build[hash:8].js", // 打包的文件名称 filename: "build[hash:8] 添加哈希值
    path: path.resolve(__dirname, "build") //resolve 绝对路径引入
  },
  devServer: {
    // 开发服务器配置
    contentBase: "./build", // 指向打包目录
    port: 3000, // 服务端口号
    progress: true, // 打包进度
    open: true, // 是否打开浏览器
    compress: false // 是否压缩
  },
  // 插件
  plugins: [
    // 数组形式 存放所有的 webpack 插件
    new HtmlWebPackPlugin({
      filename: "index.html", // 生成打包文件名
      template: "./src/index.html", // 模板路径
      minify: { // 生产模式可以进行配置
        removeAttributeQuotes: true, // 删除 html 文件双引号
        collapseWhitespace: true // 折叠控行
      },
      hash:true, // 添加哈希值
    })
  ]
};

终端执行打包测试:npm run build (build 目录下生成了我们想要生成的 index.html 文件)

样式的配置 webpack 配置 css 模块:

配置样式需要一个合适 loader,loader 会将我们的样式文件解析成模块 (module)
终端:npm i -d –save css-loader style-loader
如何使用样式 loader 进行配置呢?我们先在 src 下建 index.css 并给 body 赋予简单样式
在 webpack.config.js 进行简单配置
css-loader 主要解析我们样式中 @import 语法,style-loader 是吧 css 样式插入 head 标签中.

+ module: {
    // 添加模块模块是对象
    rules: [
      // 规则 css-loader 主要解析我们样式中 @import 语法
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"] // 执行顺序是重右向左执行 - > 重下到上
      }
    ]
  }

在 index.js 中引入样式文件 import ‘./index.css’
终端运行:npm run start 样式生效了同样我们也有对应的 less less-loader
终端:npm i -d –save less less-loader
在这里说一下 loader 的另一种写法对象写法可以给 loader 添加一些属性 options

+ module: {
    // 添加模块模块是对象
    rules: [
      // 规则 css-loader 主要解析我们样式中 @import 语法
      {
        test: /\.css$/,
        use: [ 
           {
            loader: "style-loader",
            options: {insertAt: "top" // 把标签插入顶部}
          }, 
          "css-loader"
          ] // 执行顺序是重右向左执行 - > 重下到上
      },
      {
        test: /\.less$/,
        use: [
            {
            loader: "style-loader",
            options: {insertAt: "top" // 把标签插入顶部}
          }, 
          "css-loader",
          "less-loader"
        ] // 执行顺序是重右向左执行 - > 重下到上
      }
    ]
  }

css 样式的抽离 安装抽离插件 mini-css-extract-plugin npm i -d –save mini-css-extract-plugin
引入插件并在配置文件中进行配置

const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MinCssExtractPlugin = require("mini-css-extract-plugin"); // 抽离 css 插件
module.exports = {
 mode: "development", // 打包模式
 entry: "./src/index.js", // 入口文件指定
 output: {
   // 出口文件配置 配置出口打包路径
   filename: "[name][hash:8].js", // 打包的文件名称 filename: "build[hash:8] 添加哈希值
   path: path.resolve(__dirname, "build") //resolve 绝对路径引入
 },
 devServer: {
   // 开发服务器配置
   contentBase: "./build", // 指向打包目录
   port: 3000, // 服务端口号
   progress: true, // 打包进度
   open: true, // 是否打开浏览器
   compress: false // 是否压缩
 },
 // 插件
 plugins: [
   // 数组形式 存放所有的 webpack 插件
   new HtmlWebPackPlugin({
     filename: "index.html", // 生成打包文件名
     template: "./src/index.html", // 模板路径
     minify: {
       removeAttributeQuotes: true, // 删除 html 文件双引号
       collapseWhitespace: true // 折叠控行
     },
     hash: true // 添加哈希值
   }),
   new MinCssExtractPlugin({filename: "mian.css"})
 ],
 module: {
   // 添加模块模块是对象
   rules: [
     // 规则 css-loader 主要解析我们样式中 @import 语法
     {
       test: /\.css$/,
       use: [
         MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
         "css-loader"
       ] // 执行顺序是重右向左执行 - > 重下到上
     },
     {
       test: /\.less$/,
       use: [
         MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
         "css-loader",
         "less-loader"
       ] // 执行顺序是重右向左执行 - > 重下到上
     }
   ]
 }
}

添加样式前缀 postcss-loader autoprefixer
npm i postcss-loader autoprefixer -d

 module: {
    // 添加模块模块是对象
    rules: [
      // 规则 css-loader 主要解析我们样式中 @import 语法
      {
        test: /\.css$/,
        use: [
          MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
          "css-loader",
          "postcss-loader"
        ] // 执行顺序是重右向左执行 - > 重下到上
      },
      {
        test: /\.less$/,
        use: [
          MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
          "css-loader",
           "postcss-loader",
          "less-loader" 
        ] // 执行顺序是重右向左执行 - > 重下到上
      }
    ]
 }
  • 注意 postcss-loader 需要添加一个配置文件否则不会生效更目录创建 postcss.config.js
module.exports = {plugins: [require("autoprefixer")]
};

但是我们发现问题使用 mini-css-extract-plugin 插件导致我们 css 不会被压缩。
npm 官网有给出 To minify the output, use a plugin like optimize-css-assets-webpack-plugin. Setting optimization.minimizer overrides the defaults provided by webpack, so make sure to also specify a JS minimizer: 参考链接

要使用 optimize-css-assets-webpack-plugin 插件接下来我们安装配置一下.
npm i -d optimize-css-assets-webpack-plugin

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩 js
// 配置文件中添加优化项
optimization: {minimizer: [new OptimizeCSSAssetsPlugin({})]
  }

使用 optimize-css-assets-webpack-plugin 我们发现 js 右不会被压缩 所以要使用 uglifyjs-webpack-plugin –save-dev
$ npm install uglifyjs-webpack-plugin –save-dev
配置产考链接

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩 js
// 配置文件中添加优化项
optimization: {minimizer: [new OptimizeCSSAssetsPlugin({}),new UglifyJsPlugin()]
  }

语法的转换 babel

在 index.js 里写点 es6 语法箭头函数

let fn = () => {console.log("es6 webpack");
};
fn();

终端执行: npx webpack 查看打包文件

– 我们发现打包出来的仍然是 es6 语法这个时候我们需要一个 loader 进行转换 babel-loader babel @babel/core
(babel/core 是 @babel-loader 的核心组件转化模块 @babel/preset-env) 参考链接
终端运行:npm i -d babel-loader babel @babel/core @babel/preset-env
在 module 添加配置

 // 在 rules 下添加配置
 {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              // 转化 es5 语法 --presets 预设
              presets: ["@babel/preset-env"]
            }
          }
        ]
      },

终端运行:npm run build

发现可以已经转换 es6 语法 但是仅仅这样不能转换 es6 高阶语法比如一些特殊的类函数

class Test {// new Test() a =1 实例上添加 a 属性 这个语法属于 es7 语法打包时发现并不能解析
  a = 1;
}

终端运行:npm run build 发现报错提示安装 @babel/plugin-proposal-class-properties

那我们按照要求按照一下插件 npm i -d @babel/plugin-proposal-class-properties 并进行一次配置

 // 在 rules 下添加配置
 {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              // 转化 es5 语法 --presets 预设
              presets: ["@babel/preset-env"],
               plugins: ["@babel/plugin-proposal-class-properties"]
            }
          }
        ]
      },

还有一种写法 装饰器 @Log 打包也是不被解析的 在 js 添加

按照错误提示安装 decorators-legacy 参考链接 安装官方给出配置添加

 // 在 rules 下添加配置
 {
        test: /\.js$/,
        use: {
          {
            loader: "babel-loader",
            options: {
              // 转化 es5 语法 --presets 预设
              presets: ["@babel/preset-env"],
               plugins: [ // 这里要注意添加顺序
                 ["@babel/plugin-proposal-decorators", { legacy: true}],
                ["@babel/plugin-proposal-class-properties", { loose: true}]]
            }
          }
    
      },

转化完语法接下来看一下 babel 语法的校验 @babel/plugin-transform-runtime @babel/runtime 参考链接

  {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            // 转化 es5 语法 --presets 预设
            presets: ["@babel/preset-env"],
            plugins: [["@babel/plugin-proposal-decorators", { legacy: true}],
              ["@babel/plugin-proposal-class-properties", { loose: true}],
              [
                "@babel/plugin-transform-runtime",
                {
                  absoluteRuntime: false,
                  corejs: false,
                  helpers: true,
                  regenerator: true,
                  useESModules: false
                }
              ]
            ]
          }
        },
        include: path.resolve(__dirname, "src"), // 只找__dirname - >src
        exclude: /node_modules/ // 忽略 node_modulse
      },

includes 实例方法不被解析 需要一个补丁模块 @babel/polyfill
npm install –save @babel/polyfill
Babel includes a polyfill that includes a custom regenerator runtime and core-js.
使用可以直接在 js 里引入即可。
接下来看一下代码校验 ESLint 代码校验工具参考链接
终端安装 npm i -d eslint eslint-loader
根据项目需求下载对应的 eslintrc.json 下载链接

 // 代码校验 eslint
      {
        test: /\.js$/,
        use: {
          loader: "eslint-loader",
          options: {enforce: "pre" // 强制执行顺序}
        }
      },

DEMO

  • 本文回顾 1、webpack 基本功能 build.js 打包的原理及源码分析 2、webpack 基础配置及常用插件配置安装 3、loader 的使用与配置样式的处理 4、webpack 优化项的简单使用 5、babel 语法转换与使用 babel 语法校验 . 完成以上我相信大家可以掌握并搭建简单 webpack 项目。
  • 第二章我们将讲解 webpack 其他组件的配置及 webpack 优化项 webpack 图片处理 多入口应用)
退出移动版