webpack通俗易懂(一)
- 1,为什么需要webpack
- 2,如何解决作用域问题呢
- 3,如果实现代码的拆分问题
- 4,如何让浏览器支持模块
- 5,小试 Webpack
1,为什么需要webpack
在打包工具出现之前,我们是如何在网页中使用 javascript 的呢?比如以下文件
我们自己写的 JS 业务代码是依赖以上外部 JS 文件的,根据 JS 在浏览器上的加载特性,JS 代码是从上至下加载的,所以以上代码加载的顺序是不可以颠倒的,那我们把这些文件按照预定的顺序来合并到一个文件里就解决这个问题了,但是可能会导致其他的问题,比如作用域问题,文件过大的问题,可读性差,可维护性弱等问题,
- 作用域问题
jQuery、lodash 库, 会在 window 对象上面绑定全局的变量。比如:jQuery 会在window对象上绑定 window.$,lodash会绑定 window._,又或者我们自己的 JS 文件可能会绑定全局对象 window.user等,这些变量会严重污染我们的 window 对象,使window对象变的臃肿,这就是变量的作用域问题 - 文件过大
- 可读性差
- 可维护性弱
2,如何解决作用域问题呢
早期使用grunt、gulp两个工具来管理项目资源,这两个工具称为任务执行器。它们将所有的项目文件拼接在一起,利用了js的立即执行函数(IIFE)。就解决了大型项目的作用域问题。当脚本被封装在IIFE内部的时候,我们可以安全地拼接或安全地组合所有的文件,而不必担心作用域冲突。
IIFE
- 当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问
(function (){
var name = 'Jerry'
})()
console.log(name) // name is not defined
- 将 IIFE 分配给一个变量,不是存储 IIFE 本身,是存储 IIFE 执行后返回的结果
let res = (function (){
var name = 'Jerry'
return name
})()
console.log(res) // Jerry
Grunt,Gulp 解决了作用域问题。但是,修改一个文件意味着必须重新构建整个文 件。拼接可以做到很容易地跨文件重用脚本,却使构建结果的优化变得更加困难。如 何判断代码是否实际被使用?
即使我们只用到 lodash 中的某个函数,也必须在构建结果中加入整个库,然后将它们压缩在一起。大规模地实现延迟加载代码块及无用代码的去除,需要我们手动地进行大量工作
以上代码,我们只使用了 lodash 的 join 方法,但是引入了整个 lodash 文件,思考:我们能不能把这个文件拆分成一个一个的方法的模块呢,或者实现一个方法的懒加载呢?
3,如果实现代码的拆分问题
Node.js的出现,JavaScript 模块诞生了。Node.js 是一个 JS 运行时,可以在浏览器环境之外的计算机和服务器中使用。webpack 运行在 Node.js 中。
既然不是在浏览器中 运行 JS,现在已经没有了可以添加到浏览器中的 html 文件和 script 标签。 那么 Node.js 应用程序要如何加载新的代码文件呢?
CommonJS 问世并引入了 require 机制,允许我们在当前文件中加载和使用某个模块。导入需要的每个模块,这一开箱即用的功能,帮助我们解决了代码拆分的问题
// index.js
const add = (x, y) => {
return x + y
}
const sub = (x, y) => {
return x - y
}
module.exports = {
add,
sub
}
// test.js
const fn = require('./index.js')
console.log(fn.add(10, 20)) // 30
虽然 CommonJS 是 Node.js 项目的绝佳解决方案,但浏览器不支持模块,所以我们该如何解决这个问题呢?
4,如何让浏览器支持模块
在早期,我们用 Browserify
和 RequireJS
等打包工具
编写能够在浏览器中运行 的 CommonJS 模块:
这里以 require 为例:
以上代码,使用 require 需要引入 require.js。
ECMAScript Module: 目前,我们还有一个选择,就是来自 Web 项目的好消息是,模块正在成为 ECMAScript 标准的官方功能。然而,浏览器支持不完整,版本迭代速度也不够快,还是推荐上面两个早期模块实现,比如:
此时会报错:
因为当前的通过 file 方法访问的文件违背了跨域请求的一个安全策略,这时,我们可以本地搭建一个临时的http server环境,可以使用npx
npx http-server // npx 当我们本地没有这个模块的时候,他可以帮我们去安装这个模块
访问 http://localhost:8081/ 即可:
是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式,webpack可以解决这一问题
5,webpack
webpack是一个工具,可以打包 JavaScript 应用程序 (支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如:
images , fonts 和 stylesheets 。
webpack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步加载和
预先加载代码文件,以便为我们的项目和用户提供最佳体验
5.1,Webpack 与竞品
- webpack
- Webpack 为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件 多。
- Parcel
- Parcel 是 0 配置工具, 用户一般无需再做其他配置即可开箱即用
- Rollup
- Rollup 用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。一般只用来打包JS
- Vite
- 在刚刚结束的 VueConf2021 中,除了 Vue 3.0 以外,另外一个亮点就是下一代构建工具 Vite 了。在尤雨溪分享的【 Vue 3 生态进展和计划】的演讲中,尤大神还特意提到 Vite 将成为 Vue 的现代标配。甚至最近新推出的 Petite Vue 从开发、编译、发布、Demo几乎全都是使用 Vite 完成
Webpack、Vite 作为前端热门的工程化构建工具,它们都有各自的适用场景,不存在“谁取代谁”这一说
总结:
- 构建一个简单的应用并让它快速运行起来?
使用 Parcel - 构建一个类库只需要导入很少第三方库?
使用 Rollup - 构建一个复杂的应用,需要集成很多第三方库?需要代码分拆,使用静态资源文件,还有 CommonJS 依赖?
使用 webpack
5.2,小试 Webpack
5.2.1,开发准备
在进入Webpack世界之前,我们先来用原生的方法构建一个Web应用
一个JavaScript文件,编写一段通用的函数 helloWorld:
如果以上代码引入调整顺序,代码就会报错:
如果页面引用的JS文件很少,我们可以手动的来调整顺序,但页面一旦引用大量的 JS 文件,调整顺序的心智负担和工作量可想而知,如何解决呢,那就是 webpack 了。
5.2.2,安装 Webpack
确保安装了 Node.js 的最新版本。使用 Node.js 最新的长期支持版本(LTS - Long Term Support),是理想的起步。 使用旧版本,你可能遇到各种问题,因为它们可能缺少 webpack 功能, 或者缺少相关 package
- 1, 本地安装 webpack(推荐)
推荐在本地安装webpack,即项目根目录下安装。全局安装webpack的弊端:可能同时开发的小伙伴不知道全局安装了 webpack,导致项目运行错误等问题
// webpack5
npm install --save-dev webpack
或指定版本
npm install --save-dev webpack@
--save-dev 的使用取决于我们的应用场景。如果我们仅使用 webpack 进行构建操作,那么建议在安装时使用 --save-dev 选项,因为我们可能不需要在生产环境上使用 webpack。如果需要应用于生产环境,请忽略 --save-dev 选项
// 如果我们使用 webpack v4+ 版本,并且想要在命令行中调用 webpack ,需要安装 CLI
npm install --save-dev webpack-cli // webpack-cli 表示我们可以在终端执行webpack命令
通常会通过运行一个或多个 npm scripts 以在本地
node_modules 目录中查找安装的 webpack,来运行 webpack:
"scripts": {
"build": "webpack --config webpack.config.js"
}
想要运行本地安装的 webpack,我们可以通过 node_modules/.bin/webpack 来访问它的二进制版本。或者如果我们使用的是 npm v5.2.0 或更高版本,则可以运行 npx webpack 来执行
- 2, 全局安装 webpack
npm install --global webpack // --global 在所有目录下都可以执行 webpack
不推荐全局安装 webpack。这会将项目中的 webpack 锁定到指定版本,并
且在使用不同的 webpack 版本的项目中,可能会导致构建失败
- 3,最新体验版本(不推荐)
如果我们热衷于使用最新版本的 webpack,你可以使用以下命令安装 beta 版本, 或者直接从 webpack 的仓库中安装:
npm install --save-dev webpack@next
# 或特定的 tag/分支
npm install --save-dev webpack/webpack#
安装这些最新体验版本时要小心!可能仍然包含 bug,因此不应该用于生产环境
- 4, 验证是否安装 webpack
webpack -v
- 5, 实例:
新建webpack文件夹,进入webpack根目录执行:
// 先创建一个 package.json:npm init -y
npm install webpack webpack-cli --save-dev
这样就在本地安装了webpack webpack-cli
5.2.3,运行 Webpack
实例:
创建webpack01目录,并创建3个文件
// index.js
import HelloWorld from './hello.js' // ./hello.js后的.js可省略
HelloWorld()
// hello.js
function HelloWorld() {
console.log('HelloWorld!')
}
export default HelloWorld
// html
Document
我们在这个项目根目录下使用 webpack 打包:
webpack
可以看出多了一个dist文件夹,dist -> main.js, main.js里的代码是从哪些文件里生成出来的呢?回到终端,我们再运行一下命令:
webpack --stats detailed
可以看到 asset main.js 是从入口 > ./src index 生成的。我们也可以自定义 Webpack 配置
这时候,因为我之前安装了全局的webpack所以这里用的是全局的webpack,得以验证,可以把全局的ewbpack卸载:npm uninstall webpack webpack-cli --global
这时候再运行: webpack,会提示:command not found: webpack
这时候,我们可以使用:
npx webpack
, npx 依托于 npm, npx 表示我们可以来观察我们当前文件夹里面有没有我们想运行的这个命令,当前这个 webpack01文件夹没有webpack,它就会去它的上一级目录中找。
执行npx webpack也会打包出dist -> main.js
5.2.4,自定义 Webpack 配置
command + k 可以清 vscode 编辑器里的集成终端里的命令
webpack-cli 给我们提供了很多的终端命令行指令,可以通过 webpack --help查看。
比如:
npx webpack --entry ./src/index.js --mode production
我们也可以使用别的命令,但是命令行不方便也不直观,而且不利于保存配置的内容。所以,webpack 给我们提供了通过配置文件,来自定义配置参数的能力,需要我们在项目根目录创建 webpack.config.js
5.2.3 自定义打包配置
// webpack.config.js
const path = require('path');
module.exports = { // 定义模块
entry: './src/index.js', // 入口
output: { // 出口
filename: 'index.js', // 文件名
path: path.resolve(__dirname, './back') // 路径。 path值一定设置成绝对路径
},
mode: none
}
执行 npx webpack后会出现 back -> index.js,index.html中引入:
运行后控制台也会打印出 Hello World~, 但是我们每次都要手动的修改html里的引入文件吗?下一篇介绍自动引入打包好的资源