GVKun编程网logo

使用 babel 和 webpack 时如何生成 sourcemap?(webpack 中如何配置 babel?)

7

在本文中,我们将详细介绍使用babel和webpack时如何生成sourcemap?的各个方面,并为您提供关于webpack中如何配置babel?的相关解答,同时,我们也将为您带来关于03.webpa

在本文中,我们将详细介绍使用 babel 和 webpack 时如何生成 sourcemap?的各个方面,并为您提供关于webpack 中如何配置 babel?的相关解答,同时,我们也将为您带来关于03.webpack中sourceMap等配置、06.webpack中source-map的配置、babel - 使用Webpack和Babel来搭建React应用程序、Babel 处理 webpack 无法处理的 ES 语法(Babel 6.x 和 Babel 7.x 有区别)(二)的有用知识。

本文目录一览:

使用 babel 和 webpack 时如何生成 sourcemap?(webpack 中如何配置 babel?)

使用 babel 和 webpack 时如何生成 sourcemap?(webpack 中如何配置 babel?)

我是 webpack 的新手,我需要帮助设置生成源映射。我webpack serve从命令行运行,编译成功。但我真的需要源图。这是我的webpack.config.js

var webpack = require('webpack');

module.exports = {

  output: {
    filename: 'main.js',publicPath: '/assets/'
  },cache: true,debug: true,devtool: true,entry: [
      'webpack/hot/only-dev-server','./src/components/main.js'
  ],stats: {
    colors: true,reasons: true
  },resolve: {
    extensions: ['','.js','.jsx'],alias: {
      'styles': __dirname + '/src/styles','mixins': __dirname + '/src/mixins','components': __dirname + '/src/components/','stores': __dirname + '/src/stores/','actions': __dirname + '/src/actions/'
    }
  },module: {
    preLoaders: [{
      test: /\.(js|jsx)$/,exclude: /node_modules/,loader: 'jsxhint'
    }],loaders: [{
      test: /\.(js|jsx)$/,loader: 'react-hot!babel-loader'
    },{
      test: /\.sass/,loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
    },{
      test: /\.scss/,loader: 'style-loader!css!sass'
    },{
      test: /\.(png|jpg|woff|woff2)$/,loader: 'url-loader?limit=8192'
    }]
  },plugins: [
    new webpack.HotModuleReplacementPlugin(),new webpack.NoErrorsPlugin()
  ]

};

我对 webpack 真的很陌生,虽然文档并没有真正帮助,因为我不确定这个问题具体是什么。

03.webpack中sourceMap等配置

03.webpack中sourceMap等配置

一、认识PostCSS工具

PostCSS是什么?

PostCSS是一个通过将JavaScript来转化css样式的工具,它可以帮助我们实现css样式的转化和适配,比如自动添加浏览器前缀来让一些css样式在不同版本的浏览器中都可以生效,比如统一原生html标签如button在不同浏览器中的样式差异等。

PostCSS的使用方法

PostCSS这个工具本身是没有什么用的,必须借助于对应的插件来实现css样式的转化和适配的,它的使用主要分为两个步骤:

  1. 首先查找PostCSS在当前构建工具中的拓展
    比如当前项目使用的构建工具是webpack,那么就会查找webpack中对于的postcss-loader这个拓展,通过这个loader为项目注入PostCSS这个工具

  2. 选择添加当前项目中需要使用的PostCSS相关插件
    PostCSS工具是独立的,必须借助于其对应的插件,然后将插件集成到PostCSS中,才可以实现最终的效果

PostCSS在命令行中使用

在使用webpack构建工具中使用postcss之前,我们先单独的不依赖于webpack等构建工具来使用下postcss为一些css样式自动添加浏览器前缀:

  1. 首先安装postcss
npm i postcss -D
  1. 由于要单独在命令行中使用postcss工具,所以还要安装postcss的命令行工具postcss-cli
npm i postcss-cli -D
  1. 新建一个test.css文件
.example{
	/* 此css属性用于规定用户是否可以选中页面上的文本,多个版本浏览器对其实现不一样,需要在不同浏览器中添加对应前缀 */
	user-select:none; 
	display: grid;
	transition: all .5s;
	background: linear-gradient(to bottom, white, black);
}
  1. 在命令行中执行命令如下,该命令的意思是使用postcss工具将./src/css/test.css目录下的test.css文件中的有关css样式自动添加浏览器前缀之后输出到./src/css/result.css中。
npx postcss -o ./src/css/result.css ./src/css/test.css

执行命令之后发现确实新增了一个./src/css/result.css文件,但是该文件中的样式并没有添加前缀,这是因为postcss要实现自动添加浏览器前缀,必须要借助于其对应的插件autoprefixer来实现,所以下一步我们再安装插件autoprefixer:

npm i autoprefixer -D
  1. 在安装好Autoprefixer插件之后,执行命令如下:意思是使用postcss工具并使用插件autoprefixer将./src/css/test.css目录下的test.css文件中的有关css样式自动添加浏览器前缀之后输出到./src/css/result.css中。
npx postcss --use autoprefixer -o ./src/css/result.css ./src/css/test.css

在执行上面命令之后,在result.css中转化后的css文本如下:可见已经为需要添加的css样式自动添加了浏览器前缀。

.example{
	/* 此css属性用于规定用户是否可以选中页面上的文本,多个版本浏览器对其实现不一样,需要在不同浏览器中添加对应前缀 */
	-webkit-user-select:none;
	   -moz-user-select:none;
	    -ms-user-select:none;
	        user-select:none; 
	display: grid;
	transition: all .5s;
	background: linear-gradient(to bottom, white, black);
}

Autoprefixer CSS online:Autoprefixer是一个为css样式在不同条件的浏览器版本中对应添加浏览器前缀以增强css样式兼容性的网站

PostCSS在webpack中的使用

前面我们是单独的基于postcss和postcss-cli命令行工具实现了以下给css样式自动添加浏览器前缀的过程,但是在正式的项目中我们不可能为每一个css文件都执行一次上面的命令,然后将输出的结果再拷贝过去,所以我们就要基于webpack这个构建工具来简化这一过程,最终的目的是告诉webpack:当webpack在执行打包的过程中遇到css文件,首先借助于postcss-loader找到postcss这个工具,再基于postcss工具对应的插件autoprefixer来自动为css文件中的样式添加浏览器前缀,而具体要为哪些浏览器添加前缀则是由brswerslist这个工具提供的caniuse-lite这个小工具查询到然后共享给autoprefixer的。

  1. 要让webpack在打包的过程中使用postcss,首先必须安装postcss和postcss-loader
npm i postcss postcss-loader -D
  1. 在webpack.config.js中进行配置
    该配置的意思是在webpack打包的过程中遇到css文件时,先借助于postcss-loader找到postcss工具,然后基于postcss插件autoprefixer来将css样式自动添加浏览器前缀,将添加前缀之后的css文件再依次交给css-loader和style-loader处理。
module.exports = {
	module:{
		rules:[
			{
				test: /\.css$/, // 匹配规则
				use: [
					"style-loader",
					"css-loader",
					{
						loader:"postcss-loader",
						options:{
							postcssOptions:{
								plugins:[
									require("autoprefixer")
								]
							}
						}
					}
				]
			},
		]
	}
}

postcss-preset-env插件

其实在webpack中配置postcss-loader的时候,还有一个特殊的插件:postcss-preset-env,这个插件相比于autoprefixer插件来说有着更加强大的功能:

  1. 已经集成了autoprefixer插件自动添加浏览器前缀的能力;
  2. 可以帮助我们将一些现代的css特性转化成为尽可能多的浏览器可以识别的css,并且会根据目标浏览器或者运行时环境添加所需polyfill垫片。

下面是postcss-preset-env插件的使用过程,我们基于这一插件将一个现代的css特性转化为大多数浏览器可以识别的css属性:

  1. 安装postcss-preset-env插件
npm i postcss-preset-env -D
  1. 配置postcss-preset-env插件
module.exports = {
	module:{
		rules:[
			{
				test: /\.css$/, // 匹配规则
				use: [
					"style-loader",
					"css-loader",
					{
						loader:"postcss-loader",
						options:{
							postcssOptions:{
								plugins:[
									require("autoprefixer"),
									require("postcss-preset-env")
								]
							}
						}
					}
				]
			},
		]
	}
}
  1. 将一个文本的颜色值设置为十六进制格式,并设置为8位数,一般来说颜色值都是由6位的十六进制数字表示,而设置为8位的十六进制数字其最后的两位表示颜色的透明度,但这是一个css新特性,大多数浏览器并不会支持它。
.container{
	color:#12345678;
	font-weight: bold;
	font-size: 36px;
}
  1. 执行打包,观察打包之后浏览器中的样式,发现已经转化为rgba的格式,这说明postcss-preset-env这个插件生效了。
.container {
    color: rgba(18,52,86,0.47059);
    font-weight: bold;
    font-size: 36px;
}

postcss-preset-env和autoprefixer的关系

Vue官方的脚手架搭建的项目中,关于postcss-loader的配置只需要配置一个插件:postcss-preset-env即可,这说明postcss-preset-env插件集已经内置了或者实现了autoprefixer的功能,这两个插件不需要一起配置,只需要配置一个postcss-preset-env就可以了。

另外一个在配置插件的时候,有两种写法:

  1. 使用require引入的方式,这种写法适合在调用插件的时候还需要传入参数的时候
  2. 直接使用包名字符串,这种写法比较简洁,但不是所有插件都支持这种写法
{
	loader:"postcss-loader",
	options:{
		postcssOptions:{
			plugins:[
				require("autoprefixer"),
				require("postcss-preset-env"),
				require("my-plugin")(arg1,arg2) //插件传参 
			]
			/* 或者 */
			plugins:[
				"autoprefixer",
				"postcss-preset-env"
			]
		}
	}
}

postcss.config.js配置文件抽取

在项目中如果使用了less、sass这些css拓展语言的时候,按照加载loader的规则,我们在加载less-loader之后还需要先加载postcss-loader之后,才可以接着加载css-loader,但是这种配置方式会导致.css文件和.less文件有重复的配置,所以webpack为我们提供了专门的用于配置postcss的配置文件postcss.config.js,用来解决配置重复的问题。

  1. 根目录新建postcss.config.js,然后导出一个对象:
module.exports = {
	plugins:[
		'postcss-preset-env'
	]
}
  1. 有了配置文件之后只需要在webpack.config.js按照如下配置postcss-loader即可:
module: {
	rules: [
		{
			test: /\.css$/, // 匹配规则
			use: [
				"style-loader",
				"css-loader",
				"postcss-loader"
			]
		},
		{
			test: /\.less$/, // 匹配规则
			use: [
				"style-loader",
				"css-loader",
				"postcss-loader",
				"less-loader"
			]
		}
	]
}

css-loader中importLoaders属性的用法

在webpack中处理css文件或者less文件的时候,如果按照以下配置来处理:

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

但是上述这种写法会导致处理一个css文件中又通过@import语法引入另外一个css文件的时候出现问题:

/* index.css文件 */
@import "./test.css"
.content{
	color:#12345678; /* 8位16进制颜色值写法*/
}

/* test.css文件 */
.test-demo{
	color:#12345678; /* 8位16进制颜色值写法*/
}
  1. 问题现象
    上面代码表示在index.css中基于@import语法引入了另外一个test.css文件,此时webpack按照原来的配置执行打包的时候,只会对index.css中的样式执行postcss-loader处理,并不会对引入的test.css文件中的样式执行postcss-loader的处理。

  2. 原因
    这是因为当webpack在打包的过程中遇到index.css文件的时候,会按照配置依次执行postcss-loader和css-loader,在使用css-loader处理index.css文件的时候遇到@import这种css语法的时候,就会将test.css文件也使用css-loader进行处理,但是这里需要注意的在处理test.css文件的时候,并不会再回去使用postcss-loader处理一遍的,所以就会导致index.css中的css样式会经过postcss-loader处理,但是test.css文件中的样式并不会经过postcss-loader处理。

  3. 解决方法
    我们当然是希望所有css文件无论是直接引入的还是通过@import语法引入的文件都可以正确的被postcss-loader进行处理,所以我们需要再原来配置的基础上加一个importLoaders配置,importLoaders的值为1那么代表在处理@import语法导入的css文件的时候,需要先经过当前loader配置的上1个loader处理下;如果是2就代表需要先经过当前loader配置的上2个loader处理之后再由当前loader处理。

rules:[
	{
		test:/\.css$/,
		use:[
			"style-loader",
			{
				loader:"css-loader",
				options:{
					importLoaders:1
				}
			}
			"postcss-loader"
		]
	}
]

二、加载和处理其他资源

一般在项目中除了js和css需要处理之外,图片也是使用比较多的,目前在项目中使用图片的方式基本有两种:

  1. 新建一个img标签,然后给标签的src属性进行赋值
    注意:通过require()方法引入的资源在经过webpack处理的时候其返回值和file-loader的版本有关联
  • 如果file-loader的版本是4.x,那么直接返回资源本身;
  • 如果file-loader的版本是5.x+,那么返回的是一个module对象,资源路径是存放在该对象的default属性上的;

如果采用import语法来加载,就不会有这种版本考虑的问题,直接import xxx from '文件路径'即可。

function createImage(){
	let img = new Image();
	img.src= require('../img/test1.png').default;
	document.body.appendChild(img);
}
createImage();
  1. 给一个元素如div设置背景图片,也就是css的background-image属性用url引入
function createImage(){
	const ele = document.createElement('div');
	ele.style.width = 200 + 'px';
	ele.style.height = 200 + 'px';
	ele.className = 'bg-image';
	return ele;
}
createImage();

/* 在css文件中 */
.bg-image{
	background-image:url('../img/woman.webp');
	background-size;cover;
}

file-loader的用法

webpack自身是不识别这些图片资源的,要处理这些资源必须要借助于其对应的file-loader来进行处理,file-loader的作用就是帮助我们处理import和require()方式引入文件资源,然后将处理之后的资源输出到最终的打包之后的dist文件夹中,这样页面就建立起了和资源的引用关系。

  1. 安装
npm i file-loader -D
  1. 配置file-loader
    配置file-loader的时候有几个注意点:
  2. 匹配资源的正则表达式中可以写成/.(png|jpg|jpeg|gif|svg)$/这种形式的,也可以写成 /.(png|jpe?g|gif|svg)$/,jpe?g中e后面的问好代表e可能会出现0次或者一次
  3. 匹配资源的后缀中不可以包含webp,这种格式的图片经过webpack处理重命名之后就会失效而无法加载
{
	test: /\.(png|jpe?g|gif|svg)$/,
	use: [
		{
			loader:"file-loader"
		}
	]
}

file-loader输出资源和文件夹的重命名

  1. 输出资源名称的重命名
    webpack默认基于file-loader处理文件资源之后会对文件进行重命名,一般来说会基于MD4摘要算法生成一个128位的hash值,然后每4位用一个16进制数字表示,最后输出到dist文件夹中的文件名称都会是一个32位的16进制数字组成的名称。很显然这种默认的配置不利于我们查询资源的对应关系,所以我们可以通过配置来自定义输出资源的名称规则。

webpack中关于file-loader最终输出的文件名称是基于[Placeholder]来进行处理的,下面是常见的Placeholder:

  • [name]:处理文件的名
  • [hash]:文件的内容,采用MD4散列函数处理生成的一个128位的hash值,用32个16进制数字表示
  • [contentHash]:在file-loader中和[hash]保持一致,但是在其他地方会不一样
  • [path]:处理文件相对于webpack配置文件的路径

在了解了以上占位符之后,就可以在webpack中进行配置了(下面这个配置也是Vue官方脚手架中对于file-laoder的配置),此配置输出后的文件名为:test1.6f97a9.png

{
	test: /\.(png|jpe?g|gif|svg)$/,
	use: [
		{
			loader:"file-loader",
			options:{
				name:[name].[hash:6].[ext] // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
			}
		}
	]
}
  1. 指定输出文件的存放路径
    webpack在打包的时候会见文件资源都默认全部打包到build文件夹下,这样不利于资源的分类和统一管理,如果要指定最终输出资源到一个文件夹有两个办法:
  • 通过outputPath属性声明
{
	test: /\.(png|jpe?g|gif|svg)$/,
	use: [
		{
			loader:"file-loader",
			options:{
				name:"[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
				outputPath:'image'  // 代表输出的文件夹名称为build下的image文件夹  
			}
		}
	]
}
  • 直接在name属性中和文件名称一起声明
{
	test: /\.(png|jpe?g|gif|svg)$/,
	use: [
		{
			loader:"file-loader",
			options:{
				name:"./image/[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后统一输出到image文件夹下
			}
		}
	]
}

url-loader的用法

在webpack中除了file-loader之外,还有一个专门处理图片文件资源的url-loader,url-loader的用法基本和file-loader一样,主要是原理不同:

  • file-loader的原理是对所有需要打包的图片资源做一个拷贝,拷贝到最终的dist文件夹中,然后对文件名做一个重命名
  • url-loader的原理是对所有需要打包的图片经过base64算法转化为base64data,直接嵌入到打包出来的bundle.js当中,然后在加载页面的时候会随着js一起被请求下来

但是在开发中我们希望文件大小比较大的图片直接使用图片,而对文件比较小的图片使用base64进行编码,这样做的好处在于:

  1. 大文件如果经过base64编码,会增大js文件的体积并最终影响页面的加载速度,所以大图片文件最好直接复制;
  2. 小文件如果经过base64编码,可以随着页面的js一起被请求下来,减少不必要的http请求;

上述的需求可以提供配置url-loader的limit字段来实现,limit字段的值的单位是byte。

{
	test: /\.(png|jpg|jpeg|gif|svg)$/,
	use: [
		{
			loader:"url-loader",
			options:{
				name:"img/[name].[hash:6].[ext]" ,// 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后打包后存在build目录下的img文件夹中
				limit:10 * 1024,
				esModule:false // 配置
			}
		}
	],
	 type: 'javascript/auto'  // V5官网文档说的防止重复打包图片
}

三、asset module type的介绍

在webpack5之前打包的时候处理图片资源要借助于url-loader、file-loader等专门的loader去实现图片资源的打包,但是在webpack5中已经不推荐这种做法了,而是加载这些资源都由一个内置的Asset module type(资源模块类型)来代替上面的那些loader完成图片等资源的打包。

Asset module type(资源模块类型)有下面四种类型,来对不同的loader进行替换:

  1. asset/resource 代替之前的file-loader
  2. asset/inline 代替之前的url-loader
  3. asset/source 代替之前的raw-loader
  4. asset 可以基于配置打包资源体积的最大值来决定最终打包的资源是一个独立文件还是一个base64 Data直接嵌入到js中

asset/resource的配置

相比于原来复杂的file-loader配置,这里只需要在type属性中声明当前处理资源模块的类型为asset/resource即可:

{
	test: /\.(png|jpe?g|gif|svg)$/,
	type:"asset/resource",
},

默认情况下webpack会将资源打包到build文件夹下的根目录中,如果要指定资源打包后的存放路径,有两种方法:

  1. 在output属性中配置assetmodulefilename,这里是全局配置代表不仅仅是asset/resource这一种类型,其他所有资源模块类型打包后的文件都会放在这个目录下。并且这里在配置最终生成的文件名的时候也可以和配置file-loader的时候一样,通过各种placeholder来实现资源的重命名,如下:
module.exports = {
	output: {
		filename: "bundle.js",
		path: path.resolve(__dirname, '../build'),
		assetmodulefilename:"img/[name]-[hash:6][ext]" 
	},
}
  1. 针对于asset/resource这一种资源模块类型单独配置,需要指定一个generator对象,generator是生成的意思代表生成资源的路径,推荐这种写法:
{
	test: /\.(png|jpe?g|gif|svg)$/,
	type:"asset/resource",
	generator:{
		filename:"img/[name]-[hash:6][ext]" // [ext]自身代表"."+后缀名 前面不再需要补一个点
	}
},

注意点:在配置资源文件的打包之后的存放路径的时候,可以基于各种placeholder来实现资源的重命名,但是这里有一个和配置loader时不同的地方在于:ext在这里是代表了资源的文件后缀名加一个点的;而之前配置loader的时候ext只代表文件后缀名。比如:
webpack5中的资源模块:filename:"img/[name]-hash:6",[hash:6]和ext中间不加点,最终打包出来的也是xxx.png这种资源名
webpack5之前的loader配置:filename:"img/[name]-[hash:6].ext",[hash:6]和ext中间有一个点

asset/inline的配置

asset/inline是用来代替url-loader的,它的配置很简单,只需要注意在配置的时候不要加generator对象就好了,因为asset/inline模块类型会将资源转化为base64编码的dataUrl,不存在最终输出的资源路径:

{
	test: /\.(png|jpe?g|gif|svg)$/,
	type:"asset/inline",
}

一个完整的dataUrl组成:data: + MIME type; base64,base64编码值

data:image/png;base64,YABgAAD/2wBDAAkGBwgHBgkICAgKCgkLDhcPDg0ND...
data:image/jpeg;base64,6V4mvDoaiTgEnpROIyHog6nrXegxUV5Jc966TRFP...

asset的配置

asset主要是用来解决资源体积较大的文件打包成为独立的文件,而资源提交较小的文件被打包成为base64编码的dataUrl,具体是基于parser对象中dataUrl配置的maxSize属性来配置,具体配置如下:

{
	test: /\.(png|jpe?g|gif|svg)$/,
	type:"asset",
	generator:{
		filename:"img/[name][hash:6][ext]",
	},
	parser:{
		dataUrlCondition:{
			maxSize:200 *1024
		}
	}
}

四、处理字体文件或者字体图标、音频、视频等资源

在项目中有可能会用到特殊的字体文件,或者字体图标、音视频等资源,在webpack5之前这些资源都可以基于file-loader来进行加载,但是在webpack5的asset module type推出之后,就不用专门去安装loader,而是可以配置资源类型就可以处理这些资源了,比如:

{
	test:/\.ttf|woff|woff2$/i,
	type:"asset/resource",  // 等于file-loader 因为字体文件这些资源一般不会转化为base64编码的
	generator:{
		filename:"font/[name][ext]"
	}
},
{
	test:/\.mp3|mp4|flv$/i,
	type:"asset/resource", 
	generator:{
		filename:"audio/[name][ext]"
	}
},

06.webpack中source-map的配置

06.webpack中source-map的配置

认识source-map

一般情况下真实运行在浏览器上的代码是经过webpack等前端构建工具打包之后的代码,在打包的过程中会对代码做压缩和混淆丑化,所以这就会导致运行在浏览器的代码和我们在开发阶段写的源代码其实是有差异的,主要体现在以下几个方面:

  1. 源码中ES6+的语法会通过babel工具转化为ES5语法;
  2. 源码中的代码行号以及列号经过编译打包之后肯定会和浏览器端的不一致;
  3. 源码中的变量名称、函数名等经过压缩丑化之后会被简写和替换;
  4. 源码中如果采用了TS开发,那么还会转化为JS代码在浏览器中执行

以上这些问题其实最后都会反映到一个问题上面:那就是代码在浏览器端运行的时候出错的时候,我们在浏览器的devtools中debug调试错误的时候是及其困难的的,因为没有任何一个人可以保证自己开发的代码不会出错!

那么我们如何在浏览器中调试这种打包转化后不一致的代码呢?答案就是source-map。source-map是将已经打包编译后的代码映射到原始的源文件的一个方案,配置source-map之后,就可以在浏览器中调试代码的时候精确的为我们定位到源文件中代码出错的位置。

使用source-map

使用source-map的前提是浏览器必须支持source-map文件,也就是浏览器可以读取并解析bundle.js.map这种文件,使用source-map需要两个步骤:

  1. 配置webpack打包选项devtool来生成source-map
module.exports = {
	devtool:"source-map",
}
  1. 注释
    上述配置代表最终打包后生成的js文件不仅会有一个bundle.js文件,还会有一个bundle.js.map文件,并且在bundle.js文件的最底端会有一行注释:
//# sourceMappingURL=bundle.js.map

这行代码告诉浏览器在加载bundle.js的文件的时候,要根据sourceMappingURL指向的文件路径来下载该文件对应的bundle.js.map文件,然后浏览器就可以基于bundle.js.map文件中的信息加上bundle.js中的代码还原出打包前的源代码,这就是为什么在浏览器中调试的时候可以精确的定位到源代码中错误信息,原因就是配置了source-map并且生成的bundle.js.map文件为浏览器提供了还原源代码文件的必要信息。

在浏览器中查看还原后的源文件

在配置了source-map之后,如果我们要在浏览器中查看打包前的源文件,还需要在浏览器的devtools-设置-Sources选项中打开:Enable JavaScript source maps。

对比配置source-map前后浏览器devtools中sources选项的差异:

  1. 未配置devtool:source-map
    127.0.0.1:8848本地服务器端保存了一个bundle.js文件
    未配置devtool:source-map

  2. 配置devtool:source-map
    配置devtool:source-map
    浏览器除了127.0.0.1:8848本地服务器保存了打包之后的文件之外,还有一个文件夹保存了当前bundle.js中对应的所有源代码文件,除了项目自身的还有用到webpack源码中的文件都会被还原出来。

分析生成的source-map文件

最初source-map生成的文件是原始文件大小的10倍,后来经过两个版本的优化现在已经可以做到只有原始文件大小的2.5倍左右,所以一个133kb的源文件最终的source-map文件大小应该在300kd左右。

source-map文件中以对象键值对的方式存放着如何通过打包之后的代码映射源文件的一系列信息,具体有下面7个属性:

{ 
	/* 当前使用的source-map版本 */
	"version": 3, 
	
	/* 浏览器当前加载的打包之后的文件 */
	"file": "js/bundle.js",
	
	/* 
	source-map用来和源文件进行映射的信息,由一串base64 VLQ(Veribale length quantity)可变长度值编码组成,基于此信息确定代码的位置信息
	 */
	"mappings": ";;;;;;;;;AAAA;AACA;AACA;....",
	
	/* 当前运行在浏览器中的代码是由哪些源文件转化过来的,数组中每一项都对应一个源文件路径 */
	"sources": [
	    "webpack://webpack-demo/./src/js/Commonjs.js",
	    "webpack://webpack-demo/./src/js/ESModule.js",
		"webpack://webpack-demo/webpack/bootstrap",
	],
	
	/* 打包前的源代码信息,将源代码转化为字符串和sources对应 */
	"sourcesContent": [
	  "function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}",
	  "function ESModuleSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction ESModuleMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconst ESModulec = 100;\r\n\r\n/* 基于ES Module语法 */\r\nexport default {\r\n\tESModuleSum,\r\n\tESModuleMul,\r\n\tESModulec\r\n}\r\n\r\n",
	],
	
	/* 如果是production模式,转化后的变量等名称会被混淆 names就可以还原出打包前的源代码中的变量名称;如果是development模式那么这里会是一个空数组
	 */
  "names": [
		"console",
        "log",
        "foo",
        "module",
        "exports",
        "CommonSum",
        "a",
        "b",
        "CommonMul",
	],
	
	/* 所有的sources中声明的源文件路径相对的根目录 */
  "sourceRoot": ""
}

基于webpack的devtool选项配置不同的source-map

webpack配置中的devtool配置用来控制是否生成以及如何生成最终的source-map,webpack5的官方文档中关于devtool的值总共有26个之多,不同的值会导致生成的source-map内容有所差异,并且对于webpack打包过程中的性能也有所差异,所以要基于不同的环境和不同的项目需求来灵活的配置devtool从而控制生成最佳的source-map。

1. 设置不生成source-map

1. 布尔值:false

將devtool配置为false代表不使用source-map,最终打包的文件中没有source-map相关内容。注意这是一个布尔值,不要写成devtool:"false",这样会导致打包失败。建议采用这个值来设置项目打包时不生成source-map。

2. production模式下devtool的默认值:"none"

如果mode设置为production生产模式,那么none是devtool的默认值,也就是说只要当前模式为生产模式那么默认是不会生成source-map的。

  • 注意1:如果mode设置为development开发模式,那么不可以将devtool的值设置为none,因为这个值只有在生产模式中才可以使用。

  • 注意2:如果mode设置为production生产模式,那么不用设置任何devtool选项就默认是none,此时去显式的设置反而会报错!!!

所以下面两种设置方法都会报错:

module.exports = {
	mode:"production",
	devtool:"none",
}

module.exports = {
	mode:"development",
	devtool:"none",
}

正确的设置方法是只声明mode为production,让默认配置生效即可。

module.exports = {
	mode:"production", // 生产环境下默认就是不生产source-map的
}

3. development模式下devtool的默认值:"eval"

如果当前的mode被设置为development开发模式,那么默认devtool的值是字符串eval,此时执行打包也不会生成source-map,虽然设置为eval不会生成source-map文件,但是webpack在对模块打包的时候想对比于配置了source-map会做以下事情:

  • 将源代码中模块要导出的接口转化为代码字符串,放在eval()函数中执行。因为eval函数可以将参数字符串当做js代码进行执行。

  • 在eval执行的代码字符串的最后面添加一行注释://# sourceURL=webpack://webpack-demo/./src/js/Commonjs.js?浏览器在解析到这行注释的时候,会在对应的调试面板中生成当前bundle.js中对应的源代码中的文件目录,方便我们在开发模式下调试代码。

  • eval模式下的打包构建速度是很快的,一般在开发模式下设置devtool为eval即可。

/* 配置devtool:source-map打包之后的__webpack_modules__对象 */
	var __webpack_modules__ = {
	 "./src/js/Commonjs.js":
		 function(module) {

			function CommonSum (a,b) {
				return a+b;
			}
			function CommonMul (a,b){
				return a*b;
			}
			console.log(foo);

			module.exports = {
				CommonSum,
				CommonMul
			}
		}
	}
	
/* 配置devtool:eval打包之后的__webpack_modules__对象 */
	var __webpack_modules__ = {
	"./src/js/Commonjs.js":
		function(module) {
			/* 将原本的导出的函数和变量转化为字符串放在eval函数中执行;并且将eval函数中的代码字符串转译并映射到sourceURL指定的文件中,简单的还原源代码
			 */
			eval(
				"function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}\n\n//# sourceURL=webpack://webpack-demo/./src/js/Commonjs.js?");
		},
	}

2. 设置生成source-map

设置devtool的值为"source-map"就可以让最终打包的文件中生成单独的source-map文件,在上面介绍过这里不再赘述。

3. eval-source-map

设置devtool的值为"eval-source-map"可以生成source-map,但是生成的source-map不是单独的文件,而是以DataUrl的形式添加到eval函数的最后面。

关键是//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxl...\n//这一行注释,浏览器在加载bundle.js的时候就会执行eval函数,并且基于sourceMappingURL指向的base64资源加载source-map。

// 打包生成的bundle.js文件
var __webpack_modules__ = {
	"./src/js/Commonjs.js":
		function(module) {
			/* 将原本的导出的函数和变量转化为字符串放在eval函数中执行;并且将eval函数中的代码字符串转译并映射到sourceURL指定的文件中,简单的还原源代码
			 */
			eval("function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxl...\n//# sourceURL=webpack-internal:///./src/js/Commonjs.js\n");
		},
	}

4. inline-source-map

设置devtool的值为"inline-source-map"也可以生成source-map,也不会有单独的.map文件,而是将source-map以DataUrl的形式添加到bundle.js文件的最后面。

// bundle.js文件

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianMvYnVuZGxlLmpzIiwibWFwcgluZ3MiOiI7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrREFBZTtBQUNmO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUfdrDs7Oz

5. cheap-source-map

设置devtool的值为"cheap-source-map"会生成单独的source-map文件,并且想对比于source-map会更加高效一些,因为cheap在这里本来就是低开销的意思,之所以高效的原因是因为cheap-source-map模式下生成的source-map文件中没有列映射(Column Mapping),意思就是如果有代码报错,只能定位到报错的行数,但是定位不到报错的那一行的具体列数,但是一般在开发中只要定位到行即可。

  • 使用development模式下eval默认配置定位错误到列
    定位错误到列

  • 使用cheap-source-map定位错误定位到行
    定位错误到行

6. cheap-module-source-map

设置devtool的值为"cheap-module-source-map"会生成单独的source-map文件,具体的行为类似于cheap-source-map,唯一不同的地方在于对源于loader处理过的源文件,其生成的source-map会更加优秀接近源代码。

如果下面的ES6+语法代码经过babel-laoder处理为低版本语法,那么cheap-source-map生成的source-map在定位错误的时候其实变量名称和行号都是和源文件有差别的,这对于调试错误来说不是很友好。

  • 真实的源文件:使用了const和箭头函数等ES6语法并且配置了bable-loader
const CommonSum = (a,b)=>{
return a+b;
}
const CommonMul = (a,b)=>{
	return a*b;
}
console.log(foo);
module.exports = {
	CommonSum,
	CommonMul
}
  • 使用cheap-source-map生成的source-map最终还原出来的源文件如下:可以看出由于es6+语法经过babel-loader的处理,已经将源代码转化为可以适配低版本浏览器的ES5语法,此时在定位错误的时候就会出现和真实的源文件中代码行号不一致的问题。
var CommonSum = function CommonSum(a, b) {
  return a + b;
};

var CommonMul = function CommonMul(a, b) {
  return a * b;
};

console.log(foo);
module.exports = {
  CommonSum: CommonSum,
  CommonMul: CommonMul
};

以上这个问题就可以通过配置devtool的值为cheap-module-source-map来解决,比如源文件中的js代码经过ts-loader、babel-loader的处理之后已经发生了变化,在打包前配置为cheap-module-source-map,就可以保证生成的source-map映射还原的源文件和真实的源文件一致,不会有行号、变量名前加下划线的差异,这其实也就是cheap-module-source-map和cheap-source-map两种配置不同的地方,那就是对loader处理的文件有着更加好的处理。

  • 使用cheap-module-source-map生成的source-map最终还原出来的源文件如下:可以看出和真实源文件一模一样,无任何差别,并且打包性能还比较高。
const CommonSum = (a,b)=>{
	return a+b;
}

const CommonMul = (a,b)=>{
	return a*b;
}

console.log(foo);

module.exports = {
	CommonSum,
	CommonMul
}

7. hidden-source-map

设置devtool的值为"hidden-source-map"会生成单独的source-map文件,但是在budnle.js中的最底端不会有对source-map文件的引用注释,相当于删除了打包文件中对source-map文件引用的注释。
hidden-source-map和false的区别在于是否生成了单独的source-map文件,hidden-source-map虽然会将bundle.js中的引用注释进行删除,但是如果我们手动添加该注释,那么source-map又会生效且映射出源文件。

/* 在bundle.js中添加如下注释 */
//# sourceMappingURL=bundle.js.map

8. nosources-source-map值

设置devtool的值为"nosources-source-map"会生成单独的source-map文件,但是生成的source-map只可以用来提示错误,并不会映射出源代码文件,所以也就无法定位到具体的错误信息,也无法查看源码。
点击错误信息跳转到source页面时报错:
nosources-source-map不映射源代码

devtool选项配置时不同值的组合

webpack为devtool选项提供了26个值用来配置是否生成以及如何生成source-map,除了单独定义以外还可以组合使用,但是应该按照如下的规则进行组合:

  1. inline-|hidden-|eval- 可选值;这三个值出现必须出现在第一位,三个值中选一个,不能重复选择
  2. nosources- 可选值;如果没有上面的三个值,那么这个值是第一位;如果有那么是第二位出现的
  3. cheap- 可选值;后面可以跟上module,也可以不用module
  4. source-map 固定组合,出现在末尾

综合起来它们的组合规则如下所示:

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map的最佳实践

开发环境

推荐使用source-map或者cheap-module-source-map
可以快速帮助定位错误,调试代码。

  • Vue脚手架中配置在开发环境就是source-map,生产环境是none缺省值
  • React脚手架中配置是做了一个判断:
devtool:{
	/* 判断是否为生产环境 */
	isEnvProduction?
		/* 如果是那么判断是否应用source-map? */
		shouldUseSourceMap?'source-map':false
		/* 如果不是生产环境,那么判断是否为开发环境并设置 */
	   :isEnvDevelopment && 'cheap-module-source-map'
}

测试环境

推荐使用source-map或者cheap-module-source-map

生产环境

推荐使用false或者缺省值也就是不写
避免出现源代码泄漏的风险

babel - 使用Webpack和Babel来搭建React应用程序

babel - 使用Webpack和Babel来搭建React应用程序

用Webpack(npm install -g webpack)代码打包,Webpack大致需要知道三件事:


1)让Webpack知道应用程序或js文件的根目录
2)让Webpack知道做何种转换
3)让Webpack知道转换后的文件保存在哪里

具体来说,大致要做以下几件事情:

1)在项目根目录下有一个webpack.config.js文件,这个是惯例
2)确保webpack.config.js能导出一个对象


module.exports = {};

3)告诉Webpack入口js文件在哪里

module.exports = { entry: ['./app/index.js'] }

4)告诉Webpack需要哪些转换插件

'./app/index.js'],module: { loaders: [] } }

所有的转换插件放在loaders数组中。

5)设置转换插件的细节

module: { loaders: [ { test: /\.coffee$/,include: __dirname + 'app',loader: "coffee-loader" } ] } }

  • test:运行.coffee结尾的文件
  • include:哪些文件件
  • loader:具体的转换插件

6)告诉Webpack导出到哪里

"coffee-loader" } ] },output: { filename: "index_bundle.js",path: __dirname + '/dist' } }

【文件结构】

app/
.....components/
.....containers/
.....config/
.....utils/
.....index.js
.....index.html
dist/
.....index.html
.....index_bundle.js
package.json
webpack.config.js
.gitignore

我们不禁要问,如何保证app/index.html和dist/index.html同步呢?

如果,我们每次运行webpack命令后,把app/index.html拷贝到dist/index.html中就好了。

解决这个问题的人是:html-webpack-plugin(npm install --save-dev html-webpack-plugin)。

引入html-webpack-plugin之后,webpack.config.js看起来是这样的:

var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
    template: __dirname + '/app/index.html',filename: 'index.html',inject: 'body'
});

'/dist'
    },plugins: [HTMLWebpackPluginConfig]
}
  • template: 表示源文件来自
  • filename:目标文件的名称
  • inject: 'body'表示把对dist/index_bundle.js文件的引用,放到目标文件dist/index.html的body部分

【Webpack的一些指令】

webpack webpack -w //监测文件变化 webpack -p //不仅包括转换,还包括最小化文件

【Babel】

Babel可以用来把JSX文件转换成js文件。与Babel相关的安装包括:

npm install --save-dev babel-core npm install --save-dev babel-loader npm install --save-dev babel-preset-react

  • babel-core: 就是babel本身
  • babel-loader:用来加载
  • babel-preset-react:用来完成JSX到JS的文件转换

在项目根文件夹下创建.babelrc文件。

{
    "presets": ["react"]
}

把babel-loader放到webpack.config.js文件的设置中去。

module: {
        loaders: [
            {
                test: /\.js$/,21); line-height:1.8">"babel-loader"
            }
        ]
    },plugins: [HTMLWebpackPluginConfig]
}


参考:http://tylermcginnis.com/react-js-tutorial-1-5-utilizing-webpack-and-babel-to-build-a-react-js-app/ 

Babel 处理 webpack 无法处理的 ES 语法(Babel 6.x 和 Babel 7.x 有区别)(二)

Babel 处理 webpack 无法处理的 ES 语法(Babel 6.x 和 Babel 7.x 有区别)(二)

//这是main.js是我们项目的js入口文件

//1:a导入jQuery,引入jQuery包用$变量接收,此方法与node中 const $ = require(''jquery'') 同
//此为ES6中导入模块的方式
//由于ES6的语法太高级,浏览器解析不了次引入方式
import $ from ''jquery''

import ''./css/index.scss''

//class关键字,是ES6中提供的新语法,用来实现 ES6 中面向对象变成方法
class Person{
    //使用static可以定义静态属性
    //所谓静态属性,就是可以通过类名直接访问的属性
    //实例属性,只能通过类的实例来访问的属性叫做实例属性
    static info = { name: ''zs'', age: ''20'' }
}

console.log(Person.info);
//在 webpack 中,默认只能处理一部分 ES6 的新语法,一些更高级的 ES6 语法和 ES7 语法,webpack 处理不了,这时候就需要借助第三方的loader,来帮助 webpack 处理这些更高级的语法,
//当第三方loader 把高级语法转为低级语法之后,会把结果交给webpack 去打包到bundle.js中
//通过Babel,可以帮助我们将高级语法转换为低级语法,
//1:在 webpack 中,可以运行如下两套命令, 安装两套包,全装 Bable 相关的loader功能;
//1.1:第一套: cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
//1.2:第二套:cnpm i babel-preset-env babel-preset-stage-0 -D
//2:打开 webpack 的配置文件,在module 节点下的 rules 数组中,添加一个新的匹配规则
//2.1:{ test:/\.js$/, use:''babel-loader'''', exclude:/node_modules/ }
//2.2:注意:在配置 babel 的 loader 规则的时候,必须把 node_modules 目录,通过 exclude 选项排除掉,原因有两个:
//  2.2.1:如果不排除 node_modules,则 babel 会把 node_modules 中所有的第三方 js 文件,都打包编译,这样会非常消耗CPU,同时打包速度非常慢。
//  2.2.2 哪怕,最终 babel 把所有 node_modules 中的 js 转换完毕了,但是项目也无法正常运行
//3:在项目的根目录中,新建一个叫做 .babelrc 的 babel 配置文件,这个配置文件属于JSON 格式,所以此文件必须符合 JSON 语法规范,不写注释,字符串必须用双引号
// 3.在 .babelrc 写如下配置:(可以把 presets 看做语法的意思)
    // {
    //     “presets”: ["env","stage-0"],
    //     "plugins": ["transform-runtime"]
    // }



//这样创建一个对象,和java,c#实现面向对象的方法一样,class后端借鉴过来的,来实现面向对象
// var p1 = new Person();

 上面是mian.js

下面创建 .babelrc文件在根目录

{
  "presets": ["env","stage-0"],
  "plugins": ["transform-runtime"]
}

 然后在 webpack.config.js 中添加 Babel 加载器:

{ test:/\.js$/, use: ''babel-loader'', exclude: /node_modules/ } //配置 babel 转化ES6 语法


上面的做法是 Babel^6x 版本的使用方法,babel在2018年9月份升级了一个大的版本 Babel 7.x,语法有了很大的变动,所以我上面运行的指令已经被out了,必须使用新语法,如下:
新命令如下适合 Babel 7.x和webpack 4 搭配:
  • 安装babel插件
    •   运行 cnpm i @babel/core babel-loader @babel/plugin-transform-runtime -D
    •   运行 cnpm i @babel/preset-env @babel/preset-stage-0 -D
  • 安装能够识别转换jsx语法的包 babel-preset-react
    •   运行 cnpm i @babel/preset-react -D
  • 执行命令:cnpm i @babel/plugin-proposal-class-properties -D
  • 执行命令:cnpm i @babel/runtime -D
  • 添加 .babelrc 配置文件:
{
"presets": ["@babel/preset-env","@babel/preset-react"],
"plugins": ["@babel/plugin-transform-runtime","@babel/plugin-proposal-class-properties"]
}

最后在 webpack.config.js 中配置上:

{ test:/\.js$/, use: ''babel-loader'', exclude: /node_modules/ } //配置 babel 转化ES6 语法

 

 

关于使用 babel 和 webpack 时如何生成 sourcemap?webpack 中如何配置 babel?的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于03.webpack中sourceMap等配置、06.webpack中source-map的配置、babel - 使用Webpack和Babel来搭建React应用程序、Babel 处理 webpack 无法处理的 ES 语法(Babel 6.x 和 Babel 7.x 有区别)(二)等相关内容,可以在本站寻找。

本文标签: