webpack专题
一、编译
编译命令
直接cmd下执行webpack 即可,但是配置成为node项目,就可以使用npm的快捷脚本了,
那么我再提供一个在npm下好用的script命令(只支持windows)
"scripts": {
"build":"PowerShell.exe rm ./dist/* && webpack"
},
普通编译
源代码
console.log(123);
编译后代码
(() => {
eval("console.log(123);\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
})();
静态导入编译
源代码
// index.js
import util from './util';
console.log(123);
// util.js
export default{
say(){
console.log('hello')
}
}
编译后代码
(() => {
// 所有的模块
var __webpack_modules__ = ({
"./src/index.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ \"./src/util.js\");\n\r\nconsole.log(123);\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
}),
"./src/util.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({\r\n say(){\r\n console.log('hello')\r\n }\r\n });\n\n//# sourceURL=webpack://webpack-demo/./src/util.js?");
})
});
// 模块缓存
var __webpack_module_cache__ = {};
// require 方法定义
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
(() => {
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
(() => {
__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
})();
(() => {
__webpack_require__.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
// 入口函数
__webpack_require__("./src/index.js");
})();
动态导入编译
源代码
// index.js
import('./util');
console.log(123);
// util.js
export default{
say(){
console.log('hello')
}
}
编译后代码
// main.js webpackBootstrap即启动入口文件
(() => {
// 模块和模块缓存
var __webpack_modules__ = ({});
var __webpack_module_cache__ = {};
// require 方法定义
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.m = __webpack_modules__;
(() => {
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
(() => {
__webpack_require__.f = {};
__webpack_require__.e = (chunkId) => {
return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
__webpack_require__.f[key](chunkId, promises);
return promises;
}, []));
};
})();
(() => {
__webpack_require__.u = (chunkId) => {
return "" + chunkId + ".js";
};
})();
(() => {
__webpack_require__.g = (function () {
if (typeof globalThis === 'object') return globalThis;
try {
return this || new Function('return this')();
} catch (e) {
if (typeof window === 'object') return window;
}
})();
})();
(() => {
__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
})();
(() => {
var inProgress = {};
var dataWebpackPrefix = "webpack-demo:";
__webpack_require__.l = (url, done, key) => {
if (inProgress[url]) { inProgress[url].push(done); return; }
var script, needAttach;
if (key !== undefined) {
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
var s = scripts[i];
if (s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
}
}
if (!script) {
needAttach = true;
script = document.createElement('script');
script.charset = 'utf-8';
script.timeout = 120;
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
script.setAttribute("data-webpack", dataWebpackPrefix + key);
script.src = url;
}
inProgress[url] = [done];
var onScriptComplete = (prev, event) => {
script.onerror = script.onload = null;
clearTimeout(timeout);
var doneFns = inProgress[url];
delete inProgress[url];
script.parentNode && script.parentNode.removeChild(script);
doneFns && doneFns.forEach((fn) => fn(event));
if (prev) return prev(event);
}
;
var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
script.onerror = onScriptComplete.bind(null, script.onerror);
script.onload = onScriptComplete.bind(null, script.onload);
needAttach && document.head.appendChild(script);
};
})();
(() => {
__webpack_require__.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
(() => {
var scriptUrl;
if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
var document = __webpack_require__.g.document;
if (!scriptUrl && document) {
if (document.currentScript)
scriptUrl = document.currentScript.src
if (!scriptUrl) {
var scripts = document.getElementsByTagName("script");
if (scripts.length) scriptUrl = scripts[scripts.length - 1].src
}
}
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
__webpack_require__.p = scriptUrl;
})();
(() => {
var installedChunks = {
"main": 0
};
__webpack_require__.f.j = (chunkId, promises) => {
var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
if (installedChunkData !== 0) {
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
if (true) {
var promise = new Promise((resolve, reject) => {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
promises.push(installedChunkData[2] = promise);
var url = __webpack_require__.p + __webpack_require__.u(chunkId);
var error = new Error();
var loadingEnded = (event) => {
if (__webpack_require__.o(installedChunks, chunkId)) {
installedChunkData = installedChunks[chunkId];
if (installedChunkData !== 0) installedChunks[chunkId] = undefined;
if (installedChunkData) {
var errorType = event && (event.type === 'load' ? 'missing' : event.type);
var realSrc = event && event.target && event.target.src;
error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
error.name = 'ChunkLoadError';
error.type = errorType;
error.request = realSrc;
installedChunkData[1](error);
}
}
};
__webpack_require__.l(url, loadingEnded, "chunk-" + chunkId);
} else installedChunks[chunkId] = 0;
}
}
};
// 异步加载脚本回调
var webpackJsonpCallback = (data) => {
var [chunkIds, moreModules, runtime] = data;
var moduleId, chunkId, i = 0, resolves = [];
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (__webpack_require__.o(moreModules, moduleId)) {
__webpack_require__.m[moduleId] = moreModules[moduleId];
}
}
if (runtime) runtime(__webpack_require__);
parentChunkLoadingFunction(data);
while (resolves.length) {
resolves.shift()();
}
}
var chunkLoadingGlobal = self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || [];
var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);
chunkLoadingGlobal.push = webpackJsonpCallback;
})();
// 入口函数(这个我处理过,方便阅读)
__webpack_require__.e(/*! import() */ "src_util_js").then(__webpack_require__.bind(__webpack_require__, /*! ./util */ "./src/util.js"));
console.log(123);
//# sourceURL=webpack://webpack-demo/./src/index.js?;
})();
// src_util_js.js
(self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || []).push([["src_util_js"], {
"./src/util.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => __WEBPACK_DEFAULT_EXPORT__\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({\r\n say(){\r\n console.log('hello')\r\n }\r\n });\n\n//# sourceURL=webpack://webpack-demo/./src/util.js?");
})
}]);
看到没,总的来说玩的花样越多,编译后的代码就越长
推荐阅读 文章一
编译的几个特性
支持多种模块化方案
无论是commonJS和是esm都支持,而且混合使用也没问题
万物皆可打包
本身只支持js,但是通过三方扩展的loader,延伸至万物皆可打包
Code Splitting
支持代码切割,就是把代码分成很多很多块( chunk )。对比gulp
然后实现模块的动态加载。
触发这样的场景有这几种情况:commonJS、import()和require.ensure()。
意义:在以前,为了减少 HTTP 请求,通常地,我们都会把所有的代码都打包成一个单独的 JS 文件。但是,如果这个 JS 文件体积很大的话,那就得不偿失了。这时,我们不妨把所有代码分成一块一块,需要某块代码的时候再去加载它;还可以利用浏览器的缓存,下次用到它的话,直接从缓存中读取。很显然,这种做法可以加快我们网页的加载速度
tree-shaking
即移除 JavaScript 上下文中的未引用代码(dead-code),给一张流传甚广的图,你品

ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。
但是通过require和import()动态引入的,依然无法进行tree-shaking。
定义一个工具 util.js
export const add = (x, y)=> {
return x + y
}
export const reduce= (x, y)=> {
return x - y
}
// 或者(上下都行)
export default {
add(x, y) {
return x + y
},
reduce(x, y) {
return x - y
}
}
入口index.js
import {add} from './util';
console.log(add);
编译后的文件
// 可以看到reduce因为没有使用,已经被去除了
(()=>{"use strict";console.log(((o,s)=>o+s))})();
二、多页面入口
有时候一个项目可能会有多个入口,非单一的spa应用,有可能是多个组合成一个
那么这个时候就用到了webpack的多入口了
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const config = {
mode: 'none',
entry: { // 多入口
index: './src/index.js',
second: './src/second.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
plugins: []
};
for (const key in config.entry) {
config.plugins.push(new HtmlWebpackPlugin({ // 多页面
template: './public/index.html', // 生成页面模板
filename: key + '.html', //生成页面文件名
chunks: [key] // 引用的模块编译后的js
}))
}
module.exports = config;
编译后的会生成如下结构
dist
--index.html
--index.js
--second.html
--second.js
second.html代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="second.js"></script>
</body>
</html>
HtmlWebpackPlugin插件文档可以参考:官方文档、某人博客
三、一些不重要的概念
module,chunk 和 bundle 。
其实就是同一份逻辑代码在不同转换场景下的取了三个名字:
我们直接写出来的是 module,
webpack 处理时是 chunk---比如import()、require()动态引入,导致切割的文件名
最后生成浏览器可以直接运行的 bundle()---比如(多)入口文件
如果HtmlWebpackPlugin不指定chunks,那么entry的元素都会被打到html中。
四、自定义loader
webpack只能处理js文件,其他文件无法处理
比如我自定义了配置文件,但是文件的格式不是.js而是 .env
如果直接引入进项目中,直接会报错。提示无法解析
所以需要自定义一个loader,比如起个名字叫 env-loader
我的配置文件 .env
appName=测试app
appVersion=1.0
webpack.config.js增加loader配置
const path = require('path');
module.exports = {
module: {
rules: [{
test: /\.env$/,
use: {
loader: path.resolve(__dirname, './env-loader.js'),
options: {
name: 'env-loader'
}
}
}]
}
};
编写该loader env-loader.js
const fs = require('fs');
const path = require('path');
const loaderUtils = require('loader-utils');
// 读取环境变量的文件把它转化成对象
const envCompiler = (data) => { // flie为文件路径
let d = data.replace(/\r/g, ',').replace(/\n/g, '') // 把换行和回车替换
let arr = d.split(',').map(item => {
return item.split('=')
}) // [ [ 'a', '1' ], [ 'b', '2' ] ]
let obj = {}
arr.forEach(item => {
obj[item[0]] = item[1]
})
return obj //{ a: '1', b: '2' }
// 可以接着处理
/* 像vue-cli3 新版create-react-app 一样规定环境变量的Key必须以(VUE_APP_) (REACT_APP_) 开头 */
}
module.exports = function(source) {
const options = loaderUtils.getOptions(this);
const result = JSON.stringify(envCompiler(source));
return `module.exports = ${result}`;
}
使用
import all from './.env';
console.log(all); // {appName:'测试app',appVersion:'1.0'}
五、自定义插件
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。远比loader强大
自定义插件 my-plugin.js
const fs = require('fs');
const pluginName = 'MyPlugin';
class MyPlugin {
constructor(option = {}) {
this.option = option;
}
apply(compiler) {
// 监听异步compiler的run 钩子
compiler.hooks.emit.tapAsync(pluginName, (compilation, callBack) => {
for (const key in compilation.assets) {
// 干预编译时,产出的代码
compilation.assets[key]._value = 'var dsh=123;' + compilation.assets[key]._value;
}
// 将编译后的文件信息写入
let result = '';
for (const key in compilation.assets) {
result += `${key}=${compilation.assets[key].size()}b \n`;
}
console.log(result);
fs.writeFile('./compiler-file-info.properties', result, { encoding: 'utf8' }, err => { })
callBack();
});
}
}
module.exports = MyPlugin;
使用插件webpack.config.js
const MyPlugin = require('./my-plugin.js')
module.exports = {
plugins: [
new MyPlugin({fileType: 'properties'})
]
};
看看效果
1、编译后的每个js文件,都被插入了 var dsh=123 这端代码,比如mian.js
var dsh = 123; (() => { var e, t, r, o, ...
2、根目录下生成了个配置文件 compiler-file-info.properties
main.js=3051b
532.js=154b
645.js=17050b
index.html=207b
关于插件中的核心tapable
tapable 是一个类似于 Node.js 中的 EventEmitter的库,但更专注于自定义事件的触发和处理。
webpack 通过 tapable 将实现与流程解耦,所有具体实现通过插件的形式存在。
同样的类库还有facebook家的emitter
tapble这个类库可以直接用在前端
import { SyncHook } from 'tapable';
// 创建一个同步钩子对象
const hook = new SyncHook(['name']);
hook.tap('hello', (name) => {
console.log(`hello ${name}`);
});
hook.call('ahonn');
import { AsyncSeriesHook } from 'tapable';
// 创建一个异步回调的钩子对象
const hook = new AsyncSeriesHook(['name']);
hook.tapAsync('hello', (name, cb) => {
setTimeout(() => {
console.log(`hello ${name}`);
cb();
}, 2000);
});
// 2s之后打印
hook.callAsync('ahonn', (name) => {
console.log('done');
});
六、webpack的未来
唯一不变的是变化。
模块加载在未来大概率会被浏览器原生支持,到那时会不会有新的优化方案?
打包这件事,怎么看都像一个补丁,未来的HTTP2会不会彻底使其成为伪需求?
IE8迟早淘汰,Vue/React的市场会无限增大吗,SEO怎么做,Nodejs服务端渲染是答案吗?
这依旧是一个动荡的年代
webpack专题的更多相关文章
- React开发环境搭建(react,babel,webpack webpack-dev-server)
最终效果: 配置webpack 自动编译脚本, 内容更改后浏览器页面自动刷新,提高效率. 主要靠webpack 的watch 功能. npm 全局安装的包: webpack webpack-cli w ...
- vue 专题 vue2.0各大前端移动端ui框架组件展示
Vue 专题 一个数据驱动的组件,为现代化的 Web 界面而生.具有可扩展的数据绑定机制,原生对象即模型,简洁明了的 API 组件化 UI 构建 多个轻量库搭配使用 请访问链接: https://ww ...
- 使用webpack搭建一个多页应用
一.前言 最近需要为公司的活动写8个左右的移动端分享页面,有比较多的页面是公用的,如果用传统的方式来写的话,对于公用的代码抽取,css代码的压缩都是比较麻烦的,所以选择了webpack来搭建一个基本 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- webpack之傻瓜式教程
接触webpack也有挺长一段时间了,公司的项目也是一直用着webpack在打包处理,但前几天在教新人的情况下,遇到了一个问题,那就是:尽管网上的webpack教程满天飞,但是却很难找到一个能让新人快 ...
- 细说前端自动化打包工具--webpack
背景 记得2004年的时候,互联网开发就是做网页,那时也没有前端和后端的区分,有时一个网站就是一些纯静态的html,通过链接组织在一起.用过Dreamweaver的都知道,做网页就像用word编辑文档 ...
- Webstorm+Webpack+echarts构建个性化定制的数据可视化图表&&两个echarts详细教程(柱状图,南丁格尔图)
Webstorm+Webpack+echarts ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(I ...
- 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&引用jquery来实现alert
使用webstorm+webpack构建简单入门级"HelloWorld"的应用&&构建使用jquery来实现 1.首先你自己把webstorm安装完成. 请参考这 ...
- webpack入门教程之Hello webpack(一)
webpack入门教程系列为官网Tutorials的个人译文,旨在给予想要学习webpack的小伙伴一个另外的途径.如有不当之处,请大家指出. 看完入门教程系列后,你将会学习到如下内容: 1.如何安装 ...
- webpack的使用
1.webpack是什么? 打包前端项目的工具(为项目提高逼格的东西). 2.webpack的基本命令 webpack#最基本的启动webpack命令 webpack-w #提供watch方法,实时进 ...
随机推荐
- 开箱即用!推荐一款Python开源项目:DashGo,支持定制改造为测试平台!
大家好,我是狂师. 市面上的开源后台管理系统项目层出不穷,对应所使用到的技术栈也不尽相同. 今天给大家推荐一款开源后台管理系统: DashGo,不仅部署起来非常的简单,而且它是基于Python技术栈实 ...
- SpringBoot 部署:外置依赖包
目录: 1.前言 2.瘦身前的Jar包 3.解决方案 一.前言 SpringBoot部署起来虽然简单,如果服务器部署在公司内网,速度还行,但是如果部署在公网(阿里云等云服务器上),部署起来实在头疼:编 ...
- 静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析
静态批处理[1] 定义 标明为 Static 的静态物件,如果在使用相同材质球的条件下,在Build(项目打包)的时候Unity会自动地提取这些共享材质的静态模型的Vertex buffer和Inde ...
- Clion搭建C++开发环境
1.下载和安装MinGW 1)下载链接:http://www.mingw.org/ 2)选择安装目录,目录尽可能简单(如:D:\MinGW)且不要包含中文和空格 3)添加相关的包 所需的包如下:min ...
- Oracle的listagg函数(多行按顺序合并字符串)(与wm_concat的区别)
场景: 使用wm_concat函数时,会发现无法对其拼接的字符串进行排序 使用listagg函数可实现按排序进行字符串拼接 select myGroup, listagg(myStr, ',') wi ...
- 【问题解决】centos7已经不维护了,如何继续使用yum源?
背景 CentOS 7 已于2024年6月30日停止维护,在停止维护后我们之前配置的国内镜像源大多都是空目录了,即在线国内镜像源不可用,就像下边这样提示: [root@bogon yum.repos. ...
- 游戏开发之Cocos3着色器/shader快速入门
本文为学习笔记,不排除有谬误,但确保都是亲测.另外,傻狗百度收不收录的无所谓,我来博客园就是为了记记笔记. 对于新人,首先明确一点:shader需要通过材质的方式作用于模型上 所以有个很关键的步骤,c ...
- Sentinel——热点规则
目录 热点规则 配置热点规则 API配置热点规则 热点规则 热点规则是用于实现热点参数限流的规则.热点参数限流指的是,在流控规则中指定对某方法参数的 QPS 限流后,当所有对该资源的请求URL中携带有 ...
- Java--利用打印流(PrintStream)输出信息
package demo; import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; /** ...
- <HarmonyOS第一课12>Web组件和WebView #鸿蒙课程##鸿蒙生态#
课程介绍 <HarmonyOS第一课:Web组件和WebView>是一门专为HarmonyOS开发者设计的课程,旨在掌握如何在应用中集成Web内容.课程首先介绍了基于Web技术的Web组件 ...