webpack

Author:Helene

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自Helene的博客


webpack核心

webpack核心是模块打包工具

搭建webpack项目

1、新建文件夹,进入

2、npm init //初始化npm

3、安装webpack(全局或者局部),这里局部安装

npm install webpack webpack-cli --save-dev

npm install webpack webpack-cli --g//全局安装
//不推荐全局安装webpack,因为全局安装之后如果多个webpack项目版本不一致,全局安装之后其他项目就不能再运行起来了,解决的办法就是先删除当前版本号,在安装对应版本webpack即可启动项目,
但是两个项目之间有依赖关系,又想启动webpack4.0又想启动webpack5.0项目,通过全局安装就没办法了

4、局部安装查看webpack信息可以使用

npx webpack -v 或者 webpack -v  (npx 也适用于局部安装中查看其它插件信息)

5、初始化完成,之后就可以创建一些项目相关文件夹,如

src目录 //开发目录

dist目录 //存放打包后的文件

6、根目录下创建index.html文件,并且引入打包后的js文件,如引入bundle.js

7、src下创建一些js或者css

8、给项目创建一个webpack配置文件在项目根目录,webpack.config.js

搞定!

基本配置

const path = require('path');

module.exports = {
    entry: './src/script/main.js',//打包入口,从哪个文件开始,必须是相对路径
    output: {
        path: path.resolve(__dirname + "/dist/js"), //打包后输出到的目录,必须是绝对路径// 必须是绝对路径(使用 Node.js 的 path 模块)
        filename: "bundle.js", //打包后的文件名叫什么
    },
}

//__dirname就是webpack所有当前目录的路径
//随后在项目根目录执行 webpack命令即完成打包工作

创建webpack.config.js的原因

如果直接使用webpack这个命令的话,webpack会直接在项目根目录去寻找webpack.config.js文件,它作为默认的配置去运行

webpack默认配置文件如果不叫webpack.config.js文件,那么怎么打包

使用--config 默认配置文件名,例如

webpack --config webpack.dev.config.js

即可完成打包

查看webpack历史版本

npm info webpack

当然也可以使用npm info 查看其它安装包历史版本信息

如何为webpack加一些命令行使用参数

1、可以配合npm的脚本做到

2、打包package.json

4、找到script属性

5、在这个属性里面定义一段脚本,脚本内容就是真是的webpack命令

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpack": "webpack --config webpack.config.js --progress --display-modules --display-reasons"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.2.0",
    "webpack-cli": "^4.1.0"
  }
}

"webpack(随意起名,中文除外)": "webpack --config webpack.config.js --progress --display-module --colors --displau-reason"
//比如使用--progress看到打包的过程,使用--display-modules看到打包的模块,--display-reasons打包原因
参见:https://www.webpackjs.com/api/cli/#%E5%B8%B8%E7%94%A8%E9%85%8D%E7%BD%AE
    
6、上面那段话就是webpack命令行配置

7、然后使用npm run webpack就执行以上命令了    

webpack-cli的作用是什么

就是让开发者可以在命令行中webpack命令,假设不安装webpack-cli这个包,那么就没有办法在
命令行运行webpack或者npx webpack这样的指令,

webpack.config.js mode

If not set, webpack sets production as the default value for mode.

module.exports = {
    mode: 'development',
    entry: './src/js/main.js',
    output: {
        path: path.resolve(__dirname + '/dist/js'),
        filename: "bundle.js"
    }
}

模块包括什么

模块不止js,也还包括css,图片等等....

加载某些模块需要哪些loader

因为webpack默认只能打包js模块,loader 用于对模块的源代码进行转换,可以将文件从不同的语言(如 TypeScript)转换为 JavaScript

file-loader(转换图片的loader)

1、
module.exports = {
    ...,
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [{loader: "file-loader"}]
            }
        ]
    }
}

2、上面打包之后图片打包出来的文件名是一个很长的字符串,如果想不改变图片的名字,如何做?

module: {
    rules: [
        {
            test: /\.(jpe?g|png|gif)$/,
            use: [{
                loader: "file-loader",
                options: {
                    name: '[name].[ext]'
                }
            }],
        }
    ]
}

options: {
  name: '[name]_[hash].[ext]' //资源的基本名称/内容的哈希值/资源扩展名称。。。
}

3、如何将图片打包后指定到特定文件夹

options: {
    name: '[name]_[hash].[ext]',
    outputPath: '../images/'
}


outputPath: '../images/' //指定打包到哪里的文件夹

4、常用整合

rules: [
    {
        test: /\.(jpe?g|png|gif)$/,
        use: [{
            loader: "file-loader",
            options: {
                name: '[name]_[hash].[ext]',
                outputPath: '../images/'
            }
        }],
    }
]

url-loader( 像 file loader 一样工作,但如果文件小于限制,可以返回 base64图片)

url-loader最佳使用方式是什么:

图片很小的时候使用url-loader将图片转换为data-url放到打包好的js中,减少了HTTP请求,但是如果图片过大,就要像file-loader一样将图片放到dist路径下,不要打包到压缩文件js文件中,因为这样可以让
压缩的js文件快速加载完成,页面可以快速显示出来,不然压缩的js会很大,加载它的时间很长,页面很久才能展示出来

如何实现以上说法?

module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: '../images/',
                        limit: 2048
                    }
                }],
            }
        ]
    }

 limit: 2048 //作用就是如果图片大小超过了2048字节的话,那么就会像file-loader一样打包到dist文件夹下生成一个图片,相反直接将图片变成base64字符串放到bundle.js中

css-loader style-loader

style-loader 将模块的导出作为样式添加到 DOM 中

css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码


{
    test: /\.css$/,
    use: [
        {loader: "style-loader"},
        {loader: "css-loader"}
    ],
}

打包后的css代码会到压缩后的js文件中

为什么style-loader要写在css-loader前

因为loader的加载顺序是从右往左,从下到上

sass/less/stylus文件的loader

sass-loader stylus-loader less-loader 具体看文档

{
    test: /\.scss$/,
    use: [{
        loader: "style-loader" // 将 JS 字符串生成为 style 节点
    }, {
        loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
    }, {
        loader: "sass-loader" // 将 Sass 编译成 CSS
    }]
}

postcss-loader结合autoprefixer

使用autoprefixer向CSS规则添加供应商前缀。

 {
    test: /\.scss$/,
    use: [
        {
            loader: "style-loader" // 将 JS 字符串生成为 style 节点
        },
        {
            loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
        },
        {
            loader: "sass-loader" // 将 Sass 编译成 CSS
        },
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: [
                        [
                            'autoprefixer',
                            {
                                // Options
                            },
                        ],
                    ],
                },
            },
        }
    ]
}

还需在package.json文件上加入以下才生效:

"browserslist": [
    "defaults",
    "not ie < 11",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]

importLoaders

查询参数 importLoaders,用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader。

{
  loader: 'css-loader',
  options: {
    importLoaders: 1 // 0 => 无 loader(默认); 1 => postcss-loader; 2 => postcss-loader, sass-loader
  }
},

模块化的css

避免全局样式污染

这样引入css文件
import styles from '../css/style.scss' 

不再这样引入
import '../css/style.scss' 

使用
style.样式名

配置modules:true

 {
    loader: "css-loader", // 将 CSS 转化成 CommonJS 模块
    options: {
        importLoaders: 0,
        modules: true,
    }
},

注意:css模块化还需要具体看一下

如何使用webpack打包字体文件

使用file-loader

{
    test: /\.(eot|woff|ttf|svg)$/,
    use: [{
        loader: "file-loader"
    }],
},

plugins

plugins 可以在webpack运行到某个时刻的时候,帮你做一些事情

html-webpack-plugin

作用:

会在打包结束后自动生成一个html文件,并把打包生成的js自动引入到这个html文件之中,它是在打包之后运行的

使用:

plugins: [
    new HtmlWebpackPlugin()
]

===============================

也可以使用自定义HTML的模板,使用方式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpackDemo</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

plugins: [
    new HtmlWebpackPlugin({
        template: "./index.html"
    })
]

以上方式模板里面也不需要手动引入打包生成的js,webpack会自动将它注入

clean-webpack-plugin

作用:实现重新打包dist目录先删除,然后在执行打包,实在打包之前运行的

它没有被收入官网

const {CleanWebpackPlugin} = require("clean-webpack-plugin");

new CleanWebpackPlugin(),

默认不传参数时删除的文件是output=》path=》最后一级指定的文件夹,但是如果删除整个打包文件夹,可传递参数使用:

new CleanWebpackPlugin({
    cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './dist')] // 清除的文件/文件夹
}),

entry/output

如果多个入口文件可以像如下配置:

 entry: {
    main: './src/js/main.js',
    sub: './src/js/main.js',
},

output: {
    path: path.resolve(__dirname + '/dist/js'),
    filename: "[name].js"
},


输出解析文件的目录,url 相对于 HTML 页面:

output: {
    publicPath: "http://www.baidu.com",
},

SourceMap

详解webpack中的sourcemap

SourceMap是为了解决开发代码与实际运行代码不一致时帮助我们debug到原始开发代码的技术。

开发模式时候默认开启source-map,生产模式没设置devtool的话自动是关闭source-map且设置为none,使用uglifyjs-webpack-plugin 要在里面设置sourcemap:true

mode: "production",
devtool: "cheap-module-source-map",
//production devtool: "cheap-module-source-map",
//development devtool: "eval-cheap-module-source-map",
    
如上配置,生产和开发使用不同的映射


其实sourcemap配置的关键字只有5个,eval(评估、描述)、source-map、cheap(便宜、廉价)、module、inline的任意组合,
这五个关键字每一项都代表一个特性,这四种特性可以任意组合。分表代表以下五种特性:

eval:使用eval包裹模块代码

source-map:产生.map文件

cheap:不包含列信息,也不包含loader的source-map

module:包含loader的source-map(如jsx to js,babel的sourcemap)

inline:将.map作为DataUrl嵌入,不单独生成.map文件(这个配置项比较少见)

修改模块文件之后,webpack如何实现自动编译更新

方法一:

在之前在package.json中设置启动项的地方增加--watch:

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

 devServer: {
    contentBase:'./dist/js',
},

随后重新命令行打包再修改文件就可以实现自动编译更新了,但是以上解决方案需要每次修改后自己刷新页面才会看到最新内容

方案二:

实现效果期望:第一次运行npm run dev就可以实现自动更新打包,自动打开浏览器且不需要手动刷新浏览器还可以开启http服务器可以发送ajax请求:

使用webpack-dev-server实现

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --config webpack.config.js --watch",
    "watch": "webpack-dev-server"
},
  
 devServer: {
     contentBase: path.join(__dirname, "dist/js"),
     open: true,  //自动打开浏览器并且指定端口号
    port: 3000 //修改默认端口号
 },
 
 之后运行 npm run watch就可以实现以上效果了
 
 注意:webpack5.2.0中"webpack-cli默认和webpack-dev-server不兼容,将webpack-cli版本修改为"webpack-cli": "^3.3.12"重新安装更新即可

使用webpack-dev-server为什么不生成dist目录了

webpack-dev-server状态下还是会对代码进行打包的,但是打包生成的文件并不会放在dist目录下了,而是放到了电脑的内存中了,这样可以有效提升打包速度,提升开发效率

热模块替换

devServer: {
    contentBase: path.join(__dirname, "dist/js"),
    open: true,
    hot: true,//开启热模块替换
    hotOnly: true,//启用不刷新页面的热模块替换(参见devServer.hot),作为构建失败时的回退。
},

babel

用于将 ES6+版本的代码转换为向后兼容的 JavaScript 语法

使用:
npm install --save-dev babel-loader @babel/core

module: {
  rules: [
    { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
  ]
}

....

具体查看官方文档:https://www.babeljs.cn/setup#installation

但是只使用babel是不够的,
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。

import '@babel/polyfill'

...

参考自:https://www.jianshu.com/p/67dedc89b13d

但是使用babel-polyfill之后,它会将没有使用到的ES6+的语法转化也都打包到里面了,导致打包文件很大,如何处理呢?

{
  "presets": [
    [
      "@babel/env",
      {
        "useBuiltIns": "usage"//设置这个就可以开启按需转义了,打包之后的文件也会很小,使用它的时候也不需要再在每个需要使用转义的文件定义引入import '@babel/polyfill'
      }
    ]
  ]
}

@babel/polyfill和@babel/plugin-transform-runtime区别与使用

babel/plugin-transform-runtime 到底是什么

babel 在转译的过程中,对 syntax 的处理可能会使用到 helper 函数,对 api 的处理会引入 polyfill。

默认情况下,babel 在每个需要使用 helper 的地方都会定义一个 helper,导致最终的产物里有大量重复的 helper;引入 polyfill 时会直接修改全局变量及其原型,造成原型污染。

@babel/plugin-transform-runtime 的作用是将 helper 和 polyfill 都改为从一个统一的地方引入,并且引入的对象和全局变量是完全隔离的,这样解决了上面的两个问题。

通常:打包业务代码使用@babel/polyfill,打包库文件使用plugin-transform-runtime

webpack配置react代码打包

@babel/preset-react

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage"
      }
    ],
    [
      "@babel/preset-react"
    ]
  ]
}

>https://babeljs.io/docs/en/babel-preset-react#docsNav

Tree Shaking

只打包引用的代码,没使用到的代码不被打包

如一个文件导出one、two两个方法,然后其他文件只使用了one方法,那如果不使用tree shaking的时候,one、two两个方法都会被打包进去,
但是如果使用了tree shaking,就会只打包使用了的one方法。

注意:tree shaking只支持es module的引入,其他像commonjs方式引入的模块不被支持,因为es6 模块是静态引入,而comonjs模块是动态引入,
而tree shaking只支持静态引入。


如何实现:

在webpack配置文件中新增optimization属性,然后赋值对象为以下属性:

optimization: {
    usedExports: true
}

注意:生产模式下将自动开启tree shaking,开发环境需要自己设置,因此生产模式不需要设置以上

Development 和 Production模式的区分打包

分别写两套配置文件:如:

webpack.dev.js  webpack.prod.js

然后在packag.json中指定:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
},

webpack.dev.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const path = require('path');
const webpack = require('webpack');

module.exports = {
    mode: "development",
    devServer: {
        contentBase: path.join(__dirname, "dist"),
        open: true,
        hot: true,
    },
    devtool: "eval-cheap-module-source-map",
    entry: {
        main: './src/js/main.js',
    },
    output: {
        path: path.resolve(__dirname + '/dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: '../images/',
                        limit: 2048
                    }
                }],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader" // 将 JS 字符串生成为 style 节点
                    },
                    {
                        loader: "css-loader", // 将 CSS 转化成 CommonJS 模块
                    }
                ]
            },
            {
                test: /\.(eot|woff|ttf|svg)$/,
                use: [{
                    loader: "file-loader"
                }],
            },
            {test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"},
        ]
    },
    plugins: [
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './dist')] // 清除的文件/文件夹
        }),
        new HtmlWebpackPlugin({
            template: "./index.html"
        }),
        new webpack.HotModuleReplacementPlugin(),
    ],
    optimization: {
        usedExports: true
    }
}

webpack.prod.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const path = require('path');

module.exports = {
    mode: "production",
    devtool: "cheap-module-source-map",
    entry: {
        main: './src/js/main.js',
    },
    output: {
        path: path.resolve(__dirname + '/build'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: '../images/',
                        limit: 2048
                    }
                }],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader" // 将 JS 字符串生成为 style 节点
                    },
                    {
                        loader: "css-loader", // 将 CSS 转化成 CommonJS 模块
                    }
                ]
            },
            {
                test: /\.(eot|woff|ttf|svg)$/,
                use: [{
                    loader: "file-loader"
                }],
            },
            {test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"},
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./index.html"
        }),
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './build')] // 清除的文件/文件夹
        })
    ]
}

解决开发和生产模式下的配置文件大量重复代码问题

新建webpack.common.js文件

将公用代码放置到这里:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const path = require('path');

module.exports = {
    entry: {
        main: './src/js/main.js',
    },
    output: {
        path: path.resolve(__dirname + '/build'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.(jpe?g|png|gif)$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        name: '[name]_[hash].[ext]',
                        outputPath: '../images/',
                        limit: 2048
                    }
                }],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader" // 将 JS 字符串生成为 style 节点
                    },
                    {
                        loader: "css-loader", // 将 CSS 转化成 CommonJS 模块
                    }
                ]
            },
            {
                test: /\.(eot|woff|ttf|svg)$/,
                use: [{
                    loader: "file-loader"
                }],
            },
            {test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"},
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./index.html"
        }),
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './build')] // 清除的文件/文件夹
        })
    ]
}


pro剩下:
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common');

const proConfig = {
    mode: "production",
    devtool: "cheap-module-source-map",
}

module.exports = merge(commonConfig, proConfig)


dev剩下:
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common');

const devConfig = {
    mode: "development",
    devServer: {
        contentBase: path.join(__dirname, "dist"),
        open: true,
        hot: true,
    },
    devtool: "eval-cheap-module-source-map",
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ],
    optimization: {
        usedExports: true
    }
}

module.exports = merge(commonConfig, devConfig)

如何合并上述拆分

使用webpack-merge

module.exports = merge(commonConfig, devConfig)  ,上述粘贴代码已经实现

webpack和code splitting

什么是代码分割:

它可以让代码分割到不同的文件,代码分割和webpack无关,它是单独一个概念,用来提升整个项目的性能。

webpack中实现代码分割的方式:

方式一:

同步代码,只需要在webpack.common.js中做optimization的配置即可

optimization: {
    usedExports: true,
    splitChunks: {
        // include all types of chunks
        chunks: 'all' //这里配置
    }
}

方式二:

异步代码分割:异步代码指的是import这种语法,动态导入的组件或者模块的代码,对于这种代码无需做任何配置,它会自动进行代码分割,放置到新的文件中。

SplitChunksPlugin

SplitChunks插件

webpack 代码分割,底层使用了 SplitChunksPlugin 插件

lazy Loading懒加载

通过import异步加载模块实现懒加载,它是es中的概念;

懒加载好处:

可以让页面加载速度变快,如初始化页面时用不到lodash,那么只加载需要的模块就好了,lodash不会初始化就被载入,页面初始化渲染速度更快。

打包分析

https://webpack.js.org/guides/code-splitting/#bundle-analysis

webpack官网推荐的几个

其中,下面这个观看起来更直观
https://github.com/webpack-contrib/webpack-bundle-analyzer

Prefetching/Preloading modules

> https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules

//...
import(/* webpackPrefetch: true */ 'LoginModal');

等待核心代码加载完成之后,空闲时间再去加载懒加载的资源,使用上述可以提升性能

webpack-dev-server问题

webpack可以自动打开浏览器,但是不能自动刷新页面

webpack如何对css文件进行代码分割

看文档

webpack与浏览器缓存

output: {
    path: path.resolve(__dirname + '/build'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js'
},

使用contenthash,如果文件没改动那么打包之后的对应文件contenthash值是一样的,但是如果改动了,
打包之后的hash是不同的,浏览器看到不同文件名称就会重新请求,相同文件名称使用本地缓存。

shimming(垫片)

如果使用第三方比较老的插件,比如jquery,那么引入的时候还不想每个页面挨个导入,那样可以使用如下方式:


 new webpack.ProvidePlugin({
    $: 'jquery'
})

以上方式之后就可以页面中使用jquery

webpack 环境变量

webpack --env.NODE_ENV=local --env.production --progress

webpack.config.js

module.exports = env => {
  // Use env.<YOUR VARIABLE> here:
  console.log('NODE_ENV: ', env.NODE_ENV); // 'local'
  console.log('Production: ', env.production); // true
  ......
  ,,,,,,

typescript的打包配置

https://github.com/TypeStrong/ts-loader

使用webpackDevServer实现请求转发

devServer.proxy

 const data = axios.get('/react/api/header.json')
 
 devServer: {
     contentBase: path.join(__dirname, "dist"),
     open: true,
     hot: true,
     proxy: {
         '/react/api': 'http://www.dell-lee.com'
     },//此处做代理
 },
 
以上发送的请求路径就是:http://localhost:8082/react/api/header.json
但是实际上是:http://www.dell-lee.com/react/api/header.json

但是后端使用某个接口是测试接口之后名称会改变,那前端如何处理呢?

如上面 /react/api/header.json  中的header在测试中用的test,那么可以像下面这样写

proxy: {
    '/react/api': {
        target: 'http://www.dell-lee.com',
        pathRewrite: {"^header.json": "demo.json"}
    }
},

以上发送的请求路径就是:http://localhost:8085/react/api/demo.json
但是实际上是:http://www.dell-lee.com/react/api/header.json


以上配置只是开发时候设置的转发,生产环境没设置dev-server

webpackDevServer解决单页面应用路由问题

<BrowserRouter>
    <Route path='/' exact component={Home}/>
    <Route path='/list' exact component={List}/>
</BrowserRouter>

但是上面只能匹配根目录,list匹配报错:Cannot GET /list

如何解决:

https://www.webpackjs.com/configuration/dev-server/#devserver-historyapifallback

proxy: {
    '/react/api': {
        target: 'http://www.dell-lee.com',
        pathRewrite: {"header.json": "demo.json"}
    }
},
historyApiFallback: true, //这块设置即可

Eslint在webpack中的配置

https://eslint.org/docs/user-guide/getting-started https://github.com/webpack-contrib/eslint-loader

代码检查与修复工具

配置eslint有两种方式,一种是在编辑器中直接配置,另一种在代码中配置

代码中配置方式:

eslint和eslint-loader结合使用

webpack性能提升

提升webpack打包速度的方法:

1、技术的更新迭代(node/npm/yarn)

2、尽可能少的模块上应用loader

比如babel-loader  
{
    test: /\.js$/,
    exclude: /node_modules/,//抛除安装包下的或者使用include,只对include匹配的转换
    loader: "babel-loader"
},

就是合理的使用exclude或者include可以降低loader执行频率,提升打包速度

3、尽可能少的使用plugin,并且确保plugin的可靠性

(1)比如对css进行代码压缩只是在生产版本配置,开发版本不配置,从而节约开发时每次打包时间

(2)另外一般使用的plugin都是webpack官方提供的,因为这些插件的性能经过官方测试是比较快的,
平时个人写的插件可能会解决打包时候的问题,但是这些插件的性能没法保证,因此最好使用官方推荐的插件

4、resolve参数合理配置

module.exports = {
entry: {
    main: './src/js/main.js',
},
resolve: {
    extensions: ['.js', '.jsx'],//导入文件可以省略这两个扩展名,然后自动先查找.js文件在查找.jsx文件
},

但是上述这样是合理的,不过如果在上述配置项中配置很多内容,那么每次都会进行很多文件查找,每次文件查找都会掉一次node底层这种操作,
会有性能损耗,因此只有引入js或者jsx这种逻辑文件的时候才会省略扩展名配置在这里,css和图片扩展名都不建议配置在这里

也可以给它添加别名:

resolve: {
        extensions: ['.js', '.jsx'],
        alias: {
            assets: path.resolve(__dirname, 'src/assets')
        }
    },

5、控制包文件大小

1、使用Tree Shaking只打包使用的代码
2、使用splitChunks代码拆分
3、合理使用source-map,不同环境打包使用不同source-map
4、结合status分析打包结果