webpack详解

本文深入讲解Webpack的配置与使用技巧,包括多入口文件、代码分割、模块合并、懒加载等功能,以及如何通过插件和loader处理各种文件类型,如JS、CSS、图片等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5npm相关
npm i webpack --save-dev      开发环境下的依赖
运行cnpm run dev自动打开
//package.json文件
"scripts": {
   "dev": "webpack-dev-server --open --port 3000 --hot"
}
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server",
    "build": "webpack"
  }
webpack功能
  • 代码转换
  • 文件优化
  • 代码分割
  • 模块合并
  • 自动刷新
  • 代码校验
  • 自动发布
loader功能

loader让webpack能够去处理那些非js文件,(webpack自身只能理解js)。loader能让所有类型的文件转换为webpack能够处理的有效模块,然后就可利用webpack的打包能力对其进行处理。

loader的两大目标:

  1. test属性,用于标识出应该被对应的loader进行转换的某个文件。
  2. use属性,表示在转换时,应使用哪个loader。
webpack使用
  • webpack默认只支持js文件
  • node webpack
//安装本地的webpack
cnpm i webpack webpack-cli -D
//表示开发依赖
//在命令行上输入webpack即可将入口文件打包
//webpack可以进行0配置
手动配置webpack文件

webpack配置文件webpack.config.js

module.exports = {
	mode: 'development',//模式默认两种,production  development
	entry: path.join(__dirname,'./src/main.js'),
	output: {
		path: path.resolve(__dirname,'dist'),//路径必须是一个绝对路径
		filename: 'bundle.js',//打包后的文件名
	}
}

production:开发的未压缩的文件

development:开发的压缩的文件

更改配置文件名字
在package.json文件中的script对象上
scripts:{
	"builder": "webpack --config webpack.configmy.js"
}
//命令行上运行 cnpm run builder即可
webpack以本地形式打开文件
cnpm i webpack-dev-server -D
//webpack.config.js
module.exports = {
	devServer:{ //开发服务器配置
		port: 3000,  //在3000端口打开
		progress: true,  //显示进度条
		contentBase: './build',  //以当前目录运行程序
		compress: true  //启动压缩
		
	}
}
//package.json文件
scripts:{
	"dev": "webpack-dev-server"
}
webpack解析html模块
cnpm i html-webpack-plugin -D
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
	output: {
		path: path.resove(__dirname,'./dist'),
		filename: 'bundle.[hash].js',  //每次输出的文件都不同
		//filename: 'bundle.[hash:8].js',  只显示8位hash值 
	}
	plugins: [  //数组 放着所有的webpack插件
		new HtmlWebpackPlugin({
			template: './src/index.html'  //以index.html文件为模板
			filename: 'index.html', //打包后的文件名
			minify: {
				removeAttributeQuotes:true,  //压缩时,删除属性中的双引号
				collapseWhiteSpace: true,  //折叠空行,将html文件打包成一行
				
			},
			hash: true,  //添加哈希戳
		})
	
	]
}
webpack解析css模块
cnpm i style-loader css-loader -D  //处理css文件
cnpm i less less-loader -D   //less-loader会调用less进行解析
cnpm i node-sass sass-loader -D   //处理sass和scss文件
cnpm i stylus stylus-loader -D   //处理stylus文件

//main.js文件导入css文件
require('./index.css');

//webpack.config.js
module.exports = {
	module: { //模块
		rules: [  //规则  css-loader 处理@import这种语法的,将多个css文件合并为一个css文件
		  //style-loader 他是把css插入到head的标签中
		  //loader特点:希望一个loader处理一个功能
		  //只用一个loader用字符串,多个loader用数组,当有参数时可写成对象方式
		  //loader顺序:默认从右向左执行
		  { 
		      test: /\.css$/,
              use: [
               {
		  		 loader:'style-loader',
		  		 options: {
		  		 	insertAt: 'top'  //将样式插到head顶部
		  		 }
		 	   },
		 	   'css-loader'
		 	  ]
		 },
		 { test: /\.less$/, use: ['style-loader','css-loader','less-loader'] }
		]
	
	}
}
抽离文件的插件
cnpm i mini-css-extract-plugin -D
//专门用于抽离css样式的插件

//webpack.config.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
	new HtmlWebpackPlugin({
		template: './src/index.html',
		filename: 'index.html'
	}),
	new MiniCssExtractPlugin({
		filenmae: 'main.css'   //抽离出的名字叫main.css
	})
],
module: {
	rules:[
		{
		  test:/\.css$/,
		  use: [
		    MiniCssExtractPlugin.loader,
		    'css-loader'    //先解析成css样式,然后使用MiniCssExtractPlugin这个插件的加载器将样式分离成一个main.css文件
		  ]
		}
	]
}
自动加前缀的插件
cnpm i postcss-loader autoprefixer 

module: {
	rules: [
		{
            test: /\.less$/,
            use:[
				MiniCssExtractPlugin.loader,
				'css-loader',
				'postcss-loader',
				'less-loader'
            ]
		}
	]
}

//新建一个postcss.config.js文件
module.exports = {
	plugins: [require('autoprefixer')]
}
webpack中处理js模块
  • 将es6转为es5
cnpm i babel-loader @babel/core @babel/preset-env -D
// @babel/core是babel的核心模块,@babel/preset-env 将高级语法转为低级语法
cnpm i eslint eslint-loader -D  //语法检查

cnpm i @babel/plugin-proposal-class-properties -D  //像class类这些高级语法

cnpm i @babel/runtime  @babel/plugin-transform-runtime -D @babel/runtime   //像generator遍历器,在plugins中配置

//webpack.config.js
module.exports = {
	module: {
		noParse: /jquery/,  //不去解析jquery中的依赖库
		rules: [
			{ test: /\.js$/,
			  use: {
			  	{
                    loader:'babel-loader',  //用该模块将es6转为es5
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: ['@babel/plugin-proposal-class-properties','@babel/plugin-transform-runtime']
                    },
                    {
                      loader: 'eslint-loader', //校验es语法规范
                      options: {
                        enforce: 'pre'  //强制先执行,因为loader默认是从下往上执行
                      }
                    }
			  	}
			  },
			  exclude: /node_modules/,   //不去node_module中找
			  include: path.resolve(__dirname,'src')  //去src文件夹下找
			}
		]
	}
}
webpack第三方模块的使用
module.exports = {
	resolve: {  //解析第三方包 common
		modules: [path.resolve('node_modules')],  //解析时先到该目录下寻找
		alias:{ //别名 vue vue.runtime
			bootstrap: 'bootstrap/dist/css/bootstrap.css'
		}
	}
}
cnpm i jquery 
cnpm i expose-loader

//main.js文件
import $ from 'jquery';  //此$不是window上的的$
window.$   //undefined

import $ from 'expose-loader?$!jquery';
//expose-loader 暴露 全局的loader  内联的loader
//pre 前面执行的loader   normal  普通loader  内联loader  后置 postloader

将$暴露给window的另一种方法

cnpm i jquery 
cnpm i expose-loader

//main.js文件
import $ from 'jquery';

//webpack.config.js文件
module: {
	rules: [
		{
			test:require.resolve('jquery'),
			use: 'expose-loader?$!jquery'
		}
	]
}

在每个模块中注入$对象

//webpack.config.js文件
const webpack = require('webpack')
module.exports = {
	plugins: [
		new HtmlWebpackPlugin({
			template: './src/index.html',
			filename: 'index.html'
		}),
		new Webpack.ProvidePlugin({  //在每个模块中注入$
			$: 'jquery'
		})
	]
}
//main.js文件
import $ from 'jquery'   //运行npm run build时会将jquery打包

//不像将jquery打包,只需在webpack.config.js文件中
module.exports = {
	externals: {
		jquery: '$'
	}
}
webpack打包图片
  1. 在js中创建图片
cnpm i file-loader -D  
//file-loader默认会在内部生成一张图片到dist文件夹下,并把生成的图片的名字返回回来

//main.js文件
import logo from './logo.png';
//把图片引入,返回结果是一个新的图片地址
let image = new Image();
image.src = logo;  //就是一个普通的字符串
document.body.appendChildren(image)

//webpack.config.js文件
module.exports = {
	module: {
		rules: [
			{ test: /\.(png|jpg|gif)$/, use: 'fle-loader' },
		]
	}
}
  1. 在css引入background(‘url’)

  2. 使用标签

    cnpm i html-withimg-loader -D
    cnpm i url-loader -D
    //在html文件中引入image,但是路径不在打包目录中的路径中
    module.exports = {
    	module: {
    		rules: [
    			{test: /\.html$/, use: 'html-withimg-loader'},
    			{
    				test: /\.(png|jpg|gif)$/,
    				//做一个限制,当图片小于多少k时用base64来转化
    				//否则用file-loader产生真实的图片
    				use: {
    					loader: 'url-loader',
    					options: {
    						limit: 200*1024,
    						outputPath: '/img/'  //输出路径放置在img目录下
    						publicPath: 'http://www.chenchuyin.com'  //统一给图片加上该域名
    					}
    				}
    			}
    		]
    	}
    }
    
webpack有多入口文件
cnpm i html-webpack-plugin - D
cnpm i @babel/core @babel/preset-dev @babel-loadedr @webpack-dev-server -D
//webpack.config.js文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
	//源码映射  会单独生成一个sourcemap文件,出错了胡hi标识当前出错的列和行
	devtool: 'source-map',  //增加映射文件 可以帮我们调试源代码
	//多入口
	entry: {
		home: './src/index.js',
		other: './src/other.js'
	},
	output: {
		//[name]表示 home 和 other  [hash]添加哈希戳
		filename: '[name].[hash].js',
		path: path.resolve(__dirname,'dist')
	},
	plugins: [
		//若无chunks,会把两个js文件都引入到index.html和other.html文件中
		new HtmlWebpackPlugin({
			tempalte: './index.html',
			filename: 'index.html',
			chunks: ['index']  //index.html文件引入index.js文件
		}),
		new HtmlWebpackPlugin({
			tempalte: './other.html',
			filename: 'other.html',
			chunks: ['other','index']   //other.html文件引入other.js和index.js文件
		})
	]
}
  1. 不会产生单独文件,但是可以显示行和列

    devtol:'eval-source-map'
    
  2. 不会产生列,但是是一个单独的映射文件

    devtoole: 'cheap-module-source-map'  //产生后你可以保存起来
    
  3. 不会产生文件,集成在打包后的文件中,不会产生列

    devtoole: 'cheap-module-eval-source-map'
    
自动打包出实体文件
module.exports = {
	watch: true,  //监听文件,只要文件有所修改便直接生成新的实体文件
	watchOptions:{  //监控选项
		poll: 1000,  //每秒 查看文件是否更改 1000次
		aggreatement: 500,  //防抖 如果一直输入代码,则每过500次再生成实体文件
		ignored: /node_modules/  //不需要进行监控的文件
	}
}
webpack小插件
  1. cleanWebpackPlugin
  2. copyWebpackPlugin
  3. bannerPlugin(内置的,不需要安装该模块)
cnpm i clean-webpack-plugin copy-webpack-plugin webpack -D

const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
plugins: [
	new CleanWebpackPlugin('./dist'),   //打包之前先将dist目录清除
	new CopyWebpackPlugin(
		{from: 'doc', to: './'}  //将doc.txt文件复制到输出的根目录下
	),
	new webpack.BannerPlugin('make 2019 by chenchuyin')
	//会将这句话插入到出口js文件的开头部分
]
webpack实现跨域功能
//webpack-dev-server默认打开localhost:8080,若想访问3000端口,可使用代理
module.exports = {
	devServer:{
		proxy: {  //重写的方式 把请求代理到express服务器上
			//  '/api': 'http://localhost:3000',   //配置了一个代理
			'api': {
				target: 'http://localhost:3000',
				pathRewrute: {'api': ''}  
			}
		}
	}
}

//server.js文件
const express = require('express')
let app = express();
app.get('/user', (req,res) => {
	res.json({name:'陈楚吟'})
})
app.listen(3000);

//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true);  //请求还是有api目录,显示时将其删除
xhr.onload = function(){
	console.log(xhr.response);
}
xhr.send();
devServer:{
 //我们前端只想单纯模拟数据
 before(app){
 	app.get('/user', (req,res) => {
 		res.json({name: '陈楚吟'})
 	})
 }
}

//server.js文件
const express = require('express')
let app = express();
app.listen(3000);

//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true);  //请求还是有api目录,显示时将其删除
xhr.onload = function(){
	console.log(xhr.response);
}
xhr.send();
cnpm i webpack-dev-middleware -D

devServer:{
 //有服务端,不用代理来处理,在服务端中启动webpack端口用服务端端口(即前端页面和服务器在同一个端口)

}

//server.js文件
const express = require('express');
const webpack = require('webpack');
let app = express();

//中间件
let middle = require('webpack-dev-middleware');
let config = require('./webpack.config.js');
let compiler = webpack(config);
app.use(middle(compiler));
app.get('/user', (req,res) => {
	res.json({name:'陈楚吟'})
})
app.listen(3000);
实现多线程打包
cnpm i happypack
//模块happypack可以实现多线程来打包进程
let Happypack = require('happypack')
module: {
	rules: [
		{
			test: /\.js$/,
			exclude: /node_modules/,
			include: 'src',
			use: 'Happypack/loader?id=js'
		},
		{
			test: /\.css$/,
			use: 'Happypack/loader?id=css'
		}
	]
},
palugins: [
	new Happypack({
		id: 'css',
		use: ['style-loader','css-loader']
	})
	new Happypack({
		id: 'js',
		use: [{
			loader:'babel-loader',
			options: {
				presets: [
					'@babel/preset-env',
					'@babel/preset-react'
				]
			}
		}]
	})
]
webpack自带优化
//import 在生成环境下会自动去除没用的代码
//因tree-shaking方法会将没用到的代码自动删除掉·

//require(es6)模块会把结果都放到default上
module.exports = { 
	optimization: {  //将公共部分抽离出来
		splitChunks: {   //分割代码块
			cacheGrounps:{ //缓存组
				common:{ //公共的模块
					chunks: 'initial',  //代码在刚开始时就被抽离出
					minSize: 0,  //代码有多少字节才被抽离
					minChunks: 2   //代码块被使用多少次才被抽离
				},
				vendor:{
					priority: 1,  //抽离权重,较大的先抽离
					test: /node_modules/,  //将第三方模块抽离出来
					chunks: 'initial',  
                    minSize: 0,  
                     minChunks: 2 
				}
			}
		}
	}
}
webpack懒加载
  • vue和react的懒加载其实都是通过import实现
//index.js
let button = document.createElement('button')
button.innerHTML = 'hello';
button.addEventListener('click',function(){
	//import 是草案中的语法,由jsonp实现动态加载文件
	//返回值是一个1promise对象
	import('./source.js').then(data => {
		console.log(data.default);
	})
})
webpack热更新
module.exports = {
	devServer: {
		hot: true,  //启动热更新操作
		port: 3000
	},
	plugins: [
		new webpack.NameModulesPlugin(),  //告诉我们哪个模块热更新
		new webpack.HotModuleReplacementPlugin()  //热更新插件
	]
}

//index.js文件
import str from './source';
if(module.hot){
	module.hot.accept('./source', () => {
		require('./source');
	})
}
Tapable
  • webpack本质上是一种事件流机制,它的工作流程是将各个插件串联起来,而实现这一切的核心是Tapable,Tapable是有点类似于nodeJs的events库,核心原理也是依赖于发布订阅模式。

webpack中使用vue

在普通网页中使用vue

//1.使用script标签,引入vue的包   (此包功能较全)
<scrpit src="./lib/vue.js"></srcipt>

//2.在index页面中,创建一个id为app的div容器
<div id="app"></div>

//3.通过new Vue得到一个vm实例
let vm = new Vue({
	el: '#app'
})

在webpack中尝试使用vue

//注意:在webpack中,使用import Vue from 'vue' 导入的Vue构造函数功能不完整,只提供了runtime-only的形式,并无提供像网页中那样的使用方式;
import Vue from 'vue'

let vm = new Vue({
	el: '#app'
})

解决:指定完整vue.js文件的路径
方式一:import Vue from './node_modules/vue/dist/vue.js';
方式二:import vue from 'vue'
在webpack.config.js文件中
module.exports = {
	resolve: {
		alias: {  //修改vue时被导入时包的路径
			"vue$": "vue/dist/vue.js"
		}
	}
}


cnpm i vue -S
cnpm i vue-loader vue-template-compiler -D

//webpack.config.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')

const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
    mode: "development",
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname,'./dist'),
        filename: 'bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        }),
        new VueLoaderPlugin()
    ],
    module: {
        rules: [
            { test: /\.css/, use: ['style-loader','css-loader'] },
            { test: /\.vue/, use: 'vue-loader' }
        ]
    }
}
1.安装 vue 包,cnpm i vue -S
2.由于在webpack中推荐使用vue组件模板文件定义组件,故需要安装能解析这种文件的loader    cnpm i vue-loader vue-template-compiler -D
3.在main.js中导入vue模块, import Vue from 'vue'
4.定义.vue结尾的组件,由三部分组成:template script  style
5.使用import login from './login.vue'导入这个组件
6.创建vm实例 let vm = new Vue({ el: '#app', render: c => c(login) })
7.在页面创建一个id为app的div元素,作为我们vm实例要控制的区域;

注意:App这个组件是通过vm实例的render函数渲染出来的,render函数如果要渲染组件,渲染出来的组件只能放到el:‘#app’ 所指定的元素中。

Account和Goodlist组件是通过路由配置监听到的,所以这两个组件只能展示到属于路由的中去。

包的查找规则

找vue文件

  1. 找项目根目录中有没有 node_modules 的文件夹
  2. 在node_modules中根据包名找对应的vue文件夹
  3. 在vue文件夹中,找一个叫package.json 的包配置文件
  4. 在 package.json 文件中,查找一个main 属性【main属性指定了这个包在被加载时的入口文件】
//sf-pack.js文件
//需要找到当前执行命令的路径,拿到webpack.config.json
const path = require('path')

//config配置文件
let config = require(path.resolve('webpack.config.js'));

let Compiler = require('../lib/Compiler.js');
let compiler = new Compiler(config);
//标识运行编译
compiler.run();

//Compiler.js文件
const path = require('path');
const fs = require('fs');
class Compiler{
	constructor(config){
		//entry  output
		this.config = config;
		//需要保存入口文件的路径
		this.entryId;    //'./src/index.js'
		//需要保存所有模块依赖
		this.modules = {};
		this.entry = config.entry;  //入口路径
		//工作路径
		this.root = process.cwd();
	}
	getSource(modulePath){
		let content = fs.readFileSync(modulePath,'utf-8');
		return content;
	}
	//构建模块
	buildModule(modulePath,isEntry){
		//拿到模块内容
		let source = this.getSource(modulePath);
		//模块id  modulePath = modulePath - this.root;
		let modulePath = './' +  path.relative(this.root,modulePath);
	}
	run(){
		//执行并且创建模块的依赖关系
		this.buildModule(path.resolve(this.root,this.entry),true);
		//true表示为主模块
		//发射一个文件,该文件即为打包后的文件
		this.emitFile();
	}
}
module.exports = Compiler
webpack
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值