「Webpack」初入介绍

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

(一)webpack的作用

分析你的项目结构,找到 JavaScript 模块以及其它的一些浏览器不能直接运行的拓展语言(SCSSTypeScript 等),并将其打包为合适的格式以供浏览器使用。

构建就是把源代码转换成发布到线上的可执行 JavaScriptCSSHTML 代码,包括如下内容。

  1. 代码转换:TypeScript 编译成 JavaScriptSCSS 编译成 CSSloader
  2. 文件优化:压缩 JavaScriptCSSHTML 代码,压缩合并图片等。
  3. 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  4. 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  5. 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
  6. 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  7. 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

构建其实是工程化、自动化思想在前端开发中的体现,把一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力,解放了我们的生产力。

(二)webpack的loader

loader 从本质上来说其实就是一个 node 模块

单一原则:每一个人 loader 只做一件事,因为东西越小越好组合,越好维护 从右到左,链式执行,上一个 loader 的处理结果给下一个接着处理。

模块化: 每一个模块都是独立的,不依赖任何东西,一定的输入就有一定的输出

无状态

链式调用:webpack 会按照顺序链式调用每一个 loader

统一原则:遵循 webpack 制定的设计规则和结构,输入与输出均为字符串,各个 loader 完全独立,即插即用。

1.1 在项目中安装了 webpackwebpack-cli

1.2 新建一个 webpack.config.js 文件

const path = require('path')
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: path.resolve(__dirname, 'src', 'loaders', 'log-loader')// 这个就是我们自己的写的loader所在的目录
    }]
  }
}
复制代码

1.3 分别建几个目录 src/base.js,src/index.js,src/loaders/log-loader

module.exports = 'sunseekers' // base.js
const name = require('./base') // index.js

// loaders/log-loader
module.exports = function (sourse) {
  console.log('在webpack里面配置了一个rules,现在所有js文件都会进过一个loader文件,进行处理,这里进过的是我们自己建的一个loader文件,sourse是文件的原内容。这个日志会在build的时候看到这一句话');
  return sourse
}
复制代码

1.4 配置 webpack 的命令,在 package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode production"// 主要是这一句话
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack-cli": "^3.3.11",
    "webpack": "^4.3.0"
  }
}
复制代码

html loader

关于 loader 具体的写法的使用可以查看文档,或者通过 console.log 来查看日志 loader API

file-loader 是解析图片地址,把图片从源位置拷贝到目标位置并且修改原引用地址

url-loader 可以在文件比较小的时候,直接变成base64字符串内嵌到页面中

(三)webpack的plugin

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子

webpack 基础配置无法满足需求

插件几乎能够任意更改 webpack 编译结果这里才是我们写插件最重要的原因,输出我们想要的结果

webpack 内部也是通过大量内部插件实现的

plugin 的写法比 loader 要难很多,需要我们对 webpack 的生命周期有一定的了解,了解他内置的一些属性和方法。比如 compiler 对象代表了完整的 webpack 环境配置,只有构建一次,最重要的。compilation 代表每一次资源版本的构建每一次我们修改代码更新的时候都会构建一次

3.1 在配置文件中使用自己写的插件 webpack.config.js

const path = require('path')
const FilePlugin = require('./src/plugins/filename') // 自己插件地址
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  plugins: [
    new FilePlugin({
      filename: "file.list.md"
    })
  ]
  }
}
复制代码

3.2 开始写插件

a. 插件是一 JavaScript 命名函数 b. 在函数身上定义一个 apply 方法,处理内部实例自身的时间钩子函数 c. 功能完成之后调用 webpack 提供的回调

// 生成一个新的md文档,里面放着所有的文件名
class FilesPlugin {
  constructor(options) {
    this.options = options
  }
  apply(compiler) {
    // compiler 要启动一次新的编译
    // 你在哪监听这个事件
    compiler.hooks.emit.tapAsync("EmitPlugin", (compilation, callback) => { // 这里就是我们需要了解 webpack 的生命周期和内部一些api的执行
    console.log('看看静态资源是什么格式 :',compilation.assets);
      let content = `## 文件列表\n\n`
      const assets = compilation.assets
      for (let path in assets) {
        content += `-- ${path}\n\n`
      }
      compilation.assets[this.options.filename || 'filelist.md'] = {
        source() {
          return content
        },
        size() {
          return Buffer.byteLength(content)
        }
      }
      callback()
    })

  }
}
module.exports = FilesPlugin
复制代码

个人见解

上面都是一些很简单的例子,只说告诉你怎么写,我们用的哪些插件远远比这些都要复杂的多,有些插件需要我们对 ast 有一定的了解。看了一段时间的 webpack ,我发现,要学习他们底层的源码或者知道一个东西是怎么写的,首先你要对他的流程特别清楚,然后按部就班的写。我们平时调用的 jsapi 也是一样的。你知道 api 怎么用,有哪些参数,返回什么,这个函数做了一些什么。有了这样的理解思路会好很多,写起来就没有那么的难了。

猜你喜欢

转载自juejin.im/post/7105320587358109727
今日推荐