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方法,实时进 ...
随机推荐
- 时区转换工具+PWA离线网页
时区转换工具+PWA离线网页 一.时区转换工具对比 工具 说明 Date 原生 JS API,有限的时区支持,无法指定时区,仅使用本地时区. Intl.DateTimeFormat 原生格式化显示,可 ...
- 【Linux】编译用于exynos4412(ARM)的Linux-3.14内核
[Linux]编译用于exynos4412(ARM)的Linux-3.14内核 零.准备 1.下载 Linux-3.14内核源代码 下载页面:https://www.kernel.org/pub/li ...
- web自动化:Javascript操作页面元素
某些特殊情况下,使用selenium的api无法操作页面元素,可以通过js来完成 一.Js定位 js操作中的webelement通过console控制台来进行js定位: WebElement webe ...
- 使用 Go 构建一个最小的 API 应用
最近有项目要使用 Go 开发,作为一个. NET Core 选手,准备先撸一个包含 CRUD 的最小 MVP 项目练手. 要创建一个 TODO 应用,会创建下面这些接口: API Descriptio ...
- Rust实战系列-Rust介绍
" 学习资料:rust in action[1] 1. Rust 安装 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | ...
- SaltStack 远程命令执行中文乱码问题
问题 我在一台服务器上写了一个简单的 Python 脚本 haha.py,内容如下: [root@localhost ~]# cat haha.py print("你好") 当我在 ...
- OneNote Embedded 文件滥用检测
本文分享自天翼云开发者社区<OneNote Embedded 文件滥用检测>,作者:Icecream 攻击技术 在这些网络钓鱼活动中被滥用的OneNote功能是在图片后面隐藏嵌入式文件,诱 ...
- EFCore Study(3)——“一”对多关系的设定和插入、查找级联操作
一.建立文章.评论类 /// <summary> /// 文章 /// </summary> public class Artitle { public int Id { ge ...
- Nacos简介—3.Nacos的配置简介
大纲 1.Nacos生产集群Web端口与数据库配置 2.Nacos生产集群的Distro协议核心参数 3.Nacos打通CMDB实现跨机房的就近访问 4.Nacos基于SPI动态扩展机制来获取CMDB ...
- 4G模块——大夏龙雀DX-CT511-A使用记录
4G模块--大夏龙雀DX-CT511-A使用记录 加回车换行 115200波特率 重启: AT+RESET 6.关闭HTTP服务: AT$HTTPCLOSE 关闭网路 AT+NETCLOSE 1.TC ...