webpack从零基础到企业实战

293 阅读11分钟

前言

文章涉及的内容可能不全面,但量很多,需要慢慢看。我花了很长的时间整理,用心分享心得,希望对大家有所帮助。但是难免会有打字的错误或理解的错误点,希望发现的可以邮箱告诉我ghui_master@163.com,我会及时的进行修改,只希望对你有所帮助,谢谢。

webpack从零基础到企业实战

一、webpack 是什么? webpack 是基于 Node.js 静态资源的模块打包器。当用 webpack 打包时,它会从一个起点开始查找各个模块依赖关系,并且按照这些依赖关系把这些文件打成一个或者多个包; 二、为什么使用webpack? 模块化,浏览器端的早期没有模块化,需要 js 文件只能通过 script 标签手动控制依赖管理这些 js 文件的引入顺序;而 webpack 可以通过模块化管理这些依赖; 自动编译 less、sass 可以把基于 JS 的扩展语言编译成 JS; 开发时配置代理解决开发环境跨域 三、安装webpack 全局安装 webpack 和 webpack-cli 安装 webpack-cli npm install webpack-cli -g 安装 webpack npm install webpack -g 四、示例 新建文件夹 webpack-sample 在 webpack-sample 下新建文件夹 app、public 在 app 目录下新建 Greeter.js、main.js、config.json 在 public 目录下新建 index.html 写入代码 Greeter.js let config = require('./config.json'); module.exports = function () { var greet = document.createElement('div'); greet.textContent = config.greetText; return greet; }; config.json { "greetText":"Hi there and greetings from JSON!" } main.js const greeter = require('./Greeter.js'); document.querySelector('#root').appendChild(greeter()); console.log(12334) /public/index.html

Title

12333

xxxxxx
在 webpack-sample 目录下打开命令行,输入以下打包命令 webpack-cli --entry ./app/main.js --output ./public/bundle.js 运行 index.html 页面,可以看到已经成功; 五、配置npm scripts 前面我们通过命令行打包,需要配置一大堆参数。为了减少这种情况,我们需要配置 npm scripts npm scripts 配置 首先生成一个 package.json 文件 在 package.json 中有一个 scripts 对象,在这个对象中的每一个属性都是一个可以通过 npm run 执行的命令; 以上面打包示例 { "name": "wbp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "cmdBuild": "webpack-cli --entry ./app/main.js --output ./public/bundle.js" }, "keywords": [], "author": "", "license": "ISC" } 然后在 cmd 中执行 npm run cmdBuild 就相当于在执行 webpack-cli --entry ./app/main.js --output ./public/bundle.js 命令; 六、webpack 配置文件 webpack 是基于 Node.js 的,在项目根目录下创建一个 webpack.config.js,webpack 打包时会自动加载文件名为 webpack.config.js 的文件; 我们的配置文件要导出一个配置对象; 6.0 本地安装 webpack-cli、webpack npm install webpack-cli webpack --save-dev 6.1 配置entry、output entry(入口)是 webpack 打包的起点,它从这个文件开始查找各个模块之间的依赖 output(出口)是 webpack 打包后的文件输出目录和文件名; 示例: module.exports = { entry: __dirname + '/app/main.js', output: { path: __dirname + '/public', // 打包后文件存放的地方 filename: 'bundle.js' } }; 6.2 配置 source-map 什么是 source-map? js 代码编译后变得可读性非常差,不利于调试;而浏览器有一种 source-map 机制,可以通过 source-map 把代码虚拟还原成打包之前的样子,这样方便调试; webpack在打包结束后可以自动生成 source map 在配置中加入 devtool 属性 module.exports = { devtool: "eval-source-map", // 生成source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/public', // 打包后文件存放的地方 filename: 'bundle-[hash:5].js' } }; 6.3 配置打包的 npm scripts 在 package.json 中配置 build 命令; { "name": "wbp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "cmdBuild": "webpack-cli --entry ./app/main.js --output ./public/bundle.js" }, "keywords": [], "author": "", "license": "ISC" } 在命令行中执行 npm run build 即可打包 6.4 配置 dev-server 在开发的过程中我们可以让 webpack 给我们启动一个本地服务器,它可以: 让浏览器监听你的代码的修改,并自动刷新显示修改后的结果; 提供 AJAX 接口的代理,解决开发环境的跨域问题 安装 webpack-dev-server npm install --save-dev webpack-dev-server 在 webpack 配置中加入配置项 module.exports = { devtool: "eval-source-map", // 生成 source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/public', // 打包后文件存放的地方 filename: 'bundle1.js' }, devServer: { contentBase: './public', // 本地服务器所加载的页面所在的目录 historyApiFallback: true, // 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 API 标志,那么就需要开始代理 target: 'http://localhost:8001', changeOrigin: true, // target 是域名的话,需要这个参数, secure: false // 设置支持 https 协议的代理,不检查安全与否 } } } }; 配置详解 contentBase: './public', 本地服务器所加载的页面所在的目录 historyApiFallback: true, 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, 启动时的端口号 proxy: 代理配置 配置 npm scripts,启动 webpack 的 dev-server: { "name": "wbp", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "cmdBuild": "webpack-cli --entry ./app/main.js --output ./public/bundle.js", "dev": "webpack-dev-server --open" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack-dev-server": "^3.7.2" } } 七、配置loaders webpack 中一切皆模块,js、css、图片等。但是原则上,webpack 只能处理 js 文件。此时就需要借助 loader 的力量; 7.1 loader 是什么? 原则上,webpack 只能处理 js 文件,但是通过使用不同的 loader,webpack 有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换 less 为 css,或者把下一代的 JS 文件(ES6,ES7)转换为现代浏览器兼容的 JS 文件,对 React 的开发而言,合适的 Loaders 可以把 React 中用到的 JSX 文件转换为 JS 文件。 Loaders 需要单独安装并且需要在 webpack.config.js 中的 modules 关键字下进行配置,Loaders 的配置包括以下几方面: test:一个用以匹配 loaders 所处理文件的拓展名的正则表达式(必须) use:loader 的名称(必须) include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选); query:为 loaders 提供额外的设置选项(可选) 7.2 配置Babel Babel 其实是一个编译 JavaScript 的平台,让你能使用最新的 JavaScript 代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;让你能使用基于 JavaScript 进行了拓展的语言,比如 React 的 JSX; 安装Babel,需要安装 babel-core babel 的核心 babel-loader babel的 loader (安装7.x.x 版本,如7.1.5) babel-preset-env 把 ES5/6/7 转成当前能使用的版本 babel-preset-react 解析 React 的 JSX 语法 / npm一次性安装多个依赖模块,模块之间用空格隔开 npm install babel-core babel-loader@7.1.5 babel-preset-env babel-preset-react --save-dev 在 webpack 配置文件中加入 loader 信息; 在webpack 加入 module 配置 module.exports = { devtool: "eval-source-map", // 生成 source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/build', // 打包后文件存放的地方 filename: 'bundle1.js' }, devServer: { contentBase: './public', // 本地服务器所加载的页面所在的目录 historyApiFallback: true, // 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 API 标志,那么就需要开始代理 target: 'http://localhost:8001', changeOrigin: true, // target 是域名的话,需要这个参数, secure: false // 设置支持 https 协议的代理,不检查安全与否 } } }, module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader", options: { presets: ['env', 'react'] } }, exclude: /node_modules/ } ] } }; 7.3 配置CSS loader CSS 也可以像 js 一样的导入进来; 安装 css-loader 和 style-loader; 安装 css-loader 可以使用 @import 或者 url 等方法导入其他资源 style-loader 将所有计算后的样式加入页面中 npm install --save-dev style-loader css-loader 在 webpack 中配置 css-loader module.exports = { devtool: "eval-source-map", // 生成 source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/public', // 打包后文件存放的地方 filename: 'bundle.js' }, devServer: { contentBase: './public', // 本地服务器所加载的页面所在的目录 historyApiFallback: true, // 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 API 标志,那么就需要开始代理 target: 'http://localhost:8001', changeOrigin: true, // target 是域名的话,需要这个参数, secure: false // 设置支持 https 协议的代理,不检查安全与否 } } }, module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader", options: { presets: ['env', 'react'] } }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] } ] } }; 写一个css文件 main.css * { margin: 0; padding: 0; } div { font-size: 30px; color: red; } 在main.js 中导入css import './main.css'; 7.4 配置less 配置 less-loader 以后,我们写 less webpack 会帮我们编译成 css,就不需要我们再引入 js 文件; 安装less、less-loader npm install less less-loader --save-dev 2 配置 less-loader module.exports = { devtool: "eval-source-map", // 生成 source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/build', // 打包后文件存放的地方 filename: 'bundle.js' }, devServer: { contentBase: './public', // 本地服务器所加载的页面所在的目录 historyApiFallback: true, // 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 API 标志,那么就需要开始代理 target: 'http://localhost:8001', changeOrigin: true, // target 是域名的话,需要这个参数, secure: false // 设置支持 https 协议的代理,不检查安全与否 } } }, module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader", options: { presets: ['env', 'react'] } }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] } ] } }; 7.5 配置 url-loader 配置 url-loader 后,我们可以导入图片; 安装 url-loader npm install url-loader --save-dev 配置 url-loader module.exports = { devtool: 'eval-source-map', entry: __dirname + '/app/main.js', output: { path: __dirname + '/public', filename: 'bundle.js' }, devServer: { contentBase: __dirname + '/public', historyApiFallback: true, // 单页面应用路由切换时不请求服务器 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 api,就代理转发到 target target: 'http://localhost:8001', changeOrigin: true, // 如果 target 是域名的话,需要整个参数 secure: false // 不校验安全,允许访问 https } } }, module: { rules: [ { test: /\.(js|jsx)$/, use: { loader: 'babel-loader', options: { presets: ['env', 'react'] } }, exclude: /node_modules/ }, { test: /\.css$/, use: [ 'style-loader', 'css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.(png|jpg|gif)$/, use: { loader: 'url-loader' } } ] } }; 八、插件 插件(Plugins)是用来拓展 Webpack 功能的,它们会在整个构建过程中生效,执行相关的任务。Webpack 有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。 使用插件 要使用某个插件,我们需要通过 npm 安装它,然后要做的就是在 webpack 配置中的 plugins 关键字部分添加该插件的一个实例; 让 webpack 打包后帮我们引入打包后的js文件; 实现以上功能需要一个插件;html-webpack-plugin,首先安装它; 安装 npm install html-webpack-plugin --save-dev 引入插件 let HtmlWebpackPlugin = require('html-webpack-plugin'); 在 webpack 中增加一个 plugins 字段,它的值是一个数组,使用插件就是创建一个插件的实例,然后作为数组项放到 plugins 中; let HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { devtool: "eval-source-map", // 生成 source-map entry: __dirname + '/app/main.js', output: { path: __dirname + '/build', // 打包后文件存放的地方 filename: 'bundle.js' }, devServer: { contentBase: './public', // 本地服务器所加载的页面所在的目录 historyApiFallback: true, // 单页面应用路由切换时不跳转 inline: true, // 实时刷新 port: 8080, proxy: { '/api': { // 如果接口中带有 API 标志,那么就需要开始代理 target: 'http://localhost:8001', changeOrigin: true, // target 是域名的话,需要这个参数, secure: false // 设置支持 https 协议的代理,不检查安全与否 } } }, module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader", options: { presets: ['env', 'react'] } }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' } ] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.(png|jpg|gif)$/, use: 'url-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ template: __dirname + '/public/index.html' // 指定一个模板,webpack 将会按照这个模板创建 html }) ] }; 做完这些后,我们修改输出路径为 build; 执行 npm run build 执行后我们发现在 build 目录下有一个 index.html 和 bundle.js;同时 index.html 已经自动引入了 bundle.js

webpack.docschina.org/

webpack是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle

webpack 本身是基于node.js开发的。

webpack v4.0.0 开始,可以不用引入一个配置文件(零配置文件),然而webpack仍然还是高度可配置的。

1. 安装webpack

目前*@vue/clicreate-react-app基本上采用的是webpack 4.0以上版本,所以本次课程以第四代版本为主;第四代版本需要我们安装webpackwebpack-cli*(可执行命令);

为了防止全局安装webpack的版本冲突,我们真实项目开发的时候基本上以安装在本地项目中为主;

$ npm init -y

$ npm install webpack webpack-cli --save-dev
OR
$ yarn add webpack webpack-cli -D

2.webpack的基础使用

  • 初步体验(零配置)

    /*
     * 默认会打包SRC目录中的JS文件(入口默认index.js)
     * 打包完成的目录默认是DIST/MAIN.JS
     *
     * npx:http://www.ruanyifeng.com/blog/2019/02/npx.html
     * 默认执行node_modules/bin/webpack.cmd文件
     * webpack默认支持CommonJS和ES6 Module的模块规范,依此进行依赖打包
     */
    $ npx webpack
    
  • 自定义基础配置

    • webpack.config.js OR webpackfile.js
    let path = require('path');
    module.exports = {
    	//=>打包模式  开发环境development  生产环境production
    	mode: 'production',
    	//=>入口
    	entry: './src/index.js',
    	//=>输出
    	output: {
    		//=>输出文件的文件名
    		filename: 'bundle.js',
    		//=>输出目录的"绝对路径"
    		path: path.resolve(__dirname, 'dist')
    	}
    }
    
  • 自定义配置文件名

    • $ npx webpack --config webpack.config.development.js
    • 可在package.json中配置可执行的脚本命令(区分开发环境)
    "scripts": {
        "serve": "webpack --config webpack.config.development.js",
        "build": "webpack --config webpack.config.production.js"
    },
    

3.webpack-dev-server

webpack.js.org/configurati…

  • 安装:$ yarn add webpack-dev-server -D

  • 基础配置

    /* webpack.config.js */
    //=>配置DEV-SERVER
    devServer: {
        //=>端口
    	port: 3000,
        //=>显示编译进度
        progress: true,
        //=>指定访问资源目录
        contentBase: './dist',
        //=>自动打开浏览器
        open: true
    }
    
    /* package.json */
    "scripts": {
        "serve": "webpack-dev-server",
        "build": "webpack"
    }
    
    • $ npm run serve
    • $ npx webpack-dev-server
  • 代码更改后,会自动重新编译,然后自动刷新页面

4.html-webpack-plugin

www.webpackjs.com/plugins/htm…

  • 安装:$ yarn add html-webpack-plugin -D

  • 在webpack.config.js中使用

    let HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        ...,
        //=>在webpack中使用插件
    	plugins: [
    		new HtmlWebpackPlugin({
    			//=>指定自己的模板
    			template: './src/index.html',
    			//=>输出的文件名
    			filename: 'index.html',
    			//=>给引入的文件设置HASH戳(清除缓存的),也可以在output中设置 filename: 'bundle.[hash].js' 来生成不同的文件
    			hash: true,
    			//=>控制是否以及以何种方式最小化输出 
        		//=>https://github.com/kangax/html-minifier
    			minify: {
    				collapseWhitespace: true,
    				removeComments: true,
    				removeAttributeQuotes: true,
    				removeEmptyAttributes: true
    			}
    		})
    	]
    }
    

5.webpack中的加载器loader:处理样式的

  • 安装:$ yarn add css-loader style-loader less less-loader autoprefixer postcss-loader ... -D

  • 使用

    module.exports = {
        //=>配置模块加载器LOADER
    	module: {
    		//=>模块规则:使用加载器(默认从右向左执行,从下向上)
    		rules: [{
    			test: /\.(css|less)$/, //=>基于正则表达式匹配哪些模块需要处理
    			use: [
    				"style-loader", //=>把CSS插入到HEAD中
    				"css-loader", //=>编译解析@import/URL()这种语法
                    "postcss-loader", //=>设置前缀
                    {
    					loader: "less-loader",
                        options: {
                            //=>加载器额外的配置
                        }
    				}
    			]
    		}]
    	}
    }
    

    postcss.config.js

    module.exports = {
    	plugins: [
    		require('autoprefixer')
    	]
    };
    
    

    package.json

    "browserslist": [
        "> 1%",
        "last 2 versions"
    ]
    
    

6.mini-css-extract-plugin 抽离CSS内容

www.npmjs.com/package/min…

  • 安装 $ yarn add mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
let MiniCssExtractPlugin = require('mini-css-extract-plugin'),
	OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'),
	UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
    //=>设置优化项
	optimization: {
		//=>设置压缩方式
		minimizer: [
			//=>压缩CSS(但是必须指定JS的压缩方式)
			new OptimizeCssAssetsWebpackPlugin(),
			//=>压缩JS
			new UglifyjsWebpackPlugin({
				cache: true, //=>是否使用缓存
				parallel: true, //=>是否是并发编译
				sourceMap: true, //=>启动源码映射(方便调试)
			})
		]
	},
	plugins: [
		//=>使用插件
		new MiniCssExtractPlugin({
			//=>设置编译后的文件名字
			filename: 'main.css'
		})
	],
	module: {
		rules: [{
			test: /\.(css|less)$/,
			use: [
				// "style-loader",
				//=>使用插件中的LOADER代替STYLE方式
				MiniCssExtractPlugin.loader,
				"css-loader",
                "postcss-loader",
				"less-loader"
			]
		}]
	}
}

上述JS压缩对于require/import等还存在问题,需要对于ES6中的一些语法进行处理!

7.基于babel实现ES6的转换和ESLint语法检测

babeljs.io/

eslint.org/

  • 安装 $ yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime -D
  • 安装 $ yarn add @babel/runtime @babel/polyfill
  • 安装 $ yarn add eslint eslint-loader -D
module.exports = {
	...,
	module: {
		rules: [...,{
			test: /\.js$/,
			use: [{
				loader: 'babel-loader',
				options: {
					//=>转换的语法预设(ES6->ES5)
					presets: [
						"@babel/preset-env"
					],
					//=>基于插件处理ES6/ES7中CLASS的特殊语法
					plugins: [
						["@babel/plugin-proposal-decorators", {
							"legacy": true
						}],
						["@babel/plugin-proposal-class-properties", {
							"loose": true
						}],
						"@babel/plugin-transform-runtime"
					]
				}
			}], //=>, "eslint-loader"
			//=>设置编译时忽略的文件和指定编译目录
			include: path.resolve(__dirname, 'src'),
			exclude: /node_modules/
		}]
	}
}

参考https://eslint.org/demo生成 .eslintrc.json

补充知识:在vscode中开启ES7中类的装饰器,项目根目录中设置 jsconfig.json

{
	"compilerOptions": {
		"experimentalDecorators": true
	}
}

@log
class A{
	a=1;
}

8.暴露全局loader

  • $ yarn add expose-loader -D
  • 前置加载器、后置加载器、普通加载器...
//=>内联加载器
import jquery from 'expose-loader?$!jquery';
console.log(window.$);

{
    //=>只要引入JQUERY就在全局注入$
    test: require.resolve('jquery'),
    use: ['expose-loader?$']
}

let webpack = require('webpack');
module.exports = {
    plugins: [
		//=>在每个模块中都注入$
		new webpack.ProvidePlugin({
			'$': 'jquery'
		})
	],
}

//=>页面中
console.log($);

9.webpack中图片的处理和分目录分发

  • 在JS中创建IMG
  • 在CSS中设置背景图
  • 在HTML中写死
  • ...

安装 $ yarn add file-loader url-loader html-withimg-loader -D

module.exports = {
	...,
	module: {
		//=>模块规则:使用加载器(默认从右向左执行)
		rules: [..., {
			test: /\.(png|jpg|gif)$/i,
			use: [{
				//=>把指定大小内的图片BASE64
				loader: 'url-loader',
				options: {
					limit: 200 * 1024,
    				outputPath:'/images'
				}
			}],
			include: path.resolve(__dirname, 'src'),
			exclude: /node_modules/
		}, {
			test: /\.html$/,
			use: ['html-withimg-loader']
		}]
	}
}

最后实现文件分目录发布

module.exports = {
	output: {
		//=>配置引用前缀(所有资源前加这个地址)
		publicPath: './'
	},
	plugins: [
		new MiniCssExtractPlugin({
			filename: 'css/main.css'
		})
	],
	module: {
		//=>模块规则:使用加载器(默认从右向左执行)
		rules: [...,{
			test: /\.(png|jpg|gif)$/i,
			use: [{
				options: {
					outputPath: 'images'
				}
			}]
		}]
	}
}