深入解读webpack
webpack作为模块化开发的必备工具,vue,react两大框架都会用webpack来进行打包,那么它是怎么打包解析,在项目中怎么运行的呢。
一.webpack用途
- 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
- 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
- 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
- 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
- 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
webpack
应用中有两个核心:
-
-
- 模块转换器,用于把模块原内容按照需求转换成新内容,可以加载非 JS 模块
-
- 扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
-
npm init -y
npm install webpack webpack-cli --save
三/webpack核心概念 #
- Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
- Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
- Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
- Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
- Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
- context: context即是项目打包的路径上下文,如果指定了context,那么entry和output都是相对于上下文路径的,contex必须是一个绝对路径
执行npm run build
,默认会调用 node_modules/.bin
下的webpack
命令,内部会调用webpack-cli
解析用户参数进行打包。默认会以 src/index.js
作为入口文件。
配置webpack.config.js
const path = require('path'); module.exports = { entry:'./src/index.js', output:{ filename:'bundle.js', // 打包出的结果文件 path:path.resolve(__dirname,'dist') // 打包到dist目录下 } }
配置打包的mode
我们需要在打包时提供mode
属性来区分是开发环境还是生产环境,来实现配置文件的拆分
├── build │ ├── webpack.base.js │ ├── webpack.dev.js │ └── webpack.prod.js
npm install webpack-dev-server --save-dev
- contentBase 配置开发服务运行时的文件根目录
- host:开发服务器监听的主机地址
- compress 开发服务器是否启动gzip等压缩
- port:开发服务器监听的端口
加载CSS文件,CSS文件有可能在node_modules里,比如bootstrap和antd
module: { rules: [ { test: /\.css/, + loader:['style-loader','css-loader'] } ] }
- 在 webpack 的构建流程中,plugin 用于处理更多其他的一些构建任务
- 模块代码转换的工作由 loader 来处理
- 除此之外的其他任何工作都可以交由 plugin 来完成
支持图片压缩
npm i file-loader url-loader -D
file-loader 解决CSS等文件中的引入图片路径问题
url-loader 当图片小于limit的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader 进行拷贝
分离CSS
因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载
- mini-css-extract-plugin
- filename 打包入口文件
- chunkFilename 用来打包
import('module')
方法中引入的模块
压缩JS和CSS #
用terser-webpack-plugin
替换掉uglifyjs-webpack-plugin
解决uglifyjs不支持es6语法问题
npm i uglifyjs-webpack-plugin terser-webpack-plugin optimize-css-assets-webpack-plugin -D
css和image存放单独目录
- outputPath 输出路径
- publicPath指定的是构建后在html里的路径
- 如果在CSS文件中引入图片,而图片放在了image目录下,就需要配置图片的publicPath为
/images
处理CSS3属性前缀
npm i postcss-loader autoprefixer -D
PostCSS 的主要功能只有两个
- 第一个就是前面提到的把 CSS 解析成 JavaScript 可以操作的 抽象语法树结构(Abstract Syntax Tree,AST)
- 第二个就是调用插件来处理 AST 并得到结果
::placeholder { color: red; }
postcss.config.js
module.exports={
plugins:[require('autoprefixer')]
}
webpack.config.js
{
test:/\.css$/,
use:[MiniCssExtractPlugin.loader,'css-loader','postcss-loader'],
include:path.join(__dirname,'./src'),
exclude:/node_modules/
}
- Babel其实是一个编译JavaScript的平台,可以把ES6/ES7,React的JSX转义为ES5
- babel-plugin-proposal-decorators
npm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
html-webpack-externals-plugin 外链CDN
+ const htmlWebpackExternalsPlugin= require('html-webpack-externals-plugin'); new htmlWebpackExternalsPlugin({ externals:[ { module:'react', entry:'https://cdn.bootcss.com/react/15.6.1/react.js', global:'React' }, { module:'react-dom', entry:'https://cdn.bootcss.com/react/15.6.1/react-dom.js', global:'ReactDOM' } ] })
当代码发生修改后可以自动重新编译的配置
module.exports = { //默认false,也就是不开启 watch:true, //只有开启监听模式时,watchOptions才有意义 watchOptions:{ //默认为空,不监听的文件或者文件夹,支持正则匹配 ignored:/node_modules/, //监听到变化发生后会等300ms再去执行,默认300ms aggregateTimeout:300, //判断文件是否发生变化是通过不停的询问文件系统指定议是有变化实现的,默认每秒问1000次 poll:1000 } }
- webpack定时获取文件的更新时间,并跟上次保存的时间进行比对,不一致就表示发生了变化,poll就用来配置每秒问多少次
- 当检测文件不再发生变化,会先缓存起来,等待一段时间后之后再通知监听者,这个等待时间通过
aggregateTimeout
配置 - webpack只会监听entry依赖的文件
- 我们需要尽可能减少需要监听的文件数量和检查频率,当然频率的降低会导致灵敏度下降
服务器代理
不修改路径 #
- 请求到 /api/users 现在会被代理到请求 http://localhost:3000/api/users。
proxy: { "/api": 'http://localhost:3000' }
修改路径 #
proxy: {
"/api": {
target: 'http://localhost:3000',
pathRewrite:{"^/api":""}
}
}
18.4 webpack-dev-middleware
webpack-dev-middleware就是在 Express 中提供 webpack-dev-server 静态服务能力的一个中间件
npm install webpack-dev-middleware --save-dev
const express = require('express');
const app = express();
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackOptions = require('./webpack.config');
webpackOptions.mode = 'development';
const compiler = webpack(webpackOptions);
app.use(webpackDevMiddleware(compiler, {}));
app.listen(3000);
- webpack-dev-server 的好处是相对简单,直接安装依赖后执行命令即可
- 而使用
webpack-dev-middleware
的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如 mock 服务、代理 API 请求等
拆分配置 #
可以把 webpack 的配置按照不同的环境拆分成多个文件,运行时直接根据环境变量加载对应的配置即可
- webpack.base.js:基础部分,即多个文件中共享的配置
- webpack.development.js:开发环境使用的配置
- webpack.production.js:生产环境使用的配置
- webpack.test.js:测试环境使用的配置...
- webpack-merge
const { smart } = require('webpack-merge')
const webpack = require('webpack')
const base = require('./webpack.base.js')
module.exports = smart(base, {
module: {
rules: [],
}
})
image-webpack-loader可以帮助我们对图片进行压缩和优化
npm install image-webpack-loader --save-dev
px 自动转成rem #
- 使用px2rem-loader
- 页面渲染时计算根元素的
font-size
值 - lib-flexible
cnpm i px2rem-loader lib-flexible -D
x.html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>主页</title>
<script>
let docEle = document.documentElement;
function setRemUnit () {
//750/10=75 375/10=37.5
docEle.style.fontSize = docEle.clientWidth / 10 + 'px';
}
setRemUnit();
window.addEventListener('resize', setRemUnit);
</script>
</head>
<body>
<div id="root"></div>
</body>
reset.css文件
*{ padding: 0; margin: 0; } #root{ width:375px; height:375px; border:1px solid red; box-sizing: border-box; }
webpack.config.js #
{
test:/\.css$/,//如果要require或import的文件是css的文件的话
//从右向左处理CSS文件,oader是一个函数
use:[{
loader:MiniCssExtractPlugin.loader,
options:{
publicPath: (resourcePath, context) => {
return '/';
}
//publicPath: '/'
}
},{
loader:'css-loader',
options:{
//Enables/Disables or setups number of loaders applied before CSS loader.
importLoaders:0
}
},{
loader:'postcss-loader',
options:{
plugins:[
require('autoprefixer')
]
}
},{
+ loader:'px2rem-loader',
+ options:{
+ remUnit:75,
+ remPrecesion:8
+ }
+ }]
+ },