webpack 是一个现代 JavaScript 应用程序的静态模块打包器, 目前最流行的打包神器, 接下来聊一下 webpack4
首先 webpack4 要求 node 版本在 8.5 以上, cmd 中 node -v 查看 node 版本.
1. 什么是 webpack
WebPack 可以看做是模块打包机: 它做的事情是, 分析你的项目结构, 找到 JavaScript 模块以及其它的一些浏览器不能直接运行的拓展语言(SCSS,TypeScript 等), 并将其打包为合适的格式以供浏览器使用.
构建就是把源代码转换成发布到线上的可执行 JavaScrip,CSS,html 代码, 包括如下内容.
代码转换: TypeScript 编译成 JavaScript,SCSS 编译成 CSS 等.
文件优化: 压缩 JavaScript,CSS,HTML 代码, 压缩合并图片等.
代码分割: 提取多个页面的公共代码, 提取首屏不需要执行部分的代码让其异步加载.
模块合并: 在采用模块化的项目里会有很多个模块和文件, 需要构建功能把模块分类合并成一个文件.
自动刷新: 监听本地源代码的变化, 自动重新构建, 刷新浏览器.
代码校验: 在代码被提交到仓库前需要校验代码是否符合规范, 以及单元测试是否通过.
自动发布: 更新完代码后, 自动构建出线上发布代码并传输给发布系统.
构建其实是工程化, 自动化思想在前端开发中的体现, 把一系列流程用代码去实现, 让代码自动化地执行这一系列复杂的流程. 构建给前端开发注入了更大的活力, 解放了我们的生产力.
2. 初始化项目
初始化 package.json
- npm init -y
- // package.json 文件中
- "scripts": {
- "build": "webpack --profile --progress --colors --display-error-details",
- "dev": "webpack --display-modules --profile --progress --colors --display-error-details"
- },
color 输出结果带彩色, 比如: 会用红色显示耗时较长的步骤
profile 输出性能数据, 可以看到每一步的耗时
progress 输出当前编译的进度, 以百分比的形式呈现
display-modules 默认情况下 node_modules 下的模块会被隐藏, 加上这个参数可以显示这些被隐藏的模块
display-error-details 输出详细的错误信息
全局安装
npm install webpack -g
本地安装
npm install webpack webpack-cli -D//-D 是指开发环境需要, 上线环境不需要, 下同;
一般推荐本地安装, 安装在自己的项目中
3. 快速上手
3.1 webpack 核心概念
Entry: 入口, Webpack 执行构建的第一步将从 Entry 开始, 可抽象成输入.
Module: 模块, 在 Webpack 里一切皆模块, 一个模块对应着一个文件. Webpack 会从配置的 Entry 开始递归找出所有依赖的模块.
Chunk: 代码块, 一个 Chunk 由多个模块组合而成, 用于代码合并与分割.
Loader: 模块转换器, 用于把模块原内容按照需求转换成新内容.
Plugin: 扩展插件, 在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情.
Output: 输出结果, 在 Webpack 经过一系列处理并得出最终想要的代码后输出结果.
3.2 配置 webpack
npm install webpack webpack-cli -D
创建 src
创建 dist
创建 index.html
配置文件 webpack.config.js
entry: 配置入口文件的地址
output: 配置出口文件的地址
module: 配置模块, 主要用来配置不同文件的加载器
plugins: 配置插件
devServer: 配置开发服务器
- // 基于 node 的 遵循 commonjs 规范的
- let path = require('path');//node 的模块
- module.exports = {
- entry:'./src/index.js', // 入口
- output:{
- filename:'build.js',
- // 这个路径必须是绝对路径
- path: path.resolve('./dist')
- }, // 出口
- devServer:{
- contentBase:'./dist',
- port:8080,
- compress:true,// 服务器压缩
- open:true,// 自动打开浏览器
- // hot:true// 热更新
- },// 开发服务器
- module:{}, // 模块配置
- plugins:[], // 插件的配置
- mode:'development', // 可以更改模式
- resolve:{}, // 配置解析
- }
- // 在 webpack 中如何配置开发服务器 webpack-dev-server
4. 配置开发服务器
npm i webpack-dev-server -D
contentBase 配置开发服务运行时的文件根目录
host: 开发服务器监听的主机地址
compress 开发服务器是否启动 gzip 等压缩
port: 开发服务器监听的端口
- + devServer:{
- + contentBase:path.resolve(__dirname,'dist'),
- + host:'localhost',
- + compress:true,
- + port:8080
- + }// 开发服务器
- + "scripts": {
- + "build": "webpack --mode development",
- + "dev": "webpack-dev-server --open --mode development"
- + }// 开启本地服务 npm run dev
5. 入口文件的类型
5.1 单入口 + 单出口
这种情况很少, 一般就以字符串的形式出现即可, 例如:
entry: './src/index.js',
5.2 多入口数组形式 + 单出口
entry:['./src/index.js','./src/a.js']
5.1. 多入口 + 多出口
有时候我们的页面可以不止一个 HTML 页面, 会有多个页面, 所以就需要多入口
- entry: {
- index: './src/index.js',
- main:'./src/main.js'
- },
- output: {
- path: path.resolve(__dirname, 'dist'),
- filename: '[name].[hash].js',
- publicPath:PUBLIC_PATH
- },
- new HtmlWebpackPlugin({
- minify: {
- removeAttributeQuotes:true
- },
- hash: true,
- template: './src/index.html',
- chunks:['index'],
- filename:'index.html'
- }),
- new HtmlWebpackPlugin({
- minify: {
- removeAttributeQuotes:true
- },
- hash: true,
- chunks:['main'],
- template: './src/main.html',
- filename:'main.html'
- })],
6. 支持加载 css 文件
6.1 什么是 Loader
通过使用不同的 Loader,Webpack 可以要把不同的文件都转成 JS 文件, 比如 CSS,ES6/7,JSX 等
test: 匹配处理文件的扩展名的正则表达式
use:loader 名称, 就是你要使用模块的名称
include/exclude: 手动指定必须处理的文件夹或屏蔽不需要处理的文件夹
query: 为 loaders 提供额外的设置选项
loader 三种写法
- use
- loader
- use+loader
- 6.2 css-loader
- npm i style-loader css-loader -D
配置加载器
- module: {
- + rules:[
- + {
- + test:/\.css$/,
- + use:['style-loader','css-loader'],// 从右往左写, webpack 特性
- + include:path.join(__dirname,'./src'),
- + exclude:/node_modules/
- + }
- + ]
- },
7. 支持图片
7.1 手动添加图片
npm i file-loader url-loader -D
http://npmjs.com/package/file-loader 解决 CSS 等文件中的引入图片路径问题
https://www.npmjs.com/package/url-loader 当图片较小的时候会把图片 BASE64 编码, 大于 limit 参数的时候还是使用 file-loader 进行拷贝
- let logo=require('./images/logo.png');
- let img=new Image();
- img.src=logo;
- document.body.appendChild(img);
- {
- test:/\.(jpg|png|gif|svg)$/,
- use:'url-loader',
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- }
7.2 在 CSS 中引入图片
还可以在 CSS 文件中引入图片
- .img-bg{
- background: url(./images/logo.png);
- width:173px;
- height:66px;
- }
8. 自动产出 html
8.1 什么是插件
插件是 wepback 的支柱功能. webpack 自身也是构建于, 你在 webpack 配置中用到的相同的插件系统之上!
插件使用
npm install 插件名 -D
因为插件都是类, 引用方式, 在 plugins 数组中 new 插件名 即可使用.
8.2 我们希望自动能产出 HTML 文件, 并在里面引入产出后的资源
npm i html-webpack-plugin -D
minify 是对 html 文件进行压缩, removeAttrubuteQuotes 是去掉属性的双引号
hash 引入产出资源的时候加上哈希避免缓存
template 模版路径
- plugins: [
- + new HtmlWebpackPlugin({
- + minify: {
- + removeAttributeQuotes:true
- + },
- + hash: true,
- + template: './src/index.html',
- + filename:'index.html'
- })]
9. 分离 CSS
因为 CSS 的下载和 JS 可以并行, 当一个 HTML 文件很大的时候, 我们可以把 CSS 单独提取出来加载
- npm install --save-dev extract-text-webpack-plugin@next
- {
- test:/\.css$/,
- + use: ExtractTextWebpackPlugin.extract({
- + use:'css-loader'
- + }),
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- },
- plugins: [
- + new ExtractTextWebpackPlugin('css/index.css'),
处理图片路径问题
- const PUBLIC_PATH='/';
- output: {
- path: path.resolve(__dirname, 'dist'),
- filename: 'bundle.js',
- + publicPath:PUBLIC_PATH
- },
指定打包后的图片位置
- use: [
- {
- loader: 'url-loader',
- options: {
- limit: 1024,
- + outputPath:'images/'
- }
- }
- ],
在 HTML 中使用图片
- npm i html-withimg-loader -D
- <div class="img-container"><img src="./images/logo.png" alt="logo.png"></div>
- {
- + test:/\.(html|html)$/,
- + use:'html-withimg-loader',
- + include:path.join(__dirname,'./src'),
- + exclude:/node_modules/
- }
10. 编译 less 和 sass
- npm i less less-loader -D
- npm i node-saas sass-loader -D
- @color:orange;
- .less-container{
- border:1px solid @color;
- }
- $color:green;
- .sass-container{
- border:1px solid $color;
- }
- const cssExtract=new ExtractTextWebpackPlugin('css.css');
- const lessExtract=new ExtractTextWebpackPlugin('less.css');
- const sassExtract=new ExtractTextWebpackPlugin('sass.css');
- {
- test:/\.less$/,
- use: lessExtract.extract({
- use:['css-loader','less-loader']
- }),
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- },
- {
- test:/\.scss$/,
- use: sassExtract.extract({
- use:['css-loader','sass-loader']
- }),
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- },
11. 处理 CSS3 属性前缀
为了浏览器的兼容性, 有时候我们必须加入 - webkit,-ms,-o,-moz 这些前缀
Trident 内核: 主要代表为 IE 浏览器, 前缀为 - ms
Gecko 内核: 主要代表为 Firefox, 前缀为 - moz
Presto 内核: 主要代表为 Opera, 前缀为 - o
Webkit 内核: 产要代表为 Chrome 和 Safari, 前缀为 - webkit
- npm i postcss-loader autoprefixer -D
- https://github.com/postcss/postcss-loader
- postcss.config.js
- module.exports={
- plugins:[require('autoprefixer')]
- }
- .circle {
- + transform: translateX(100px);
- {
- test:/\.css$/,
- use: cssExtract.extract({
- + use:['css-loader','postcss-loader']
- }),
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- },
12. 转义 ES6/ES7/JSX
Babel 其实是一个编译 JavaScript 的平台, 可以把 ES6/ES7,React 的 JSX 转义为 ES5
- npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 babel-preset-react -D
- {
- test:/\.jsx?$/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ["env","stage-0","react"]
- }
- },
- include:path.join(__dirname,'./src'),
- exclude:/node_modules/
- },
13. 如何调试打包后的代码
webapck 通过配置可以自动给我们 source maps 文件, map 文件是一种对应编译文件和源文件的方法
source-map 把映射文件生成到单独的文件, 最完整最慢
cheap-module-source-map 在一个单独的文件中产生一个不带列映射的 Map
eval-source-map 使用 eval 打包源文件模块, 在同一个文件中生成完整 sourcemap
cheap-module-eval-source-map sourcemap 和打包后的 JS 同行显示, 没有映射列
devtool:'eval-source-map'
14. 拷贝静态文件
有时项目中没有引用的文件也需要打包到目标目录
- npm i copy-webpack-plugin -D
- new CopyWebpackPlugin([{
- from: path.join(__dirname,'public'),// 静态资源目录源地址
- to:'./public' // 目标地址, 相对于 output 的 path 目录
- }]),
15. 打包前先清空输出目录
- npm i clean-webpack-plugin -D
- new cleanWebpaclPlugin(path.join(__dirname,'dist'))
16. 压缩 CSS
webpack 可以消除未使用的 CSS, 比如 bootstrap 中那些未使用的样式
- npm i -D purifycss-webpack purify-css
- npm i bootstrap -S
- {
- test:/\.css$/,
- use: cssExtract.extract({
- use: [{
- loader: 'css-loader',
- + options:{minimize:true}
- },'postcss-loader']
- }),
- },
- + new PurifyCSSPlugin({
- + //purifycss 根据这个路径配置遍历你的 HTML 文件, 查找你使用的 CSS
- + paths:glob.sync(path.join(__dirname,'src/*.html'))
- + }),
17. resolve 解析
17.1 extensions
指定 extension 之后可以不用在 require 或是 import 的时候加文件扩展名, 会依次尝试添加扩展名进行匹配
- + resolve: {
- // 自动补全后缀, 注意第一个必须是空字符串, 后缀一定以点开头
- + extensions: ["",".js",".css",".json"],
- + },
- 17.2 alias
配置别名可以加快 webpack 查找模块的速度
每当引入 jquery 模块的时候, 它会直接引入 jqueryPath, 而不需要从 node_modules 文件夹中按模块的查找规则查找
不需要 webpack 去解析 jquery.js 文件
- const bootstrap=path.join(__dirname,'node_modules/bootstrap/dist/css/bootstrap.css')
- resolve: {
- + alias: {
- + 'bootstrap': bootstrap
- + }
- },
18. 区分环境变量
许多 library 将通过与 process.env.NODE_ENV 环境变量关联, 以决定 library 中应该引用哪些内容. 例如, 当不处于生产环境中时, 某些 library 为了使调试变得容易, 可能会添加额外的日志记录 (log) 和测试(test). 其实, 当使用 process.env.NODE_ENV === 'production' 时, 一些 library 可能针对具体用户的环境进行代码优化, 从而删除或添加一些重要代码. 我们可以使用 webpack 内置的 DefinePlugin 为所有的依赖定义这个变量:
- npm install cross-env -D
- "scripts": {
- + "build": "cross-env NODE_ENV=production webpack --mode development",
- "dev": "webpack-dev-server --open --mode development"
- },
- plugins: [
- + new webpack.DefinePlugin({
- + NODE_ENV:JSON.stringify(process.env.NODE_ENV)
- + }),
- if (process.env.NODE_ENV == 'development') {
- console.log('这是开发环境');
- } else {
- console.log('这是生产环境');
- }
来源: https://juejin.im/post/5af8fa806fb9a07ac162876d