webpack基础
在开始之前,确保大家已经安装了node和npm/yarn环境。
首先我们新建一个目录用来初始化一个新的项目
使用npm:
使用yarn:
上面的选项大家可以自己填入,也可直接回车快速跳过完成初始化,完成后会生成package.json文件
接下来我们在局部安装webpack:
yarn add webpack
如果你习惯使用npm,那么就使用命令npm install 进行安装。
webpack安装完成后,我们还要安装一个辅助的依赖:webpack-dev-server,
它可以在开发环境为我们提供热更新,接口代理等等很实用的服务。
yarn add webpack-dev-server
关于yarn的add命令使用大家可以去查阅官方文档:
https://yarnpkg.com/zh-Hans/docs/cli/add
好了,我们先看下安装完成后的package.json文件:
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"webpack": "^4.28.2",
"webpack-dev-server": "^3.1.14"
}
}
到这一步,我们就需要了解webpack的一些基础知识了。
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(static module bundler)。在 webpack 处理应用程序时,它会在内部创建一个依赖图(dependency graph),用于映射到项目需要的每个模块,然后将所有这些依赖生成到一个或多个bundle。
webpack的核心概念有以下四点,这些大家必须清楚:
入口(entry)
输出(output)
loader
插件(plugins)
入口(entry)
入口起点指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始,webpack 会找出有哪些模块和 library 是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js,也可以通过在 webpack 配置中配置 entry 属性,来指定一个不同的入口起点(或者也可以指定多个入口起点)。
出口(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,主输出文件默认为 ./dist/main.js,其他生成文件的默认输出目录是 ./dist。
你可以通过在配置中指定一个 output 字段,来配置这些处理过程。
loader
作为开箱即用的自带特性,webpack 自身只支持 JavaScript。而 loader 能够让 webpack 处理那些非 JavaScript 文件,并且先将它们转换为有效 模块,然后添加到依赖图中,这样就可以提供给应用程序使用。
webpack 的配置中 loader 有两个特征:
test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
use 属性,表示进行转换时,应该使用哪个 loader。
插件(plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,插件的范围包括:打包优化、资源管理和注入环境变量。
通常,我们的项目还需要继续扩展webpack,为此我们可以在项目根目录下创建一个 webpack.config.js 文件,webpack 会自动使用它。
这里给大家一个链接里面有详细的配置说明:
https://webpack.docschina.org/configuration
好了,如果看了上面的链接还有兴趣往下看的我们继续。
归根结底,webpack本质上就是一个js配置文件,那么我们首先初始化这个文件的内容:
module.exports={
}
然后我们在package.json里增加一个快速启动webpack-dev-server的脚本:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev":"webpack-dev-server --open --config webpack.config.js"
},
此时,我们就可以使用npm/yarn run dev命令了。前面提到过,核心配置文件入口和出口这两个选项是必须的,此时我们可以正常运行命令,但是结果是错误的。
提示我们需要安装webpack-cli:
yarn add webpack-cli
接下来我们创建一个main.js作入口文件,并在webpack.config.js中做相应的配置:
const path = require('path')
module.exports={
entry:{
main: './main'
},
output:{
path: path.join(__dirname,'./dist'), //打包后文件的输出目录
publicPath: '/dist/', //资源文件引用的目录
filename:'main.js' //打包后的文件名称
}
}
此时我们运行yarn run dev:
此时在浏览器会打开一个新的页面:
很显然,这里我们缺少页面入口html文件。
启动命令后,页面执行的url是:
localhost:8080
后面并没有具体的页面信息,那么webpack-dev-server到底会使用哪个页面作为起始页面呢,这点我们不得而知,只能去查看源代码试着找到一些线索。
在我们的工程目录下面,可以找到我们安装的依赖:
我们找到webpack-dev-server.js一探究竟:
大家可以看到:
也就是说webpack-dev-server的内部服务器处理是通过Server这个东西来实现的,我们继续找Server,在38行我们找到了:
好了,我们接着去看看Server内部的实现,我们会发现其内部是采用Express作为服务器的实现:
当我们启动程序后,会执行下面的代码,这里我们可以找到我们想要的:
我们进入这个文件我们会找到如下的一段代码:
return new Promise(((resolve) => {
handleRequest(context, filename, processRequest, req);
function processRequest() {
try {
let stat = context.fs.statSync(filename);
if (!stat.isFile()) {
if (stat.isDirectory()) {
let { index } = context.options;
if (index === undefined || index === true) {
index = 'index.html';
} else if (!index) {
throw new DevMiddlewareError('next');
}
filename = path.posix.join(filename, index);
stat = context.fs.statSync(filename);
if (!stat.isFile()) {
throw new DevMiddlewareError('next');
}
} else {
throw new DevMiddlewareError('next');
}
}
} catch (e) {
return resolve(goNext());
}
在这我们找到了我们想要的index.html从哪里来,在其中我们输出一些log:
let filename = getFilenameFromUrl(context.options.publicPath, context.compiler, req.url);
console.log(req.url,filename, '1111')
大家会看到输出:
这是我们期望看到的,因为localhost:8080后面没有跟任何路径。
if (index === undefined || index === true) {
index = 'index.html';
} else if (!index) {
throw new DevMiddlewareError('next');
}
从这几行代码可以看出,如果如果HTTP请求路径/后没有子路径,则默认返回一个硬编码的index.html,好了大概就是这样子的,具体的我们在自己研究研究。
既然知道原因,我们创建一个index.html文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack 基础</title>
</head>
<body>
<div>
webpack 基础
</div>
</body>
</html>
再次运行:
ok,大体的流程我们就清楚了。
我们在main.js中添加一句代码修改显示的文字:
document.getElementById('app').innerHTML = '我改啊改'
然后在index.html中引入打包后的main.js:
<script type="text/javascript" src="/dist/main.js"></script>
最终浏览器编译后的文件如下: