webpack配置

demo 下载地址:

demo1

webpack 初识

- 安装开发依赖 webpack webpack-cli

- 打包 npx webpack

- 支持模块化

- 打包模式 mode

demo2

webpack 打包含依赖的文件

demo3

webpack配置文件

  • 脚本配置
  • 文件名修改
  • 打包后文件分析

配置文件名由来

打包后文件分析 自调用函数

demo4

配置文件的配置项介绍

  • entry
  • output
  • devServer
  • modules ---- loader
  • plugins
  • optimization

demo5

本地起服务器

- 安装:yarn add webpack-dev-server -D

- 启动: npx webpack-dev-server

- 配置服务器

webpack.config.js

 devServer: {
     port: 3000,
     contentBase: './dist',
     progress: true,
     open: false,
  }

- 配置执行脚本

package.json

script: {

  dev: "webpack-dev-server --config webpack.config.dev.js"

}

demo6

html插件-html-webpack-plugin

  • yarn add html-webpack-plugin

  • 创建模板 src/index.html

  • 添加配置 webpack.config.js

    • 引入插件
    • plugins: []
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html', // 指定模板
            filename: 'index.html', // 引入打包后js的html的文件名
            minify: {
                removeAttributeQuotes: true, // 压缩双引号
                collapseWhitespace: true, // 压缩空白行
            },
            hash: true, // 引用bundle.js时添加hash戳
        })
    ]
    

demo7

样式文件的打包

  • CSS css-loader style-loader
  • less less-loader css-loader style-loader
  • sass node-sass css-loader style-loader
  • stylus stylus stylus-loader
  • 安装loader
  • 添加配置
 module: {
        rules: [{
                test: /\.css$/,
                use: [{
                    loader: 'style-loader',
                    options: {
                        insert: function(element) {
                            var parent = document.querySelector('head');
                            var lastInsertedElement = window._lastElementInsertedByStyleLoader;
                            if (!lastInsertedElement) {
                                parent.insertBefore(element, parent.firstChild);
                            } else if (lastInsertedElement.nextSibling) {
                                parent.insertBefore(element, lastInsertedElement.nextSibling)
                            } else {
                                parent.appendChild(element)
                            }

                        }
                    },
                }, 'css-loader']
            },
            { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
        ]
    }
  • 抽离css打包后的css样式, link标签引入 mini-css-extract-plugin
    (需要自己压缩生产的css文件: 需要插件optimize-css-assets-webpack-plugin,在配置文件中添加optimization; 影响: mode: production失效,需要使用插件压缩打包后的js: uglifyjs-webpack-plugin)

  • 自动添加前缀 autoprefixer包 postcss-loader 需要配置文件 postcss.config.js

demo8

打包后样式文件的抽离,link标签的引入

  • 安装插件 yarn add mini-css-extract-plugin -D
  • 配置
    plugins: [
        new MiniCssExtractPlugin({

        })
    ],
    module: {
        rules: [{
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            },
            { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
        ]
    }

打包后css文件的压缩和js文件的压缩

  • 安装插件 yarn add optimize-css-assets-webpack-plugin -D
  • 配置
 optimization: {
        minimizer: [new OptimizeCssAssetsWebpackPlugin()]
    }
  • 压缩js uglifyjs-webpack-plugin
yarn add uglifyjs-webpack-plugin -D
    optimization: {
        minimizer: [new OptimizeCssAssetsWebpackPlugin(), new UglifyjsWebpackPlugin({
            cache: true,
            parallel: true,
            sourceMap: true,
        })]

需要避免有es6语法,不然会报错

css样式自动添加前缀

  • 安装npm 包 + loader autoprefixer + postcss-loader
yarn add autoprefixer postcss-loader -D
  • 配置
    1- 添加配置文件 postcss.config.js 告诉postcss-loader使用什么插件
// postcss.config.js
module.exports = {
   plugins: [require('autoprefixer')]
}

2- 在css-loader使用前添加post-loader, 并进行插件的配置

 {
               test: /\.less$/,
               use: [
                   MiniCssExtractPlugin.loader,
                   'css-loader',
                   {
                       loader: 'postcss-loader',
                       options: {
                           plugins: loader => [require('autoprefixer')({ browsers: ['> 0.15% in CN'] })]
                       }
                   },
                   'less-loader'
               ]
           },

总结:

  • 抽离css打包后的css样式, link标签引入 mini-css-extract-plugin
    (需要自己压缩生产的css文件: 需要插件optimize-css-assets-webpack-plugin,在配置文件中添加optimization; 影响: mode: production失效,需要使用插件压缩打包后的js: uglifyjs-webpack-plugin)
  • 自动添加前缀 autoprefixer包 postcss-loader 需要配置文件 postcss.config.js

demo9

babel-将es6/es7语法转化成es5语法

1- es6 语法

  • 安包
    yarn add babel-loader @babel/core @babel/preset-env -D
  • 配置 - (es6转成es5)
     {
                test: /\.js$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env"]
                    }
                }]
            }

2- es6 高级语法转化成es5 ---- @babel/plugin-proposal-class-properties; @babel/plugin-proposal-decorators

  • 安包
yarn add  @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
  • 配置
{
                test: /\.js$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env"],
                         plugins: [
                        ['@babel/plugin-proposal-decorators', { 'legacy': true }],
                        ['@babel/plugin-proposal-class-properties', { "loose": true }]
                    ]
                    },
                 

                }]
            }

3- 装饰器语法报错的解决
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)

  • 新增配置文件jsconfig.json
// jsconfig.json
{
    "compilerOptions": {
        "experimentalDecorators": true,
    }
}
  • 重启vscode

4- 内置api 和promise的转化---- @babel/plugin-transform-runtime(必配)

  • 代码
function * gen(params) {
    yield 1;
}
console.log(gen().next());
  • npm run dev
    控制台报错
    Uncaught ReferenceError: regeneratorRuntime is not defined
    原因:打包对gen进行了解析, 使用到了regeneratorRuntime, 但是不会自动帮引入这个api
  • 安包
    yarn add @babel/plugin-transform-runtime -D
    yarn add @babel/runtime --save
  • 配置
 {
                test: /\.js$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env"],
                         plugins: [
                        ['@babel/plugin-proposal-decorators', { 'legacy': true }],
                        ['@babel/plugin-proposal-class-properties', { "loose": true }],
                        "@babel/plugin-transform-runtime" // 新增插件
                    ]
                    },
                 

                }],
                include: path.resolve(__dirname, "src"),// 查找范围
                exclude: /node_modules/, // 不查找文件
            }

5- 解析实例上的方法 ----
实例上的方法默认不会解析

  • 代码
// a.js
"SSS".includes("S")
  • npm run build
    return _context.stop();\n }\n }\n }, _marked);\n}\n\nconsole.log(gen().next());\n"SSS".includes('S');\n\n//# sourceURL=webpack:///./src/a.js?");
    可以看到代码没有被解析
  • 安包
    yarn add @babel/polyfill
  • 引用包
// a.js
require('@babel/polyfill');
"SSS".includes("S")

6- js添加校验 -- eslint

  • 安包
    yarn add eslint eslint-loader
  • 配置文件 .eslintrc.json 新建 (官网--DEMO-download)
  • 配置 新增moudule.rules, 设计执行的顺序,强制优先执行 enforce: "pre"
       {
                test: /\.js$/,
                use:[{
                    loader: 'eslint-loader',
                    options: {
                        enforce: "pre"
                    }
                }],
            },

demo10

全局变量的设置

exp: 将 $ from "jquery"挂载到window上
一、 expose-loader
1- expose-loader (全局loader/内联loader)

  • 安包
    yarn add expose-loader
  • 引用:
  1. 方法一 // // 这种会报错
import $ from 'expose-loader?$!jquery'; 
console.log(window.$, $);
// 通过属性名 "libraryName" 暴露 file.js 的 exports 到全局上下文。
// 在浏览器中,就将可以使用 window.libraryName 访问。
  1. 方法二

// webpack.config.dev.js

{
                test: require.resolve('jquery'),
                use: {
                    loader: "expose-loader",
                    options: {
                        exposes: {
                            globalName: '$'
                        }
                    }
                }
            },

然后正常引用

require('jquery')
console.log("window.$", window.$);

这种方式比较麻烦的是必须在每个模块中引入jquery,解决方式是推过webpack的插件在每个js文件注入$

二、 webpack.providePlugin() 在每个模块注入$,没有挂载到window上

// webpack.config.dev.js
const webpack = require('webpack');
plugins: [
        new webpack.ProvidePlugin({
            $: "jquery"
        })
    ],

三、在index.html中引入cdn,并且配置不打包jquery

    

配置引入不打包

// webpack.config.dev.js
    externals: {
        jquery: "$"
    }

demo11

图片处理 file-loader/ html-withimg-loader / url-loader

  • 在js中创建图片引入: 需要结合import 和require 引入图片 file-loader
  • css background: 不需要进行处理
  • html中引入图片: html-withimg-loader
  • 根据图片大小采用不同的方式处理图片 : url-loader
    小于limit将图片转化成base64格式,大于使用file-loader处理
  1. js
import url from './images/1.png'
var img = new Image();
img.src = url;
  • yarn add file-loader
  • 配置
{
                test: /.(png|jpg|svg|jpeg)$/,
                use: 'file-loader',
            }
  1. css
    不需要处理

  2. HTML :

    使用html-withimg-loader打包html中img引入的图片,很好用,但是webpack4.x里会和html-webpack-plugin产生冲突;
    原因是file-loader升级了,以前4.2的时候是没事的,现在file-loader升级到5.0了,所以需要在file-loader的options里使用一个配置:
    esModule:false
    这样就解决了;

  • 代码
// index.html
    
  • yarn add html-withimg-loader

  • 配置

    rules: [{
                test: /\.(png|jpg|svg|jpeg)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        esModule: false
                    }
                }]
            },
            {
                test: /\.(html|htm)$/,
                use: [{
                    loader: 'html-withimg-loader',
    
                }]
            }]
    
  1. 图片打包格式
  • yarn add url-loader -D
  • 配置
  rules: [
            // {
            //     test: /\.(png|jpg|svg|jpeg)$/,
            //     use: [{
            //         loader: 'file-loader',
            //         options: {
            //             esModule: false
            //         }
            //     }]
            // },
            {
                test: /\.(png|jpg|svg|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        esModule: false, // 不加的话会有这种情况 img属性src="[object Module]"
                        limit: 1024 * 100, // 当小于100kb时候生产base64                 
                    }
                }
            },

demo12

打包文件分类

主要解决方案是在生成资源的额时候配置路径

  • CSS
 plugins: [
           new MiniCssExtractPlugin({
            filename: 'css/main.css' // 抽离后的文件样式名
        }),
    ],
  • IMAGE
rules: [{
                test: /\.(png|jpg|svg|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        esModule: false, // 不加的话会有这种情况 img属性src="[object Module]"
                        limit: 1, // 当小于100kb时候生产base64  
                        outputPath: 'img/'
                    }
                }
            }]

引用打包的资源前面添加CDN

添加publicPath的配置

  • 给所有资源添加
    output: {
        filename: 'bundle.[hash:8].js', // 指定每次打包时文件名含hash戳,位数是8位
        path: path.resolve(__dirname, 'build'),
        publicPath: "https://m.yqb.com"
    },
  • 给单种类型资源添加
{
                test: /\.(png|jpg|svg|jpeg)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        esModule: false, // 不加的话会有这种情况 img属性src="[object Module]"
                        limit: 1, // 当小于100kb时候生产base64  
                        outputPath: '/img/',
                        publicPath: 'https://m.yqb.com'
                    }
                }
 },

source-map: production模式下如果代码报错无法定位,souce-map源码映射帮助代码调试

  • devtool: "source-map"
    单独生成一个sourcemap文件,标识出错的行列
  • devtool: "eval-source-map"
    不会产生单独的文件,标识出错的行列
  • devtool: "cheap-module-source-map"
    产生一个单独的映射文件, 不标识错误列
  • devtool: "cheap-moudle-eval-source-map"
    不产生文件,集成在打包后的文件中,不标识错误列

demo13

打包多页应用

修改配置 entry 和 output, 多html

    entry: {
        home: './src/index.js',
        other: './src/other.js'
    },
    output: {
        filename: '[name].[hash:8].js',
        path: path.resolve(__dirname, 'build'),
    },
     plugins: [
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'home.html',
            chunks: ['home'],
            minify: false,
            hash: true,
        }),
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'other.html',
            chunks: ['other'],
            minify: false,
            hash: true,
        })]

多页面应用抽离公共代码

 optimization: {
        splitChunks: {
            cacheGroups: {
                vendors:  {
                     priority: 1, // 优先抽取
                     test: /[\\/]node_modules[\\/]/,
                    chunks: 'initial',
                    // minSize: 0, // 公共代码大小超过0字节
                    // minChunks: 1, // 使用次数超过1次
                    },
                common: {
                    chunks:'initial',
                    minSize: 0,
                    minChunks: 2,
                },
            }
              
        },

watch 的使用----实现实时打包

// webpack.config.js
watch: true,
watchOptions: {
    poll: 1000, // 每秒问我1000次
    aggregateTimeout: 500, // 防抖,停止输入代码500s后打包文件
    ignored: /node_modules/,  // 不需要监控哪个文件
}

webpack的小插件的应用

  • clean-webpack-plugin 需安装: 新打包前删除打包文件夹如build, 参数为空或对象
yarn add clean-webpack-plugin --save-dev
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 plugins: [
        new CleanWebpackPlugin(),
    ],

  • copy-webpack-plugin 需安装: 拷贝文件到打包后文件夹 参数: 数组 + 对象
yarn add copy-webpack-plugin --save-dev
// webpack.config.js
const CopyWebpackPlugin  = require('copy-webpack-plugin');
 plugins: [
        new CopyWebpackPlugin({
             patterns: [{
                from: './doc',
                to: './doc' // 在build文件夹下的路径
            }]
        }),
    ],
  • bannerPlugin 内置 需要引入webpack: 在打包后的js文件中插入指定字符串 参数: 字符串
const webpack = require('webpack');
 plugins: [
        new webpack.BannerPlugin('我是summer 2020.07.23')
    ],

demo14

跨域

  • 代理 http-proxy
// webpack.config.js
 devServer: {
        proxy: { // 请求8080端口以/api开始的接口重定向到3000端口
            "/api": {
                target: "http://localhost:3000",
                pathRewrite: {
                    '/api': '',
                }
            }
        }
    },
  • 前端mock数据
// index.js
let xhr = new XMLHttpRequest();
xhr.open('GET', "/user", true);
xhr.onload = function() {
    console.log(xhr.response);
}
xhr.send()

// webpack.config.js
    devServer: {
        before(app) {
            app.get('/user', (req, res) => {
                res.json({ name: 'before 3000端口' })
            })
        },
    },
  • 服务端启动webpack(有服务端不用代理,端口使用服务端端口)webpack-dev-middleware
    yarn add webpack-dev-middleware -D
// server.js 


const express = require('express');
let app = express();

const webpack = require('webpack');
// 中间件
const middle = require('webpack-dev-middleware');
const config = require('./webpack.config.dev.js');
const complier = webpack(config);
app.use(middle(complier))


app.get('/user', (req, res) => {
    res.json({ name: 'express 3000端口' })
})

app.listen(3000);

resolve

解析第三方模块

    resolve: {
        modules: [path.resolve('node_modules')], // 指定只查找当前目录下的node_modules
        // mainFields: ['style', 'main'], // 指定查找顺序
        // mainFiles: [], // 指定入口文件
        // alias: { // 别名
        //     bootstrap: 'bootstrap/dist/css/bootstrap.css'
        // }
        extensions: ['.js', '.css', '.json'] // 文件省略后缀的情况下的查找顺序
    },

定义环境变量 (区分开发和产线环境)webpack 内置插件 definePlugin

const webpack = require('webpack');

    plugins: [
        new webpack.DefinePlugin({
            DEV: JSON.stringify('production'),
            FLAG: 'true',
        })
    ],

区分不同环境 webpack-merge

yarn add webpack-merge -D

// webpack.config.js
const commonConfig = { ... };
 
const productionConfig = { ... };
 
const developmentConfig = { ... };
 
module.exports = env => {
  switch(env) {
    case 'development':
      return merge(commonConfig, developmentConfig);
    case 'production':
      return merge(commonConfig, productionConfig);
    default:
      throw new Error('No matching configuration was found!');
  }
}

demo15

webpack 的优化

yarn add webpack webpack-cli html-webpack-plugin @babel/core babel-loader @babel/preset-env @babel/preset-react webpack-dev-server -D

noParse: 不去解析指定库中的依赖关系,提高打包速度

使用场景: 提前知道第三方库没有依赖

 module: {
        noParse: /jquery/, // 不去解析jquery中的依赖库
    },

IgnorePlugin: webpack内置插件--忽略掉第三方插件的某些内容,手动实现按需引用

运用场景: moment第三方插件,语言包的引用
package.json: main:moment.js -> aliasedRequire('./locale/' + name);
引入locale,所有语言包

  • 没配置前打包文件大小

Version: webpack 4.44.0
Time: 2231ms
Built at: 2020-07-27 7:12:16 AM
Asset Size Chunks Chunk Names
main.js 1.37 MiB main [emitted] main
Entrypoint main = main.js

  • 配置
const webpack = require('webpack');
module.exports= {
      plugins: [
        new webpack.IgnorePlugin(/\.\/locale/, /monent/)
    ],
}

  • 配置后打包文件大小
    Version: webpack 4.44.0
    Time: 2425ms
    Built at: 2020-07-27 7:42:21 AM
    Asset Size Chunks Chunk Names
    bundle.1956a6f4.js 862 KiB main [emitted] [immutable] main
    index.html 308 bytes [emitted]
    Entrypoint main = bundle.1956a6f4.js

dllPlugin---动态链接库 DllReferencePlugin + DllPlugin webpack内置插件

应用场景: 打包时不打包react, react-dom
yarn add react react-dom
目的:不希望在开发热更新时重复打包第三方模块,否则速度太慢。
打包完第三方依赖后,就要去打包业务代码,这个时候就需要让业务代码知道不要再去打包哪些第三方模块了
直接从打包好的__dll_react.js里面去取就可以了
在这里打包第三方依赖的时候,生成一份说明文件manifest.json,来让webpack在打包业务代码的时候
知道打包哪些模块需要从__dll_react.js里面取而不是重新打包

总结整体流程:
通过dllPlugin生成manifets.json和_dll_react.js,vendor.js会自执行返回一个加载函数_dll_react(名字可配置),通过闭包将模块存储在内存中,注意_dll_react是一个全局变量。
webpack通过DllReferencePlugin在打包的时候分析业务代码中使用了哪些第三方模块,哪些模块是不需要打包进业务代码中,而是去_dll_react.js中获取。
vendor中获取的模块是通过调用全局函数_dll_react(id)来进行引入。

happypack --- 使用多线程打包

yarn add happypack

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const Happypack = require('happypack')
module.exports = {
    plugins: [
        new Happypack({
            id: 'js',
            use :  [{
                loader: 'babel-loader',
                options: {
                    presets: ["@babel/preset-env", '@babel/preset-react'],
                },
            }],
        })
    ],
    module: {
        rules: [{
            test: /\.js$/,
            use: 'Happypack/loader?id=js',
            include: path.resolve(__dirname, "src"), // 查找范围
            exclude: /node_modules/, // 不查找文件
        }],
    },

}

webpack自带优化

  • tree-shaking
    默认情况下在production模式下, import 会自动去除掉没用的代码 tree-shaking
    commonjs模块会把结果放到default上
    结论:建议使用import 语法
  • scope hosting
    会自动省略一些可以简化的代码: 比如简单的表达式计算

抽离公共代码

使用场景:多页应用引入公共模块

  • 抽离自定义公共文件
    demo13
optimization: {
  splitChunks: {  // 分割代码块
    cacheGroups: { // 缓存组
     common:  {chunks: 'initail',
      minSize: 0, // 公共代码大小超过0字节
      minChunks: 1, // 使用次数超过1次
    }
  }
}
  • 抽离第三方模块
optimization: {
  splitChunks: {  // 分割代码块
    cacheGroups: { // 缓存组
     vendors:  {
       priority: 1, // 优先抽取
       test: /node_modules/,
      chunks: 'initail',
      minSize: 0, // 公共代码大小超过0字节
      minChunks: 1, // 使用次数超过1次
    }
  }
}

chunks:表示从哪些chunks里面抽取代码,除了三个可选字符串值 initial、async、all 之外,还可以通过函数来过滤所需的 chunks;
minSize:表示抽取出来的文件在压缩前的最小大小,默认为 30000;
maxSize:表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
minChunks:表示被引用次数,默认为1;上述配置commons中minChunks为2,表示将被多次引用的代码抽离成commons。

懒加载的实现 ----import语法

let button = document.createElement('button');
button.innerHTML = "hello"
button.addEventListener('click', function () {
  // jsonp实现动态加载文件
  import('./test.js').then(data=> {
    console.log(data);
    console.log(data.default);
  })
})
document.body.appendChild(button)

热更新

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
  
    devServer: {
        contentBase: './build',
        hot: true, // 启用热更新
    },
    plugins: [
    
        new webpack.NamedModulesPlugin(), // 打印更新的模块路径
        new webpack.HotModuleReplacementPlugin() // 热更新插件
    ],

}
// index.js
if(module.hot){ // 如果当前文件支持热更新,调用accept监听test文件的热更新
  module.hot.accept('./test.js', function () {
    console.log('文件更新了');
    let str = require('./test.js')
    console.log(str);
  })
}

你可能感兴趣的:(webpack配置)