webpack
- [webpack中文网]: https://doc.webpack-china.org/
webpack简介
webpak是什么
Webpack是一个模块打包器(bundler)。
在Webpack看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理
它将根据模块的依赖关系进行静态分析,生成对应的静态资源
webpack五个核心概念
Entry 入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
Output 输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名
Loader Loader 让 webpack 能够去处理那些非 JavaScript 文件 (webpack 自身只理解JavaScript)
Plugins 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
Mode 模式(Mode)指示 webpack 使用相应模式的配置。
–Mode=development // 开发环境
–mode=production // 生产环境
快速上手
初始化设置
全局安装 npm install webpack webpack-cli -g
项目安装 npm install webpack webpack-cli -D
创建文件
|- build----------构建生成的文件所在的文件夹
|- src------------源码文件夹
|- js---------------js源文件夹
|- css--------------css源文件夹
|- index.js-----入口文件
|- webpack.config.js-----webpack配置文件
|- package.json---项目包配置文件
webpack.config.js
// resolve用来拼接绝对路径的方法
const { resolve } = require('path');
module.exports = {
// webpack配置
// 入口起点
entry: './src/index.js',
// 输出
output: {
// 输出文件名
filename: 'built.js',
// 输出路径
// __dirname nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, 'build')
},
// loader的配置
module: {
rules: [
// 详细loader配置
// 不同文件必须配置不同loader处理
{ // 排除某些文件(exclude)
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader进行处理
use: [
// use数组中loader执行顺序:从右到左,从下到上 依次执行
// 创建style标签,将js中的样式资源插入进行,添加到head中生效
'style-loader',
// 将css文件变成commonjs模块加载js中,里面内容是样式字符串
'css-loader'
]
}
]
},
// plugins的配置
plugins: [
// 详细plugins的配置
],
// 模式
mode: 'development', // 开发模式
// mode: 'production'
}
// 运行指令: webpack
运行指令
开发环境
webpack webpack src/js/index.js -o build/js/built.js --mode=development
// 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成浏览器能识别的语法。
生产环境
webpack src/js/index.js -o build/js/built.js --mode=production
// 功能:在开发配置功能上多一个功能,压缩代码。
结论
webpack 能够编译打包 js 和 json 文件。
能将 es6 的模块化语法转换成浏览器能识别的语法。能压缩代码。
问题
不能编译打包 css、img 等文件。不能将 js 的 es6 基本语法转化为 es5 以下语法。
插件plugins
第一步下载插件 :
npm install --save-dev html-webpack-plugin
第二步引入插件:
const htmlWebpackPlugins = require(‘html-webpack-plugin’)
第三步调用插件
plugins: [
// plugins配置
new htmlWebpackPlugins ({
template:’./src/index.html’
})
]
常用loader
css/less
css-loader
// 将css文件变成commonjs模块加载js中,里面内容是样式字符串
style-loader
// 创建style标签,将js中的样式资源插入进行,添加到head中生效
less-loader
// 编译less 还需要下载less模块
打包图片资源
打包css中的图片资源
下载loader包
npm install --save-dev url-loader file-loader
loader设置
{
// css加载图片资源配置
test: /\.(jpg|png|gif)$/,
use: [{
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base6 处理
limit: 8 * 1024,
// 关闭url-loader的es6模块化,使用commonjs解析
sModule: false,
// 给图片进行重命名
// [hash:10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]'
}
}]
}
打包html中的图片资源
// 下载loader包
npm install --save-dev html-loader
// loader设置
test:\/.html$\,
use:[{loader: 'html-loader'}]
其他资源打包
在loader中匹配其他资源
exclude:/\.(html|js|css|...)/ // 取反
loader: 'file-loader',
devserver
devserver是用来自动化(自动编译,自动打开浏览器,自动刷新)
每次执行只会在内存中打包,不会有任何输出结果
安装:
npm install webpack-dev-server
配置
devServer:{
// 项目构建后的路径
contentBase: resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 自动打开浏览器
open:true
}
运行指令
npx webpack-dev-server
css集成兼容压缩处理
提取css成单独文件
下载插件
npm install --save-dev mini-css-extract-plugin
// 引入插件
MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 在loader中配置插件的loader方法('style-loader'可以去掉)
use: [
miniCssExtractPlugin.loader,
'css-loader'
]
// plugin配置
new MiniCssExtractPlugin({
filename: 'css/built.css'
})
css兼容处理
安装loader
npm install --save-dev postcss-loader postcss-preset-env
// 设置nodejs环境变量 (这个变量表示css兼容以开发环境处理)
process.env.NODE_ENV = 'development';
// loader设置
{
loader: 'postcss-loader',
options: { ident: 'postcss', plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]}
}
// package.json设置css兼容的规范选项
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
css压缩
安装插件
npm install --save-dev optimize-css-assets-webpack-plugin
引入插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin' )
插件调用
plugins:[
new OptimizeCssAssetsWebpackPlugin()
]
js处理
js语法检测
下载安装包
npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
// loader配置
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复 eslint 的错误
fix: true
}
// package.json配置
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
js兼容性处理
下载安装包
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
// loader配置
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets:[
['@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
js与html压缩
// 生产环境下会自动压缩 js 代码
mode: 'production'
// 压缩html代码使用 HtmlWebpackPlugin插件
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩 html 代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
webpack 优化配置
HMR
HMR(模块热替换): 当一个模块发生变化,只会打包这一个模块(而不是打包所有代码) 极大提升构建速度
安装:
npm install webpack-dev-server
html文件默认不使用HMR功能
样式文件处理用style-loader可以使用HMR功能
js代码需要修改代码添加HMR功能
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启 HMR 功能
hot: true
}
// 监听hot
if(module.hot){
// 一旦module.hot为true,说明开启了HRM功能
module.hot.accept('./add.js',function(){
//方法会监听add.js文件的变化,一旦发生变化,其他模块不会重新打包构建
// 而是执行后面的回调函数
add();
})
}
source-map
source-map: 一种提供源代码到构建后代码映射技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)
source-map无需安装下载直接再config.js中调用
devtool: 'source-map'
oneof
oneof: 优化成产打包速度,在oneof内的文件只匹配一次loader
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
use: [{
loader: 'eslint-loader',
options: {
fix: true
}
}]
},
{
oneof:[...] // ...moreLoader
}
]
缓存
bebal缓存
// bebal-loader
options:{
// 开启缓存
cacheDirectory: true
}
文件资源缓存
hash:每次webpack构建是会生成一个唯一的hash值
chunkhash:根据chunk成成的hash值,如果来源于同一个chunk那么hash值就一样
contenthash:根据文件内容成产的hash,不同文件的hash值一定不一样
tree shaking
tree shaking 去除无用代码
前提 1.使用ES6模块化 2.开启production环境
有可能出现去除css的情况
// 在package中配置
{
"sideEffects":["*.css","*.less"]
}
code split
split 将文件拆分成多个文件
方法1 多入口文文件
entry:{
index:'./src/js/index.js',
test:'./src/hs/test.js'
},
outout:{
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
}
// config.js配置
// 会将公共的文件单独打包一个chunk
optimization: {
splitChunks: {
chunks: 'all'
}
}
方法2 通过js代码,让某个文件单独打成一个chunk
// import动态导入语法,能将文件单独打包
import(/*webpackChunkName:'test'*/'.test')
.then(({add})=>{
add(1,2)
})
.catch(()=>{
console.log('err')
})
懒加载和预加载
懒加载 当文件需要使用的时候才加载
// import作为callback
import(/*webpackChunkName:'test'*/'.test').then()
预加载 等其他资源加载完毕后才加载
import(/*webpackChunkName:'test,webpackPrefetch:true '*/'.test').then()
多进程打包
下载安装
npm install --save-dev thread-loader
/*
开启多进程打包。
进程启动大概为 600ms,进程通信也有开销。
只有工作消耗时间比较长,才需要多进程打包
*/
use: [
{
loader: 'thread-loader',
options: {
workers: 2 // 进程 2 个
}
},{
// ...more
}
]
externals
externals可以拒绝打包通过cdn引用的库
// config.js
externals: {
// 拒绝 jQuery 被打包进来
jquery: 'jQuery'
}
dll
使用dll技术,对某些库(jquery,vue)进行单独打包
// 创建新的文件 webpack.dll.js
// webpack.dll.js 配置
// resolve用来拼接绝对路径的方法
const { resolve } = require('path');
// webpack自带插件
const webpack = require('webpack')
const dll = {
entry:{
// 最终打包生成的[name]
jquery:['jquery']
},
output:{
filename:'[name].js',
path: resolve(__dirname, 'dll'),
library:'[name]_[hash]' // 打包库向外暴露的内容叫什么名字
},
plugins:[
// 打包生成一个manifest.json 提供和jquery映射
new webpack.DllPlugin({
// 映射库暴露的内容名称
name:'[name]_[hash]',
// 输出文件路径
path:resolve(__dirname,'dll/manifest.json')
})
],
mode:'production'
}
// 运行文件
webpack --config webpack.dll.js
完成上面的步骤后回到config.js
// config.js
// 下载一个新的插件
npm i add-asset-html-webpack-plugin -D
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
// webpack自带插件
const webpack = require('webpack')
plugins:[
// 告诉webpack哪些库不参与打包
new webpack.DllReferencePlugin({
manifest:resolve(__dirname,'dll/manifest.json')
}),
// 将某个文件打包输出去,并在 html 中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
]
webpack 配置详情
output
output: {
// 文件名称(指定名称+目录)
filename: 'js/[name].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js', // 非入口 chunk 的名称
// library: '[name]', // 整个库向外暴露的变量名
// libraryTarget: 'window' // 变量名添加到哪个上 browser
// libraryTarget: 'global' // 变量名添加到哪个上 node
// libraryTarget: 'commonjs'
}
module
module: {
rules: [
// loader 的配置
{
test: /\.css$/,
// 多个 loader 用 use
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
// 排除 node_modules 下的 js 文件
exclude: /node_modules/,
// 只检查 src 下的 js 文件
include: resolve(__dirname, 'src'),
// 优先执行
enforce: 'pre',
// 延后执行
// enforce: 'post',
// 单个 loader 用 loader
loader: 'eslint-loader',
options: {}
},
{
// 以下配置只会生效一个
oneOf: []
}
]
}
resolve
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块是去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
devserver
devServer: {
// 运行代码的目录
contentBase: resolve(__dirname, 'build'),
// 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动 gzip 压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启 HMR 功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示~
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦 devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器
(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}
optimization
optimization: {
splitChunks: {
chunks: 'all'
},
// 将当前模块的记录其他模块的 hash 单独打包为一个文件 runtime
// 解决:修改 a 文件导致 b 文件的 contenthash 变化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生产环境的压缩方案:js 和 css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动 source-map
sourceMap: true
})
]
}
问题
开发中所有样式文件合并在一个文件还是分离多个文件?
如何管理多个html文件
生产环境最终应该提交什么样的代码