前言
这是我花了几个星期学习webpack4的学习笔记。内容不够细,因为一些相对比较简单的,就随意带过了。希望文章能给大家带来帮助。如有错误,希望及时指出。例子都在learn-webpack仓库上。如果你从中有所收获的话,希望你能给我的github
点个star
。
library
当你要开发第三方库供别人使用时,就需要用到library
和libraryTarget
这两个配置了。
library
output: {
filename: 'library.js',
library: 'library',
libraryTarget: 'umd'
},
library
: 当配置了这个library
参数后,会把library
这个key
对应的value
即上面代码library
挂载到全局作用域中。html
用script
标签引入,可以通过访问全局变量library
访问到我们自己开发的库。
libraryTarget
:这个默认值为var
。意思就是让library定义的变量挂载到全局作用域下。当然还有浏览器环境的window
,node
环境的global
,umd
等。当设置了window
、global
,library
就会挂载到这两个对象上。当配置了umd
后,你就可以通过import
,require
等方式引入了。
externals
exterals
是开发公共库很重要的一个配置。当你的公共库引入了第三方库的时候,公共库会把该第三方库也打包进你的模块里。当使用者也引入了这个第三方库后,这个时候打包就会又打了一份第三方库进来。
所在在公共模块库中配置如下代码
externals: {
// 前面的lodash是我的库里引入的包名 比如 import _ from 'lodash'
// 后面的lodash是别人业务代码需要注入到他自己模块的lodash 比如 import lodash from 'lodash',注意不能import _ from 'lodash',因为配置项写了lodash 就不能import _
lodash: 'lodash'
},
前面的lodash
是我的库里引入的包名 比如 import _ from 'lodash'
,后面的lodash
是别人业务代码需要注入到他自己模块的lodash
比如 import lodash from 'lodash'
,注意不能import _ from 'lodash'
,因为配置项写了lodash
就不能import _
。
本人做了个试验,当自己开发的包不引入lodash
,业务代码中也不引入lodash
,那么打包业务代码的时候,webpack
会把lodash
打进你业务代码包里。
当然externals
,配置还有多种写法,如下
externals: {
lodash: {
commonjs: 'lodash',
commonjs2: 'lodash',
amd: 'lodash',
root: '_'
}
}
externals: ['lodash', 'jquery']
externals: 'lodash'
具体请参考官网externals
发布自己开发的npm包
学了上面的配置后,就需要学习下如何将自己的包发布到npm
仓库上了。
-
package.json
的入口要改成dist
目录下的js文件如:"main": "./dist/library.js"
- 注册npm账号。npm会发送一份邮件到你的邮箱上,点击下里面的链接进行激活。
- 命令行输入
npm login
进行登录,或者npm adduser
添加账号 npm publish
当出现如下提示代表发布成功
// 当出现类似如下代码时,表示你已经发布成功
➜ library git:(master) ✗ npm publish
+ atie-first-module-library@1.0.0
遇到的问题:
当你遇到npm ERR! you must verify your email before publishing a new package
说明你还没有激活你的邮箱,去邮箱里点击下链接激活下就ok了
当你已经登录了提醒npm ERR! 404 unauthorized Login first
,这个时候你就要注意下你的npm
源了,看看是否设置了淘宝源等。记得设置回来npm config set registry https://registry.npmjs.org/
PWA
http-server
workbox-webpack-plugin
相信很多朋友都有耳闻过PWA
这门技术,PWA
是Progressive Web App
的英文缩写, 翻译过来就是渐进式增强WEB应用, 是Google 在2016年提出的概念,2017年落地的web技术。目的就是在移动端利用提供的标准化框架,在网页应用中实现和原生应用相近的用户体验的渐进式网页应用。
优点:
- 可靠 即使在不稳定的网络环境下,也能瞬间加载并展现
- 快 快速响应,并且 动画平滑流畅
应用场景:
当你访问正常运行的服务器页面的时候,页面正常加载。可当你服务器挂了的时候,页面就无法正常加载了。
这个时候就需要使用到pwa技术了。
这里我编写最简单的代码重现下场景:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: './index.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
}
// index.js
console.log('this is outer console')
// package.json
{
"name": "PWA",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "http-server ./dist"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^2.0.1",
"html-webpack-plugin": "^3.2.0",
"http-server": "^0.11.1",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.1",
}
}
执行下npm run build
.
├── bundle.js
└── index.html
为了模拟服务器环境,我们安装下http-server
npm i http-server -D
配置下package.json
,"start": "http-server ./dist"
执行npm run start
来启动dist文件夹下的页面
这个时候控制台会正常打印出'this is outer console'
当我们断开http-server
服务后,在访问该页面时,页面就报404了
这个时候就需要使用到pwa技术了
使用步骤:
安装: npm i workbox-webpack-plugin -D
webpack配置文件配置:
// webpack.config.js
const {GenerateSW} = require('workbox-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: './index.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin(),
new GenerateSW({
skipWaiting: true, // 强制等待中的 Service Worker 被激活
clientsClaim: true // Service Worker 被激活后使其立即获得页面控制权
})
]
}
这里我们写一个最简单的业务代码,在注册完pwa之后打印下内容:
// index.js
console.log('this is outer console')
// 进行 service-wroker 注册
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('./service-worker.js')
.then(registration => {
console.log('====== this is inner console ======')
console.log('SW registered: ', registration);
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
执行下打包命令:npm run build
.
├── bundle.js
├── index.html
├── precache-manifest.e21ef01e9492a8310f54438fcd8b1aad.js
└── service-worker.js
打包之后会生成个service-worker.js
与precache-manifest.e21ef01e9492a8310f54438fcd8b1aad.js
接下来再重启下http-server
服务:npm run start
页面将会打印出
this is outer console
====== this is inner console ======
...
然后我们再断开http-server
服务
刷新下页面,竟然打印出了相同的代码。说明pwa离线缓存成功。
typescript
使用webpack打包ts文件,就需要安装ts-loader
安装:npm i ts-loader typescript -D
webpack.config.js
文件中添加解析typescript
代码的loader
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
entry: './src/index.ts',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: /node_modules/
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
}
配置了webpack.config.js
还不行,还得在根目录文件下新增个.tsconfig.json
文件
{
"compilerOptions": {
"outDir": "./dist/", // 默认解析后的文件输出位置
"noImplicitAny": true, // 存在隐式 any 时抛错
"module": "es6", // 表示这是一个es6模块机制
"target": "es5", // 表示要讲ts代码转成es5代码
"allowJs": true // 表示允许引入js文件。TS 文件指拓展名为 .ts、.tsx 或 .d.ts 的文件。如果开启了 allowJs 选项,那 .js 和 .jsx 文件也属于 TS 文件
}
}
新建index.ts
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function() {
alert(greeter.greet());
}
document.body.appendChild(button);
执行打包命令,访问打包后的页面,页面正常执行。
当需要使用lodash
等库时,
需安装:npm i @types/lodash -D
修改页面代码 引入 lodash
import * as _ from 'lodash'
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function() {
alert(_.join(['lodash', greeter.greet()], '-'));
}
document.body.appendChild(button);
提醒:ts使用的包,可通过https://microsoft.github.io/TypeSearch
这个网址去查对应的包使用指南
使用WebpackDevServer
实现请求转发
当我们工作本地开发某一个需求的时候,需要将这块需求的请求地址转发到某个后端同事的本地服务器或某个服务器上,就需要用到代理。然后其他页面的请求都走测试环境的请求。那么我们该怎样拦截某个请求,并将其转发到我们想要转发的接口上呢?
这个时候就需要用到webpack-dev-server
主要看devServer
配置:
// webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "development",
entry: './index.js',
output: {
filename: 'bundle.js'
},
devServer: {
contentBase: './dist',
open: true,
hot: true
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
// package.json
scripts: {
"server": "webpack-dev-server"
}
// index.js
import axios from 'axios'
const div = document.createElement('div')
div.innerHTML = 'hahahha'
div.addEventListener('click', () => {
alert('hahah')
axios.get('/list').then(res => {
console.log(res)
})
})
document.body.appendChild(div)
在写一个本地启动的服务端代码
const express = require('express')
const app = express()
app.get('/api/list', (req, res) => {
res.json({
success: true
})
})
app.listen(8888, () => {
console.log('listening localhost:8888')
})
执行npm run server
命令,浏览器会自动打开页面。点击div后,会发起请求。
浏览器提示http://localhost:8080/api/list 404 (Not Found)
,表示该接口不存在。
因为我们webpack
启动静态资源服务器默认端口为8080,所以他求会直接请求到8080的/api/list接口。所以会提示找不到该接口。
为了解决这个问题,我们就需要将该请求从8080端口代理到8888端口(也就是我们自己本地启动的服务)
配置webpack.config.js
这里我只展示devServer
代码
// webpack.config.js
devServer: {
contentBase: './dist',
open: true,
hot: true,
proxy: {
'/api': {
target: 'http://localhost:8888'
}
}
},
配置devServer
的proxy
字段的参数,将请求/api
开头的请求都转发到http://localhost:8888
,
通过这个方法可以解决一开始提到的本地开发的时候,只想把部分接口转发到某台部署新需求的服务器上。比如当你这个项目请求很多,不同接口部署在不同的端口或者不同的服务器上。那么就可以通过配置第一个路径,来区分不同的模块。并转发到不同的服务上。如:
// webpack.config.js
devServer: {
contentBase: './dist',
open: true,
hot: true,
proxy: {
'/module1': {
target: 'http://localhost:8887'
},
'/module2': {
target: 'http://localhost:8888'
},
'/module3': {
target: 'http://localhost:8889'
}
}
},
当你要代理到某个https的接口上,就需要设置secure: false
// webpack.config.js
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}
}
target: '', // 代理的目标地址
secure: false, // 请求https的需要设置
changeOrigin: true, // 跨域的时候需要设置
headers: {
host: 'http://www.baidu.com', //修改请求域名
cookie: ''
}
...
其他关于devServer
的配置详见devServer
WebpackDevServer解决单页面路由404问题
相信大家都是开发过vue或者react单页面带路由的应用。这里就忽略业务代码,介绍下devServer
的historyApiFallback
参数
devServer: {
historyApiFallback: true, // 当设置为true时,切换路由就不会出现路由404的问题了
}
eslint
安装eslint
: npm i eslint -D
目录下新建.eslintrc.json
文件。
environment
: 指定脚本的运行环境
globals
: 脚本在执行期间访问的额外全局变量。
rules
: 启动的规则及其各自的错误级别。
解析器选项
: 解析器选项
编辑你的eslint
的规则
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"semi": 2
}
}
vscode
安装eslint
插件。
配置下webpack.config.js
配置。
...
devServer: {
overlay: true,
contentBase: './dist',
hot: true,
open: true
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: ['eslint-loader']
}]
}
...
eslint-loader
是用于检查js
代码是否符合eslint
规则。
这里devServer
中的overlay
的作用是,当你eslint报错的时候,页面会有个报错蒙层。这样就不局限于编辑器(vscode)的报错提醒了。
如果js代码使用了多个loader,那么eslint-loader一定要写在最右边。如果不写在最后一个的话,需在里面添加enforce: "pre"
,这样不管写在哪个位置都会优先使用eslint-loader
校验下代码规范。
{
loader: 'eslint-loader',
options: {
enforce: "pre",
}
}
提升webpack
打包速度的方法
1. 跟上技术的迭代
- 升级
webpack
版本node
版本npm
等版本
2. 尽可能少的模块上应用loader
-
include
exclude
3. 尽可能少的使用plugin
4. resolve
resolve: {
extensions: ['.js'],
alias: {
'src': path.resolve(__dirname, '../src')
}
}
extensions
: 可以让你import模块的时候不写格式,当你不写格式的时候,webpack会默认通过extensions中的格式去相应的文件夹中找
alias
:当你import
的路径很长的时候,最好使用别名,能简化你的路径。
比如:import index.js from '../src/a/b/c/index.js'
设置别名:
resolve: {
alias: {
'@c': path.resolve(__dirname, '../src/a/b/c')
}
}
这样你的import
导入代码就可以改成import index.js from '@c/index.js'
5. dllPlugin
我们先记录下不使用dll
打包时间787ms
:
Time: 787ms
Built at: 2019-05-04 18:32:29
Asset Size Chunks Chunk Names
bundle.js 861 KiB main [emitted] main
index.html 396 bytes [emitted]
接下来我们就尝试使用dll
技术
我们先配置一个用于打包dll
文件的webpack
配置文件,生成打包后的js
文件与描述动态链接库的manifest.json
// webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
vendor: ['jquery', 'lodash'] // 要打包进vendor的第三方库
},
output: {
filename: '[name].dll.js', // 打包后的文件名
path: path.resolve(__dirname, './dll'), // 打包后存储的位置
library: '[name]_[hash]' // 挂载到全局变量的变量名,这里要注意 这里的library一定要与DllPlugin中的name一致
},
plugins: [
new webpack.DllPlugin({ // 用于打包出一个个单独的动态链接库文件
name: '[name]_[hash]', // 引用output打包出的模块变量名,切记这里必须与output.library一致
path: path.join(__dirname, './dll', '[name].manifest.json') // 描述动态链接库的 manifest.json 文件输出时的文件名称
})
]
}
重点:这里引入的DllPlugin插件,该插件将生成一个manifest.json文件,该文件供webpack.config.js中加入的DllReferencePlugin使用,使我们所编写的源文件能正确地访问到我们所需要的静态资源(运行时依赖包)。
配置下package.json
文件的scripts
: "build:dll": "webpack --config webpack.dll.config.js"
执行下 npm run build:dll
Time: 548ms
Built at: 2019-05-04 18:54:09
Asset Size Chunks Chunk Names
vendor.dll.js 157 KiB 0 [emitted] vendor
除了打包出dll
文件之外,还得再主webpack
配置文件中引入。这里就需要使用到DllReferencePlugin
。具体配置如下:
// webpack.config.js
new webpack.DllReferencePlugin({
manifest: require('./dll/vendor.manifest.json')
}),
这里的manifest
:需要配置的是你dllPlugin
打包出来的manifest.json
文件。让主webpack
配置文件通过这个
描述动态链接库manifest.json
文件,让js
导入该模块的时候,直接引用dll
文件夹中打包好的模块。
看似都配置好了,接下来执行下命令 npm run build
使用dll
打包后时间:
Time: 471ms
Built at: 2019-05-04 18:19:49
Asset Size Chunks Chunk Names
bundle.js 6.43 KiB main [emitted] main
index.html 182 bytes [emitted]
直接从最开始的787ms
降低到471ms
,当你抽离的第三方模块越多,这个效果就越明显。
浏览器跑下html
页面,会报错
Uncaught ReferenceError: vendor_e406fbc5b0a0acb4f4e9 is not defined
这是因为index.html
还需要通过script
标签引入这个dll
打包出来的js
文件
我们如果每次自己手动引入的话会比较麻烦,如果dll
文件非常多的话,就难以想象了。
这个时候就需要借助add-asset-html-webpack-plugin
这个包了。
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, './dll/vendor.dll.js')
})
通过这包,webpack
会将dll
打包出来的js
文件通过script
标签引入到index.html
文件中
这个时候你在npm run build
,访问下页面就正常了
6. 控制包文件大小
tree-shaking 等
7. thread-loader parallel-webpack happypack等多进程打包
8. 合理使用sourceMap
9. 结合stats分析打包结果
借助线上或者本地打包分析工具
10. 开发环境内存编译
开发环境的时候不会生成dist文件夹,会直接从内存中读取,因为内存读取比硬盘读取快
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。