webpack简介

此文章为阅读《Webpack实战》时结合自己的理解与实践做的笔记,如果错误欢迎指正。

1.1 何为Webpack

 Webpack是一个开源的Js模块打包工具,其核心是解决模块之间的依赖问题,将各个模块按照特定的规则和顺序组织在一起,最终合并成一个JS文件。
 那么为什么要打包呢?最开始接触到前端的时候其实最简单的三个html、js和css文件就可以完成一个网页,直接放到服务器上给用户来拿去不就可以了?

1.2 为什么需要Webpack

 最开始的网站很简单,确实可以只靠几个文件就可以了,但是后续网站的需求越来越复杂,就需要模块的提供更多的工具来帮助建设网站。

1.2.1 何为模块

 用最直观的方式来说,模块就是我们在工程中引入一个日期处理的npm包,或者是写一个提供工具方法的JS文件。这种包和文件都可以称为模块。

1.2.2 JavaScript中的模块

 在大多数的程序语言中都有模块开发的概念,但是由于历史原因,JavaScript中没有模块开发的概念(因为当初觉得不会用JavaScript去实现复杂场景,当时只当一个简单的脚本语言来使用)。所以当时使用Js的原始方式为将多个script脚本引入到Html中,没有什么import操作和命名空间的概念。比如如下的页面,如果even.js需要使用到cal.js中的函数,直接按顺序执行声明即可。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>这是一个简单的页面</title>
</head>
<body>

<h1>做个加法叭</h1>
<input type="number" value="5" />
<input type="number" value="2" />
<input id="submit" type="submit" value="计算"></input>
<h1>结果入下:</h1>
<p id="result"></p>
</body>
<script type="text/javascript" src="./cal.js"></script>
<script type="text/javascript" src="./event.js"></script>
</html>

cal.js

const myAdd = (a, b) => a + b

even.js

const btn = document.getElementById("submit")
try{
    function start(){
        const inputs = document.getElementsByTagName("input")
        const a = inputs[0].value
        const b = inputs[1].value
        console.log(myAdd(a,b))
    }
    start()
}catch(err){
    console.log(err)
}
btn.addEventListener("click", () => {
    const inputs = document.getElementsByTagName("input")
    const a = inputs[0].value
    const b = inputs[1].value
    console.log(myAdd(a,b))

})

并且会有如下缺点:

  • 需要手动维护JavaScript的加载顺序,多个script脚本通常会有依赖关系,并且这个关系是隐式的,需要特定去维护。比如上面的event.js依赖于cal.js,cal.js一定需要在event.js之前执行,就需要特别注意这样的顺序关系。再加上这种关系是隐式的,若这个文件项目特别大,接到别人转手的项目也就会很难下手。
  • 每一个script标签都需要请求一次静态文件,在http2之前过多的请求需要建立多次连接,那网络开销也会很大。
  • 命名问题,多个script脚本引入就相当于从全局作用域中开始执行这个脚本,那么可能会有重名问题。比如声明另一个文件。
    cal2.js
const myAdd = (a, b) => a + b

并按照cal.js ->event.js -> cal2.js的顺序进行引入,由于这里使用的const声明,所以其实是会报重复声明的错:


报错

假如改成了var,那么第一次执行的myAdd和后面点击执行的myAdd就不是一个函数了。下面图片中第一个是start执行的时候,第二个点击的时候的效果。


image.png

 那么在之前引入三方库的时候谁知道有没有命名冲突呢,或者命名冲突后难道要一个个改吗。所以总结起来之前的网页编写中存在着下列问题1. 各js文件之前没有显式的依赖关系 2.每个js文件对应着一个请求,网络开销大(不知道http2有没有这方面的问题,这里欠个债之后来讲)3. 存在着未知的命名冲突问题。
模块化解决了上述的所有问题:
  • 通过在js中导入导出语句可以看出js文件中的依赖关系
  • 借助打包工具可以将多个文件打包成一个文件,比如将cal.js打包到evet.js里,就少了一个cal.js的请求。从而减少网络开销。
  • 多个模块之前的作用域是相互隔离的,彼此不会有命名冲突(其实还不太懂是作用域上的什么原理)

 自2009年就有了对js模块化的社区版本,如用于服务器的commonJS,直到2015年ES6才有了正式的模块化概念。(难怪之前写前端代码的时候糊里糊涂有时候用module.exports+require,有时候用import+export)
可以在浏览器中使用ES6但无法使用CommonJS,在浏览器中引入脚本的时候带上type="module"既可以使用ES6中的模块化。如将上面的页面换成模块化的写法:

使用ES6中模块化导入脚本

再在even.js中引入cal.js,
三个脚本

可以看到html中没有显式引入cal.js和cal2.js,由于event.js中有对它的引用,在event.js脚本执行的时候自动引入了。
自动根据依赖关系进行引入

综上所述,ES6中的module解决了上面说到的问题1(利用import建立依赖关系)和问题3(每个文件是一个作用域,可以通过as解决命名冲突),那么Webpack则是进一步解决问题2和其他问题:

  • ES6无法使用代码分片和删除死代码
  • 大多数npm模块还是用的commonJS的形式,没法拿到浏览器中使用。
  • 还没解决问题二中代码打包的问题
    所以就到了webPack出场了。

1.2.3 模块打包工具

打包工具就是为了解决模块间的依赖,使打包出的产物能够在浏览器上运行。主要的工作方式为两种:

  • 根据依赖关系打包出最终的一个JS文件。
  • 在页面初始时加载一个入口模块,异步加载其他模块。

1.2.4 为什么选择Webpack

Webpack有如下优势:

  • 支持多种模块标准,包括AMD、CommonJS和ES6 module。
  • 有完备的代码分片解决方案,能够分割打包后的资源,在首屏只加载必要的部分,不必要的放在后面去加载。
  • 可以处理多种类型的资源。
  • 有庞大的社区支持。

1.3 安装

依次需要:

  • 安装Nodejs(跳过)
  • 使用npm安装webpack,这里npm安装有全局和本地两种形式,具体的区别可以看https://blog.csdn.net/weixin_45719444/article/details/127662714
    全局安装会绑定命令行环境变量,这样在cmd中的任何路径下都能够运行这些命令。比如npm create-react-app相当于运行C:\Users\89731\AppData\Roaming\npm\create-react-app,而npx会运行当前路径下的.\node_modules.bin\create-react-app
    npm中全局安装的模块

 这里使用本地安装,因为可以切换着不用不同的webpack版本,如果要和别人协作的话就需要共同用某一个特定的版本。执行安装命令:

npm config set registry https://registry.npmmirror.com
npm install webpack webpack-cli -D

 可以看到.\node_modules\.bin中真的就有webpack,要运行webpack需要使用npx命令,也需要cmd在这个项目文件路径中。


安装后的项目中的本地模块文件夹中

1.4 打包第一个应用

index.html,需要注意到里面引用的js文件是打包后的产物,虽然后续的js文件都是使用es module的格式去写的,webpack打包后的产物是直接的原生js文件,引用的不需要说明type="module"。

<!DOCTYPE html>
<html lang="zh-CN">
   <head>
       <meta charset="utf-8">
       <title>Title</title>
   </head>
   <body>
        <script src="./dist/main.js"></script>
   </body> 
</html>

index.js

import addContent from "./add-content";
document.write("My first webpack app<br/>")
addContent()

add-content.js

export default function() {
    document.write("Hello World!")
}

 接下来使用webpack命令打包入口文件index.js

npx webpack --entry=./index.js --output-filename="main.js"  --mode=development

 可以看到目录中多出了一个打包的js文件。


打包出的js文件

打开index.html后也可以正常执行了。
 命令行中的entry参数指明入口文件,output-filename是输出资源名,mode是指打包模式,提供了有development、production、none这三种模式。

1.4.2 使用npm script

 从上面的例子可以看出每次打包都需要输入较长的命令行,为了使命令行更加简洁,可以在package.json中的script字段添加脚本命令。该字段的原理如下:

该知识点摘自一个网站

script字段的原理

 所以如上所说,当我们添加了字段:


使用script简化webpack构建命令

 那么在npm run build的时候会先将./node_modules/.bin加到Path路径中,再执行“webpack --entry=./index.js --output-filename=main.js --mode=development”,那么它执行的其实和npx webpack一样,执行的是shell所在目录下的"./node_modules/.bin中的命令。

1.4.3 使用默认目录配置

 上面的index.js是放在工程根目录下的,通常是会将项目中的文件分成源码目录/src和资源输出目录/dist中的,webpack的entry的默认值就是/src/index.js,所以我们可以将index.js放到src目录中,然后省略entry参数,webpack的默认资源输出目录就是dist,可以尝试将打包文件名称改成bundle.js。


修改结果

1.4.4 使用配置文件

 webpack提供多项配置,可以通过命令行的方式进行指定,我们可以尝试使用npx webpack -h查看一下可以配置的选项。


npx webpack -h的结果

 但是当项目需要加入很多配置的时候,使用命令行的方式进行指定是一件很麻烦的事情,webpack提供了webpack.config.js的配置文件供我们进行配置。

import {Configuration} from 'webpack'

/**
 * @type{Configuration}
 */
const config = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js'
    },
    mode: "development"
}

module.exports = config

 代码中的Configuration是使用的'webpack'包中types.d.ts中的类型定义,@type{Configuration}是JS doc中的一个语句,帮助声明紧跟着的变量的类型。由于这里示例是js文件而不是ts文件,所以需要使用这种方式去说明一个变量的类型,有了类型在输入的时候就可以有提示。
 可以看到配置文件有层级关系,如output-filename是配置文件中output字段中的filename字段。
 定义好这个配置文件后直接运行npx webpack或者在package.json的script字段中加入webpack命令即可。

1.4.5 webpack-dev-server

 使用webpack进行打包后会发现一个问题,每次修改js文件后需要重新运行一遍打包命令去更新bundle.js才能更新页面,这对开发来说是不方便的,可以使用webpack-dev-server来帮助自动更新打包。
首先安装webpack-dev-server

npm install -D webpack-dev-server

接着在webpack.config.js中的webpack-dev-server进行相应配置。(书中的字段已经过期了,我只能自己摸索了)

// import {Configuration} from 'webpack'
// import {Configuration as devServerConfig} from 'webpack-dev-server'
const path = require('path');

/**
 * @type{Configuration}
 */
const config = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    mode: "development",
    /**
     * @type{devServerConfig}
     */
    devServer: {
        static: './dist'
    }
}

module.exports = config

 devServer相当于是一个本地服务器,其网址为http://[dev url]:[port](url默认值为localhost,port默认值为8080),打包出的js文件会存放为http://[dev url]:[port]/bundle.js中并且会保存在内存中,并不会生成出来。
 其中devServer中的static字段表示从本地的哪个文件夹中查找文件,由于打包出的js文件存放在http://[dev url]:[port]/bundle.js中,而index.html的访问地址应为http://[dev url]:[port]或者http://[dev url]:[port]/index.html,为了保证在index.html能使用"./bundle.js"访问到js文件,需要将index.html也放到dist文件中(这里书本好像没说,看得我云里雾里)。
 之后运行npm run webpack就可以了,可以在提示的url中访问到正确的结果,在add-content.js上的修改也会实时更新。

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

推荐阅读更多精彩内容