0基础快速入门WebPack(3)——图解详述plugins(插件)的安装及sourceMap的使用及WebpackDevServer正向代理和模块热更新等(附详细案例源码解析过程及版本迭代过程)

1. 重点提炼

  • webpack
  • plugins
    • HtmlWebpackPlugin:辅助生成html
    • clean-webpack-plugin :清除构建目录
    • mini-css-extract-plugin : css提取
  • sourceMap
    • dev-tool
  • WebpackDevServer:本地服务器
    • Proxy:代理配置
  • Hot Module Replacement:热更新
    • live reload
    • HMR 的 API:热更新接口

2. 配置环境

npm init -y

npm i webpack webpack-cli

\test01\package.json :加入启动简化的命令

{
  "name": "test01",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11"
  },
  "devDependencies": {
    "raw-loader": "^4.0.1"
  }
}

新建webpack.config.js的webpack的配置文件。

测试一下环境:

\test01\src\data\test.txt

测试数据

\test01\src\test.js

import data from './data/test.txt';
 
console.log(data);

\test01\html\1.test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
<script src="../dist/test.js"></script>
 
</body>
</html>

\test01\webpack.config.js

const path = require('path');
 
module.exports = {
 
    mode: 'development',
    entry: {
        'test': './src/test.js',
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    },
        module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },
};
npm install --save-dev raw-loader
npm start

image-20200724102435559

测试成功!

image-20200616174550762

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.19
Branch: branch01

commit description:a0.19(Webpack03配置并测试Webpack环境)
tag:a0.19

3. Plugins(插件)

扩展 webpack 本身的一些功能,它们会运行在各种模块解析完成以后(相当于loader解析完成后,在打包编译之前)的打包编译阶段,比如对解析后的模块文件进行压缩等都可以利用插件的形式进行实现。

3.1 HtmlWebpackPlugin

在打包结束后,⾃动生成⼀个 html 文件,并把打包生成的 js模块引⼊到该 html 中,其实在上一篇当中提过,如Vue、React的模板文件放在当前的html的文件夹中,其实我们最终的希望的是src目录下的所有模块全部打包放在dist目录下,同时html文件下的文件们也放入在dist目录下,这样所有需要在服务器上运行的文件我们都通通都组织在dist目录下了,而不再是分散在各个文件夹下了,然后一个个去组织,这样太麻烦了,我们就可以通过当前插件的方式做到这种效果。

HtmlWebpackPlugin简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的webpack bundle 尤其有用。 你可以让插件为你生成一个HTML文件,使用lodash模板提供你自己的模板,或使用你自己的loader

npm install --save-dev html-webpack-plugin
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
    ...
  plugins: [
     new HtmlWebpackPlugin({
       title: "My App",
       filename: "app.html",
       template: "./src/html/index.html"
     })
  ]
};
<!--./src/html/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=htmlWebpackPlugin.options.title%></title>
</head>
<body>
    <h1>html-webpack-plugin</h1>
</body>
</html>

html 模板中,可以通过 <%=htmlWebpackPlugin.options.XXX%> 的方式获取配置的值

更多的配置

  • title: ⽤来生成⻚面的 title 元素
  • filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直接配置子目录
  • template: 模板⽂件路径,⽀持加载器(loader),⽐如 html!./index.html
  • inject: true | 'head' | 'body' | false,注⼊所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部(默认注入在body里),'head' 将放置到 head 元素中
  • favicon: 添加特定的 favicon 路径到输出的 HTML 文件中
  • minify: {} | false, 传递 html-minifier 选项给 minify 输出
  • hash: true | false,如果为 true,将添加 webpack 编译生成的 hash 到所有包含的脚本和 CSS ⽂件,对于解除 cache 很有用
  • cache: true | false,如果为 true,这是默认值,仅在文件修改之后才会发布文件
  • showErrors: true | false,如果为 true,这是默认值,错误信息会写入到 HTML ⻚面中
  • chunks: 允许只添加某些块 (⽐如,仅 unit test 块)
  • chunksSortMode: 允许控制块在添加到⻚面之前的排序方式,⽀持的值:'none' | 'default' |{function}-default:'auto'
  • excludeChunks: 允许跳过某些块,(⽐如,跳过单元测试的块)

3.1.1 example01

HtmlWebpackPlugin应用。

3.1.1.1 example01-1

安装插件

npm install --save-dev html-webpack-plugin

\test01\src\template.js

console.log('template');

通常在这里我们需要引用template.js文件,这里我们完全利用webpack去处理。

\test01\html\template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
 
</body>
</html>

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
 
module.exports = {
 
    mode: 'development',
 
    entry: {
        // 'test': './src/test.js',
 
        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    },
 
 
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },
 
    plugins: [
        new HtmlWebpackPlugin({
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html"
        }),
    ],
};

npm start

image-20200724120322614

打包后,我们看dist目录,首先template.js被移动过来了,template.html重命名为app.html被移动过来了。

image-20200616181741471

生成后的app.html会自动帮助我们插入script标签,src则自动引入入口文件(打包后的template.js,设置几个入口文件就会一并打包进去),插件就帮助我们处理,我们无需处理,因此项目中的往html引入cssjs都可通过这种方式进行处理。

模板是不需要进行模块化处理的,所有没有放入loader里处理,而是在最后所有loader处理完毕,模块解析完毕后,紧接着在输出的时候把该文件带着一起输出过去了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
<script src="template.js"></script></body>
</html>

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.20
Branch: branch01

commit description:a0.20(Webpack03——example01-1使用HtmlWebpackPlugin插件生成html自动引入js)
tag:a0.20

3.1.1.2 example01-2

假设有多个html模板(不止一个首页),怎么弄?仔细观察文档的配置项,**filenametemplate**都是字符串类型,既不是数组也不是对象,配置项肯定是不允许多个html的,仅能代表一个,除非支持逗号分隔。

我们尝试一下:

可以参考一下官网

image-20200616183023557

以上是配置项没做过多说明,但是下面demo中就有说明:

Generating Multiple HTML Files

To generate more than one HTML file, declare the plugin more than once in your plugins array

要生成多个HTML文件,请在插件数组中多次声明该插件

webpack.config.js

{
  entry: 'index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin(), // Generates default index.html
    new HtmlWebpackPlugin({  // Also generate a test.html
      filename: 'test.html',
      template: 'src/assets/test.html'
    })
  ]
}

\test01\html\template1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
 
 
</body>
</html>

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
 
module.exports = {
 
    mode: 'development',
    entry: {
        // 'test': './src/test.js',
 
        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    },
 
 
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },
 
    plugins: [
        new HtmlWebpackPlugin({
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html"
        }),
        new HtmlWebpackPlugin({
            // 模板文件存放的目录
            template: "./html/template1.html",
            // 生成(打包)后的html存放目录
            filename: "app1.html"
        }),
    ],
};

npm start

image-20200724121429587

image-20200616230927353

\test01\dist\app1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 
 
 
<script src="template.js"></script></body>
</html>

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.21
Branch: branch01

commit description:a0.21(Webpack03——example01-2使用HtmlWebpackPlugin插件生成多个html自动引入js)
tag:a0.21

3.1.1.3 example01-3

除此之外,还可以往模板中注入变量,即往模板中注入数据(具体参考3.1注入说明),如设置一个title,可在模板中使用该变量作为标题

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
 
module.exports = {
 
    mode: 'development',
    // mode: 'production',
    entry: {
        // 'test': './src/test.js',
 
        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    },
 
 
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },
 
    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html"
        })
    ],
};

\test01\html\template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
<!--    <title>Title</title>-->
    <title><%=htmlWebpackPlugin.options.title%></title>
</head>
<body>
 
 
</body>
</html>

npm start

image-20200724122144942

\test01\dist\app.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
<!--    <title>Title</title>-->
    <title>My App</title>
</head>
<body>
 
<script src="template.js"></script></body>
</html>

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.22
Branch: branch01

commit description:a0.22(Webpack03——example01-3使用HtmlWebpackPlugin插件引入title变量)
tag:a0.22

html 模板中,可以通过 <%=htmlWebpackPlugin.options.XXX%> 的方式获取配置的值

更多的配置

  • title: ⽤来生成⻚面的 title 元素
  • filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直接配置子目录
  • template: 模板⽂件路径,⽀持加载器(loader),⽐如 html!./index.html
  • inject: true | 'head' | 'body' | false,注⼊所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部(默认注入在body里),'head' 将放置到 head 元素中
  • favicon: 添加特定的 favicon(网页图标) 路径到输出的 HTML 文件中
  • minify(压缩): {} | false, 传递 html-minifier 选项给 minify 输出
  • hash: true | false,如果为 true,将添加 webpack 编译生成的 hash 到所有包含的脚本和 CSS ⽂件,对于解除 cache 很有用
  • cache: true | false,如果为 true,这是默认值,仅在文件修改之后才会发布文件
  • showErrors: true | false,如果为 true,这是默认值,错误信息会写入到 HTML ⻚面中
  • chunks: 允许只添加某些块 (⽐如,仅 unit test 块)
  • chunksSortMode: 允许控制块在添加到⻚面之前的排序方式,⽀持的值:'none' | 'default' |{function}-default:'auto'
  • excludeChunks: 允许跳过某些块,(⽐如,跳过单元测试的块)
3.1.1.4 example01-4

压缩html

image-20200616232757329

3.1.1.4.1 Minification

If the minify option is set to true (the default when webpack’s mode is 'production'), the generated HTML will be minified using html-minifier-terser and the following options:

如果将minify选项设置为true(当webpack的模式为“生产”时为默认值),则将使用html-minifier-terser和以下选项来缩小生成的HTML:

{
 collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
}

To use custom html-minifier options pass an object to minify instead. This object will not be merged with the defaults above.

要使用自定义html-minifier选项,请通过传递一个对象来缩小。该对象不会与上面的默认值合并。

To disable minification during production mode set the minify option to false.

要在生产模式下禁用压缩,请将minify选项设置为false。

3.1.1.4.2 demo

安装

npm install html-minifier-terser

demo01\webpack-demo02\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {

    mode: 'development',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
    ],
};

image-20200724131116903

npm start

image-20200724131136584

image-20200724131151451

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.23
Branch: branch01

commit description:a0.23(Webpack03——example01-4-demo使用HtmlWebpackPlugin插件配置minify=>压缩html文件)
tag:a0.23

3.1.1.5 example01-5

如果想在dist下将js输出到另一个目录:

    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist/js'),
        filename: "[name].js"
    }

output中设置的path可能会影响后续的loaderplugins中所有打包输出的路径,即很多情况下loaderplugins都以Webpack中的output中的path为基准路径,这点尤为重要,小心出错。

image-20200724131835176

image-20200617103557339

如果想输出不同的路径,我们再filename就行配置。

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {

    mode: 'development',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
    ],
};

npm start

image-20200724131944466

image-20200617104514667

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.24
Branch: branch01

commit description:a0.24(Webpack03——example01-5-使用HtmlWebpackPlugin插件配置文件的自定义dist下的目录)
tag:a0.24

注意:使用框架react、vue、angular都会基础配置默认配置好(脚手架工具)。

3.2 clean-webpack-plugin

删除(清理之前的构建目录)构建目录

npm install --save-dev clean-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
    ...
  plugins: [
    ...,
    new CleanWebpackPlugin(),
    ...
  ]
}

3.2.1 example02

\test01\webpack.config.js

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

module.exports = {

    mode: 'development',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
    ],
};

image-20200617170936866

npm start

image-20200617170956910

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.25
Branch: branch01

commit description:a0.25(Webpack03——example02-使用clean-webpack-plugin插件删除(清理之前的构建目录)构建目录)
tag:a0.25

3.3 mini-css-extract-plugin

提取 CSS 到一个单独的文件中

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    ...,
  module: {
      rules: [
          {
              test: /\.s[ac]ss$/,
              use: [
                  {
                      loader: MiniCssExtractPlugin.loader
                    },
          'css-loader',
          'sass-loader'
        ]
            }
      ]
    },
  plugins: [
    ...,
    new MiniCssExtractPlugin({
        filename: '[name].css'
    }),
    ...
  ]
}

3.3.1 example03

3.3.1.1 example03-1

\test01\src\css\index.css

body {
    background: red;
}

\test01\src\template.js

import './css/index.css';
 
console.log('template');

\test01\webpack.config.js

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

module.exports = {

    mode: 'development',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

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

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
    ],
};

npm install --save-dev css-loader

npm install --save-dev style-loader

npm start

运行\test01\dist\app.html

image-20200617173417194

我们不希望通过js的方式生成style标签,希望把样式文件单独拿出来,放在dist目录下面的publiccss目录里面,然后被引用。

image-20200617173522956

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.26
Branch: branch01

commit description:a0.26(Webpack03——example03-1-使用"style-loader","css-loader"只能是引入style属性,而不是引入css文件了)
tag:a0.26

3.3.1.2 example03-2

我们需要安装mini-css-extract-plugin插件

npm install --save-dev mini-css-extract-plugin

并且会用到MiniCssExtractPlugin.loader,去掉style-loader

\test01\webpack.config.js

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

module.exports = {

    mode: 'development',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
};

npm start

image-20200618212423925

\test01\dist\app.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My App</title>
    <link href="./public/css/template.css" rel="stylesheet">
</head>
<body>
<script src="./public/js/template.js"></script>
</body>
</html>

image-20200725133439895

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.27
Branch: branch01

commit description:a0.27(Webpack03——example03-2-使用mini-css-extract-plugin插件发布后引入css文件了)
tag:a0.27

更多细节参看文档:

image-20200618213120439

image-20200618213202545

注意:webpack更新地很快,别死记硬背,变化会非常地快,如果出现问题,一定要关注版本问题,同一个插件可能因为版本不同,导致不同的结果,兼容性有很大的问题。

4. sourceMap(源文件映射)

涉及打包编译,一定会用到它,翻译过来就是源文件映射,实际上我们新建的src目录就是源文件,而dist目录就是打包发布文件。

我们发布后的文件与开发的源文件是千差万别的,运行的时候万一报错,找这个错,是十分困难的。

我们实际运行在浏览器的代码是通过 webpack 打包合并甚至是压缩混淆过的代码,所生成的代码并不利于我们的调试和错误定位,我们可以通过 sourceMap 来解决这个问题,sourceMap 本质是一个记录了编译后代码与源代码的映射关系的文件,我们可以通过 webpackdevtool 选项来开启 sourceMap

module.exports = {
  mode: 'production',
 
  ...
}

首先,编译后会为每一个编译文件生成一个对应的 .map 文件,同时在编译文件中添加一段对应的 map 文件引入代码

...
//# sourceMappingURL=xx.js.map
...
/*# sourceMappingURL=xx.css.map*/

同时,现代浏览器都能够识别 sourceMap 文件,如 chrome,会在 Sources 面板中显示根据编译文件与对应的 map 文件定位到源文件中,有利于我们的调试和错误定位

image-20190630015447876

4.1 example04

4.1.1 example04-1

\test01\src\fn.js

export default function() {
    console.lo('fn........dsdsds');
}

\test01\src\template.js

import fn from './fn';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
    fn();
};

\test01\webpack.config.js

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

module.exports = {

    // mode: 'development',
    mode: 'production',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
};

npm start

image-20200618220041739

很难定位到错误位置!!!并且时常报错也不是很准确!

image-20200618215156449

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.28
Branch: branch01

commit description:a0.28(Webpack03——example04-1-发布模式下打包后代码有问题,很难定位到源文件)
tag:a0.28

4.1.2 example04-2

那如何根据这个报错,找到源文件出错位置呢?

这个时候就需要sourceMap了。

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
module.exports = {
 
    //mode: 'development',
    mode: 'production',
    devtool: 'source-map',
    
    entry: {
        // 'test': './src/test.js',
 
        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },
 
 
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },
 
            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },
 
    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: true
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
 
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
};

npm start

image-20200618220340261

image-20200618220413150

\test01\dist\public\js\template.js.map

这里的映射信息可能看得不太懂,其实这个是给浏览器看的。

源文件路径及对应编译后的文件路径

“mappings”:记录源文件对应的信息(记录对应位置关系),就能知道源文件中的代码与编译后的文件代码的对应关系了。(它规定一种编码格式)

{
  "version": 3,
  "sources": [
    "webpack:///public/js/template.js"
  ],
  "names": [
    "modules",
    "installedModules",
    "__webpack_require__",
    "moduleId",
    "exports",
    "module",
    "i",
    "l",
    "call",
    "m",
    "c",
    "d",
    "name",
    "getter",
    "o",
    "Object",
    "defineProperty",
    "enumerable",
    "get",
    "r",
    "Symbol",
    "toStringTag",
    "value",
    "t",
    "mode",
    "__esModule",
    "ns",
    "create",
    "key",
    "bind",
    "n",
    "object",
    "property",
    "prototype",
    "hasOwnProperty",
    "p",
    "s",
    "__webpack_exports__",
    "console",
    "log",
    "document",
    "onclick",
    "async",
    "lo"
  ],
  "mappings": "CAAS,SAAUA,GAET,IAAIC,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAJ,EAAQG,GAAUK,KAAKH,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBO,EAAIT,EAGxBE,EAAoBQ,EAAIT,EAGxBC,EAAoBS,EAAI,SAASP,EAASQ,EAAMC,GAC3CX,EAAoBY,EAAEV,EAASQ,IAClCG,OAAOC,eAAeZ,EAASQ,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEX,EAAoBiB,EAAI,SAASf,GACX,oBAAXgB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeZ,EAASgB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeZ,EAAS,aAAc,CAAEkB,OAAO,KAQvDpB,EAAoBqB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQpB,EAAoBoB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFAzB,EAAoBiB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOpB,EAAoBS,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRxB,EAAoB4B,EAAI,SAASzB,GAChC,IAAIQ,EAASR,GAAUA,EAAOoB,WAC7B,WAAwB,OAAOpB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBS,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRX,EAAoBY,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG9B,EAAoBiC,EAAI,GAIjBjC,EAAoBA,EAAoBkC,EAAI,GAnFpD,CAsFC,CAEJ,SAAU/B,EAAQD,EAASF,KAM3B,SAAUG,EAAQgC,EAAqBnC,GAE7C,aAEAA,EAAoBiB,EAAEkB,GAQZnC,EAAoB,GAQ9BoC,QAAQC,IAAI,YAEZC,SAASC,QAAUC,iBAdfJ,QAAQK,GAAG",
  "file": "./public/js/template.js",
  "sourceRoot": ""
}

\webpackTest01\dist\index.js

image-20200619154546941

最后一句代码://# sourceMappingURL=index.js.map

index.js.map 这个就是当前文件对应的映射文件

浏览器解析完sourcemap后,生成如下信息:

但是我们打开浏览器,去定位报404,没定位成功。

image-20200618222958926

发现index.css指向没问题,但是template.js指向是有问题的,可能是配置问题,我们改一下output。

image-20200618223044866

    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
        // filename: "./public/js/[name].js"
    },

还是不行,试着改成开发模式。

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

module.exports = {

    mode: 'development',
    //mode: 'production',
    devtool: 'source-map',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },


    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
};

调成开发模式,是压缩后的代码,一压缩就失效,改成开发模式就好了。

image-20200725135213864

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.29
Branch: branch01

commit description:a0.29(Webpack03——example04-2-开发模式下打包后代码映射没有问题)
tag:a0.29

4.1.3 example04-3

官网看手册!!!!

webpack4.0+webpack配置devtool: "source-map" 生成的map代码没有sourcesContent,没有sourcesContent的结果是你只能定位要压缩代码的位置,无法定位到源码的位置

minimize

如果mode是production类型,minimize的默认值是true,执行默认压缩,

sourceMap 正确配置如下:

const buildConfig = {
    mode: "production",
    output: {
        path: distPath,
        filename: "./js/[name].[hash].min.js",
        publicPath: "./"
    },
    optimization: {    // 1. 这个配置必须
        minimize: false
    },
    plugins: [
    ].concat(baseConfig.htmlArray),
    devtool: "source-map" // 2. 这个配置必须
}
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {

    // mode: 'development',
    mode: 'production',
    devtool: 'source-map',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },

    optimization: {    // 1. 这个配置必须
        minimize: false
    },

    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
};

npm start

image-20200725135213864

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.30
Branch: branch01

commit description:a0.30(Webpack03——example04-3-生产模式下打包后代码解决映射出错的问题)
tag:a0.30

4.2 扩展

image-20200619155025672

我们在官网能看到它有很多选项:

+++ 非常快速, ++ 快速, + 比较快, o 中等, - 比较慢, --

devtool构建速 度重新构建速 度是否适用于生产环境品质(quality)
(none)+ + ++ + +yes打包后的代码
eval+ + ++ + +no生成后的代码
cheap-eval-source-map++ +no转换过的代码(仅限 行)
cheap-module-eval-source-map0+ +no原始源代码(仅限 行)
eval-source-map+no原始源代码
cheap-source-map+0no转换过的代码(仅限 行)
cheap-module-source-map0-no原始源代码(仅限 行)
inline-cheap-source-map+0no转换过的代码(仅限 行)
inline-cheap-module-source-map0-no原始源代码(仅限 行)
source-mapyes原始源代码
inline-source-mapno原始源代码
hidden-source-mapyes原始源代码
nosources-source-mapyes无源代码内容

4.2.1 对于生产环境

这些选项通常用于生产环境中:

(none)(省略 devtool 选项) - 不生成 source map。这是一个不错的选择。

source-map - 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。

你应该将你的服务器配置为,不允许普通用户访问 source map 文件!(上线推荐hidden-source-map

hidden-source-map - 与 source-map 相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。

你不应将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。

nosources-source-map - 创建的 source map 不包含 sourcesContent(源代码内容)。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。

这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。

在使用 uglifyjs-webpack-plugin 时,你必须提供 sourceMap:true 选项来启用 source map 支持。

4.2.2 特定场景

以下选项对于开发环境和生产环境并不理想。他们是一些特定场景下需要的,例如,针对一些第三方工具。

inline-source-map - source map 转换为 DataUrl 后添加到 bundle 中。

cheap-source-map - 没有列映射(column mapping)的 source map,忽略 loader source map。

inline-cheap-source-map - 类似 cheap-source-map,但是 source map 转换为 DataUrl 后添加到 bundle 中。

cheap-module-source-map - 没有列映射(column mapping)的 source map,将 loader source map 简化为每行一个映射(mapping)。

inline-cheap-module-source-map - 类似 cheap-module-source-map,但是 source mapp 转换为 DataUrl 添加到 bundle 中。

4.2.2.1 example05
    // devtool: 'source-map',
    devtool: 'inline-source-map',

image-20200619160131185

image-20200725135213864

生成的文件中就没有index.js.map文件了!

image-20200619160208823

index.js.map文件的对应信息在其注释里,并转成了dataurl格式。

\webpackTest01\dist\index.js

!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t);console.lo("fn")}]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL2ZuLmpzIl0sIm5hbWVzIjpbImluc3RhbGxlZE1vZHVsZXMiLCJfX3dlYnBhY2tfcmVxdWlyZV9fIiwibW9kdWxlSWQiLCJleHBvcnRzIiwibW9kdWxlIiwiaSIsImwiLCJtb2R1bGVzIiwiY2FsbCIsIm0iLCJjIiwiZCIsIm5hbWUiLCJnZXR0ZXIiLCJvIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJlbnVtZXJhYmxlIiwiZ2V0IiwiciIsIlN5bWJvbCIsInRvU3RyaW5nVGFnIiwidmFsdWUiLCJ0IiwibW9kZSIsIl9fZXNNb2R1bGUiLCJucyIsImNyZWF0ZSIsImtleSIsImJpbmQiLCJuIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY29uc29sZSIsImxvIl0sIm1hcHBpbmdzIjoiYUFDRSxJQUFJQSxFQUFtQixHQUd2QixTQUFTQyxFQUFvQkMsR0FHNUIsR0FBR0YsRUFBaUJFLEdBQ25CLE9BQU9GLEVBQWlCRSxHQUFVQyxRQUduQyxJQUFJQyxFQUFTSixFQUFpQkUsR0FBWSxDQUN6Q0csRUFBR0gsRUFDSEksR0FBRyxFQUNISCxRQUFTLElBVVYsT0FOQUksRUFBUUwsR0FBVU0sS0FBS0osRUFBT0QsUUFBU0MsRUFBUUEsRUFBT0QsUUFBU0YsR0FHL0RHLEVBQU9FLEdBQUksRUFHSkYsRUFBT0QsUUFLZkYsRUFBb0JRLEVBQUlGLEVBR3hCTixFQUFvQlMsRUFBSVYsRUFHeEJDLEVBQW9CVSxFQUFJLFNBQVNSLEVBQVNTLEVBQU1DLEdBQzNDWixFQUFvQmEsRUFBRVgsRUFBU1MsSUFDbENHLE9BQU9DLGVBQWViLEVBQVNTLEVBQU0sQ0FBRUssWUFBWSxFQUFNQyxJQUFLTCxLQUtoRVosRUFBb0JrQixFQUFJLFNBQVNoQixHQUNYLG9CQUFYaUIsUUFBMEJBLE9BQU9DLGFBQzFDTixPQUFPQyxlQUFlYixFQUFTaUIsT0FBT0MsWUFBYSxDQUFFQyxNQUFPLFdBRTdEUCxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sS0FRdkRyQixFQUFvQnNCLEVBQUksU0FBU0QsRUFBT0UsR0FFdkMsR0FEVSxFQUFQQSxJQUFVRixFQUFRckIsRUFBb0JxQixJQUMvQixFQUFQRSxFQUFVLE9BQU9GLEVBQ3BCLEdBQVcsRUFBUEUsR0FBOEIsaUJBQVZGLEdBQXNCQSxHQUFTQSxFQUFNRyxXQUFZLE9BQU9ILEVBQ2hGLElBQUlJLEVBQUtYLE9BQU9ZLE9BQU8sTUFHdkIsR0FGQTFCLEVBQW9Ca0IsRUFBRU8sR0FDdEJYLE9BQU9DLGVBQWVVLEVBQUksVUFBVyxDQUFFVCxZQUFZLEVBQU1LLE1BQU9BLElBQ3RELEVBQVBFLEdBQTRCLGlCQUFURixFQUFtQixJQUFJLElBQUlNLEtBQU9OLEVBQU9yQixFQUFvQlUsRUFBRWUsRUFBSUUsRUFBSyxTQUFTQSxHQUFPLE9BQU9OLEVBQU1NLElBQVFDLEtBQUssS0FBTUQsSUFDOUksT0FBT0YsR0FJUnpCLEVBQW9CNkIsRUFBSSxTQUFTMUIsR0FDaEMsSUFBSVMsRUFBU1QsR0FBVUEsRUFBT3FCLFdBQzdCLFdBQXdCLE9BQU9yQixFQUFnQixTQUMvQyxXQUE4QixPQUFPQSxHQUV0QyxPQURBSCxFQUFvQlUsRUFBRUUsRUFBUSxJQUFLQSxHQUM1QkEsR0FJUlosRUFBb0JhLEVBQUksU0FBU2lCLEVBQVFDLEdBQVksT0FBT2pCLE9BQU9rQixVQUFVQyxlQUFlMUIsS0FBS3VCLEVBQVFDLElBR3pHL0IsRUFBb0JrQyxFQUFJLEdBSWpCbEMsRUFBb0JBLEVBQW9CbUMsRUFBSSxHLHNDQ2pGakRDLFFBQVFDLEdBQUciLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMCk7XG4iLCJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbigpIHtcbiAgICBjb25zb2xlLmxvKCdmbicpO1xufSJdLCJzb3VyY2VSb290IjoiIn0=

注意webpack针对不同应用可能会产生各种各样的问题,去官网看文档。

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.31
Branch: branch01

commit description:a0.31(Webpack03——example05-devtool: 'inline-source-map’的使用)
tag:a0.31

5. WebpackDevServer

实际脚手架工具都有它,如React、Vue启动的时候,都会在本地给我们启动一个服务器。Webpack就给我们提供了这种本地开发的服务器。

每次的代码修改都需要重新编译打包,刷新浏览器,特别麻烦,我们可以通过安装 webpackDevServer 来改善这方面的体验

npm install --save-dev webpack-dev-server

启动命令:

npx webpack-dev-server

或者,package.json 中添加 scripts

...,
"scripts": {
  "server": "webpack-dev-server"
}

5.1 example06

5.1.1 example06-1

npm install --save-dev webpack-dev-server

实际上装了WebpackDevServer后,内部就自动会装一个开发服务器,它是node写的,内部用到是一个express框架,直接调用一个命令就可以把服务启动起来。

\test01\package.json

{
  "name": "test01",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack",
    "server": "webpack-dev-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "uglifyjs-webpack-plugin": "^2.2.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11"
  },
  "devDependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.6.0",
    "html-webpack-plugin": "^4.3.0",
    "mini-css-extract-plugin": "^0.9.0",
    "raw-loader": "^4.0.1",
    "style-loader": "^1.2.1",
    "webpack-dev-server": "^3.11.0"
  }
}

npm run server

image-20200619105254691

它帮助我们自动在本地启一个服务器,自动调用webpack.config中的配置,进行webpack打包,打包后的根目录就是output中的path

image-20200725184859562

image-20200619105350011

同时我们发现一个问题,dist目录下什么东西都没有,那么我们是怎么看到网页内容的呢?其实在这里需要注意的一件事情是开发服务器并不会帮助我们打包以后进行output输出,即将打包后的内容输出到dist目录下。而是在内存中开辟一个虚拟路径,即打包后的文件和数据都是存放在内存中的,我们是看不到的。

为什么这么去做呢?主要原因是这样做,程序编译、运行会更快,因为它会把打包编译生成的代码放在内存中(硬盘的操作必然比内存的操作慢很多),通过express对对应的路径进行访问,实际我们访问url时候,此时url指向的是内存中的某一块,从而访问速度也非常地快。实际上在开发环境下,在启动vuereact时候,一样也看不到输出(原理同),但可以显示在网页上,只有用build命令打包生产,才可以看到目录中运行的文件。

综上它其实就是一个开发服务器,很多脚手架工具都是用它实现运行应用的。

image-20200619105858045

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.32
Branch: branch01

commit description:a0.32(Webpack03——example6-1-webpack-dev-server的使用)
tag:a0.32

5.1.2 example06-2

修改 webpack.config.js

module.exports = {
  ...,
  devServer: {
      // 生成的虚拟目录路径(在内存中模拟生成的虚拟路径)
      contentBase: "./dist",
      // 自动开启浏览器
      open: true,
      // 端口
      port: 8081
    }
}

启动服务以后,webpack 不在会把打包后的文件生成到硬盘真实目录中了,而是直接存在了内存中(同时虚拟了一个存放目录路径),后期更新编译打包和访问速度大大提升!

contentBase: "./dist" )=> output path 是默认会被加入 devServer 的 contentBase 中,contentBase配与不配,它都会加进去的。

具体配置参见官方文档:

image-20200619114616788

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

module.exports = {

    // mode: 'development',
    mode: 'production',
    // devtool: 'source-map',
    devtool: 'inline-source-map',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },

    optimization: {    // 1. 这个配置必须
        minimize: false
    },

    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],

    devServer: {
        // 多目录,默认会(如果还想将其他目录映射到静态目录中可进行配置此参数)
        contentBase: [path.join(__dirname, 'html')], // 项目目录下的html目录映射进去
        open: true, // 自动打开浏览器
        port: 8081,
        index: 'app.html' // 默认首页
    }
};

image-20200619115907758

image-20200619115943084

image-20200619120030697

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.33
Branch: branch01

commit description:a0.33(Webpack03——example6-1-webpack-dev-server的自定义contentBase)
tag:a0.33

5.2 Proxy

当下前端的开发都是前后端分离开发的,前端开发过程中代码会运行在一个服务器环境下(如当前的 WebpackDevServer),那么在处理一些后端请求的时候通常会出现跨域的问题。WebpackDevServer 内置了一个代理服务,通过内置代理就可以把我们的跨域请求转发目标服务器上(WebpackDevServer 内置的代理发送的请求属于后端 - node,不受同源策略限制),具体如下:

const Koa = require('koa');
const KoaRouter = require('koa-router');
 
const app = new Koa();
const router = new KoaRouter();
 
router.get('/api/info', async ctx => {
    ctx.body = {
        username: 'zMouse',
        gender: 'male'
    }
})
 
app.use( router.routes() );
app.listen(8787);
axios({
  url: 'http://localhost:8787/api/info'
}).then(res => {
  console.log('res',res.data);
})

默认情况下,该代码运行以后会出现跨域请求错误,修改 webpack 配置

module.exports = {
  ...,
  devServer: {
      // 生成的虚拟目录路径
      contentBase: "./dist",
      // 自动开启浏览器
      open: true,
      // 端口
      port: 8081,
      proxy: {
      '/api': {
          target: 'http://localhost:8787'
        }
    }
    }
}

通过 proxy 设置,当我们在当前 WebpackDevServer 环境下发送以 /api 开头的请求都会被转发到 http://localhost:8787 目标服务器下

axios({
  //url: 'http://locahost:8081/api/info',
  url: '/api/info'
}).then(res => {
  console.log('res',res.data);
})

注意 url 地址要填写 WebpackDevServer 域,比如当前 WebpackDevServer 开启的是 http://localhost:8081,也就是我们当前前端代码运行的环境,那么请求的 url 也必须发送到这里,当我们的请求满足了 proxy 中设置的 /api 开头,那么就会把请求转发到 target ,所以最后的实际请求是:http://lcoahost:8787/api/info

注意:它仅做正向代理,就如同我们开发时用redux发请求时用的http-proxy-middleware,反向代理时建议用Nginx

5.3 example07

5.3.1 example07-1

新建一个server01目录

npm init -y

npm i koa koa-router

\server01\app.js

const Koa = require('koa')
const KoaRouter = require('koa-router')
 
const app = new Koa();
const router = new KoaRouter();
 
router.get('/', async ctx => {
    ctx.body = '大前端开发';
})
 
app.use(router.routes())
 
app.listen(7777);

启动:

node app

image-20200619122147886

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.34
Branch: branch01

commit description:a0.34(Webpack03——example07-1-配置nodejs的koa)
tag:a0.34

5.3.2 example07-2

\server01\app.js

const Koa = require('koa')
const KoaRouter = require('koa-router')
 
const app = new Koa();
const router = new KoaRouter();
 
// router.get('/', async ctx => {
//     ctx.body = '大前端开发';
// })
 
router.get('/api/data', async ctx => {
    ctx.body = '大前端开发';
})
 
app.use(router.routes())
 
app.listen(7777);

\test01\src\template.js

import fn from './fn';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
 
    let rs = await fetch("http://localhost:7777/api/data")
 
    console.log('rs', rs);
 
    // fn();
};

涉及跨域问题。

image-20200619122722652

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.35
Branch: branch01

commit description:a0.35(Webpack03——example07-2-前端请求后台,必然出跨域的问题)
tag:a0.35

5.3.3 example07-3

\test01\webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
 
module.exports = {
 
    // mode: 'development',
    mode: 'production',
    devtool: 'source-map',
    entry: {
        // 'test': './src/test.js',
 
        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },
 
 
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },
 
            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },
 
    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: true
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
 
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],
 
    devServer: {
        //output path 是默认会被加入 devServer 的 contentBase 中,contentBase配与不配,它都会加进去的
        // 多目录,默认会(如果还想将其他目录映射到静态目录中可进行配置此参数)
        contentBase: [path.join(__dirname, 'html')], // 项目目录下的html目录映射进去
        open: true, // 自动打开浏览器
        port: 8081,
        index: 'app.html', // 默认首页
        proxy: {
            // 服务器代理,当访问'/api'时,就代理到'http://localhost:7777'
            '/api': {
                target: 'http://localhost:7777'
            }
        }
    }
};

\test01\src\template.js

import fn from './fn';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
 
    // let rs = await fetch("http://localhost:7777/api/data")
    let rs = await fetch('/api/data')
 
    console.log('rs', rs);
 
 
    // fn();
};

image-20200619123549764

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.36
Branch: branch01

commit description:a0.36(Webpack03——example07-3-前端请求后台,解决跨域的问题)
tag:a0.36

6. Hot Module Replacement(模块热替换<局部更新>)

6.1 引例

在开发过程中,我们希望代码变化后,页面可以跟着刷新,这个称为live reload,翻译过来是热重载。

我们在开发应用的时候,经常希望代码发生改变以后,能跟辅助重新刷新页面。webpack能重新编译,又能重新刷新页面。

它是通过WDS做到的。

image-20200619125758521

它是通过websocket进行通讯的!

image-20200619130010505

其实webpack的内部运用到express框架(提供http服务),它来开服务器,我们通过浏览器访问该服务器,浏览器会跟express服务器建立一个链接,且是长链接websocket。当我们在后台修改代码以后,webpack会发现我们修改代码了,之后它会帮助进行编译,编译完成之后,它会通过express发送一个实时消息给当前的客户端浏览器(令其刷新页面),通过websocket办到的。

在之前当代码有变化,我们使用的 live reload,也就是刷新整个页面,虽然这样为我们省掉了很多手动刷新页面的麻烦(丢失页面当中很多状态),但是这样即使只是修改了很小的内容,也会刷新整个页面,无法保持页面操作状态。HMR 随之就出现了,它的核心的局部(模块)更新,也就是不刷新页面,只更新变化的部分。

module.exports = {
  ...,
  devServer: {
      // 生成的虚拟目录路径
      contentBase: "./dist",
      // 自动开启浏览器
      open: true,
      // 端口
      port: 8081,
      // 开启热更新
      hot:true,
      // 即使 HMR 不生效,也不去刷新整个页面(选择开启)
    hotOnly:true,
      proxy: {
      '/api': {
          target: 'http://localhost:8787'
        }
    }
    }
}

开启 HMR 以后,当代码发生变化,webpack 即会进行编译,并通过 websocket 通知客户端(浏览器),我们需要监听处理来自 webpack 的通知,然后通过 HMR 提供的 API 来完成我们的局部更新逻辑

export default function() {
    console.log('start1!');
}
import fn1 from './fn1.js';
box1.onclick = fn1;
 
if (module.hot) {//如果开启 HMR
    module.hot.accept('./fn1.js', function() {
      // 更新逻辑
      box1.onclick = fn1;
    })
}

上面代码就是 当 ./fn1.js 模块代码发生变化的时候,把最新的 fn1 函数绑定到 box1.onclick 上

从上面就可以看到,HMR 其实就是以模块为单位,当模块代码发生修改的时候,通知客户端进行对应的更新,而客户端则根据具体的模块来更新我们的页面逻辑(这些逻辑需要自己去实现),好在当前一些常用的更新逻辑都有了现成的插件

6.2 css热更新

样式热更新比较简单,style-loader 中就已经集成实现了,我们只需要在 use 中使用就可以了

react和vue脚手架工具已经把热更新集成进去了。

6.3 react 热更新

  • https://github.com/gaearon/react-hot-loader

  • react 脚手架中也有集成

6.4 vue 热更新

  • https://github.com/vuejs/vue-loader
  • vue 脚手架中也有集成

6.5 example08

6.5.1 example08-1

\test01\src\template.js

import fn from './fn';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
 
    // let rs = await fetch("http://localhost:7777/api/data")
    let rs = await fetch('/api/data')
 
    // console.log('rs', rs);
 
    // console.log('我修改了代码!!!!!!!');
 
    fn();
};
 
console.log(module.hot);

image-20200619131735735

\test01\webpack.config.js

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

module.exports = {

    // mode: 'development',
    mode: 'production',
    // devtool: 'source-map',
    devtool: 'inline-source-map',

    entry: {
        // 'test': './src/test.js',

        'template': './src/template.js'
    },
    output: {
        // 这个路径是默认会被加入 devServer 的 contentBase 中
        path: path.resolve(__dirname, 'dist'),
        // filename: "[name].js"
        filename: "./public/js/[name].js"
    },

    optimization: {    // 1. 这个配置必须
        minimize: false
    },

    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },

            {
                test: /\.css$/,
                // use: ["style-loader","css-loader"]
                // 不希望通过js的方式生成style标签,因此不需要"style-loader",
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    "css-loader"
                ]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: "My App",
            // 模板文件存放的目录
            template: "./html/template.html",
            // 生成(打包)后的html存放目录
            filename: "app.html",
            minify: {
                collapseWhitespace: true, // 折叠有助于文档树中文本节点的空白
                removeComments: true, // 删除HTML注释
                removeRedundantAttributes: true, // 当值匹配默认值时删除属性。
                removeScriptTypeAttributes: true, // type="text/javascript"从script标签中删除。其他type属性值保持不变
                removeStyleLinkTypeAttributes: true, // type="text/css"从style和link标签中删除。其他type属性值保持不变
                useShortDoctype: true // doctype用短(HTML5)文档类型替换
            },
        }),
        // new HtmlWebpackPlugin({
        //     // 模板文件存放的目录
        //     template: "./html/template1.html",
        //     // 生成(打包)后的html存放目录
        //     filename: "app1.html"
        // }),
        new CleanWebpackPlugin(),
        // 设置css提取路径
        new MiniCssExtractPlugin({
            filename: './public/css/[name].css'
        })
    ],

    devServer: {
        // 多目录,默认会(如果还想将其他目录映射到静态目录中可进行配置此参数)
        contentBase: [path.join(__dirname, 'html')], // 项目目录下的html目录映射进去
        open: true, // 自动打开浏览器
        port: 8081,
        index: 'app.html', // 默认首页
        proxy: {
            // 服务器代理,当访问'/api'时,就代理到'http://localhost:7777'
            '/api': {
                target: 'http://localhost:7777'
            }
        },
        // 开启热更新
        hot:true,
        // 即使 HMR 不生效,也不去刷新整个页面(选择开启)
        hotOnly:true,
    }
};

image-20200619131933291

module.hot.accept: 当我们的模块中有更新的时候,webpack进行编译,同时将编译的模块以事件的形式通知给它。监听哪个模块的更新。

\test01\src\template.js

import fn from './fn';
import fn1 from './fn1';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
 
    // let rs = await fetch("http://localhost:7777/api/data")
    let rs = await fetch('/api/data')
 
    // console.log('rs', rs);
 
    // console.log('我修改了代码!!!!!!!');
 
    fn();
};
 
let input = document.querySelector('input');
input.onfocus = function() {
    fn1();
}
 
// console.log(module.hot);
 
if (module.hot) {
    // module.hot.accept: 监听哪个模块的更新
    module.hot.accept('./fn', () => {
        console.log('fn更新了');
    });
 
    module.hot.accept('./fn1', () => {
        console.log('fn1更新了....');
    });
}

\test01\src\fn1.js

export default function() {
    console.log('我是新的代码');
}

当我们代码更新,webpack会进行监听,重新编译之后,会把更新的模块以事件的一种机制通过websocket通知浏览器,浏览器通过当前的提供的module.hot.accept来监听是哪个模块发生了更新,它不会刷新我们整个页面,以模块进行局部更新。

webpack的内容非常庞大,建议过过文档即可,之后使用到了再去看,不用死记硬背。

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.37
Branch: branch01

commit description:a0.37(Webpack03——example08-1-模块热更新)
tag:a0.37

6.5.2 example08-2

针对动态生成的页面:

\test01\src\meAlert.js

export default function meAlert() {
    let button = document.createElement('button');
    button.innerHTML = '按钮1';
    document.body.appendChild(button);
    return button;
}

\test01\src\template.js

import fn from './fn';
import fn1 from './fn1';
import meAlert from './meAlert';
import './css/index.css';
 
console.log('template');
 
document.onclick = async function() {
 
    // let rs = await fetch("http://localhost:7777/api/data")
    let rs = await fetch('/api/data')
 
    // console.log('rs', rs);
 
    // console.log('我修改了代码!!!!!!!');
 
    fn();
};
 
let input = document.querySelector('input');
input.onfocus = function() {
    fn1();
}
 
let btn = meAlert();
 
 
 
// console.log(module.hot);
 
if (module.hot) {
    // module.hot.accept: 监听哪个模块的更新
    module.hot.accept('./fn', () => {
        console.log('fn更新了');
    });
 
    module.hot.accept('./fn1', () => {
        console.log('fn1更新了....');
    });
 
    module.hot.accept('./kkbAlert', () => {
        console.log('按钮更新了');
        btn.remove();
        btn = meAlert();
    })
}

但是这种更新逻辑是按照原生写的,全这样写累死,框架有自己的更新逻辑。当我们的组件发生更新的时候,会调用框架中对应的渲染接口,而不是这样写,具体参考以上提供的vue或者react的热更新链接。

参考:https://github.com/6xiaoDi/blog-Webpack/tree/a0.38
Branch: branch01

commit description:a0.38(Webpack03——example08-2-模块热更新-针对动态生成的页面)
tag:a0.38



(后续待补充)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值