背景
- 随着前端的快速发展,目前前端的开发已经变的越来越复杂了
- 比如开发过程中我们需要通过模块化的方式来开发
- 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码
- 比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化
- 等等….
- 但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题
webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;我们来对上面的解释进行拆解
打包bundler : webpack可以将帮助我们进行打包,所以它是一个打包工具
静态的static :这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器)
模块化module : webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等
现代的modern:正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展
- JavaScript的打包
- 将ES6转换成ES5的语法
- TypeScript的处理,将其转换成JavaScript
- Css的处理
- CSS文件模块的加载、提取
- Less、Sass等预处理器的处理
- 资源文件img、font
- 图片img文件的加载
- 字体font文件的加载
- HTML资源的处理
- 打包HTML资源文件
处理vue项目的SFC文件
我们发现是可以正常进行打包的,但是有一个问题,webpack是如何确定我们的入口的呢?
- 事实上,当我们运行webpack时,webpack会查找当前目录下的src/index.js作为入口
- 所以,如果当前项目中没有存在src/index.js文件,那么会报错
通过命令(局限)
通过配置来指定入口和出口npx webpack --entry ./src/main.js --output-path ./build
通过scripts(局限)
但是每次这样执行命令来对源码进行编译,会非常繁琐,所以我们可以在package.json中增加一个新的脚本
通过配置文件(常用)
webpack内置了一些配置,无需我们自己手动配置。
如果我们希望自己手动配置一些东西,就要在项目下创建一个 webpack.config.js 文件,这个就是webpack的配置文件。
webpack每次打包时,都会检查项目下有没有这个文件,没有的话就用默认配置,有的话就用这里的配置。
下面是一个最基本的 webpack.config.js 的例子: ```javascript // ./src/webpack.config.js
// 这个是nodejs的路径api,这里用于帮助我们得到绝对路径 const path = require(‘path’);
// 这里是导出一个对象,对象的属性就是配置 module.exports = { // 指定项目的js入口 entry: “./src/main.js”,
// 指定打包输出后到哪个文件夹,默认是项目下的dist文件夹 output: { // 这里提供nodejs的path 路径API,获取当前文件 webpack.config.js 所在文件夹的绝对路径,然后指向build文件夹 // 意思是输出到项目下的build文件夹,而不是默认的dist path: path.resolve(__dirname, “./build”),
// 指定输出后的文件名,默认就是bundle.jsfilename: "bundle.js"
} }
如果我不想用 webpack.config.js 这个名字,想换一个,可以在package.json里的scripts里面设置<br /><br />其他配置可以查看官网:[https://webpack.docschina.org/configuration/](https://webpack.docschina.org/configuration/)<a name="QuR9b"></a># 入口什么是入口?就是整个项目js的入口,webpack需要一个入口,才能识别到哪些文件、代码需要打包,哪些不需要。<a name="uvAhm"></a># 依赖图- webpack到底是如何对我们的项目进行打包的呢?- 事实上webpack在处理应用程序时,它会根据命令或者配置文件找到入口文件- 从入口开始,会生成一个依赖关系图,这个依赖关系图会包含应用程序中所需的所有模块(比如.js文件、css文件、图片、字体等)- 然后遍历图结构,打包一个个模块(根据文件的不同使用不同的loader来解析)<br />也就是所有和主入口有关系的文件,都会被打包。怎么产生关系?通过主入口引入的文件(如import、require等),以及后面再引入到这个文件的文件,这些都是。<br />这是一种图结构,文件和文件可能相互依赖,同时也会识别哪些已经加载过,不会重复打包<a name="zptT8"></a># 加载器 Loader- loader可以用于对模块的源代码进行转换- 我们可以将css文件也看成是一个模块,我们是通过import来加载这个模块的- 在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能webpack 目前来说只是帮我们打包js文件,其他文件如.css是不认识的。<br />而通过安装一些加载器Loader,可以帮助webpack认识这些文件,并且一起打包。<a name="pfPoz"></a>## 如何使用Loader?1、安装对应的loader,比如要处理和识别css,就要安装css-loader```bashnpm install loader名字 -D
2、给webpack添加对应loader的配置,告诉webpack怎么使用
// ./src/webpack.config.jsconst path = require('path');module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "./build"),filename: "bundle.js"},// 配置loader模块module: {rules: [ // 规则属性{}, // loader对应的配置{},......{},]}}
其他loader可以查看:
WebPack-loader 加载器
插件 Plugin
loader做不了的事情,webpack某个阶段想做的事情,都是通过插件完成(如打包时自动删除没有使用的文件)
loader只是处理webpack打包,而插件是在任何webpack生命周期都会起作用。
- Loader是用于特定的模块类型进行转换
- Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等

其他插件可以查看
Webpack - Plugin 插件
打包模式 Mode
背景
默认打包,是把所有js代码都拷贝到1个叫 bundle.js 的文件内,丑化和优化过的,只有1行。
但是缺点是,自己调试的时候,如果有个地方出错了,你很难找到到底是哪里出错了,因为都是只有1行,而且不会告诉你哪个位置(如下图)
开发模式(方便调试错误)

如果把mode设置成开发模式,那么打包出来的bundle.js 就是没有压缩和丑化的。
但结果还是看的有点吃力,这是因为devtool默认值是”eval”,把这个改为”source-map”就可以了。
会多一个 .js.map 的文件,这个是一个映射文件,意思是打包后的bundle.js 通过映射,映射到我们的源代码里面,这样就可以查看了。

其他选项
- Mode配置选项,可以告知webpack使用响应模式的内置优化
- 默认值是production(什么都不设置的情况下)
- 可选值有:
'none' | 'development' | 'production'
webpack中使用Vue
开发环境配置、生产环境配置
背景
- 目前我们所有的webpack配置信息都是放到一个配置文件中的: webpack.config.js
- 当配置越来越多时,这个文件会变得越来越不容易维护
- 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的
- 所以,我们最好对配置进行划分,方便我们维护和管理
比如开发的时候,配置是开发环境的,打包上生产环境的时候,还是用这套配置就不合适,但是不可能打包的时候把代码改成生产的,然后又改回开发的。
方案一:两个配置文件
编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;
1、创建文件
2、修改脚本
3、安装webpack-merge插件
npm install webpack-merge -D
4、编写公共配置
把开发的配置和生成环境的配置抽出来放到对应的文件,这里只留下两边共有的
// webpack.comm.config.jsconst path = require("path");const HtmlWebpackPlugin = require("html-webpack-plugin");const { DefinePlugin } = require("webpack");const { VueLoaderPlugin } = require('vue-loader/dist/index');module.exports = {target: "web",entry: "./src/main.js",output: {path: path.resolve(__dirname, "../build"), // 路径要调整一下filename: "js/bundle.js",},resolve: {extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],alias: {"@": path.resolve(__dirname, "../src"), // 路径要调整一下"js": path.resolve(__dirname, "../src/js") // 路径要调整一下}},module: {rules: [// 公共的规则],},plugins: [new HtmlWebpackPlugin({template: "./public/index.html",title: "哈哈哈哈"}),new DefinePlugin({BASE_URL: "'./'",__VUE_OPTIONS_API__: true,__VUE_PROD_DEVTOOLS__: false}),new VueLoaderPlugin()],};
5、编写开发配置文件
// webpack.dev.config.js// 1、引入安装的webpack-mergeconst { merge } = require('webpack-merge');// 2、引入公共文件const commonConfig = require('./webpack.comm.config');// 3、用nodeJS的方式导出模块,merge函数就是合并两个配置的module.exports = merge(commonConfig, {mode: "development",devtool: "source-map",devServer: {contentBase: "./public",hot: true,// host: "0.0.0.0",port: 7777,open: true,// compress: true,proxy: {"/api": {target: "http://localhost:8888",pathRewrite: {"^/api": ""},secure: false,changeOrigin: true}}},})
6、编生产配置文件
//webpack.prod.config.jsconst { CleanWebpackPlugin } = require("clean-webpack-plugin");const CopyWebpackPlugin = require('copy-webpack-plugin');// 1、引入安装的webpack-mergeconst {merge} = require('webpack-merge');// 2、引入公共文件const commonConfig = require('./webpack.comm.config');// 3、用nodeJS的方式导出模块,merge函数就是合并两个配置的module.exports = merge(commonConfig, {mode: "production",plugins: [new CleanWebpackPlugin(),new CopyWebpackPlugin({patterns: [{from: "./public",globOptions: {ignore: ["**/index.html"]}}]}),]})
方案二:一个配置文件,设置参数
运行原理图

