webpack的5个核心概念

webpack

概念

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

核心概念

1. 入口(entry)

entry对象是用于webpack查找启动并构建bundle。entry是应用程序的起点入口,从这个起点开始,程序启动执行。如果传递的是数组,那么数组的每一项都会执行。入口起点(entry point)指示Webpack应该使用哪个模块

分为一下几种情况

  1. 多入口配置:同时构建多个独立的应用程序或页面,可以在Webpack配置中使用对象语法来定义多个入口点

    module.exports = {
      entry: {
        app: './src/app.js',
        adminApp: './src/adminApp.js',
      },
    };
    

    ​ 这个例子中,app和adminApp都是入口点,Webpack会分别为他们构建依赖图并生成对应的输出文件。

  2. 数组入口

    如果入口是一个数组。WebPack会处理数组中所有模块并未每个模块创建一个chunk。这通常用于多页应用,每个html都有一个入口点。例如:

    module.export = {
    	entry:['./src/page1.js','./src/page2.js']
    }
    

    这个例子中page1,page2都会作为入口点处理

默认值是./src/index.js,可以通过https://webpack.docschina.org/configuration中配置entry属性,来指定一个(或多个)不同入口的起点。例如:

module.exports = {
    entry: './path/to/my/entry/file.js'
}
entry值的类型
  • 字符串:单入口,打包形成一个块chunk,最终只会输出一个bundle文件,chunk的名称默认是main
  • 数组:多入口,所有的入口文件最终也只会形成一个chunk,最终输出一个bundle文件,chunk的名称默认为main。一般只用在HMR功能中让html热更新生效
  • 对象:多入口,有多少个key就会形成多少个chunk,也就输出多少个bundle文件,每个键(key)会是chunk的名称。在对象类型中,每个key的值还可以是一个数组,不仅仅是一个字符串。

2. 输出(output)

output 指示webpack如何去输出、以及在哪里输出你的bundle、asset和其他你所打包或使用webpack载入的内容。输出bundle的默认值是./dist/main.js,其他生成文件默认放置在./dist文件夹中

可以通过在配置中指定一个output字段,来配置这些处理过程:

const path = require('path')

module.exports = {
    entry:'./path/to/my/entry/file.js'
    
    output:{
        path:path.resolve(_dirname,'dist')
        filename:'my-first-webpack.bundle.js'
    }
}

第一行的path是nodejs用于操作文件路径的核心模块。

path.resolve(__dirname, 'dist') 是一个Node.js的方法,它返回一个绝对路径。__dirname 是当前模块文件所在的目录的绝对路径,‘dist’ 是你希望输出文件的目录。所以 path.resolve(__dirname, 'dist') 将会返回当前目录下的 ‘dist’ 文件夹的绝对路径。

filename: 'my-first-webpack.bundle.js' 是指定输出文件的名称。在这个例子中,输出的文件名是 ‘my-first-webpack.bundle.js’。

2.1 output.filename(文件名和目录)

此选项决定了每次输出的bundle和目录和名称。这些bundle将写入output.path选项指定的目录下。

  1. 对于单个 入口起点 ,filename会是一个静态名称。
module.exportd = {
    output:{
        filename:'js/bundle.js'
    }
}

在这段代码中,filename属性指定了输出文件的名称和路径。具体来说,输出文件将被命名为bundle.js,并且位于js目录下。这意味着生成的输出文件将位于项目的js文件夹中,并命名为bundle.js

  1. 多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个bundle,应该使用其他方法来让每个bundle都有一个唯一的名称

    //多入口-使用入口名称
    module.exports = {
        output:{
            filename:'[name].bundle.js'
        }
    }
    //多入口--使用每次构建过程中,唯一的hash生成
    modle.export = {
        output:{
            filename:'[name],[hash].bundle.js'
        }
    }
    

    第一段代码表示使用入口名称作为输出文件名。例如,如果有两个入口文件entry1.jsentry2.js,那么生成的输出文件名将会是entry1.bundle.jsentry2.bundle.js

    第二段代码表示使用每次构建过程中唯一的hash值作为输出文件名的一部分。这样可以确保每次构建生成的文件名都是唯一的,避免浏览器缓存问题。例如,如果有两个入口文件entry1.jsentry2.js,那么生成的输出文件名将会是entry1,hash1.bundle.jsentry2,hash2.bundle.js,其中hash1hash2是每次构建过程中生成的唯一hash值。

2.2 output.path(文件目录)

output.path指定所有输出文件的目录,即将来所有资源输出的公共目录。path必须是绝对路径

moudle.exports = {
    output:{
        path:path.resolve(__dirname,'dist/assets')
    }
}

2.3 output.publicPath(引用资源的路径前缀)

publicPath指定的是html文件中的所有资源引入的公共路径前缀。但它不会对文件的路径造成影响,而是在html文件引入各种资源时,讲publicPath作为前缀加到引入的路径前面。

在 vue-cli 生成的 webpack 配置中,生产环境下 publicPath 的值默认是 ‘/’,即当前目录的根目录。

img

img

打包过后,我们打开 html 文件,可以看到 html 文件中引入的资源路径为:

img

可以看到,都在路径前面加了 / 符号。当我们打开浏览器访问生成的 html 文件时,会发现报错,资源访问不到,报404,此时资源的访问类似如下:

img

在服务器上可能会是如下,但访问一样可能会有问题。

img

我们可以将 publicPath 修改为相对路径,或者直接把它注释掉也行。

2.3.1 path与publicPath的区别
  • path指的是打包后在硬盘中的存储位置,是webpack所有的输出的路径,必须是绝对路径。比如:输出的js、图片HtmlWebpackPlugin文件等,都会存放在以path为基础的目录下
  • publicPath配置选项在各种场景中都非常有用。可以通过它来指定应用程序中所有资源的基础路径,主要是对页面引入的资源的路径做对应的补全
2.4 output.chunkFilename(非入口chunk的名称)

output.chunkFilename决定了非入口(non-entry)chunk文件的名称。也就是除了入口文件生成的chunk外,其他文件生成的chunk文件命名

module.exports = {
  //...
  output: {
    chunkFilename: 'js/[name]_chunk.js'   //非入口chunk的名称
  }
};

3. loader

webpack本身只能打包javascript文件和json文件,webpack本身不支持打包其他文件,如css、vue文件等,

但是可以通过loader让webpack处理这些类型的文件。loader可以将文件从不同语言(如Typescript)转换为javascript或将内联图像转换为打dataURLloader允许直接在JavaScript模块中import css文件

通过使用不同的loaderwebpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换 scsscss,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件。对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。

在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。

  2. use 属性,定义出在进行转换时,应该使用哪个 loader。

  3. include/exclude(可选):手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)

  4. query(可选):为loaders提供额外的设置选项

    const path = require('path')
    
    module.exports = {
        output:{
            filename:'my-first-webpack.bundle.js',
        },
        module:{
            rules:[
                {test:/\.txt$/,loader:'raw-loader'},
                {test:/\.css$/,use:['style-loader','css-loader']} 	//使用多个loader的话应该用use
            ]
        }
    }
    

    以上配置中,对一个单独的moudle对象定义了rules属性,里面包含两个必须属性:test和use。这相当与告诉webpack碰到require()/ import语句中被解析为.txt的路径时,在对它打包之前,先使用raw-loader转换一下。

    使用多个loader标签的话应该使用use,use数组中的loader执行顺序:从右到左,一次执行。比如上面的css文件,首先css-loader会编译成js加载到js文件夹中,然后再由style-loader创建标签,将js中的样式资源插入到head中。

    3.1 CSS-loader

    webpack提供两个工具处理样式表,css-loaderstyle-loader,二者处理的任务不同。css-loader使你能够使用类似import的方法来引入 css 文件,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中,由此就可以在JS文件中引入css文件了。

    //安装
    npm install --save-dev style-loader css-loader
    //css-loader版本太高编译可能会出错,降低版本比如css-loader@1.0.1可用
    
    //使用
    module.exports = {
       ...
        module: {
            rules: [
                {
                    test: /(\.jsx|\.js)$/,
                    use: {
                        loader: "babel-loader"
                    },
                    exclude: /node_modules/
                },
                {
                    test: /\.css$/,  //对同一个文件引入多个loader的方法。loader的作用顺序是后面的loader先开始作用
                    use: [
                        {
                            loader: "style-loader"
                        }, {
                            loader: "css-loader"
                        }
                    ]
                }
            ]
        }
    };
    

    假设有一个main.css文件

    body:{
        background:green;
    }
    

    为了让webpack能找到"main.css"文件,我们把它导入main.js文件中,如下:

    //main.js
    import React from 'react';
    import {render} from 'react-dom';
    import Greeter from './Greeter';
    import './main.css';//使用require导入css文件
     
    render(<Greeter />, document.getElementById('root'));
    

    **通常情况下css会和js打包到同一个文件夹中,并不会打包为一个单独的css文件。**不过通过合适的配置webpack也可以把css打包为一个单独的文件

4. 插件(plugin)

loader用于转换某些类型的模块,而插件则可以用于更广的任务,包括:打包优化、压缩、资源管理、注入环境等。插件目的在于解决loader无法实现的其他事。

要使用某个插件,需要通过npm安装,然后在plugins属性下添加该插件的一个实例。由于插件可以携带参数/选项,你可以在webpack,向plugins属性传入new实例。多数插件可以通过选项自定义,也可以在配置文件中因为不同目的而多次使用同一个插件

//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

在上面的示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动注入所有生成的 bundle。

4.1 BannerPlugin(添加版权说明)

添加一个给打包后代码添加版权声明的插件,该插件事webpack内置插件不用安装

const webpack = require('webpack');
module.exports = {
...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('wenxuehai版权所有,翻版必究')
    ],
};
4.2 Hot Module Replacement 插件(热加载)

模块热替换作用:是指在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:

  • 保留在完全重新加载页面时丢失的应用程序状态
  • 只更新变更内容,以节省宝贵的开发时间
  • 调整样式加快速度-几乎相当于在浏览器调试器中更改样式
4.2.1热更新配置

webpack.config.js

// 在这里做打包配置
const path = require('path'); // 引入node的path模块(loader模块)
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 引入热更新插件,为webpack中自带的插件
const webpack = require('webpack');

// Common.js语法
module.exports = {
  mode: 'development', // 默认模式为production,可不写,不写时打包运行命令行会有警告
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    hot: true, // 让webpack-dev-server开启热更新的功能
    hotOnly: true // 即便是hot配置(HMR)没有生效,也不让浏览器进行刷新,失效时不做其他处理
  }, 
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader'
      ]
    }]
  },
  plugins: [
  	new HtmlWebpackPlugin({
      template: './src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin() // 实例化热更新插件
  ],
  output: {
    filename: 'dist.js', // 打包之后的输出文件
    path: path.resolve(__dirname, 'dist') 
  }
}

// 配置完之后,一定要重新执行打包,不然配置项不会生效
// 样式文件变化,页面只修改展示样式,不会进行页面的刷新操作,也不会更改之前js操作的内容。

配置项的作用

  • hot:true === >如果编译报错,会抛出错误,你重新改成正确的,这个时候又会触发重新编译,整个浏览器会重新刷新!
  • hotOnly:true ===>如果编译报错,你再改成正确的,重新编译,浏览器不会刷新!
  • HotModuleReplacementPlugin为webpack自带的热更新插件,所以要直接引入webpack,在plugins配置项中进行实例化。
4.3 ExtractTextWebpackPlugin插件(抽离css

在默认情况下,webpack 不会将 css 样式作为一个独立文件,而是会将 css 也打包到 js 文件中,打包生成的 js 文件在渲染时会通过 js 语法来将样式通过 style 标签的形式来插入到页面中。

可以通过 ExtractTextWebpackPlugin 插件来将 css 样式独立成 css 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。

const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),   //ExtractTextPlugin 对每个入口 chunk 都生成一个对应的文件,所以当你配置多个入口 chunk 的时候,必须使用 [name], [id] 或 [contenthash]
      // allChunks: true,   //当使用 `CommonsChunkPlugin` 并且在公共 chunk 中有提取的 chunk(来自`ExtractTextPlugin.extract`)时,`allChunks` **必须设置为 `true`。
    }),
  ]
}
4.3.1 allChunks选项(是否也将异步加载的样式一起提取出来)

ExtractTextWebpackPlugin 插件的 allChunks 选项的默认值为 false。allChunks 选项的意思是是否需要将异步加载的样式一起提取出来。因为在默认情况下,就算使用了 ExtractTextWebpackPlugin 插件,如果该样式或者样式文件是异步加载的话,那么这些样式是不会被提取到独立的 css 文件中的,而是仍然会打包到 js 文件中。所以allChunks:false为默认值,默认是从 entry 的入口提取代码,但是不会提取异步加载的代码;allChunks:true则是提取所有模块的代码(包括异步加载的模块)到一个文件里面。如果使用了异步加载样式,但是 allChunks 又设为了 false,那么我们就需要设置 ExtractTextPlugin.extract 的 fallback, fallback是在异步代码加载的 css 代码没有被提取的情况下, 以style-loader的情况去加载异步组件的样式。

可以参考:https://github.com/sevenCon/blog-github/blob/master/articles/webpack学习笔记(2)-ExtractTextWebpackPlugin的使用.md

https://blog.csdn.net/weixin_41134409/article/details/88416356

5. 模式(mode)

通过选择develpment,production或none之中的一个,来设置mode参数,可以启用webpack内置在相应的环境下优化。其默认为production

module.exports = {
    mode:'production'
}

在配置文件中直接配置mode选项将告知webpack使用相应模式内置优化,mode选项有developmentproductionnone

  • development : 开发模式,打包的代码不会被压缩,开启代码调试,
  • production: 生产模式,则正好反之

img

将 mode 设为development或者production,webpack会自动同时也设置 process.env.NODE_ENV 的值,我们可以在任何文件夹中直接拿到该值。但如果只设置 NODE_ENV,则不会自动设置 mode。(在node中,全局变量 process 表示的是当前的node进程。process.env 属性包含着用户环境的信息。process.env 本身并不存在NODE_ENV这个属性,我们一般会自己去定义 NODE_ENV 属性,用它来判断是生产环境还是开发环境)

5.1 vue-cli项目mode配置详解

在webpack中,一般都会在配置文件中配置NODE_ENV的值。在使用vue-cli默认生成的项目中,NODE_ENV配置情况如下

//webpack.dev.conf.js 文件下,引入了 dev.env.js 文件
new webpack.DefinePlugin({
    'process.env':require('../config/dev.env')
})
//dev.env.js 文件中
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})
//webpack.prod.conf.js 文件下,引入了 prod.env.js 文件
const env = require('../config/prod.env')
new webpack.DefinePlugin({
      'process.env': env
}),
//prod.env.js 文件中
module.exports = {
  NODE_ENV: '"production"'
}

从上面可以知道,在开发环境下,配置文件将 NODE_ENV 配置成了 ‘development’;在生产环境下,配置文件将 NODE_ENV 配置成了 ‘production’。

我们在运行项目时,会执行 npm run dev 或者 npm run build,这两个命令时使用了开发环境或者生产环境的配置文件来生成运行项目,由此也对应着配置了对应的 NODE_ENV 的值,我们也就能够在项目的任一文件中(配置文件不一定,因为要看配置了 NODE_ENV 的值的配置文件有没有生效了才行)获取到对应的 NODE_ENV 的值。

5.2 process.env.NODE_ENV配置

process是node的全局变量,并且process有env这个属性,但是没有NODE_ENV这个属性。NODE_ENV变量并不是process.env变量并不是process.env直接就有的,而是通过设置得到的,但是NODE_ENV变量通常约定用于定义环境类型。

这个变量的作用是:我们可以通过判断这个变量区分开发环境或生产环境

(1)可以通过webpack的内置插件 DefinePlugin 来设置全局变量值:

new webpack.DefinePlugin({      'process.env.NODE_ENV': JSON.stringify('production')}),

设置完后在执行脚本上可以取到该值,比如:

// main.jsconsole.log(process.env.NODE_ENV);    //production

但是在webpack的配置文件 webpack.config.js 中取不到该值。

(2)通过 cross-env 包设置

先下载 cross-env 包:

cnpm i cross-env -D

设置 package.json 文件:

"build": "cross-env NODE_ENV=test webpack --config webpack.config.js"

此时在配置文件中可以取到该值(process.env.NODE_ENV),但是在可执行脚本中取不到,需要配合DefinePlugin 插件使用

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

只会_摆烂

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

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

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

打赏作者

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

抵扣说明:

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

余额充值