声明
文章主要用通俗易懂的语言来解释概念和一些重点知识,方便复习,不会太详细说明细节操作。
概念
一个打包器,识别import和export,根据依赖关系把引入的js按顺序打包,最后只输出一个js文件。
为什么要学习webpack(学某样东西总要有个理由)?
在远古时代原始时代开发的时候我们把所有js代码写在script标签里面。
<script>
/*非常长的代码*/
</script>
但慢慢会发现代码非常长,不好维护,于是把一些代码抽出来放在一个js文件里,代码就变成了
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
<script src="d.js"></script>
<script src="e.js"></script>
<script src="f.js"></script>
<script src="g.js"></script>
<script src="h.js"></script>
<script>
/*一些代码*/
</script>
于是诞生了新的问题,一次要请求多个js,增加了服务器负担,而且js的引入要按顺序。
为了解决这些边角问题,让我们只需专注开发,webpack诞生了。
一步一步根据文档学习(建议读官网的,中文文档坑太多了,不会英语的直接点翻译就好)
入门
项目
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- index.html
|- /src
|- index.js
package.json
{
"scripts": {
"build": "webpack"
}
}
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
};
执行命令
$ npx webpack --config webpack.config.js
[webpack-cli] Compilation finished
asset main.js 69.3 KiB [compared for emit] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
cacheable modules 530 KiB
./src/index.js 257 bytes [built] [code generated]/*重点*/
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]/*重点*/
webpack 5.4.0 compiled successfully in 1934 ms
提示
利用npm run build 把index.js里面的js代码输出到dist/main.js。
资产管理
loader是什么?
www.webpackjs.com/concepts/lo…
开发
使用 source map(一般用来调试看源码 )
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
代码分割(重点)
防止重复
假设a,b.js都引入了x模块,打包的时候代码就会重复,因为X模块在a和b都引入了,这个时候使用SplitChunksPlugin就可以把共同的依赖提取到一个现有的条目块或一个全新的块。
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
动态导入
利用import('xxx).then()的异步导入。
function getComponent() {
const element = document.createElement('div');
return import('lodash')
.then(({ default: _ }) => {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
})
.catch((error) => 'An error occurred while loading the component');
}
getComponent().then((component) => {
document.body.appendChild(component);
});
预取/预加载模块
预加载prefetch:会在使用之前,提前加载js文件,等其他资源加载完毕,浏览器空闲了,再偷偷加载资源,兼容性比较差,慎用。
正常加载可以认为是并行加载(同一时间加载多个文件)
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
这将导致将其附加在页面的开头,这将指示浏览器在空闲时间预取login-modal-chunk.js文件。
快取
用webpack打包之后会生成一个dist,把dist部署就可以让客服端(通常是浏览器)访问,但这一步会比较浪费资源,因为这一步客服端会拿新生成的dist下面的js文件和缓存里的js文件作对比,如果文件名不一样,就会重新去获取资源,然而我们有时候只修改一行代码,dist下的js文件名就全部都不一样,客户端就会再一次去请求全部资源,造成了资源浪费。快取要做的就是把更新频率低和更新频率高的代码分隔开,输出多个js文件,这样即使频繁修改代码,也只是更新有改动的小部分js文件,节省了很多资源。
Library
概念:自己写模块函数,然后利用webpack打包输出一个库。
举个栗子:
src/ref.json
[
{
"num": 1,
"word": "One"
},
{
"num": 2,
"word": "Two"
},
{
"num": 3,
"word": "Three"
},
{
"num": 4,
"word": "Four"
},
{
"num": 5,
"word": "Five"
},
{
"num": 0,
"word": "Zero"
}
]
src/index.js
import _ from 'lodash';
import numRef from './ref.json';
export function numToWord(num) {
return _.reduce(numRef, (accum, ref) => {
return ref.num === num ? ref.word : accum;
}, '');
};
export function wordToNum(word) {
return _.reduce(numRef, (accum, ref) => {
return ref.word === word && word.toLowerCase() ? ref.num : accum;
}, -1);
};
webpack.config.js
var path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack-numbers.js',
library: 'webpackNumbers'//暴露变量
},
externals: {
lodash: {
commonjs: 'lodash',
commonjs2: 'lodash',
amd: 'lodash',
root: '_'
}
}
};
摇树
概念:摇树,顾名思义,一个树上长满了绿叶(有用的代码),把灰色的叶子(没用的代码)摇下来。
具体看:webpack.js.org/guides/tree…
将文件标记为无副作用(只是标记还没删除)
就是利用import和export识别出那些不依赖第三方库的模块,然后标记。(被标记的模块要是没被引用到,在后面就可以安全被删除.)
操作:在package.json中
{
"name": "your-project",
"sideEffects": false
}
如果一些代码依赖第三方库,则提供一个数组,数组里面的文件不会被识别
{
"name": "your-project",
"sideEffects": ["./src/some-side-effectful-file.js","*.css"]
}
最后是删除代码:
只需要在webpack中配上这个属性就可以了
mode: 'production'
生产环境构建
开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置.
白话:就是生产环境配置一个webpack,开发环境配置一个webpack,然后生产和开发环境都要用的配置又一个webpack(公共),利用webpack-merge把公共的webpack合并到这两个环境的webpack,最后在npm配两个命令,一个是执行生产环境webapck,另一个是执行开发环境的webapck。具体操作看下面。
项目
webpack-demo
|- package.json
|- webpack.common.js
|- webpack.dev.js
|- webpack.prod.js
|- /dist
|- /src
|- index.js
|- math.js
|- /node_modules
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
webpack.dev.js
const { merge } = require('webpack-merge');//用来合并两个webpack文件
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
});
webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
package.json
{
"name": "development",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"start": "webpack serve --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
源映射(读源码会用到)
可以映射源码,下面是具体配置。
webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
});
懒加载
懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
直接举例子:
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
const print = module.default;
print();
});
打包之后:
...
Asset Size Chunks Chunk Names
print.bundle.js 417 bytes 0 [emitted] print
index.bundle.js 548 kB 1 [emitted] [big] index
index.html 189 bytes [emitted]
...
也就是说只有在点按钮的时候就会发请求加载print.bundle.js并执行print的代码。
说多一个预加载,就这个例子来说预加载就是浏览器在空闲的时候会去加载print,在按钮被点击执行print代码的时候直接就执行不用加载。具体操作:
import(/* webpackChunkName: "print",webpackPrefetch:true */ './print')
在注释里面加webpackPrefetch:true等于告诉浏览器在空闲时间加载print。
以上就是webapck的主要知识了,其他想到再补充。