跳转到主要内容

主页内容

Webpack5

由 webadmin 发布于 阅读 84 次
1、Webpack介绍
1.1、webpack网址

webpack官网:https://webpack.js.org/

中文网站:https://www.webpackjs.com/、https://webpack.docschina.org/guides/

1.2、webpack简介

        webpack 是一个用于现代JavaScript 应用程序的打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图,然后将项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,可以在浏览器上直接运行。在webpack 看来,前端的所有资源文件(js、json、css、img、html..) 都会作为模块处理。

       思考一个问题:当前端项目越来越大的时候,我们还能按照以往的思维方式继续开发么?问题症结所在:文件管理、ES6代码的转换、项目的打包…

       解决方案:前端工程化(Webpack),在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化、标准化。例如砌一堵墙:怎么简单怎么来,只要材料准备齐全,一个人直接干就行。盖一栋楼:需要很多大型机器,也需要更多的人,重要的必须要规范化干活,避免发生危险。

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

总结:

  • webapck是一个构建工具,是基于node的,电脑上必须安装node,node版本需要大于16。
  • 打包器,是从入口开始,按照模块依赖进行打包,最终得到浏览器的可以识别的静态资源。
  • 从某种程度来说,webpack代表的是一种架构能力。
2、webpack的安装和配置
2.1、体验webpack(li隔行变色案例)

(1)、新建1_project空白文件夹,在该目录下运行npm init -y初始化包管理配置文件 package.json。

(2)、新建src目录,在src目录下新建index.html、和index.js

<!-- 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>
    <script src="./index.js"></script>
</head>
<body>
    <ul>
        <li>这是第 1 个li</li>
        <li>这是第 2 个li</li>
        <li>这是第 3 个li</li>
        <li>这是第 4 个li</li>
        <li>这是第 5 个li</li>
        <li>这是第 6 个li</li>
        <li>这是第 7 个li</li>
        <li>这是第 8 个li</li>
        <li>这是第 9 个li</li>
    </ul>
</body>
</html>

(3)、运行npm i jquery -S安装jquery。

jquery安装成功后在package.json中的dependencies配置项下有jquery的版本信息:

在index.js引入jquery。并编写隔行变色的代码:

//index.js
import $ from 'jquery';
$(function() {
    $('ul li:odd').css('background-color','red');
    $('ul li:even').css('background-color','green');
})

在index.html中引入index.js,运行index.html,结果如下:

无法运行。

(4)、安装webpack

运行:npm install webpack webpack-cli -D安装webpack和webpack-cli

安装完成以后,在devDependencies配置项中就有了webpack和webpack-cli这两个包。

(5)、配置webpack

在项目根目录下新建webpack.config.js配置内容如下:

//webpack.config.js
module.exports = {
    mode: 'development'
}

package.json文件的scripts下新增dev配置项:

"scripts": {
    "dev": "webpack"
},

然后在项目根目录下运行:npm run dev

运行成功后,项目目录下出现了一个dist文件夹,打开dist文件夹,下面有一个main.js,将这个自动生成的main.js引入index.html中,项目运行成功。

2.2、安装webpack和webpack-cli

运行:npm install webpack webpack-cli -D

2.3、配置webpack

webpack.config.js 是webpack 的配置文件。webpack 在真正开始打包构建之前,会先读取这个配置文件,从而基于给定的配置,对项目进行打包。

(1)、mode配置项的应用场景

mode配置项是webpack 运行的模式,可选值有两个developmentproduction

development:打包用时短,生成的文件大且没有被压缩和混淆。(项目开发阶段使用)

production:打包用时长,生成的文件小且被压缩和混淆。(项目发布上线的时候使用)

(2)、webpack4.x和webpack5.x的默认约定

a、默认的打包入口文件为 src -> index.js
b、默认的输出文件路径为 dist -> main.js

测试将src目录的名称改变或者src下的index.js改成index1.js,再次运行npm run dev,结果如下:

由于没有遵守默认约定,所以npm run dev运行报错。

但是我们可以在 webpack.config.js 中修改打包的默认约定。

webpack.config.js 配置文件中,通过entry节点指定打包的入口。通过output节点指定打包的出口。

(3)、配置webpack的entry

const path = require('path');//引入node的path模块
module.exports = {
    mode: 'development',
    //entry: 指定要处理的文件
    //path.join()用于拼接多个路径片段并返回规范化后的路径
    entry: path.join(__dirname, 'src/index1.js'),
}
  • const path = require('path')用来引入node的path模块。
  • path.join()方法用于拼接多个路径片段并返回规范化后的路径。
  • __dirname是一个全局变量,用于表示当前模块的目录名。它返回的是当前模块文件所在的目录的绝对路径。(本例中__dirname写在根目录下的webpack.config.js中,__dirname就代表根目录的路径。)

通过配置entry节点,修改默认约定后,再次运行npm run dev,不再报错,项目正常被打包。

(4)、配置webpack的output

output节点用来配置生成的文件的保存目录以及生成文件的文件名。

const path = require('path');
module.exports = {
    mode: 'development',
    //entry: 指定要处理的文件
    entry: path.join(__dirname, 'src/index1.js'),
    //指定生成的文件要存放在哪里
    output: {
        path: path.join(__dirname, 'lib/'),//指定存放的目录
        filename:'xxx.js'//指定生成的文件名
    }
}

修改完成后,运行npm run dev:

(5)、devServer配置项

webpack.config.js中的devServer节点配置:

配置运行npm run dev后自动打开浏览器访问项目。

devServer: {
	host: '127.0.0.1',//运行项目的ip
	open: true,//运行npm run dev后自动打开浏览器
	port: 8080//访问项目的端口
},

注意:凡是修改了webpack.config.js文件必须重新运行npm run dev命令重启项目服务。

3、webpack的插件
3.1、webpack-dev-server插件

每当修改了源代码,webpack会自动进行项目的打包和构建。如果不使用这个插件则需要每次修改了源代码都要运行一遍npm run dev

(1)、安装:npm install webpack-dev-server@4.x -D

(2)、配置:修改package.json中的scripts中的dev 命令

"scripts": {
	//由原先的"dev": "webpack"
	//改为:
     "dev": "webpack serve"
  },

(3)、再次运行npm run dev命令,重新进行项目的打包。

注意:webpack-dev-server 会启动一个实时打包的http服务器。

此时只要修改源代码,webpack-dev-server 都能监听到修改,然后自动打包。每次修改完以后效果都能直接体现在页面。无需每次运行npm run dev来打包。

如果要停止webpack-dev-server,则按下ctrl+c即可停止。

访问webpack-dev-server启动的服务器地址:http://localhost:8081/,页面是空的。这是由于我安装的是新版本的插件。

需要在webpack.config.js中新增一个配置项:

devServer: {
        // contentBase: __dirname, -- 请注意,这种写法已弃用
        static: {
            directory: path.join(__dirname, "/"),
        },
},

加上以上配置项以后,按ctrl+c停止之前的服务,然后再次运行npm run dev:

点击src目录可以访问到src目录下的index.html页面。

进入页面后,发现修改源代码以后,页面并没有实时改变最新效果,这是因为webpack-dev-server实时生成的打包文件并没有直接输出到output配置的目录中,而是存储在内存中,可以通过http://localhost:8081/main.js来访问到。由于存储在内存中,不在磁盘上,所以在根目录下看不见这个文件。如果要实时看到修改后的效果,需要将src/index.html中的main.js的引入方式变成:<script src="../main.js"></script>修改成这样以后,index.html文件中引入的实际是内存中的main.js。这个main.js会根据我们修改源代码实时变化,在页面中就能看到实时效果。

3.2、html-webpack-plugin插件

当我们访问http://localhost:8081/的时候,总是会进入一个目录页面,需要点击src目录才能访问到src下面的index.html。我们希望每次访问这个地址直接进入index.html页面的话,可以使用html-webpack-plugin插件来解决。

(1)、安装插件:npm install html-webpack-plugin -D

(2)、配置:

在webpack.config.js文件中进行如下配置:

//导入html插件,得到一个构造函数
const HtmlPlugin = require('html-webpack-plugin');
//创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
    template: './src/index.html',//指定原文件的存放路径
    filename: './index.html'//指定生成文件的存放路径
});
//插件的数组,将来 webpack 在运行时,会加载并调用这些插件
plugins:[htmlPlugin]//通过plugins节点使htmlPlugin插件生效

现在的webpack.config.js文件内容如下:

const path = require('path');
//导入html插件,得到一个构造函数
const HtmlPlugin = require('html-webpack-plugin');
//创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
    template: './src/index.html',//指定原文件的存放路径
    filename: './index.html'//指定生成文件的存放路径
});
module.exports = {
    mode: 'development',
    //entry: 指定要处理的文件
    entry: path.join(__dirname, 'src/index.js'),
    //指定生成的文件要存放在哪里
    output: {
        path: path.join(__dirname, 'dist'),//指定存放的目录
        filename:'main.js'//指定生成的文件名
    },
    devServer: {
        // contentBase: __dirname, -- 请注意,这种写法已弃用
        static: {
            directory: path.join(__dirname),
        },
    },
    //插件的数组,将来 webpack 在运行时,会加载并调用这些插件
    plugins:[htmlPlugin]//通过plugins节点使htmlPlugin插件生效
}

再次运行npm run dev,访问http://localhost:8081/,即可看到首页,由于根目录下的index.html存储在内存中,不在硬盘上,所以在项目根目录下看不见这个文件。此时修改源代码,页面是实时变化的。

html-webpack-plugin插件有2个作用:

  • 通过 HTML 插件可以复制指定的文件到项目根目录中(index.html文件),也被放到了内存中。
  • HTML插件在生成 index.html 页面的时候,自动注入了打包的main.js 文件。

    所以在src/index.html中可以直接去掉/dist/main.js的引入,插件会自动注入那份在内存中的main.js。

4、webpack加载器(loader)

在实际开发过程中,webpack 默认只能打包处理以.js 后缀名结尾的模块。其他非,js 后缀名结尾的模块,
webpack 默认处理不了,需要调用loader 加载器才可以正常打包,否则会报错。

loader 加载器的作用:协助 webpack 打包处理特定的文件模块。比如:

  • CSS-loader 可以打包处理.CSS 相关的文件
  • less-loader 可以打包处理.less 相关的文件
  • babel-loader 可以打包处理 webpack 无法处理的高级JS语法 
4.1、打包处理css文件  

 在项目根目录下新建css文件夹,在css文件夹中新建index.css文件。在src/index.js中导入这个css文件:

//导入样式(在webpack中,一切都是模块,都可以通过es6语法导入)
import '../css/index.css';

因为webpack默认只能处理.js文件,所以产生了一个报错:

要想让webpack能够处理.css文件,必须配置处理css文件的loader。

(1)、安装处理css文件的loader:style-loader和css-loader

运行命令:npm i style-loader css-loader -D

(2)、在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

module:{ //所有第三方模块文件的匹配规则
        rules:[
            //其中,test 表示匹配的文件类型,use 表示对应要调用的 loader
            {test:/\.css$/,use:['style-loader','css-loader']}
            //注意:
            // • use 数组中指定的 loader 顺序是固定的
            // • 多个 loader 的调用顺序是:从后往前调用
        ]
}
4.2、打包处理less文件

在css文件夹中新建index.less文件,在src/index.js中导入这个less文件:

//导入样式(在 webpack 中,一切皆模块,都可以通过 ES6 导入语法进行导入和使用)
//如果某个模块中,使用 from 接收到的成员为 undefined,则没必要进行接收,而是直接用import导入即可
import '../css/index.less';

保存后,报错:

因为webpack默认不能处理.less的文件,需要安装处理.less文件的less-loader加载器和less包。

(1)、安装处理less文件的less-loader加载器和less包

运行:npm i less-loader@12.2.0 less@4.x

(2)、配置

在 webpack.config.js 的module -> rules 数组中,添加处理.less的loader规则如下:

module:{ //所有第三方模块文件的匹配规则
        rules:[
            //处理,css文件的loader
            //其中,test 表示匹配的文件类型,use 表示对应要调用的 loader
            {test:/\.css$/,use:['style-loader','css-loader']},
            //注意:
            // • use 数组中指定的 loader 顺序是固定的
            // • 多个 loader 的调用顺序是:从后往前调用
            //处理.less文件的加loader
            {test:/\.less$/,use:['style-loader','css-loader','less-loader']}
        ]
}
4.3、打包处理样式表中与 url 路径相关的文件

(1)、安装处理样式表中与url路径相关的文件所需的loader

运行 npm i url-loader file-loader -D 命令。

(2)、在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

module:{ //所有第三方模块文件的匹配规则
        rules:[
            //处理样式表中与url路径相关的文件所需的loader
            {test:/\.jpg|png|gif$/,use:['url-loader?limit=2229']}
        ]
    }

以上配置中,?之后的是 loader 的参数项:

limit:用来指定图片的大小,单位是字节(byte)

只有≤ limit 大小的图片,才会被转为 base64 格式的图片

(3)、实例测试

在src目录下新建images文件夹,在文件夹中放入一个图片文件,在html中准备一个img标签:

在index.js中导入这张图片:

//导入图片文件
import logo from './iamges/logo.png';

使用jQuery将具有类名为logo的元素的src属性更改为变量logo的值。

$('.logo').attr('src',logo);

查看页面效果:

图片被转换成base64格式的图片加载进了img标签中。

是否会被转换成base64取决于在 webpack.config.js 的 module -> rules 数组中的配置:

{test:/\.jpg|png|gif$/,use:['url-loader?limit=154000']}//这个配置是图片大小≤154000字节的图片会被转换成base64

4.4、打包和处理js中的高级语法

webpack 只能打包处理一部分高级的Javascript 语法。对于那些 webpack 无法处理的高级js 语法,需要借助于 babel-loader 进行打包处理。例如 webpack 无法处理下面的Javascript 代码:

//1、定义一个名为info的装饰器
function info(target){
    //2、为目标添加静态属性info
    target.info = "Person info"
}
//3、为Person类应用info装饰器
@info
class Person{}
//4、打印Person的静态属性info
console.log(Person.info);

 以上代码会产生报错:

(1)、安装 babel-loader 相关的包

运行:npm i babel-loader @babel/core @babel/plugin-proposal-decorators -D

(2)、在 webpack.config.js 的 module ->rules 数组中,添加 loader 规则如下:

module:{ //所有第三方模块文件的匹配规则
        rules:[
            //使用babel-loader处理高级的js语法
            //注意:必须使用 exclude 指定排除项;因为node_modules 目录下的第三方包不需要被打包
            //因为第三方包的兼容性不需要程序员关心
            {test:/\.js$/,use:'babel-loader',exclude: /node_modules/}
        ]
}

(3)、在项目根目录下,创建名为 babel.config.js 的配置文件,定义 Babel 的配置项如下:

module.exports = {
    //声明babel可用插件
    plugins:[
        //将来webpack在调用babel-loader的时候会先加载plugins里面的插件来使用
        ['@babel/plugin-proposal-decorators',{legacy: true}]
    ]
};
5、打包发布

(1)、在 package.json 文件的 scripts 节点下,新增 build 命令如下:

"scripts": {
    //项目发布时:运行npm run build命令
    "build": "webpack --mode production"
  },

--mode 是一个参数项,用来指定 webpack 的运行模式。production 代表生产环境,会对打包生成的文件进行代码压缩和性能优化。

注意:通过--mode指定的参数项,会覆盖 webpack.config.js 中的model选项。

(2)、运行npm run build命令:

运行完命令以后,生成了dist文件夹,这个文件夹可以直接部署上线。

(3)、现在的html和js、图片等文件在同一目录下,如果希望将js放在js文件夹中,可以修改webpack.config.js文件中的output配置项:

output: {
        path: path.join(__dirname, 'dist'),//指定存放的目录
        //原来的写法:filename:'main.js'//指定生成的文件名
        //改成:
        filename:'js/main.js'
},

(4)、把图片文件统一生成到 images 目录中

修改webpack.config.js 中的 url-loader配置项,新增 outputPath选项即可指定图片文件的输出路径:

//原来的写法:{test:/\.jpg|png|gif$/,use:['url-loader?limit=2229']},
//修改成:
//在配置 url-loader 的时候,多个参数之间,使用&符号进行分隔
{test:/\.jpg|png|gif$/,use:['url-loader?limit=2229&outputPath=images']},

配置完成后,重新运行:npm run build

(5)、每次发布之前都要先手动删除dist文件夹来清理旧文件,如何自动清理 dist 目录下的旧文件。可以使用clean-webpack-plugin 插件来实现。

安装clean-webpack-plugin插件:npm install clean-webpack-plugin -D 

配置webpack.config.js:

//1、导入构造函数
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
//2、在plugins节点添加插件使插件生效
plugins:[htmlPlugin,new CleanWebpackPlugin()],//通过plugins节点使htmlPlugin插件生效
//或者
//1、先实例化一下CleanWebpackPlugin
const cleanWebpackPlugin = new CleanWebpackPlugin();
//2、在plugins节点添加插件使插件生效
plugins:[htmlPlugin, cleanWebpackPlugin],
6、Source Map

Source Map 就是一个信息文件,里面储存着位置信息。也就是说,Source Map 文件中存储着压缩混淆后的代码,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大的方便后期的调试。

6.1、默认的Source Map存在的问题

开发环境下默认生成的 Source Map,记录的是生成后的代码的位置。会导致运行时报错的行数与源代码的行数不一致的问题。示意图如下:

6.2、在开发模式下解决默认Source Map存在的问题

开发环境下,推荐在 webpack.config.js 中添加如下的配置,即可保证运行时报错的行数与源代码的行数保持一致:

//eval-source-map 仅限在“开发模式“下使用,不建议在“生产模式”下使用。
//此选项生成的 Source Map 能够保证“运行时报错的行数“与“源代码的行数“保持一致
devtool:'eval-source-map',

经过以上配置,再次查看报错行号:

6.3、在生产环境下需要关闭Source Map

在生产环境下,如果省略了 devtool选项,则最终生成的文件中不包含Source Map。这能够防止原始代码通过Source Map 的形式暴露给别有所图之人。

所以为了安全,建议在生产环境下都关闭Source Map。关闭Source Map即:不添加devtool:'eval-source-map'配置项。

6.4、在生产环境下只定位行数,不暴露源代码

在生产环境下如果只想定位报错的具体行数,且不想暴露源码。此时可以将 devtool 的值设置为nosources-source-map。实际效果如图所示:

//只定位报错的具体行数,不暴露源码
evtool:'nosources-source-map',
6.5、Source Map的最佳实践

(1)、开发环境下:

  • 建议把devtool 的值设置为 eval-source-map
  • 好处:可以精准定位到具体的错误行

(2)、生产环境下:

  • 建议关闭 Source Map 或将 devtool 的值设置为 nosources-source-map
  • 好处:防止源码泄露,提高网站的安全性
7、webpack中的“@”

在webpack中,@符号代表源代码的src目录。

webpack.config.js文件中添加如下配置:

resolve:{
        alias:{
            //配置@代表src目录
            '@': path.join(__dirname,'./src/')
        }
}

配置完成以后,在需要导入文件的地方都不必再使用类似:./或者../的形式来定位目录了,而是直接使用@来定位到src目录。例如:

//原来的写法:import msg from '../../msg.js';
//@符号表示src源代码目录,从外往里查找
import msg from '@/msg.js';
//导入src/js/test/info.js
import '@/js/test/info.js';