前言
webpack4 发布已经有一段时间了, 我在实践的过程中发现, 很多项目配置在 webpack3 下工作正常, 但是升级到 webpack4 直接就崩了, 如果想要 webpack4 正常工作, 很多插件也需要升级到新版. 下面是我使用 webpack4 配置的一个学习案例, 包含了日常开发的常用配置项, 比如多入口文件配置, 模板文件自定义, 版本号控制, js 和 CSS 分离, css 自动添加前缀, scss 转 css, 图片及字体文件的处理, babel 编译 JS 语法和 API 等等
版本号
当我们修改代码后, 需要重新打包文件, 这时候为了避免浏览器缓存, 往往需要为文件添加一个版本号
- var webpack = require('webpack');
- var path = require('path');
- var htmlWebpackPlugin = require('html-webpack-plugin');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- plugins: [
- new HtmlWebpackPlugin({
- inject:'body', // 插入位置
- favicon: './favicon.ico', // icon 图标
- title: 'webpack learn', // 生成的 html 文件的标题
- filename: 'index.html', // 生成的 html 文件名称
- minify:{
- removeComments: false, // 删除注释
- collapseWhitespace: false // 删除空格
- }
- })
- ]
- }
复制代码
打包后的代码
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>webpack learn</title>
- <link rel="shortcut icon" href="favicon.ico">
- </head>
- <body>
- <script type="text/javascript" src="main.js?f8f60ca3f6ee44382620"></script></body>
- </html>
复制代码
html-webpack-plugin 这个插件会生成一个名为 index.html, 标题是 webpack learn 的文件, 并且自动把打包后的文件插入到 html 中. 如果不配置这个插件, filename 的默认值就是 index.html,title 默认是 Webpack APP
inject 表示插入的位置, 默认是 body, 可选值 head favicon 表示可以添加一个 icon 图标 minify 表示对压缩文件, removeComments 和 collapseWhitespace 的默认值都是 false
模板文件
html-webpack-plugin 插件可以使用模板文件, 这样我们就可以自定义一个 html 文件, 然后让这个插件把打包后的文件自动插入到模板文件中
tmp/tmp.html 模板文件
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>template file</title>
- <body>
- <div>hello template</div>
- </html>
复制代码
插件配置项
- plugins: [
- new HtmlWebpackPlugin({
- filename: 'index.html',
- template: './tmp/tmp.html'
- })
- ]
复制代码
打包后生成的 index.html
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>template file</title>
- <body>
- <div>hello template</div>
- </html>
- <script type="text/javascript" src="main.js?b321307e65d6b0fbee0b"></script>
复制代码
多页面
对于多页面一般会对应多个入口文件, 不同的 html 页面输出对应不同的入口文件, html-webpack-plugin 插件支持配置多页面. 多页面需要配置 chunks 和 excludeChunks,chunks 表示所包含的入口文件, excludeChunks 表示要排除的入口文件
- var webpack = require('webpack');
- var path = require('path');
- var HtmlWebpackPlugin = require('html-webpack-plugin');
- module.exports = {
- entry: {
- a: './src/main-a.js',
- b: './src/main-b.js',
- c: './src/main-c.js'
- },
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- plugins: [
- new HtmlWebpackPlugin({
- filename: 'a.html',
- template: './tmp/tmp.html',
- chunks: ['a'] // 加载 a 对应的打包文件
- }),
- new HtmlWebpackPlugin({
- filename: 'b.html',
- template: './tmp/tmp.html',
- chunks: ['b'] //// 加载 b 对应的打包文件
- }),
- new HtmlWebpackPlugin({
- filename: 'c.html',
- template: './tmp/tmp.html',
- excludeChunks: ['a', 'b'] // 加载非 a,b 对应的打包文件
- })
- ]
- }
复制代码
运行结果
<!-- a.html -->
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>webpack learn</title>
- <body>
- <div>hello template</div>
- </html>
- <script type="text/javascript" src="a.js?82a9a04389852053c167"></script>
- <!-- b.html -->
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>webpack learn</title>
- <body>
- <div>hello template</div>
- </html>
- <script type="text/javascript" src="b.js?82a9a04389852053c167"></script>
- <!-- c.html -->
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>webpack learn</title>
- <body>
- <div>hello template</div>
- </html>
- <script type="text/javascript" src="c.js?82a9a04389852053c167"></script>
复制代码
内联
除了以链接的形式引入入口文件, 也可以内联到页面中. html-webpack-inline-source-plugin 插件专门用来处理入口文件内联的
- var webpack = require('webpack');
- var path = require('path');
- var HtmlWebpackPlugin = require('html-webpack-plugin');
- var HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- plugins: [
- new HtmlWebpackPlugin({
- inlineSource: '.(js|css)$' // 所有的 js 和 css 文件内联引入
- }),
- new HtmlWebpackInlineSourcePlugin()
- ]
- }
复制代码
babel
babel 可以把 es 最新标准的代码转为 es5 代码, 首先需要安装 babel-core 核心程序, 及 babel-loader
npm i babel-loader babel-core -D
复制代码
由于 ES 每年会发布一个新版本, 所以在进行转换时, 需要选择从哪个标准进行转换, 可供选择的有'es2015','es2016','es2017','latest','env'等多个不同的标准.
babel-preset-env 标准是使用最多的, babel-preset-env 在没有任何配置选项的情况下, 与 babel-preset-latest(或者 babel-preset-es2015,babel-preset-es2016 和 babel-preset-es2017 一起) 的行为完全相同
npm i babel-preset-env -D
复制代码
- var webpack = require('webpack');
- var path = require('path');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- module: {
- rules:[{
- test: /\.js$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['env'],
- cacheDirectory: true
- }
- }
- }]
- }
- }
复制代码
rules 属性中配置的 exclude 表示 node_modules 文件夹不需要 babel 进行转换. 也可以配置一个 include 选项, 比如
include: path.resolve(__dirname, 'src')
, 表示只有 src 文件夹中的文件需要转换
cacheDirectory 选项的默认值是 false, 设置为 true 将缓存 loader 的执行结果, 加快编译速度
API 转换
babel 默认只转换 JavaScript 语法, 对于新的 API, 比如 Iterator,Generator,Set,Maps,Proxy,Reflect,Symbol,Promise 等是不会转换的. 如果需要转换 API, 需要使用 babel-polyfill,babel-polyfill 是一个全局垫片
npm i babel-polyfill -D
复制代码
入口文件引入 babel-polyfill
- // main.js
- import 'babel-polyfill';
- let set = new Set([1,2,3]);
复制代码
babel-polyfill 是一个全局垫片, 开发中更常用的是 babel-plugin-transform-runtime 这个局部垫片, 因为它可以使包的体积更小
npm i babel-plugin-transform-runtime babel-runtime -D
复制代码
- var webpack = require('webpack');
- var path = require('path');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- module: {
- rules:[{
- test: /\.js$/,
- exclude: /node_modules/,
- use: {
- loader: 'babel-loader',
- options: {
- presets: ['env'],
- cacheDirectory: true,
- plugins: ['transform-runtime']
- }
- }
- }]
- }
- }
复制代码
CSS
处理 css 会用到 css-loader 和 style-loader,css-loader 用于读取并加载 css 文件, style-loader 将它插入到页面中
- // main.js
- require('./assets/styles/cssdemo.css');
复制代码
- var webpack = require('webpack');
- var path = require('path');
- var HtmlWebpackPlugin = require('html-webpack-plugin');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- module: {
- rules:[{
- test: /\.css$/,
- use: ['style-loader', 'css-loader']
- }]
- },
- plugins: [
- new HtmlWebpackPlugin({})
- ]
- }
复制代码
自动前缀
由于各大浏览器对 CSS 兼容性不同, 部分 CSS 特性需要加上浏览器前缀才能正常工作, postcss-loader 可以帮我们自动完成加前缀的工作
npm i postcss-loader autoprefixer postcss-import -D
复制代码
- module: {
- rules: [{
- test: /\.css$/,
- use: ['style-loader', {
- loader: 'css-loader',
- options: { importLoaders: 1 }
- },
- {
- loader: 'postcss-loader',
- options: {
- plugins: [
- require('postcss-import'), // 解决 css 中 import 其他 css
- require('autoprefixer')
- ]
- }
- }
- ]
- }]
- }
复制代码
sass
需要安装 sass-loader 及 node-sass.
npm i sass-loader node-sass -D
复制代码
- module: {
- rules: [{
- test: /\.scss$/,
- use: ['style-loader','css-loader',
- {
- loader: 'postcss-loader',
- options: { plugins: [require('autoprefixer')] }
- },
- 'sass-loader'
- ]
- }]
- }
复制代码
分离 css
默认情况下, CSS 会被打包到入口 JS 文件中. 如果需要把 CSS 分离出来, 需要使用 extract-text-webpack-plugin 插件
npm i extract-text-webpack-plugin@next -D
复制代码
- var webpack = require('webpack');
- var path = require('path');
- var HtmlWebpackPlugin = require('html-webpack-plugin');
- var ExtractTextPlugin = require('extract-text-webpack-plugin');
- module.exports = {
- entry: './src/main.js',
- output: {
- path: path.join(__dirname, 'dist'),
- filename: '[name].js?[hash]'
- },
- module: {
- rules: [{
- test: /\.scss$/,
- use: ExtractTextPlugin.extract({
- fallback: 'style-loader',
- use: ['css-loader',
- {
- loader: 'postcss-loader',
- options: { plugins: [require('autoprefixer')] }
- },
- 'sass-loader'
- ]
- })
- }]
- },
- plugins: [
- new HtmlWebpackPlugin({}),
- new ExtractTextPlugin('main.css')
- ]
- }
复制代码
图片资源
webpack 处理图片, 字体, 音乐, 视频等资源时, 需要安装 file-loader
npm i file-loader -D
复制代码
- // main.js
- require('./assets/styles/cssdemo.css');
复制代码
- /* cssdemo.css */
- body {
- background: url('../images/dog.jpg') no-repeat;
- }
- h1 {
- color: red;
- }
复制代码
- module: {
- rules: [{
- test: /\.css$/,
- use: ['style-loader', 'css-loader']
- }, {
- test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
- loader: 'file-loader'
- },
- {
- test: /\.(png|jpe?g|gif|svg)(\?\S*)?$/,
- loader: 'file-loader',
- query: {
- name: '[name].[ext]?[hash]'
- }
- }
- ]
- }
复制代码
如果是在 html 模板中, 通过 img 标签引入图片, 需要使用 ${require('')} 将相对路径包裹一次
<img src="${require('../src/images/dog.jpg')}" alt="">
复制代码
第三方库
比如说我们通过 npm 安装了 jQuery, 只需要通过 provide-plugin 插件就可以自动加载模块, 而不必到处 import 或 require
npm i provide-plugin -D
复制代码
- var ProvidePlugin = require('provide-plugin');
- plugins: [
- new webpack.ProvidePlugin({
- $: 'jquery',
- jQuery: 'jquery'
- })
- ]
复制代码
在项目中使用
console.log($('#box'), jQuery('#box'))
复制代码
如果是把 jQuery 保存到了本地, 可以通过设置别名来引用
- resolve:{
- alias:{
- jQuery$: path.resolve(__dirname,'src/libs/jquery.min.js')
- }
- }
复制代码
实用配置
最后基于上面的所有介绍, 把所有的配置综合到一起. 由于代码比较多, 就不再这里展开了, 我把示例代码放到了 GitHub
- https://github.com/wmui/webpack-demo
- Blog https://www.86886.wang
参考文章
Babel 入门教程 http://www.ruanyifeng.com/blog/2016/01/babel.html
webpack 实用配置 https://www.cnblogs.com/xiaohuochai/p/7007391.html
babel-preset-env 使用方法 https://www.babeljs.cn/docs/plugins/preset-env/
来源: https://juejin.im/post/5b7f7bcf6fb9a019d137d06f