Webpack 4.X 从零配置SPA单页应用

在三大框架潮流的推动下,大大小小的SPA单页面应用层出不穷,工程化 / 模块化 / 自动化 渐渐成为开发的核心思想,但是他们都有一个特点:

源代码无法在浏览器里直接运行,必需通过编译才行

因此也带来了很多构建工具的兴起;诸如具有代表性的 GulpGruntwebpack 等等

今天,我们具体介绍 webpack 4.Xwebpack也从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的核心配置:
1. mode:模式----4.X新增,配置当前环境
2. entry:入口----要打包的文件
3. output:出口----配置编译完成目录
4. module:模块----浏览器不识别的文件
5. plugins:插件----hook函数辅助开发,提高开发效率
6. 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指定资源文件引用的目录

①:当entryString时,编译入口文件并输出

const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js', 
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist'),
  }
}

在这里插入图片描述
②:当entryArray时,编译入口文件合并输出

const path = require('path')

module.exports = {
  mode: 'development',
  entry: ['./src/index.js', './src/home.js'], 
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist'),
  }
}

在这里插入图片描述
③:当entryObject时,编译多个入口文件并输出

  • entry 入口指定多个keyvalue 时,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为供浏览器识别
①:处理 Css | Less | Sass | Stylus

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、Safariwebkit-webkit-
Firefoxgecko-moz-
Operapresto-o-
IEtrident-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的使用顺序: 从后向前
1.首先把 .css文件利用 postcss-loader 过滤
2.接着利用 css-loader 处理 css 为浏览器所能识别的文件
3.最后利用 style-loader 插入到浏览器 style 标签中
4.如果是 lessstylussass,借住各自的 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!
在这里插入图片描述


②:处理 JS | JSX

上面的代码我们在 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中 运行,IE11IE10IE9 都没有问题
在这里插入图片描述
因为在IE8中,Object.defineProperty没有实现,并且不让别人访问或修改!!!
在这里插入图片描述


③:处理 jpe?g | png | gif | svg …

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打包为base64background.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'
}

④:处理 mp4 | webm | ogg | mp3 …

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
          }
        }
      },
	]
...

在这里插入图片描述


⑤:处理 woff2 | eot | ttf | otf …

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
  • 作用:提高开发效率,自动化 / 工程化
①:HtmlWebpackPlugin
  • 自动或依据模板生成一个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>

:::构建成功,测试效果 :::
在这里插入图片描述

②:CleanWebpackPlugin
  • 作用:清空上一次编译结果目录

安装:

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')] // 指定绝对路径
    })
 ]
...
③:MiniCssExtractPlugin
  • 作用:抽取合并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
    })
 ]
...

:::测试效果 :::
在这里插入图片描述

④:OptimizeCSSPlugin
  • 作用:用于合并,压缩css代码

安装:

npm i optimize-css-assets-webpack-plugin -D

使用:

webpack.config.js

const OptimizeCSSPlugin= require('optimize-css-assets-webpack-plugin');

...
 plugins: [
    new OptimizeCSSPlugin()
 ]
...

:::测试效果 :::
在这里插入图片描述

⑤:PurifyCssWebpack
  • 作用:用于去除无用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;
}

在这里插入图片描述

⑥:UglifyJsPlugin
  • 作用:压缩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语句
        }
      },
    })
 ]
...

:::测试效果 :::
在这里插入图片描述

⑦:GenerateAssetPlugin
  • 作用:抽离生成额外配置文件

安装:

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: []
    })
 ]
...

:::测试效果 :::
在这里插入图片描述

⑧:SplitChunksPlugin
  • 作用:打包分割代码

配置:

optimization 属性 splitChunks

默认属性:

属性作用
chunksall, async, initial 三选一, 插件作用的chunks范围
minSize最小尺寸
misChunks最小chunks
maxAsyncRequests最大异步请求chunks
maxInitialRequests最大初始化chunks
namesplit 的 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
        }
      },
    },
...

:::测试效果 :::
在这里插入图片描述

⑨:BundleAnalyzerPlugin
  • 作用:打包体积分析,方便定位优化

安装:

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' // 插件输出的详细程度
    }),
 ]
...

:::测试效果
在这里插入图片描述

⑩:CompressionPlugin
  • 作用:开启gizp压缩,打包体积优化
  • 注意:需要和服务器配合
  • 版本:新版本可能不兼容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 // 压缩率小于才会被压缩
    }),
 ]
...

:::测试效果
在这里插入图片描述

⑪:CopyWebpackPlugin
  • 作用:在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 | DllPlugin
  • Happypack多进程Loader转换,加速构建
  • DllPlugin抽离第三方模块,加速构建

总结:都是加速webpack构建,这里就不在演示了

链接:Happypack DllPlugin

⑬:NotifierWebpackPlugin
  • 作用:控制台友好的错误信息提示插件

安装:

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,
	    })
	]
...

:::测试效果 :::
在这里插入图片描述

⑭:Notify
  • 作用:搭配上面的错误友好信息插件,桌面提示插件

安装:

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-serverhotOnly干掉也可以

我们可以看到,热加载成功,但是我们会看到编译过程,我们可以利用配置清除

...
  devServer: {
    host: 'localhost', // host地址
    port: '8080', // 端口
    open: true, //自动拉起浏览器
    hot: true, //热加载
    hotOnly: false, // 热加载不更新
    publicPath: '', // 基础路径
    // proxy: {}, // 跨域
    // bypass: {} // 拦截器
    quiet: true, // 隐藏控制台编译过程
    clientLogLevel: 'warning', // 隐藏客户端编译过程/结果
    overlay: {warnings: true, errors: true}, // 客户端显示警告/报错信息
  },
...

在这里插入图片描述


总结:这些只是配置方面,研究事物不能局限在表面…

待续…


附文章:
Webpack 4.X 配置cdn加载资源
Webpack 各版本 ( v1 - v4 ) 的区别
Webpack 4.X 自定义 loader 和 plugins
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小小白zyw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值