认识 Webpack (一)

什么是 Webpack ?为什么要用 Webpack?估计很多小伙伴都知道,但是说不出来~~~记得以前看过一篇文章 愿未来没有 Webpack,里面有一段话:我明明只是想打印一个 Hello World ,却需要安装 1300 个不同的依赖包......当然,这是极端抬杠。不可否认,webpack 确实增加了不少学习成本,但是日常开发中,它也确实带给了我们很多便利。对于咱们苦逼的搬砖党来说,大家都在用它,并且它确实好用,也只能默默留着两行泪去学习~~~

本文综合整理来源:webpack 中文网从基础到实战 手把手带你掌握新版Webpack4.0

在了解 webpack 的用途之前,我们先来看一段非常简单的代码

<div id="root"></div>
<script src="lesson/index.js"></script>
// lesson => index.js
const dom = document.getElementById('root')
// header 部分
const header = document.createElement('div')
header.innerText = "header"
dom.append(header)
// sidebar 部分
const sidebar = document.createElement('div')
sidebar.innerText = "sidebar"
dom.append(sidebar)
// content 部分
const content = document.createElement('div')
content.innerText = "content"
dom.append(content)

很简单的一段代码,就不解读了。这种面向过程的编写方式会导致所有代码都堆在一个文件中,过于冗余且不利于维护。这个时候我们就引入了面向对象编程的概念,那我们就重构一下上面的代码。

<body>
  <div id="root"></div>
</body>
<script src="lesson/header.js"></script>
<script src="lesson/sidebar.js"></script>
<script src="lesson/content.js"></script>
<script src="lesson/index.js"></script>
// header.js
function Header() {
  const header = document.createElement('div')
  header.innerText = "header"
  dom.append(header)
}
// sidebar.js
function Sidebar() {
  const sidebar = document.createElement('div')
  sidebar.innerText = "sidebar"
  dom.append(sidebar)
}
// content.js
function Content() {
  const content = document.createElement('div')
  content.innerText = "content"
  dom.append(content)
}
// index.js
const dom = document.getElementById('root')
new Header()
new Sidebar()
new Content()

我们将各个模块所负责的功能都单独放在对应的 js 文件里面,这样便于后期维护,但是这又延伸出了一些问题?

  • 加载多个 js 文件,页面加载速度变慢

  • 从代码中看不出文件的相互关系,例如:如果打开 index.js 可能会一头雾水,又要去看 html 中的引入文件才行

  • html 中文件的引入顺序不能出错,否则报错之后很难进行排错处理

开发中所有问题的曝光都是为了更完美的解决,写过 Vue 或者 React 的小伙伴估计都清楚项目开发中我们引入文件的方式一般为 import xxx from './xxx',这样不仅解决了加载多个 js 运行变慢的问题,并且让文件之间的依赖关系更强,同时引入文件的顺序也不会影响代码的正确执行。我们通过这种方式继续改造一下代码:

<div id="root"></div>
<script src="lesson/index.js"></script>
import Header from './header'
import Sidebar from './sidebar'
import Content from './content'
const dom = document.getElementById('root')
new Header()
new Sidebar()
new Content()

相信大家都知道这么写肯定会报错,首先我们并没有导出就直接引入了,而且浏览器不认识 import 语法。所以这个时候我们就要借助 webpack 来让浏览器识别 import 这种语法结构。

  • 首先我们在 lesson 文件夹中使用 npm init -y 快速初始化项目。
// lesson
npm init -y
  • 初始化 webpackwebpck-cli,这里限定了版本号~~~因为 webpack 版本更新太快了,这里就先用这个版本,实际项目开发中会专门在 webpack.config.js 进行配置。
npm install webpack@4.25.1 webpack-cli@3.1.2 -D
  • 然后运行 npx webpack index.js 进行编译
npx webpack index.js

就这样,项目就被我们用 webpack 完成了打包和编译,给我们重新生成了一个 dist 目录,里面有一个 main.js 的文件就是打包所有代码之后生成的对应文件,默认会帮我们进行代码压缩。然后修改 index.htmlscript 的引入指向,综合汇总代码:

<body>
  <div id="root"></div>
</body>
<script src="./lesson/dist/main.js"></script>
// index.js // 使用 npx webpack index.js 进行打包
import Header from './header'
import Sidebar from './sidebar'
import Content from './content'
new Header()
new Sidebar()
new Content()
// Header
function Header() {
  const dom = document.getElementById('root')
  const header = document.createElement('div')
  header.innerText = "header"
  dom.append(header)
}
// 请导出
export default Header
// Sidebar
function Sidebar() {
  const dom = document.getElementById('root')
  const sidebar = document.createElement('div')
  sidebar.innerText = "sidebar"
  dom.append(sidebar)
}
export default Sidebar
// Content
function Content() {
  const dom = document.getElementById('root')
  const content = document.createElement('div')
  content.innerText = "content"
  dom.append(content)
}
export default Content

一个小小的栗子下来,是不是对 webpack 能做什么有了一定的了解。有没有认为webpackjs 的一个翻译器,帮我们把 js 的高级语法转换为浏览器可以理解的语法~~~其实 webpack 远远没有我们想象的那么强大(当然还是很强大的),它只认识 import 这种语法,像 ES6 的一些新 API 其实 webpack 并不能认识,需要借助 bable 帮我们处理(这是后话~~)。webpack 的核心定义是:模块打包工具

我们日常开发中通过 import 其实引入的就是我们自己写入的模块,而 webpack 做的事情就是把这些模块进行整合打包处理。当然模块的引入方式不仅仅只有 import,像我们在 node 中使用的 require 也可以通过 webpack 进行打包。其实 webpack 刚开始的时候只支持 js 模块打包,但是随着技术的迭代更新和发展,现在 webpack 同时支持 imgcss字体 等不同种类的模块打包。

此处延伸扩展学习文档:webpack 模块模块方法模块变量

搭建 Webpack 环境


Webpack 是基于 node.js 开发的模块打包工具,它本质上是由 node 实现的。所以搭建 Webpack 环境首先要安装 node 的环境,这里就不累赘介绍 node 的安装了~~~首先我们创建了一个 webpack-demo 的文件夹,然后命令行进入到这个文件夹,老规矩先来个 npm init -y它的含义是可以快速帮我们以 node 规范的形式创建一个项目或者创建一个 node 的包文件。此时即帮我们生成了一个 packge.json 的文件,接下来就是安装 webpack

  • 全局安装 Webpack (不推荐)
npm install webpack webpack-cli -g

如果全局安装成功,此时运行 webpack-v 就可以打印出对应的版本号。当然这种全局安装的方式非常不推荐,因为 webpack 现在的版本更新太快了,很多以前老版本的配置在新版本中可能就被移除了~~~举个栗子:如果我们两个项目都用 webpack 进行打包,一个项目当初是通过 webpack 3.0 进行配置的,而另一个项目是通过 webpack 4.0 进行配置的,此时用我们全局安装的 webpack 进行打包,就会因为版本不同出现问题。所以就引入了另一种安装 webpack 的方法,当然在这之前我们要卸掉刚刚全局安装的 webpack

npm uninstall webpack webpack-cli -g
  • 在项目内安装 webpack (推荐)

进入我们的 webpack-demo 文件夹,在项目内进行安装:

npm install webpack webpack-cli --save -dev
// --save -dev === -D 所以也可以写成下面这样:
// npm install webpack webpack-cli -D

安装完成之后,文件夹中就会多出一个 node_modules 的文件夹,当然此时如果我们输入 webpack -v 是无法查看对应版本信息的,因为 node 在全局中无法找到 webpack 指令,所以此时可以借助 npx webpack -v 来查看我们项目中 webpack 的版本,因为 npx 会帮助我们在项目的 node_modules 文件夹中去找 webpack。当然日常开发中我们一般会锁定一个 webpack 的稳定版本,所以此时我们在安装的时候可以指定对应的版本号:

npm install webpack@4.25.1 webpack-cli@3.1.2 -D

如果我们不知道 webpack 有哪些版本,可以直接通过如下命令查看它的所有版本信息:

npm info webpack
  • webpack.config.js 进行基础配置

前面打包都是通过 npx webpack index.js 来进行的,这是因为我们指定了打包的文件为 index.js。日常开发中,我们会对 webpack 进行相关配置,里面可以指定打包的入口文件,打包输出的地址等等......此时就我们可以新建一个配置文件 webpack.config.js

// webpack.config.js
const path = require('path')
module.exports = {
  entry: './index.js', // 项目打包的入口文件
  output: { // 打包的最终文件放在哪里
    filename: 'bundle.js', // 打包生成的文件名
    path: path.resolve(__dirname, 'dist') // 打包生成文件的文件夹名称
  }
}

上述代码就是 webpack 最基础的配置文件,它规定了打包的入口文件为 index.js,打包完成后生成的文件名为 bundle.js,同时 bundle.js 会放置在你规定的 dist 文件夹下。此时我们运行 npx webpack 即可按照我们制定的规则进行打包。

vue.config.js 的设置规则一样,webpack.config.js 的名称也是官方规定好的,不能随意更改这个名字。如果此时我们将 webpack.config.js 改为 webpackconfig.js,那我们要怎么样才能正常运行代码呢?此时需要手动指定 webpackconfig.js 作为我们的配置文件,可以使用如下方法:

npx webpack --config webpackconfig.js
  • 结构优化

了解了所有的基础打包步骤之后,我们可以对代码整体的结构进行优化。新建一个 src 目录用于放置我们代码中的源代码部分(index.js、header.js、sidebar.js、content.js),然后将 index.html 移入到 dist 目录下,记得修改 index.html 引入打包后的脚本文件的地址,因为我们改动了打包的入口文件 index.js 的地址,所以 webpack.config.js 中也要进行修改:

const path = require('path')
module.exports = {
  entry: './src/index.js', // 项目打包的入口文件
  output: { // 打包的最终文件放在哪里
    filename: 'bundle.js', // 打包生成的文件名
    path: path.resolve(__dirname, 'dist') // 打包目录
  }
}

我们知道,在 vue 项目的构建中,我们通常的打包命令为 npm run build,虽然我们这个项目只是一个入门基础学习项目,但是我们也想用 vue 的项目解构和打包指令进行打包,此时就可以配置 npm scripts,即修改 package.json 文件中的 scripts

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "keywords": [],
  "scripts": {
    "build": "webpack" // 使用 bulid 映射 webpack 指令
  },
  "author": "Chen",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.25.0",
    "webpack-cli": "^4.1.0"
  },
  "dependencies": {
    "file-loader": "^6.1.1"
  }

此时我们就可以通过 npm run build 对我们的项目进行整体打包了,最终我们得到一个类似 vue 项目的结构,具体结构图如下:

项目结构图.png

  • 综合小结

经过上述一系列操作,我们也算是完成了一个初步的打包 demo ,同时我们也总结一下 webpack 的三种打包方式:

global 即全局安装 webpack
webpack index.js

local 项目中局部安装 webpack
npx webpack index.js

npm scripts 修改 scripts 配置
npm run build => webpack

最终我们也算是得到了一个类似 vue-cli 脚手架帮我们搭建的项目结构,那么回过头来,我们在安装 webpack 的时候同时安装了 webpack-cli 这个东西,那么 webpack-cli 有什么用呢?

webpack-cli 的作用就是可以使我们在命令行里正确运行 webpack 这个命令,如果我们不安装这个包,那么就无法在命令行里运行 webpack 或者 npx webpack 这样的命令。

此处可以延伸扩展学习:webpack 起步

  • 查漏补缺

我们通过 npm run build 打包之后,命令行也给我们输出了如下信息:

命令行输出信息.png

这些信息都是什么意识呢?我们一个一个来瞅一瞅:

  • Hash:本次打包对应的唯一哈希值

  • Version:打包使用的 webpack 版本

  • Time:当前打包的整体耗时是 411ms

  • Asset:打包出了一个 bundle.js 文件

  • Sizebundle.js 的文件大小

  • Chunks:每一个 js 文件对应的 ID

  • Chunk Name:每一个 js 文件对应的名字

那么问题来了,这里为什么是 main 呢,我们好像全程都没有用到过 main 这个词,其实这个 main 来源于 webpack.config.js,如下栗子:

// webpack.config.js
const path = require('path')
module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js'
  }, 
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

我们前面对 entry 的配置其实是一种简写模式,其实真正在打包过程中 entry 的配置应该如上代码,main 指向的就是打包的入口文件。上述代码中我们还设置了一个 modewebpack 打包时如果我们没有填写该项,即会默认使用 production 模式,即打包出来的 bundle.js 文件会被压缩。mode 还可选值为 development ,此时打包生成的 bundle.js 文件就不会被压缩。

webpack 入门基础配置基本就是以上这些内容,什么是 loader ?什么是 plugins ? 什么是 babel ?就让我们一点一滴在后续剥开它们神秘的面纱。再次强烈推荐大家学习慕课网 Dell Lin 老师的 从基础到实战 手把手带你掌握新版Webpack4.0 课程,老师所有的课程说实话都非常棒,大家该剁手时一定要毫不留情啊!!!

此整理仅供记录学习,如果文中有不对的地方或者理解有误的地方欢迎大家提出并指正。每一天都要相对前一天进步一点,加油!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273