Webpack的基础应用

1.基本使用示例

1.1 安装

  • 全局安装
npm i webpack webpack -cli --global 
或者
npm i webpack webpack -cli --g
  • 本地安装
npm install webpack webpack-cli webpack-dev-server --save-dev
或者
npm install webpack webpack-cli webpack-dev-server -D

1.2 运行

  • 在终端中输入webpack 回车 会生成dist文件夹即打包后的文件 (使用全局webpack
  • npx webpack(使用当前目录webpack)

1.3 自定义Webpack配置

  • 新建webpack.config.js
  • commonJs写法抛出配置模块
const path = require('path')
module.exports={
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./dist')
    },
    mode:'development',
    //精准定位代码行数,便于查看
    devtool: 'inline-source-map'
}

1.4 在Webpack中使用插件

下面以安装HtmlWebpackPlugin为例:

安装:

npm install html-webpack-plugin -D

webpack.config.js中添加配置:

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

module.exports={
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./dist')
    },
    mode:'development',
    devtool: 'inline-source-map' ,
    plugins:[
        new HtmlWebpackPlugin()
    ],
}

新建public文件夹,里面新建index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模版文件</title>
</head>
<body>
    <div id="app">
    </div>
</body>
</html>

新建src文件夹,里面新建index.js:

let a=12;
console.log(a);

new HtmlWebpackPlugin()中添加配置项:

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

module.exports={
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./dist')
    },
    mode:'development',
    devtool: 'inline-source-map' ,
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html',//指向的html
            filename:'index.html',//被打包后的html文件名,
            inject:'body'//js打包后的生成位置
        })
    ],
}

配置package.json:

{
  "scripts": {
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "devDependencies": {
    "html-webpack-plugin": "^5.6.3",
    "webpack": "^5.98.0",
    "webpack-cli": "^6.0.1",
    "webpack-dev-server": "^5.2.0"
  }
}

npm run build进行打包,打包之后在dist生成相应文件:
在这里插入图片描述
dist/index.html中内容如下:
在这里插入图片描述

1.5 每次打包时先自动清除原有打包文件

output里面加入属性clean:true:

output:{
    filename:'bundle.js',
    path:path.resolve(__dirname,'./dist'),
    clean: true
},

2.资源模块

module.rule.typestring类型,可设置值有 'javascript/auto' | 'javascript/dynamic' | 'javascript/esm' | 'json' | 'webassembly/sync' | 'webassembly/async' | 'asset' | 'asset/source' | 'asset/resource' | 'asset/inline'。具体用法参见https://www.webpackjs.com/configuration/module/#ruletype,下面只是简单介绍几个常见的用法。

在讲解资源模块这个之前,需要引入vue,这样子好做测试。可以上网查询一下怎么从0到1搭建vue脚手架,后续我也会专门讲这个。在这先简单讲述一下:

2.1 配置Vue

安装vue:

npm i vue@2

安装vue-loadervue-template-compiler(vue模板编译器):

npm i -D vue-loader vue-template-compiler

原本webpack只能解析JSJSON文件,在js上加载css等其他文件需要通过loader来实现:

npm i -D css-loader style-loader

webpack.config.js中追加module属性,并且引入vue-loader插件;并且追加关于css样式的loader配置:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
//引入vue-template-compiler插件
const {VueLoaderPlugin} = require('vue-loader');

module.exports={
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./dist'),
        clean: true
    },
    mode:'development',
    devtool: 'inline-source-map' ,
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
             {
                test:/\.css$/,
                use:['style-loader','css-loader']
            }
        ]
    },
    plugins:[
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            template:'./public/index.html',//指向的html
            filename:'index.html',//被打包后的html文件名,
            inject:'body'//js打包后的生成位置
        })
    ],
}

新建一个App.vue文件:

<template>
   <div id="app">
   </div>
</template>

<script>
export default {
  name: "App"
}
</script>
<style scoped>

</style>

index.js入口文件中初始化vue实例:

import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip=false;
new Vue({
    el:"#app",
    //渲染函数,这里h是形参,它表示的是createElement
    //执行到这儿,创建一个app对应的vnode虚拟节点,把虚拟节点最终变成
    //真实的dom节点插入到App里面替换掉id为app的元素
    render:h=>h(App),
})

项目大致目录结构如下:
在这里插入图片描述
运行npm run start,发现报错:
在这里插入图片描述
这是因为vue-loader版本不兼容,把vue-loader修改为如下版本,好与vue-template-compiler版本匹配:

"vue-loader": "^15.10.1",

再次运行就不报错了。

2.2 resource资源

发送单独文件并导出Url

我们先在App.vue中追加一些会用到图片的元素,并且在src下新建一个images,在里面放置一张图片resource-original-img.jpg:

<template>
  <div id="app">
    <div class="resource-img-area">
    </div>
  </div>
</template>

<script>
export default {
  name: "App"
}
</script>
<style scoped>
.resource-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/resource-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
</style>

webpack.config.jsmodule.rules中追加配置:

{
     test:/resource-original-img.jpg/,
     type:'asset/resource'
}

并且在output中追加配置:

output:{
    ...
    assetModuleFilename:'images/[name].[contenthash][ext]',
},

然后运行项目,打开控制台看网络面板:
在这里插入图片描述
打包npm run build之后:
在这里插入图片描述

2.3 inline资源

导出资源的Data Url,实际展示出的文件的urlbase64格式。
App.vue中继续追加一些会用到图片的元素,在images里面放置一张图片inline-original-img.jpg:

<template>
  <div id="app">
    <div class="resource-img-area"></div>
    <div class="inline-img-area"></div>
  </div>
</template>

<script>
export default {
  name: "App"
}
</script>
<style scoped>
.resource-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/resource-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.inline-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/inline-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
</style>

webpack.config.jsmodule.rules中追加配置:

 {
     test:/inline-original-img.jpg/,
     type:'asset/inline'
 }

然后运行项目,打开控制台看网络面板,是base64格式:
在这里插入图片描述
打包npm run build之后,不会单独创建新jpg文件:
在这里插入图片描述

2.4 source资源

导出资源的源代码。

source的形式是将资源的内容原样应用到项目中,不生成文件,只利用内容,例如,在src下新建txt/a.txt文件:
在这里插入图片描述
webpack.config.jsmodule.rules中追加配置:

 {
    test: /\.txt$/,
    type: 'asset/source'
 }

App.vue中追加内容:

<template>
  <div id="app">
    <div class="resource-img-area"></div>
    <div class="inline-img-area"></div>
    <div class="source-txt-area"></div>
  </div>
</template>

<script>
import helloWebpack from './txt/a.txt'

export default {
  name: "App",
  mounted() {
    const SourceTxtArea=document.querySelector(".source-txt-area");
    SourceTxtArea.textContent=helloWebpack;
  },
}
</script>
<style scoped>
.resource-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/resource-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.inline-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/inline-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
</style>

运行项目之后,txt文件中的内容显示在界面:
在这里插入图片描述
执行npm run build,在dist中没有新增资源。

2.5 asset资源

表示通用资源。可以通过配置临界值实现自动切换资源格式(在resource和inline之间切换),更加灵活可控(默认大小为8kb,可以自己设置)。

App.vue中继续追加一些会用到图片的元素,在images里面放置一张图片asset-original-img.jpg:

<template>
  <div id="app">
    <div class="resource-img-area"></div>
    <div class="inline-img-area"></div>
    <div class="source-txt-area"></div>
    <div class="asset-img-area"></div>
  </div>
</template>

<script>
import helloWebpack from './txt/a.txt'
export default {
  name: "App",
  mounted() {
    const SourceTxtArea=document.querySelector(".source-txt-area");
    SourceTxtArea.textContent=helloWebpack;
  },
}
</script>
<style scoped>
.resource-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/resource-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.inline-img-area{
   width: 100px;
   height: 100px;
   background-image: url("./images/inline-original-img.jpg");
   background-repeat: no-repeat;
   background-size: 100% 100%;
 }
.asset-img-area{
  width: 100px;
  height: 100px;
  background-image: url("./images/asset-original-img.jpg");
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
</style>

webpack.config.jsmodule.rules中追加配置:

 {
      // 这张图片是29.8KB左右
      test:/asset-original-img.jpg/,
      type:'asset',
      parser:{
          dataUrlCondition:{
               maxSize:32 * 1024 //当图片大小大于32KB时生成资源文件,否则为base64url
          }
      }
}

由于该图片大小小于限制值,所以会以base64url的格式显示:
在这里插入图片描述
如果把限制改为24KB:

 {
      // 这张图片是29.8KB左右
      test:/asset-original-img.jpg/,
      type:'asset',
      parser:{
          dataUrlCondition:{
               maxSize:24 * 1024 //当图片大小大于24KB时生成资源文件,否则为base64url
          }
      }
}

由于该图片大于限制值,所以会以resource的格式显示:
在这里插入图片描述

3.loader

原本webpack只能解析JSJSON文件,在js上加载css等其他文件需要通过loader来实现。

3.1 加载css

安装:

npm i -D css-loader style-loader

在规则中添加css规则:

module:{
    rules:[
        ...
            {
                test:/\.css$/,
                //注意顺序。链式调用会从后往前执行
                use:['style-loader','css-loader']
            }
    ]
}

3.2 加载less或者sass(scss)

less为例子。安装:

npm i -D less-loader less

在规则中添加less

{
    test:/\.(css|less)$/,//注意兼容less和css
    use:['style-loader','css-loader','less-loader']//多个处理器,注意是从后往前执行
},

3.3 抽离和压缩css

3.3.1 抽离css文件

本地安装mini-css-extract-plugin(该插件之后可以取代style-loader

npm install mini-css-extract-plugin -D

plugin中添加:

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports={
    ...
    plugins:[
        ...
        new MiniCssExtractPlugin({
            filename: "style/[name].css"
        })
    ],
}

在原有css规则中替换掉style-loader

{
    test:/\.(css|less)$/,//注意兼容sass和scss
    use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
    //多个处理器,注意是从后往前执行
},

npm run build打包之后会在diststyle文件夹下生成css文件:
在这里插入图片描述

3.3.2 压缩css文件

本地安装css-minimizer-webpack-plugin

npm install css-minimizer-webpack-plugin -D

和其它插件不同,它需在optimization中添加配置,并且mode要切换为production

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");


module.exports={
    ...
    mode:'production',
    ...
    optimization:{
        minimizer: [
            new CssMinimizerPlugin()
        ]
    }
}

npm run build打包之后,css文件被压缩:
在这里插入图片描述

3.4 加载图片

依赖于资源模块,在上面资源模块那一节我详细讲述过。

3.5 加载字体

依旧依赖于资源模块。

https://www.dafont.com/找到一个字体包进行安装。放在src/fonts文件夹下:
在这里插入图片描述
App.vue中引入字体:

<template>
  <div id="app">
    ...
    <div class="fonts-area">
      <span>来测试一下字体:hello webpack!!!</span>
    </div>

  </div>
</template>

<script>
...
</script>
<style lang="less" scoped>
...
@font-face {
  font-family: "custom-font";
  src: url('./fonts/KOMIKAX_.ttf') format('truetype');
}
.fonts-area{
  width: 100%;
  height: 30px;
  font-family:"custom-font";
  font-size:24px;
}
</style>

运行之后查看界面:
在这里插入图片描述
给字体添加规则:

rules:[
    ...
  {
        test:/\.(woff|woff2|eot|ttf|otf)$/,
        type:'asset/resource'//会在dist生成新资源
  }
]

修改一下output.assetModuleFilename配置:
在这里插入图片描述
npm run build会在dist中的resources文件夹生成字体新文件:
在这里插入图片描述

3.6 加载数据(json、csv、tsv、xml)

对于jsonwebpack本身就支持;其它类型需要根据数据格式安装不同的loader模块

本地安装csv-loaderxml-loader:

npm install csv-loader xml-loader -D

添加规则(不添加规则,直接在代码中引入xmlcsv文件会报错):

{
    test:/\.(csv|tsv)$/,
    use:'csv-loader'
},
{
    test:/\.xml$/,
    use:'xml-loader'
}

下面来测试一下:新建一个xml文件:
在这里插入图片描述
App.vue中添加:

<script>
import helloWebpack from './txt/a.txt';
import bookstore from './xml/bookstore.xml'
export default {
  created() {
  },
  mounted() {
    const box3=document.querySelector(".box3");
    box3.textContent=helloWebpack;
    console.log(bookstore)

  },
  methods:{

   },
}
</script>

控制台如下:
在这里插入图片描述

3.7 自定义JSON模块parser

通过自定义parser替代特定的webpack loader,可将任何tomlyamljson5的文件作为模块导入。

本地安装tomlyamljson5

npm install toml yaml json5 -D

添加规则:

const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')

module.exports={
    ...
    module:{
        rules:[
            ...
            {
                test:/\.toml$/,
                type:'json',
                parser:{
                    parse:toml.parse
                }
            },
            {
                test:/\.yaml$/,
                type:'json',
                parser:{
                    parse:yaml.parse
                }
            },
            {
                test:/\.json5$/,
                type:'json',
                parser:{
                    parse:json5.parse
                }
            }
        ]
    },
   ...
}

新建一个json文件:
在这里插入图片描述
App.vue中添加:

<script>
...
import fruits from "./json/fruits.json"
export default {
  created() {
  },
  mounted() {
    ...
    console.log(fruits)

  },
  methods:{

   },
}
</script>

控制台如下:
在这里插入图片描述

4.babel-loader(javascript兼容性处理)

js虽然可被webpack接受,但如果是es6及以上的新增语法,转为es5及以下的版本,则可能会出现兼容性报错问题。所以需要babel-loader

src/index.js引入es6语法:
在这里插入图片描述
打包后去dist/bundle.js文件中找testA变量:
在这里插入图片描述
发现打包后的语法还是用的es6的,这可能在某些低版本浏览器上不兼容。

安装babel-loader@babel/core(babel核心库),@babel/preset-env(预设配置集合):

npm i -D babel-loader @babel/core @babel/preset-env

如需兼容async/await语法则还需要添加regeneratorRuntime模块:

npm install --save @babel/runtime
npm install --save-dev @babel/plugin-transform-runtime

添加规则:

{

    test:/\.m?js$/,//注意问号是可选的意思,表示前面一个字符选和不选
    exclude:/node_modules/,//注意:需在exclude中排除node_modules包
    use:{
        loader:'babel-loader',
        //配置对象
        options:{
            //   预设
            presets:['@babel/preset-env'],
            plugins:[
                [
                    '@babel/plugin-transform-runtime'
                ]
            ]
        }
    }
}

打包后dist/bundle.js文件中找testA变量:
在这里插入图片描述
ES6的语法已经转为了ES5了。

5.代码分离(Code Splitting)

项目中若存在多个入口文件时,则需要代码分离;若存在多个模块共用的代码时,也需要分离代码来防止重复打包。(上述有两种情况都需要代码分离)

5.1 使用entry配置手动分离代码

配置如下:

entry:{
    index:'./src/index.js',
    another:'./src/another-module.js'
},
output:{
    filename:'[name].bundle.js',
    ...
},

打包后在dist文件夹中生成两个bundle文件:
在这里插入图片描述

这种方法有一些不好的地方:当几个入口文件存在相同文件时,会重复打包至各自模块。

如下,在src/common下有一个common.js文件:
在这里插入图片描述
index.jsanother-module.js中都引用这个模块:
在这里插入图片描述
在这里插入图片描述
执行打包命令之后,会把common.js内容重复打包到两次,分别在index.bundle.jsanother-module.bundle.js中:

在这里插入图片描述
在这里插入图片描述

5.2 用entry dependencies去重和分离

src/common中新建module2.js文件:

const module2=100;
export default module2;

src/index.jssrc/another-module.js中都引入module2.js这个模块:
在这里插入图片描述
在这里插入图片描述
添加配置:

 entry:{
        index:{
            import:'./src/index.js',
            dependOn: "sharedModule",
        },
        another:{
            import:'./src/another-module.js',
            dependOn:"sharedModule",
        },
        sharedModule: [path.resolve(__dirname,'./src/common/common.js'),path.resolve(__dirname,'./src/common/common2.js')], //默认从node_modules中寻找
    },

打包后多出的sharedModule.bundle.js即为定义中的模块,此时common.jscommon2.js是共用的,做到了模块的去重和分离。总共生成了index.bundle.jsanother-module.bundle.jssharedModule.bundle.js
在这里插入图片描述

5.3 用SplitChunksPlugin去重和分离

配置时依旧可以采用独立命名。

entry:{
    index:'./src/index.js', 
    another:'./src/another-module.js'
},

optimization优化配置项中添加splitChunks:

optimization:{
    ...
    splitChunks:{
        chunks:'all'
    }
},

npm run build之后就可以看到生成了一个公共文件。
在这里插入图片描述
SplitChunksPlugin的主要作用是将代码分割成多个块(chunks),这些块可以在需要时按需加载,从而减少初始加载时间。具体功能包括:

  • 提取公共模块‌:将多个入口共享的模块提取到一个单独的chunk中,减少重复代码。‌
  • 按需加载‌:将不常用的代码或模块延迟加载,直到真正需要时才加载,减少初始加载时间。‌
  • 优化缓存‌:通过合理的缓存策略,提高缓存命中率,进一步减少加载时间。

下面是它的一个示例:

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '\~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

这些配置选项包括:

  • chunks‌:
    定义如何分割chunks,可以是’async’(动态导入的文件其静态依赖会根据规则分离)、‘all’(所有文件都会根据规则分离)、‘initial’(入口文件的静态依赖会根据规则分离)。
  • minSize‌:定义分割的最小体积(以字节为单位)。
  • maxSize‌:定义分割的最大体积。
  • minChunks‌:定义模块被多少个chunks共享时才会被分割。
  • maxAsyncRequests‌:定义异步请求的最大数量。
  • maxInitialRequests‌:定义初始请求的最大数量。
  • automaticNameDelimiter‌:用于生成chunk名称的分隔符。
  • cacheGroups‌:定义缓存组,可以进一步优化缓存策略。

5.4 动态导入拆分代码

5.4.1 基本场景

其实就是编译器一看到import(文件路径),就会把这个文件视为要拆分的文件,自动会把它单独打一个包。实现代码如下:

// 假设我们有一个moduleA.js文件
// src/modules/moduleA.js
export default '我是module A!';

// 在主文件中,我们可以这样动态引入moduleA.js
// src/index.js
function loadModuleA() {
    return import(/* webpackChunkName:'moduleA' */'./mudules/moduleA.js')
        .then(moduleA => {
            console.log(moduleA.default); // 输出: '我是module A!'
        })
        .catch(error => {
            console.error('Module loading failed:', error);
        });
}
// 当你需要的时候调用这个函数
loadModuleA();

这样子就可以把moduleA.js单独打包!!!
在这里插入图片描述

5.4.2 动态导入的作用1----懒加载(按需加载)

只有在一些代码块完成某操作时候,才会引用或即将引用另外一些新的代码块。比如第一次加载首屏时候,某些页面是不会加载的。

//src/modules/add.js
export function add(...args){
    let sum=0;
    for (let i=0;i<args.length;i++){
        sum+=args[i]
    }
    return sum;
}
//src/App.vue
<template>
  <div id="app">
    ...
    <div class="add-area">
      <button @click="addFun">
        加法
      </button>
    </div>
  </div>
</template>

<script>
export default {
  ...
  methods:{
    addFun(){
     //这里有句注释,我们把它称为 webpack 魔法注释: webpackChunkName: 'add' ,
     //告诉webpack打包生成的文件名为 add。
      import(/* webpackChunkName:'add' */'./modules/add.js').then(({add})=>{
        console.log(add(3,4,5))
      })
    }
  }
}
</script>
<style lang="less" scoped>
...
</style>

点击页面上加法按钮,控制台网络上会单独加载add.bundle.js的文件:
在这里插入图片描述

5.4.3 动态导入的作用2----预获取prefetch/预加载preload

如果我们需要加载的资源很庞大,这时候用上面的方法就不太合适,因为这需要一定的加载时间。这个时候我们可以选择使用预获取prefetch或者预加载preload,在网络空闲时候提前加载好某些资源

Webpack v4.6.0+增加了对预获取和预加载的支持。

在声明import时,使用下面这些内置指令,可以让webpack输出 "resource hint(资源提示)",来告知浏览器:

  • webpackPrefetch(预获取):将来某些导航下可能需要的资源
  • webpackPreload(预加载):当前导航下可能需要资源

下面来看一个预获取的例子

//src/index.js
//测试预获取
const button = document.createElement('button')
button.textContent = '点击执行加法运算'
button.addEventListener('click', () => {
    import(/* webpackChunkName: 'minus', webpackPrefetch: true */
        './modules/minus.js').then(({ minus }) => {
        console.log(minus(10, 5))
    })
})
document.body.appendChild(button)

如上添加魔法注释:webpackPrefetch: true。告诉webpack执行预获取。这会生成<link rel="prefetch" href="./modules/minus.js">并追加到页面头部,指示着浏览器在闲置时间预取minus.js文件。启动服务,在浏览器上查看:
在这里插入图片描述
我们发现,在还没有点击按钮时,minus.bundle.js就已经下载下来了。同时,在index.htmlwebpack自动添加了一句:
在这里插入图片描述
点击按钮,会立即调用已经下载好的 math.bundle.js 文件中的 add 方法。

prefetch指令相比,preload指令有许多不同之处:

  • preload chunk会在父chunk加载时,以并行方式开始加载。prefetch chunk会在父chunk加载结束后开始加载。
  • preload chunk具有中等优先级,并立即下载。prefetch chunk在浏览器闲置时下载。
  • preload chunk会在父chunk中立即请求,用于当下时刻。prefetch chunk会用于未来的某个时刻。
  • 浏览器支持程度不同。

创建一个print.js文件

//src/modules/print.js
export const print = () => {
 console.log('preload chunk.')
}

追加index.js文件:

//src/index.js
//测试预加载
const button2 = document.createElement('button')
button2.textContent = '点击执行字符串打印'
button2.addEventListener('click', () => {
    import(/* webpackChunkName: 'print', webpackPreload: true */
        './modules/print.js').then(({ print }) => {
        print()
    })
})
document.body.appendChild(button2)

6.缓存

打包后的dist文件部署到服务器以后便能被浏览器客户端所访问,由于浏览器的解析特性会优先选择缓存资源,我们要确保文件发生更新时浏览器能识别到,所以需要对输出文件的文件名做处理。

ps:我们既需要webpack编译的文件被客户端缓存,又需要在文件内容变化时候,能请求到新的文件。

6.1 输出文件的文件名

我们可以通过替换output.filename中的substitutions设置,来定义输出文件的名称。webpack提供了一种使用 substitution(可替换模板字符串)的方式,通过带括号字符串来模板化文件名,其中,[contenthash] substitution 将根据资源内容创建出唯一的hash。当资源内容发生变化时, [contenthash] 也会发生变化。

output:{
    filename:'[name].[contenthash:7].bundle.js',
    ...
},

在这里插入图片描述

6.2 缓存第三方库(如lodash)

将第三方库 (library) (例如lodash) 提取到单独的vendor chunk文件中,是比较推荐的做法。因为第三方库文件很少像本地源码那样会频繁修改,所以我们可以利用client的长效缓存机制,命中缓存来消除请求,并减少向server获取资源,同时保证clientserver的代码版本一致。

optimization:{
        minimizer: [
            new CssMinimizerPlugin()
        ],
        splitChunks:{
            chunks:'all',
            cacheGroups:{
                vendor:{
                    test:/[\\/]node_modules[\\/]/,
                    name:'vendors',
                    chunks:'all'
                }
            },
        }
}

安装lodash第三方库:

  "lodash": "^4.17.21",

src/index.js中追加内容:
在这里插入图片描述
打包后会将第三方库提取到单独的vendor chunk文件中:
在这里插入图片描述

6.3 将js 文件放到一个文件夹中

目前,全部 js 文件都在 dist 文件夹根目录下,我们尝试把它们放到一个文件夹中,这个其实也简单,修改配置文件:

  filename:'scripts/[name].bundle.js',

打包后在scripts中生成js文件:
在这里插入图片描述

截止目前,我们已经把JS文件、样式文件及图片等资源文件分别放到了scriptsstyleresources 三个文件夹中。

7. 拆分开发环境和生产环境配置

现在,我们只能手工的来调整mode选项,实现生产环境和开发环境的切换,且很多配置在生产环境和开发环境中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等。

7.1 公共路径

publicPath用于配置 Webpack 打包后的资源路径,指示浏览器加载 JavaScriptCSS、图片等静态文件时的根路径。它可以设置为相对路径、绝对路径或完整的 URL(例如CDN 地址)。

7.1.1 publicPath 的配置形式

绝对路径:
当你设置 publicPath 为绝对路径时,Webpack 会将所有资源的加载路径都从该路径开始。

module.exports = {
  output: {
    //dev这意味着所有的资源会从网站的根目录下的 /dev/ 路径加载
    publicPath: '/dev/'
  }
};

这时候,需要输入网址http://localhost:8080/dev/,查看控制台网络,发现 http://localhost:8080/scripts/index.bundle.js变成了 http://localhost:8080/dev/scripts/index.bundle.js
在这里插入图片描述
但一般情况下我们都会去掉dev,所以开发阶段我们应该像下面这样子写:

 publicPath: '/'

如上,在开发阶段publicPath/,我们用vscode打开项目然后找到dist/index.html并且右键,点击open with live Server用服务器模拟运行打包后的index.html
在这里插入图片描述
发现页面一片空白,并且所有资源的路径都不对。比如http://127.0.0.1:5500/style/index.css这明显是错的,我们想要的应该是http://127.0.0.1:5500/dist/style/index.css(多一个dist)。

module.exports = {
  output: {
    publicPath: '/dist/'
  }
};

再次找到dist/index.html并且右键,点击open with live Server用服务器模拟运行打包后的index.html
在这里插入图片描述
相对路径:
如果你设置 publicPath 为相对路径,资源的加载路径会相对于当前页面的位置。

module.exports = {
  output: {
    publicPath: './'
  }
};

假设页面路径是 https://example.com/page/,那么资源将从 https://example.com/page/ 开始加载。

动态配置(开发环境与生产环境不同的路径):
可以根据环境(开发或生产)动态调整publicPath,这在使用 CDN 或根据部署环境的不同需要调整路径时特别有用。

output: {
    publicPath: process.env.NODE_ENV === 'production' ? '/dist/' : '/'
}

开发环境:publicPath'/',表示资源相对于当前页面加载。
生产环境:publicPath'/static/',所有资源将从 https://example.com/static/ 加载。

7.1.2 如何选择合适的publicPath 配置

  • 开发模式:在开发环境下,通常使用相对路径(如 '/' './')),这样可以在本地开发时,资源加载会相对当前页面路径。
  • 生产模式:在生产环境中,通常会使用绝对路径或者 CDN 路径(如 '/dist/''https://cdn.xxx.com/dist/')来提高性能和可靠性。
  • 混合模式:有时候你希望根据环境变量(如process.env.NODE_ENV)来动态选择路径,这样开发和生产环境可以使用不同的路径配置。

7.2 环境变量

想要消除webpack.config.js在 开发环境 和 生产环境 之间的差异,你可能需要环境变量。

webpack命令行 环境配置的--env参数,可以允许你传入任意数量的环境变量。而在webpack.config.js 中可以访问到这些环境变量。例如,--env production--env goal=local。这时候我们需要修改一下 webpack配置,要使用env变量,你必须将module.exports转换成一个函数:

//...
module.exports = (env) => {
   console.log(env)
   return {
   //...
   // 根据命令行参数 env 来设置不同环境的 mode
      mode: env.production ? 'production' : 'development',
   //...
   }
}

然后执行npx webpack --env production。打印的结果如下:
在这里插入图片描述

7.3 拆分配置文件

上面是通过函数来区分生产和开发环境。也可以将生产和开发环境单独放到不同的配置文件中。如 webpack.config.dev.js (开发环境配置)和webpack.config.prod.js (生产环境配置)。在项目根目录下创建一个配置文件夹 config 来存放他们。
两个文件内容分别如下:

//webpack.config.dev.js
module.exports={
        entry: {
            index: "./src/index.js",
            another: "./src/another-module.js",
        },
        output: {
            filename: "scripts/[name].bundle.js",
            path: path.resolve(__dirname, "./dist"),
            clean: true,
            assetModuleFilename: "resources/[name].[contenthash][ext]",
        },
        mode: 'development',
        ...   
};
//webpack.config.prod.js
module.exports={
        entry: {
            index: "./src/index.js",
            another: "./src/another-module.js",
        },
        output: {
            filename: "scripts/[name].bundle.js",
            path: path.resolve(__dirname, "./dist"),
            clean: true,
            assetModuleFilename: "resources/[name].[contenthash][ext]",
            publicPath: "/dist/",
        },
        mode: 'production',
        ...   
};

分别运行这两个文件:

//开发环境:
npx webpack serve -c ./config/webpack.config.dev.js
//生产环境(没有serve):
npx webpack -c ./config/webpack.config.prod.js

7.4 npm脚本

配置npm脚本来简化命令行的输入,这时可以省略npx:

    "start": "webpack serve -c ./config/webpack.config.dev.js",
    "build": "webpack -c ./config/webpack.config.prod.js"

这时候就可以用npm run startnpm run build了。执行npm run start没问题,执行npm run build发现打包的文件放在了config/dist中:
在这里插入图片描述
排查原因,是因为webpack.config.prod.js中的output中的path设置错误:
在这里插入图片描述
改为以下配置即可(多出一个点):

        path: path.resolve(__dirname, "../dist"),

7.5 提取公共配置

这时,我们发现这两个配置文件里存在大量的重复代码,可以手动的将这些重复的代码单独提取到一个文件里,创建webpack.config.common.js,配置公共的内容:

...

module.exports={
    //用SplitChunksPlugin去重和分离
    entry: {
        index: "./src/index.js",
        another: "./src/another-module.js",
    },
    output: {
        // 注意这个dist的路径设置成上一级
        path: path.resolve(__dirname, "../dist"),
        clean: true,
        assetModuleFilename: "resources/[name].[contenthash][ext]",
    },
    module: {
        ...
    },
    plugins: [
        ...
    ],
    optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    chunks: "all",
                },
            },
        },
    },
};

改写webpack.config.dev.js

module.exports = {
  output: {
    // 开发环境不需要配置缓存,所以不需要hash
    filename: "scripts/[name].js",
  },
  mode: "development",
  // 配置 source-map
  devtool: "inline-source-map",
};

改写webpack.config.prod.js

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  output: {
    filename: "scripts/[name].[contenthash].js",
    publicPath: "/dist/",
  },
  mode: "production",
  optimization: {
    // 生产环境 css 压缩
    minimizer: [new CssMinimizerPlugin()],
  },
};

7.6 合并配置文件

配置文件拆分好后,新的问题来了,如何保证配置合并没用问题呢?webpack-merge 这个工具可以完美解决这个问题。

安装 webpack-merge :

npm install webpack-merge -D

创建 webpack.config.js ,合并代码:

//config/webpack.config.js 
const {merge} = require('webpack-merge')

const commonConfig = require('./webpack.config.common')
const productionConfig = require('./webpack.config.prod')
const developmentConfig = require('./webpack.config.dev')

module.exports=((env)=>{
    switch(true){
        case env.development:
            return merge(commonConfig,developmentConfig)
        case env.production:
            return merge(commonConfig,productionConfig)
        default:
            return new Error('No matching configuration was found')
    }
})

修改package.json中的script脚本:

 "scripts": {
    "start": "webpack serve --env development -c ./config/webpack.config.js",
    "build": "webpack --env production -c ./config/webpack.config.js"
  },

这样子就大功告成了。npm run buildnpm run serve都会成功。

内容概要:本文围绕“考虑电动汽车聚合可调节能力的含波动性电源电氢耦合系统多目标优化运行”展开研究,提出了一种基于Matlab代码实现的多目标优化模型。该模型深度融合电-氢耦合系统与高比例波动性可再生能源(如风电、光伏),充分挖掘电动汽车(EV)集群作为移动储能单元的灵活调节潜力,通过聚合调控提升系统对新能源的消纳能力与运行经济性。研究系统构建了电动汽车可调度能力、电解水制氢与储氢动态过程、多能源协同互补的优化调度框架,并结合智能优化算法实现经济性、低碳性与运行稳定性等多重目标的协同优化。文中配套提供了完整的Matlab仿真代码、相关数据及可能的论文支撑材料,极大地方便了模型的复现、验证与后续深化研究。; 适合人群:具备电力系统、综合能源系统、优化理论或新能源技术等相关领域基础知识的研究生、科研人员,以及从事新型电力系统规划、清洁能源消纳与智慧能源管理的工程技术人员。; 使用场景及目标:①开展高渗透率可再生能源接入下的综合能源系统多目标优化调度研究;②探究电动汽车集群在电网削峰填谷、平抑新能源出力波动及提供辅助服务方面的应用价值与潜力;③学习并掌握电氢耦合系统的建模方法、多目标优化求解技术及其在Matlab/Simulink环境下的仿真实现流程。; 阅读建议:此资源不仅提供可运行的代码,更蕴含了前沿的科研思路与创新方法,建议读者结合所提供的代码、数据与可能的论文文档,系统性地学习从问题建模、算法设计到仿真分析的完整科研过程,并重点关注其中关于需求侧资源聚合、多能互补协同与绿色低碳运行的核心理念。
内容概要:本文档名为《经济学期刊论文复现:数字化转型能促进企业的高质量发展吗》,表面上聚焦于经济学领域中数字化转型对企业高质量发展影响的研究,实则是一份涵盖多学科交叉的科研仿真代码资源合集。资源以Matlab、Simulink、Python为主要工具,系统整合了电力系统仿真、微电网优化调度、路径规划、信号处理、图像处理、机器学习预测模型等方向的可复现算法与仿真模型。尽管标题指向经济学实证分析,但内容重心在于提供顶级期刊论文的复现代码,如企业全要素生产率(TFP)测算方法(OL、FE、LP、OP、GMM)、风光储氢系统优化、需求响应与综合能源系统调度等,并融合智能优化算法与深度学习技术进行数据建模与预测分析,体现出极强的工程化与科研实用性。; 适合人群:具备一定编程基础,熟练掌握Matlab/Simulink/Python等仿真工具,从事工程仿真、经济实证研究或交叉学科科研工作的研究生、高校教师及科研人员。; 使用场景及目标:① 复现经济学顶刊论文中的计量经济模型,深入探究数字化转型对企业全要素生产率的影响机制;② 借助提供的代码资源开展电力系统故障仿真、微电网优化、多能系统调度等科研项目的算法验证与仿真分析;③ 应用机器学习与深度学习模型完成负荷预测、风电光伏出力预测、电池健康状态评估等典型实证任务; 阅读建议:此资源虽冠以经济学论文之名,实质为多领域高价值仿真代码集成,建议读者依据自身研究方向筛选适配内容,优先关注“顶刊复现”“论文复现”类项目,结合配套数据与代码进行实证推演,并通过公众号“荔枝科研社”获取完整资料与持续技术支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

太阳与星辰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值