赞
踩
在三大框架潮流的推动下,大大小小的SPA
单页面应用层出不穷,工程化 / 模块化 / 自动化 渐渐成为开发的核心思想,但是他们都有一个特点:
源代码无法在浏览器里直接运行,必需通过编译才行
因此也带来了很多构建工具的兴起;诸如具有代表性的 Gulp
、Grunt
、webpack
等等
今天,我们具体介绍 webpack 4.X
,webpack
也从V1过渡到V4,不久之后发布V5版本( lz学不动了!!!)
建议node
版本在 5.0以上
npm init // 可选属性创建 | npm init -y // 自动创建
npm i webpack -D
npm i webpack-cli -D
安装完,执行 webpack -v
| webpack-cli -v
,验证是否安装成功
这里我们基础配置index.html
用于测试我们的打包后的效果,webpack.config.js
用来配置编译需求,src
项目源码,package.json
项目基本配置
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack 4.X</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/build.js"></script>
</body>
</html>
webpack.config.js
webpack
采用CommonJS
的规范,moudle.exports
导出
const path = require('path') // node提供的path工具,用来做路径的拼接、转换等
const fs = require('fs') // node提供的fs文件系统,用来操作文件、文件夹等
module.exports = {
mode: 'development ',
entry: '',
output: {},
module: {},
plugins: [],
devServer: {}
}
src
– index.js
入口文件
– home.js
测试多入口文件
– assets
静态资源
:::-- img
图片
:::-- font
字体文件
:::-- media
音视频文件
:::-- css
样式文件
package.json
这里我们使用 npm run test 命令启动 webpack 编译
...
"scripts": {
"test": "webpack -p --progress --color --config webpack.config.js"
},
webpack
执行命令之后可以添加一些参数,下面是参数列表:
参数名 | 作用 |
---|---|
webpack --config XXX.js | 使用另一份配置文件来打包 |
webpack --watch | 监听变动并自动打包 |
webpack -p | 压缩混淆脚本,这个非常非常重要! |
webpack -d | 生成map映射文件,告知哪些模块被最终打包到哪里 |
webpack --progress | 显示进度条 |
webpack --color | 添加颜色 |
webpack
从 编译 -> 输出 -> 运行 等等都是由很多配置内容完成
webpack
的核心配置:
mode
:模式----4.X新增,配置当前环境
entry
:入口----要打包的文件
output
:出口----配置编译完成目录
module
:模块----浏览器不识别的文件
plugins
:插件----hook函数辅助开发,提高开发效率
devServer
:服务器----webpack提供的本地服务器
mode
作用:代表当前的环境:development
代表开发模式,production
(默认)代表生产模式
区别:mode
entry
作用:将要打包的入口文件
可选类型:String | Array | Object
// String
entry: './src/index.js',
// Array
entry: ['./src/index.js', './src/home.js'],
// Object
entry: {
'path/js': './src/index.js',
home: './src/home.js'
}
output
属性名 | 作用 |
---|---|
filename | 向硬盘写入编译文件的名称 |
path | 向硬盘写入编译文件的绝对路径 |
publicPath | 指定资源文件引用的目录 |
①:当entry
为String
时,编译入口文件并输出
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist'),
}
}
②:当entry
为Array
时,编译入口文件合并输出
const path = require('path')
module.exports = {
mode: 'development',
entry: ['./src/index.js', './src/home.js'],
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist'),
}
}
③:当entry
为Object
时,编译多个入口文件并输出
entry
入口指定多个key
,value
时,output
的 filename
要注意,不可写死entry
入口的 key
可以指定为路径(多页面),编译输出也是路径entry
入口的 value
可以指定数组形式,编译合并输出const path = require('path')
module.exports = {
mode: 'development',
entry: {
vendor: ['./src/index.js', './src/home.js'], // 合并打包为输出为vender.js
'path/index': './src/index.js', // 路径模式打包
home: './src/home.js' // 打包输出为home.js
},
output: {
filename: '[name].[hash:7].[ext]', // path原名/路径指定hash输出
path: path.resolve(__dirname, 'dist'),
}
}
module
webpack
中任何一个东西都称为模块(css/img/video/woff
…),而且只识别 js
。loader
编译js
为供浏览器识别install
npm i style-loader -D — 把处理完的 css 插入到 style 标签里
npm i css-loader -D — 处理 css
npm i postcss-loader -D — 处理不用的浏览器厂商前缀
npm i autoprefixer -D — 搭配 postcss-loader 个性化配置
浏览器 | 内核 | 前缀 |
---|---|---|
Chrome、Safari | webkit | -webkit- |
Firefox | gecko | -moz- |
Opera | presto | -o- |
IE | trident | -ms- |
… | … | … |
base
:
// index.css
section {
display: flex;
align-items: center;
transition:all 1s;
}
// index.js
import './asset/css/index.css'
document.getElementById('root').innerHTML = `<section>Webpack</section>`
webpack.config.js
:
const path = require('path') module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'build.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(css)$/, use: [ 'style-loader', 'css-loader', 'postcss-loader' ] }, ] }
loader
的使用顺序:
从后向前
.css
文件利用 postcss-loader
过滤
css-loader
处理 css
为浏览器所能识别的文件
style-loader
插入到浏览器 style
标签中
less
、stylus
、sass
,借住各自的 loader
追加到 postcss-loader
即可
这里编译会报错:No PostCSS Config found in: W:\webpack\src\asset\css
,缺少PostCSS config
配置文件,这里我们搭配 autoprefixer
进行配置
根目录新建 postcss.config.js
module.exports = {
"plugins": {
"autoprefixer": {}
}
}
package.json
追加 browserslist
...
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
...
:::编译完成
:::打开 index.html
,我们可以看到一切OK!
上面的代码我们在 Chrome
无压力,我们在毒瘤 IE11
中 运行试试效果
果不其然,报错了,因为我们在项目中使用到 ES6
提供的 模板字符串
,一些浏览器在ES6
发布后没有做出相应更新,出现不识别的情况
随之出现babel-loader
:可以用来处理ES6语法,将其编译为浏览器可以执行的js
语法
接下来,我们安装使用:
babel-preset-es2015
落伍了,官方推荐使用babel-preset-env
install
这里的 babel-loader 与 babel/core 版本要对应
npm i babel-loader @babel/core @babel/preset-env -D
webpack.config.js
...
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
include: path.join(__dirname, 'src'), // 具体到 src 更快的搜索速度
exclude: '/node_modules/', // 排除node_modules,第三方代码已经处理,不需要二次处理
presets: '@babel/preset-env' // 将ES6 解析 为ES5
}
},
]
...
:::编译完成
:::然后我们在毒瘤 IE
中 运行,IE11
、IE10
、IE9
都没有问题
因为在IE8
中,Object.defineProperty
没有实现,并且不让别人访问或修改!!!
install
npm i url-loader file-loader -D
base
:
// index.css section { display: flex; align-items: center; transition: all 1s; width: 100vw;height: 100vh; background-image: url(../img/background.jpg) // 引入背景图 } // index.js import './asset/css/index.css' import icon from './asset/img/icon.png' // 创建img对象 let img = new Image() img.width = 200 img.height = 200 img.src = icon // 创建section对象 let section = document.getElementById('section') section.appendChild(img) section.innerHTML += '<img src="./asset/img/bj.jpg">' // 插入 document.getElementById('root').appendChild(section)
:::这里配置如果文件大于 10K file-loader
原路径输出,否则利用url-loader
打包为base64
,从而减少http请求,但是会变大!
`webpack.config.js
... rules: [ { test: /\.(jpe?g|png|gif|svg)$/, use: { loader: 'url-loader', options: { name: '[name].[hash:8].[ext]', // 以原图片名输出 outputPath: 'images/', // 输出路径 publicPath:'./dist/images', // 公共路径 预防404 limit: 10240 // 超过10K打包为图片,反之打包为base64 } } }, ] ...
我们看到效果完全一致,icon
打包为base64
,background.jpg
原路径输出
:::但是,有一个问题,我们看到页面无法显示图片,这张图片,我们是直接页面 img
src
引入的
这里因为html
中直接使用img
标签src
加载图片的话,因为没有被依赖,图片将不会被打包
官方文档,提供loader
解决类似问题 html-withimg-loader
npm i html-withimg-loader -D
wepback.config.js
追加插件
{
test: /\.(htm|html)$/i,
loader: 'html-withimg-loader'
}
webpack.config.js
... rules: [ { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/, use: { loader: 'url-loader', options: { name: '[name].[hash:8].[ext]', // 以原文件名输出 outputPath: 'media/', // 输出目录 publicPath:'./dist/media', // 公共路径 预防404 limit: 102400 } } }, ] ...
webpack.config.js
... rules: [ { test: /\.(woff2|eot|ttf|otf)$/, use: { loader: 'url-loader', options: { name: '[name].[hash:8].[ext]', // 以原文件名输出 outputPath: 'font/', publicPath:'./dist/font', limit: 10240 } } } ] ...
plugins
webpack
的功能,Hook Funtion
html
,动态hash
引入 JS
CSS
等安装:
npm i html-webpack-plugin -D
使用:
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入 ... plugins: [ new HtmlWebpackPlugin({ title: 'Webpack 4.X', // 文件标题 filename: 'index.html', // 文件名 template: path.resolve(__dirname, 'index.html'), // 依赖模板 inject: true, // js放置位置: true -- body 底部 | head -- head标签 | false -- 不加载js hash: true, // 添加hash minify: { collapseWhitespace: true, // 移除空格 removeAttributeQuotes: true, // 移除引号 removeComments: true // 移除注释 } }) ] ...
index.html
模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- EJS 语法引入title,配置 minify :removeComments 会移除注释 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
:::构建成功,测试效果 :::
安装:
npm i clean-webpack-plugin -D
使用:
webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // webpack 4.0 解构赋值,源码 export { CleanWebpackPlugin }
...
plugins: [
new CleanWebpackPlugin(['dist']) // 可配置绝对路径
new CleanWebpackPlugin({
dry:true, // true -- 仅仅报告要删除的文件并不删除 fale -- 全部删除
cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, 'test')] // 指定绝对路径
})
]
...
css
,减小JS
文件体积,并自动添加hash
值安装:
npm i mini-css-extract-plugin -D
使用:
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin') ... module: { rules: [ { test: /\.(css)$/, use: [ MiniCssExtractPlugin.loader, // 当前是production环境,如果是development环境 style-loader 'css-loader', 'postcss-loader' ] }, ] } plugins: [ new MiniCssExtractPlugin({ filename: 'css/common.css', // 指定路径,默认hash }) ] ...
:::测试效果 :::
css
代码安装:
npm i optimize-css-assets-webpack-plugin -D
使用:
webpack.config.js
const OptimizeCSSPlugin= require('optimize-css-assets-webpack-plugin');
...
plugins: [
new OptimizeCSSPlugin()
]
...
:::测试效果 :::
css
样式安装:
npm i purifycss-webpack purify-css glob -D
使用:
webpack.config.js
const PurifyCssWebpack = require('purifycss-webpack') // 依赖 purify-css
const glob = require("glob") // 搜索资源
...
plugins: [
new PurifyCssWebpack({
//*.html 表示 src 文件夹下的所有 html 文件,还可以清除其它文件 *.js、*.php···
paths: glob.sync(path.join(__dirname, 'src/*.js'))
})
]
...
:::测试效果 :::
// index.css section { display: flex; align-items: center; transition: all 1s; width: 100vw;height: 100vh; background-image: url(../img/background.jpg); background-color: #000; } .aaa { font-size: 12px; } .bbb { font-size: 12px; }
js
代码,去除 debugger
console
等等安装:
npm i uglifyjs-webpack-plugin -D
使用:
webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); ... plugins: [ new UglifyJsPlugin({ cache: true, // 开启缓存 parallel: true, // 多线程加速构建 sourceMap: true, // 使用sourceMap捕获错误 uglifyOptions: { compress: { drop_console:true, // 放弃对 console 函数的调用 drop_debugger:true, // 删除 debugger语句 } }, }) ] ...
:::测试效果 :::
安装:
npm i generate-asset-webpack-plugin -D
使用:
webpack.config.js
const GenerateAssetPlugin = require('generate-asset-webpack-plugin') // 配置方法 const createJson = function () { let serveConfigJson = { baseURI: '172.16.0.94:80' } return JSON.stringify(serveConfigJson,null,4) } ... plugins: [ new GenerateAssetPlugin({ filename: 'serve.config.json', // 输出到dist根目录下的serve.config.json fn: (compilation, cb) => { cb(null, createJson(compilation)) }, extraFiles: [] }) ] ...
:::测试效果 :::
配置:
optimization
属性 splitChunks
默认属性:
属性 | 作用 |
---|---|
chunks | all, async, initial 三选一, 插件作用的chunks范围 |
minSize | 最小尺寸 |
misChunks | 最小chunks |
maxAsyncRequests | 最大异步请求chunks |
maxInitialRequests | 最大初始化chunks |
name | split 的 chunks name |
cacheGroups | 缓存组,细分打包 |
… | … |
我们这里就不写默认属性配置了,直接把node_modules
的依赖包打到verder.js
,引用2次以上的公共模块打到common.js
webpack.config.js
... optimization: { splitChunks: { chunks:'initial', // 对入口文件处理 cacheGroups: { vendor:{ test: /node_modules\//, name:'js/vendor', priority: 10, enforce: true }, common: { minChunks:2, name: 'js/common', priority: 10, enforce: true } }, }, ...
:::测试效果 :::
安装:
npm i webpack-bundle-analyzer -D
使用:
webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') ... plugins: [ new BundleAnalyzerPlugin({ analyzerMode: 'server', // 服务器启动 analyzerHost: '127.0.0.1', // host analyzerPort: '5555', // 端口 reportFilename: 'report.html', // 输出文件 defaultSizes: 'parsed', // 报告显示模块大小 openAnalyzer: true, // 浏览器自动打开 generateStatsFile: false, // 不输出json文件 statsFilename: 'stats.json', // 输出json文件名 statsOptions: null, // 外配置 logLevel: 'info' // 插件输出的详细程度 }), ] ...
:::测试效果
webpack 4.X
目前用的@1.1.12
安装:
npm i compression-webpack-plugin@1.1.12 -D
使用:
webpack.config.js
const CompressionWebpackPlugin = require('compression-webpack-plugin')
...
plugins: [
new CompressionPlugin({
test: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i, // 匹配资源
filename: '[path].gz[query]', // 输出名称
algorithm: 'gzip', // 压缩方式
threshold: 10240, // 处理大于10240字节才会压缩
minRatio: 0.8 // 压缩率小于才会被压缩
}),
]
...
:::测试效果
webpack
中拷贝文件或者文件夹安装:
npm i copy-webpack-plugin -D
使用:
webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
...
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, './src/static'), // 起始位置
to: path.resolve(__dirname, './dist/static') // 结束位置
}
]
})
]
...
:::测试效果 :::
Happypack
:多进程Loader
转换,加速构建DllPlugin
:抽离第三方模块,加速构建总结:都是加速webpack
构建,这里就不在演示了
安装:
npm i friendly-errors-webpack-plugin -D
使用:
webpack.config.js:
...
plugin = [
new NotifierWebpackPlugin({
// 编译成功处理
compilationSuccessInfo: {
messages: ['Compiler result at http://localhost:8080']
},
// 编译失败处理
onErrors:(result,errors) => {},
// 是否每次编译完成清除控制台
clearConsole:true,
})
]
...
:::测试效果 :::
安装:
npm i node-notifier -D
使用:
webpack.config.js
... plugin = [ new NotifierWebpackPlugin({ // 编译成功处理 compilationSuccessInfo: { messages: ['Compiler result at http://localhost:8080'] }, // 编译失败处理 onErrors:(result,errors) => { if (result == 'error') { notify.notify({ title: 'Webpack error', message: `${result}:${errors[0].name}`, subtitle: errors[0].file || '', icon: path.resolve(__dirname, 'src/assets/img/chat_head_img.jpg') }) } }, // 是否每次编译完成清除控制台 clearConsole:true, }) ] ...
:::测试效果 :::
devServer
安装:
npm install webpack-dev-server -D
使用:
webpack.config.js
const webpack = require('webpack') ... plugins: [ new webpack.NamedModulesPlugin(), // 显示热加载模块名称 new webpack.HotModuleReplacementPlugin() // 热模块替换 ], devServer: { host: 'localhost', // host地址 port: '8080', // 端口 open: true, //自动拉起浏览器 hot: true, //热加载 hotOnly: true, // 热加载不更新 publicPath: '', // 基础路径 // proxy: {}, // 跨域 // bypass: {} // 拦截器 }, ...
package.json
...
"scripts": {
"dev": "webpack-dev-server --progress --mode development",
"test": "webpack -p --progress --color --config webpack.config.js"
},
...
:::测试效果 :::
我们然后修改index.js
,发现热加载不会生效,并且控制台有警告信息
查阅文档,我们缺少热模块更新,在需要的地方,更新即可
if (module.hot) module.hot.accept()
同样,我们可以把webpack-dev-server
的hotOnly
干掉也可以
我们可以看到,热加载成功,但是我们会看到编译过程,我们可以利用配置清除
...
devServer: {
host: 'localhost', // host地址
port: '8080', // 端口
open: true, //自动拉起浏览器
hot: true, //热加载
hotOnly: false, // 热加载不更新
publicPath: '', // 基础路径
// proxy: {}, // 跨域
// bypass: {} // 拦截器
quiet: true, // 隐藏控制台编译过程
clientLogLevel: 'warning', // 隐藏客户端编译过程/结果
overlay: {warnings: true, errors: true}, // 客户端显示警告/报错信息
},
...
总结:这些只是配置方面,研究事物不能局限在表面…
待续…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。