【新手友好】webpack5知识体系之前端构建初体验

120 阅读2分钟

前言:阅读本文,你将学会用webpack5构建一个简单的项目,手写一个style-loader,以及手写一个plugin。无论是满足对webpack5的好奇心,还是面试都会有帮助的,希望大家一起动手写起来吧!

前端构建初体验

webpack5及相关工具安装

目录创建:

  • mkdir webpack-demo
  • cd webpack-demo
  • npm init

webpack&webpack-cli

  • install:npm i webpack webpack-cli -D
  • check: 检查./node_modules/目录下是否有webpack

构建一个最简单的前端项目

目标:

  • html、js、css等资源正常输出
  • 构建的结果可以在浏览器跑起来

安装插件:

  • npm i html-webpack-plugin -D
  • npm i css-loader style-loader -D
  • 首先在src目录下创建index.html、index.css、index.js文件
  • 配置webpack.config.js
  • 在package.json文件中增加build命令: "build": "webpack",

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./index.css">
  <script src="./index.js"></script>
</head>
<body>
  
</body>
</html>

index.js

import './index.css'

console.log('this is demo')

index.css

body{
  height: 100px;
  background-color: blue;
}

webpack.config.js

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

module.exports = {
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './src/index.html')
    })
  ]
}

运行npm run build 构建完会打包一个dist目录 再打开dist目录下的index.html文件,样式就生效了,ok第一个小目标完成 (ᕑᗢᓫ∗)˒

image.png

如何实现style-loader?

  • 创建loader/loader1.js
  • 在pitch方法有三个参数:remainingRequest:未执行的loader,previousRequest:已经执行的loader,data:用来和loader方法交互;
  • 把样式文件插入到html,loader方法中的sourc不能直接用,所以得在pitch中执行;
  • stringifyRequest来处理remainingRequest路径
  • npm i loader-utils@1.2.3, ps:不知道为啥最新版没有stringifyRequest方法;
  • return 一段js代码,创建一个style标签,获取路径的内容,判断内容是否是esModule,是的话取default,将content插到head中;
const { stringifyRequest } = require('loader-utils')
function loader(source){
  return source
}
loader.pitch = function(remainingRequest, previousRequest, data){
  console.log(11, remainingRequest, 22, previousRequest, 33, data)
  const modulePath = stringifyRequest(this, `!!${remainingRequest}`)
  return `
    var element = document.createElement('style');
    var content = require(${modulePath});
    content = content.__esModule ? content.default : content;
    element.innerHTML = content;
    var parentEle = document.querySelector('head');
    parentEle.appendChild(element)
  `
}
module.exports = loader

接下来是配置webpack.config文件:

  resolveLoader: {
    alias: {
      'loader1': path.resolve(__dirname, './loader/loader1')
    }
  },
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'loader1',
        'css-loader'
      ]
    }]
  },

运行npm run build,打开dist/index.html,也有样式了(◕ˇ∀ˇ◕。)

如何实现一个plugin插件?

目标:

  • 实现一个CSP策略的插件
  • CSP: 内容安全策略,白名单机制

接下来是我们的实践过程:

  • 首先创建plugins/test-plugin.js文件
// 依赖HtmlWebpackPlugin
const HtmlWebpackPlugin = require('html-webpack-plugin')

function CSPPlugin(options){
  this.options = options
}
CSPPlugin.prototype.apply = function(compiler){
  console.log(111)
  // 跟着HtmlWebpackPlugin去调用插件
  compiler.hooks.thisCompilation.tap('CSPPlugin', (compilation) => {
    // html处理完的时机
    HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync
    ('CSPPlugin', (data, cb) => {
      console.log(data.html)
      cb(null, data)
    })
  })
}
module.exports = {
  CSPPlugin
}

代码14行的data.html打印出来如下图: WechatIMG356.png

  • 接着去完成plugin.apply方法的内容:
CSPPlugin.prototype.apply = function(compiler){
  // 跟着HtmlWebpackPlugin去调用插件
  compiler.hooks.thisCompilation.tap('CSPPlugin', (compilation) => {
    // html处理完的时机
    HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync
    ('CSPPlugin', (data, cb) => {
      // 插入一个meta标签,需要加一个http-equiv属性
      const insertHtml = `<meta http-equiv="Content=Security-Policy" 
      content=default-src 'self'">`
      // 找到html对应的插入位置
      const index = data.html.indexOf('<head>')
      // +6是略过<head>标签
      data.html = data.html.substring(0, index + 6) + insertHtml +
      data.html.substring(index + 6)
      cb(null, data)
    })
  })
}
  • 接着来配置webpack.config文件: 引入我们自定的plugin:
const { CSPPlugin } = require('./plugins/test-plugin')
  • 在plugins中配置:
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './src/index.html')
    }),
    new CSPPlugin({})
  ]

运行npm run build命令,查看dist/index.html文件已经插入了meta标签(˶˚ ᗨ ˚˶)

image.png