从头搭建一个基于webpack的项目

从头搭建一个基于webpack的项目

一、起步

1、创建目录,初始化npm,安装webpack

mkdir vue3-spa-template

cd  vue3-spa-template

npm init -y

npm install webpack webpack-cli --save-dev

备注:在安装一个 package时,此 package 要打包到生产环境中时,你应该使用 npm install --save。此package只用于开发环境时(例如,linter, 测试库等),你应该使用 npm install --save-dev

2、添加README.md,为项目添加必要说明

在这里插入图片描述

3、创建第一个bundle

  • 安装lodash库:npm install --save lodash
  • 添加dist/index.js和src/index.html

在这里插入图片描述

在这里插入图片描述

备注:执行 npx webpack,会将我们的脚本 src/index.js 作为 入口起点,会生成 dist/main.js 作为 输出,所以我们index.html模板这里的js路径要先写成main.js。

  • 执行npx webpack,会在dist文件夹下生成打包的main.js文件,在浏览器中打开index.html如下所示。

在这里插入图片描述

4、创建webpack的配置文件webpack.config.js

  • 添加build/webpack.config.js,并初始化基本配置

在这里插入图片描述

  • 执行 npx webpack --config ./build/webpack.config.js后效果和第3步手动执行的结果一样,生成main.js 。

5、添加npm script

输入一大段字符来运行webpack打包程序无疑是繁琐的,所以我们在package.json中配置script脚本如下,这样我们就可以使用npm run build来执行打包动作了

在这里插入图片描述

参考链接:https://webpack.docschina.org/guides/getting-started/

二、管理资源

1、跟随主流

前端主流做法是将输出的打包文件命名为 bundle,因此,我们将上述 htmlwebpack.config.js 中的 main.js 更名为 bundle.js

2、CSS文件加载

为了在 JavaScript 模块中 import 一个 CSS 文件,你需要安装 style-loadercss-loader,并在 module 配置 中添加这些 loader。

npm install style-loader css-loader --save-dev

在这里插入图片描述

备注:应保证 loader 的先后顺序:'style-loader' 在前,而 'css-loader' 在后。

  • 添加用于测试的样式文件

在这里插入图片描述

备注:这里css样式文件会被打包到bundle.js中,而不是插入到index.html中

3、加载图片资源

在 webpack 5 中,可以使用内置的资源模块( Asset Modules),它是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

在 webpack 5 之前,通常使用:

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

在这里插入图片描述

修改index.js测试打包后结果:

在这里插入图片描述

4、加载字体文件

字体文件的加载同图片资源一样,同样使用内置的资源模块( Asset Modules)进行加载,无需额外配置loader。

在这里插入图片描述

添加字体文件并引用测试

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hi5o9R4Q-1690792169175)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230726150819606.png)]

参考链接:https://webpack.docschina.org/guides/asset-management/

三、管理输出

到目前为止,我们都是在 index.html 文件中手动引入所有资源,然而随着应用程序的不断增长,一旦开始 使用哈希值进行文件命名 并输出 多个 bundle,手动管理 index.html 文件将变得困难。然而,使用一些插件可以让这个过程更容易管理。

在这里插入图片描述

1、设置html模板(htmlwebpackplugin)

  • 安装htmlwebpackplugin
npm install --save-dev html-webpack-plugin
  • 添加public文件夹,里面放入favicon.ico图标文件和index.html模版,index.html模版如下图所示:

在这里插入图片描述

  • 调整 build/webpack.config.js 文件,利用public文件夹下的内容通过配制自动生成dist/index.html模版:

在这里插入图片描述

2、自动清理dist文件夹

你可能已经注意到,由于遗留了之前的指南的代码示例,/dist 文件夹显得相当杂乱。webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。

通常比较推荐的做法是,在每次构建前清理 /dist 文件夹,这样只会生成用到的文件。可以使用 output.clean 配置项实现这个需求。

在这里插入图片描述

参考链接:https://webpack.docschina.org/guides/output-management/

四、开发环境

1、添加 mode 区分开发环境 development 和 生产环境 production

在这里插入图片描述

2、使用source-map

当 webpack 打包源代码时,可能会很难追踪到 error(错误)和 warning(警告)在源代码中的原始位置。

为了更容易地追踪 error 和 warning,JavaScript 提供了 source map 功能,可以将编译后的代码映射回原始源代码。source map 会直接告诉你错误来源于哪一个源代码。

可用选项参考:https://webpack.docschina.org/configuration/devtool

在这里插入图片描述

3、热更新:webpack-dev-server

webpack-dev-server 提供了一个基本的 web server,并具有实时重新加载的功能。

更多配置文档:https://webpack.docschina.org/configuration/dev-server

  • 安装webpack-dev-server
npm install --save-dev webpack-dev-server
  • 修改webpack.config.js

在这里插入图片描述

  • 添加npm script

在这里插入图片描述

此时可以直接运行 npm run dev 开发需求了,并且在每一次代码变更后都会自动重新编译

参考链接:https://webpack.docschina.org/guides/development/

五、代码分离、bundle分析

1、代码分离

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

  • 改造build/webpack.config.js文件,创建多入口(每个入口中都引入并使用lodash)

在这里插入图片描述

在这里插入图片描述

  • 分离前后打包文件大小对比

在这里插入图片描述

在这里插入图片描述

2、bundle分析

一旦开始分离代码,一件很有帮助的事情是,分析输出结果来检查模块在何处结束。webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。我们利用该插件分析打包情况。

  • 安装插件
npm install --save-dev webpack-bundle-analyzer
  • 修改build/webpack.config.js,添加插件配置

在这里插入图片描述

  • 在浏览器查看分析结果

在这里插入图片描述

参考链接:https://webpack.docschina.org/guides/code-splitting/

六、环境变量

随着webpack.js的配置越来越多,有些配置我们希望只在开发环境上生效,有些配置我们希望只在生产环境上生效,所以是时候引入环境变量进行控制了。

1、安装cross-env,package.json中添加NODE_ENV变量进行环境控制

cross-env:它是运行跨平台设置和使用环境变量的脚本(解决了,使用NODE_ENV =production, 来设置环境变量时windows环境下报错的问题)

  • 安装cross-env
npm install --save-dev cross-env
  • 修改package.json中的script命令

在这里插入图片描述

2、使用环境变量(webpack.config.js)

在这里插入图片描述

七、缓存

通过配置确保 webpack 编译生成的文件在没有改变时能够被客户端缓存,而在文件内容变化后,又能够请求到新的文件。

1、[contenthash]

[contenthash] 将根据资源内容创建唯一哈希值。当资源内容发生变化时,[contenthash] 也会发生变化。

  • 修改build/webpack.config.js,添加contenthash并截取保留8位

在这里插入图片描述

  • 查看打包结果

在这里插入图片描述

  • 问题(特别注意:按我们的理解,我现在现在什么都不修改,再次npm run build时hash值应该不会变化,但受webpack版本的影响,有时会发现即使内容没有改变,只要重新编译就会生成不同的hash值(当前版本5.88.2没有遇到此问题)

在这里插入图片描述

按照官网提示,以防万一的情况,我们配置引导提取模板来规避上述问题

2、引导提取模板

正如我们在 代码分离 中所学到的,SplitChunksPlugin 可以用于将模块分离到单独的 bundle 中。webpack 还提供了一个优化功能,可以使用 optimization.runtimeChunk 选项将 runtime 代码拆分为一个单独的 chunk。将其设置为 single 以为所有 chunk 创建一个 runtime bundle

实际上前边为了看到打包前后文件体积大小的差异,我们进行了多入口的实验

  • 修改配置项

在这里插入图片描述

  • 打包查看输出结果,如果引导文件被打包生成:

在这里插入图片描述

  • 将第三方库(library)(例如 lodash 或 vue)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。通过SplitChunksPlugin 插件的 cacheGroups 选项来实现抽取,现修改build/webpack.config.js文件如下:

  • 执行 npm run build后会将以使用的lodash抽取到vendors.hash.bundle.js

在这里插入图片描述

参考链接:https://webpack.docschina.org/guides/caching/

八、处理less文件、兼容性处理

实际开发过程中我们通常是使用css预处理器来书写css样式

1、处理less文件

  • 安装sass
npm install less less-loader --save-dev
  • 修改build/webpack.config.js文件如下:

在这里插入图片描述

  • 修改原src/style.csssrc/style.less,并在修改引用后进行build

在这里插入图片描述

2、PostCSS自动补全浏览器前缀

在编写css样式的时候,浏览器的兼容性是我们应该考虑的一个问题,为此我们使用postcss、postcss-loader、 postcss-preset-env进行样式兼容处理。

  • 安装(webpack5中不需要安装postcss-preset-env)
npm install --save-dev postcss-loader postcss postcss-preset-env
  • 修改 webpack.config.js 如下:

在这里插入图片描述

在这里插入图片描述

  • 添加.browserslistrc文件如下,兼容使用率大于0.2%的浏览器,同时官方没有超过24个月不维护的浏览器。

在这里插入图片描述

  • 添加样式属性打包测试

在这里插入图片描述

在这里插入图片描述

参考链接:

https://webpack.docschina.org/loaders/less-loader/

https://webpack.docschina.org/loaders/postcss-loader/

九、提取、优化、压缩CSS

1、清理无用文件

  • 移除another-module相关配置,改回单入口配置,减少生成的文件
  • 直到目前为止,css的样式还是被打在了index.hash.bundle.js当中,没有被单独的抽取出来,从下边两图中可以看出:

在这里插入图片描述

在这里插入图片描述

2、使用MiniCssExtractPlugin剥离CSS文件

本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

本插件基于 webpack v5 的新特性构建,并且需要 webpack 5 才能正常工作。

  • 安装
npm install --save-dev mini-css-extract-plugin
  • 修改webpack.config.js如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1w16c1q-1690792169178)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230730095525826.png)]

在这里插入图片描述

  • 查看build效果

在这里插入图片描述

3、使用CssMinimizerWebpackPlugin优化和压缩CSS

  • 安装
npm install css-minimizer-webpack-plugin --save-dev
  • 修改webpack配置如下:

在这里插入图片描述

  • 再度build查看:

在这里插入图片描述

参考链接:

https://webpack.docschina.org/plugins/mini-css-extract-plugin/

https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/

十、配置babel将ES6语法解析为ES5

1、基础配置

  • 安装
npm install -D babel-loader @babel/core @babel/preset-env
npm install -D @babel/plugin-transform-runtime
  • @babel/plugin-transform-runtime的作用:

Babel 在每个文件都插入了辅助代码,使代码体积过大

Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以引入 Babel runtime 作为一个独立模块,来避免重复引入。

  • 修改webpack配置如下:

在这里插入图片描述

  • 添加babel.config.js

在这里插入图片描述

2、某些ES6语法未被转化为ES5问题

以下版本:默认情况下,Webpack假定你的目标环境支持ES2015的一些特性

“@babel/core”: “^7.22.9”,

“@babel/plugin-transform-runtime”: “^7.22.9”,

“@babel/preset-env”: “^7.22.9”,

“babel-loader”: “^9.1.3”,

“webpack”: “^5.88.2”,

“webpack-bundle-analyzer”: “^4.9.0”,

“webpack-cli”: “^5.1.4”,

“webpack-dev-server”: “^4.15.1”

  • 方式一:当前版本下,webpack5配置babel将不会转换箭头函数语法、const语法等ES6语法(默认targets为default)

    需要显式设置targets: 'ie 11’等,才会转化为ES5语法

在这里插入图片描述

  • 方式二:指定输出

例:如果你不想要箭头函数,那么就这样做

// webpack.config.js
module.exports = {
  // ...
  output: {
    // ...
    environment: {
      // ...
      arrowFunction: false, // 不要输出箭头函数
    },
  },
};

3、打包查看输出

  • 源代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkFAFVbz-1690792169179)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230730113843366.png)]

  • 打包后代码

在这里插入图片描述

参考链接:https://webpack.js.org/loaders/babel-loader/#install

十一、JS压缩TerserWebpackPlugin

webpack v5 开箱即带有最新版本的 terser-webpack-plugin。如果你使用的是 webpack v5 或更高版本,同时希望自定义配置,那么仍需要安装 terser-webpack-plugin

按照官网的说法,我们实际上不需要去进行特殊的配置了,但是我们在配置CSS代码压缩时有主意到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oama1t5J-1690792169179)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230730115522064.png)]

因此,我们修改webpack配置如下:

在这里插入图片描述

十二、图片压缩

图片压缩分为无损压缩有损压缩,它们共同要使用的包是:image-minimizer-webpack-pluginimagemin

npm install image-minimizer-webpack-plugin imagemin --save-dev

1、无损压缩

  • 安装所需包
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev
  • 修改webpack配置

在这里插入图片描述

  • 添加一个大图片(仅测试用)

在这里插入图片描述

  • 使用图片压缩前文件大小及打包速度

在这里插入图片描述

  • 使用无损压缩优化图片

在这里插入图片描述

2、有损压缩

  • 安装包
npm install @squoosh/lib --save-dev
  • 修改webpack配置

在这里插入图片描述

  • build查看打包后文件大小及打包时长

在这里插入图片描述

3、总结

我们通过以上实验发现,在压缩图片后确实可以达到减少 文件大小 的目的,但是也有相应的弊端:打包 时长过长,因此,在使用时要根据实际情况来抉择。

参考链接:https://webpack.js.org/plugins/image-minimizer-webpack-plugin/

十二、添加@路径解析、配置jsconfig.json解决resolve.alias跳转问题

1、配置路径别名、添加顺序解析:尝试按顺序解析后缀名

  • 修改webpack配置

在这里插入图片描述

2、使用路径别名添加编辑器提示

  • 添加jsconfig.json文件对相关路径别名进行配置

在这里插入图片描述

3、测试

在这里插入图片描述

十三、一些小的优化点

1、指定资源输出目录

在这里插入图片描述

2、优化图片小于10kb使用base64来处理

现在,webpack 将按照默认条件,自动地在 resourceinline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。(type需要设置为:asset,不能指定为asset/resouce)

可以通过在 webpack 配置的 module rule 层级中,设置 Rule.parser.dataUrlCondition.maxSize 选项来修改此条件:

  • 引入一个9k的图片资源

在这里插入图片描述

在这里插入图片描述

  • 修改webpack配置

在这里插入图片描述

在这里插入图片描述

十四、这是一个阶段性成果,到目前为止,以上的所有配置不仅可以搭配vue使用,也可以搭配其它框架,如:react;下一阶段的配置属于大同小异,基本只需要替换vue相关的配置即可。

十五、加入Vue全家桶

1、让webpack能编译.vue文件

  • 安装vue-loader
npm install vue-loader -D
  • 修改webpack配置

在这里插入图片描述

2、加入Vue全家桶(vue3、vue-router、pinia、elementPlus)

  • 安装
npm install vue vue-router vuex element-plus --save
2.1、新建App.vue,修改src/index.js引入vue。
// src/index.js
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);
console.log(app);
app.mount("#app");
// src/App.vue
<script setup>
	import { ref } from "vue";
	const welcome = ref("Hello vue");
</script>

<template>
	<div>{{ welcome }}</div>
</template>

<style lang="less" scoped></style>
2.2、引入vue-router,新建src/router/index.js,新建Home.vue,修改App.vue
// src/index.js
import { createApp } from "vue";
import router from "./router";
import App from "./App.vue";

const app = createApp(App);

app.use(router);
app.mount("#app");
// src/router/index.js
const { createRouter, createWebHashHistory } = require("vue-router");

const routes = [
	{
		path: "/home",
		name: "Home",
		component: () => import("../views/Home.vue"),
	},
];

const router = createRouter({
	history: createWebHashHistory(),
	routes,
});

export default router;
<!-- src/views/Home.vue -->

<script setup></script>

<template>
	<div class="home">Home Page</div>
</template>

<style lang="less" scoped>
	.home {
		height: 50px;
		width: 50px;
		background-color: aquamarine;
	}
</style>
<!-- src/App.vue -->
<script setup>
	import { ref } from "vue";
	const welcome = ref("Hello vue");
</script>

<template>
	<div>{{ welcome }}</div>
	<RouterLink to="/home">Home</RouterLink>
</template>

<style lang="less" scoped></style>
2.3、引入并使用pinia
  • 修改src/index.js
// src/index.js
import { createApp } from "vue";
import router from "./router/index";
import App from "./App.vue";
import { createPinia } from "pinia";

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount("#app");
  • 新建文件src/store/index.js
import { defineStore } from "pinia";

// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore("useUser", {
	state: () => {
		return {
			age: 18,
			name: "小华",
		};
	},
	getters: {
		// 自动将返回类型推断为数字
		fatherAge(state) {
			return state.age + 18;
		},
	},
	// 相当于vue中的methods,可以是异步的
	actions: {
		addUserAge() {
			this.age++;
		},
	},
});
  • 修改src/App.vue
<!-- src/App.vue -->
<script setup>
	import { useUser } from "@/stores/index";
	import { storeToRefs } from "pinia"; // 保持响应式
	import { ref } from "vue";
	const welcome = ref("Hello vue");

	const user = useUser();
	// 默认情况下,您可以通过 store 实例访问状态来直接读取和写入状态:
	const addUserAge = () => {
		// 方式一:非解构修改数据
		// user.age++;
		// 方式二:非解构修改数据
		user.$patch(state => {
			state.age += 1;
		});
	};
</script>

<template>
	<div>{{ welcome }}</div>
	<RouterLink to="/home">Home {{ user.name }} {{ user.age }}</RouterLink>
	<button @click="addUserAge">过生日</button>
	<RouterView></RouterView>
</template>

<style lang="less" scoped></style>

  • 修改src/views/Home.vue
<script setup>
	import { useUser } from "@/stores/index";
	import { storeToRefs } from "pinia"; // 保持响应式
	const user = useUser();
	// // 解构时,必须要storeToRefs
	const { name, age, fatherAge } = storeToRefs(user);
	// 默认情况下,您可以通过 store 实例访问状态来直接读取和写入状态:
	const addUserAge = () => {
		age.value++;
	};
	console.log(name, age);
</script>

<template>
	<div class="home">
		<span>Home Page</span>
		<div>姓名:{{ name }}</div>
		<div>年龄:{{ age }}</div>
		<div>父亲年龄:{{ fatherAge }}</div>
		<button @click="addUserAge">过生日</button>
		<button @click="user.addUserAge()">调用actions</button>
	</div>
</template>

<style lang="less" scoped>
	.home {
		width: 200px;
		background-color: aquamarine;
	}
</style>
2.4、引入elementPlus
  • 自动按需导入
npm install -D unplugin-vue-components unplugin-auto-import
  • 修改webpack配置
// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
  • 修改src/index.js
// src/index.js
import { createApp } from "vue";
import router from "./router/index";
import App from "./App.vue";
import { createPinia } from "pinia";
import ElementPlus from "element-plus";

const app = createApp(App);

app.use(createPinia());
app.use(ElementPlus, { size: "middle", zIndex: 3000 });
app.use(router);

app.mount("#app");

3、处理控制台警告

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKrEkCKO-1690792169180)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230731141103144.png)]

const webpack = require("webpack");

module.exports = {
  // ...
  plugins: [
    new webpack.DefinePlugin({
        // 解决浏览器报的warning
        __VUE_OPTIONS_API__: true, // 是否支持vue2的optionsAPI
        __VUE_PROD_DEVTOOLS__: false, // 开发阶段tree shaking
    }),
  ],
}

十六、实现pinia数据持久化

1、自己编写插件实现

2、使用第三方插件

  • 安装
npm i pinia-plugin-persistedstate -D
  • 修改src/index.js
// src/index.js
import { createApp } from "vue";
import router from "./router/index";
import App from "./App.vue";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import ElementPlus from "element-plus";

const app = createApp(App);

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

app.use(pinia);
app.use(ElementPlus, { size: "middle", zIndex: 3000 });
app.use(router);

app.mount("#app");
  • 修改src/stores/index.js
import { defineStore } from "pinia";

// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore("useUser", {
	state: () => {
		return {
			age: 18,
			name: "小华",
		};
	},
	getters: {
		// 自动将返回类型推断为数字
		fatherAge(state) {
			return state.age + 18;
		},
	},
	// 相当于vue中的methods,可以是异步的
	actions: {
		addUserAge() {
			this.age++;
		},
	},
  // 持久化具体配置查看:https://prazdevs.github.io/pinia-plugin-persistedstate/guide/
	persist: {
		storage: sessionStorage, // 默认被存储在localstorage中
	},
});

十七、eslint&prettier进行js(ts vue)文件规范检查

  • 为vscode安装eslint插件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6cAdNrYT-1690792169180)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230731151144349.png)]

  • 为vscode安装Prettier插件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQ1iFscp-1690792169180)(/Users/liangchenyang/Documents/Web/Vue模板项目(脚手架)/从头搭建一个基于webpack的Vue3项目.assets/image-20230731154858432.png)]

  • 安装eslint包
npm install eslint -D
  • 安装eslint-config-airbnb-base
npx install-peerdeps --dev eslint-config-airbnb-base
  • 安装eslint-plugin-vue
npm install --save-dev eslint-plugin-vue
  • 安装prettier和eslint-config-prettier
npm install prettier eslint-config-prettier -D
  • 安装eslint-plugin-prettier
npm install --save-dev eslint-plugin-prettier
  • 添加.eslintrc.js
// 解决代码中存在的问题避免错误
module.exports = {
	// 规则到此不往上找
	root: true,
	// env环境设置,预定义全局变量,这些环境并不是互斥的,所以你可以同时定义多个
	env: {
		browser: true,
		node: true,
		es2021: true,
	},
	// 扩展风格,extends属性值可以省略包名的前缀 eslint-config-
	extends: [
		'plugin:vue/vue3-essential',
		'airbnb-base',
		// 'prettier', // eslint-config-prettier 来关掉 (disable) 所有和 Prettier 冲突的 ESLint 的配置,prettier 一定要是最后一个,才能确保覆盖
		'plugin:prettier/recommended', // 同时使用了 eslint-plugin-prettier 和 eslint-config-prettier 可以这么配置
	],
	settings: {
		'import/resolver': { webpack: { config: 'build/webpack.config.js' } },
	},
	// 针对个别文件设置新的检查规则
	// 要为特定类型的文件指定处理器,请使用 overrides 键和 processor 键的组合
	// 若要禁用一组文件的配置文件中的规则,请使用 overrides 和 files
	overrides: [],
	parserOptions: {
		// 指定要使用的 ECMAScript 版本
		ecmaVersion: 'latest',
		// 设置为 script (默认) 或 module(如果你的代码是 ECMAScript 模块)
		sourceType: 'module',
	},
	// 插件名称可以省略 eslint-plugin- 前缀。插件可以处理除js外的别的文件格式,引入插件的目的就是为了增强 ESLint 的检查能力和范围。
	plugins: ['vue', 'prettier'],
	// 规则列表
	rules: {
		// 提醒你不要直接修改函数的入参,为这个规则添加一个白名单,即指定的入参名称不予限制
		// 'no-param-reassign': [
		// 	'error',
		// 	{
		// 		props: true,
		// 		ignorePropertyModificationsFor: [
		// 			'e', // for e.returnvalue
		// 			'ctx', // for Koa routing
		// 			'req', // for Express requests
		// 			'request', // for Express requests
		// 			'res', // for Express responses
		// 			'response', // for Express responses
		// 			'state', // for vuex state
		// 		],
		// 	},
		// ],
		// 提醒你不要直接修改函数的入参
		'no-param-reassign': 0,
		// .vue文件必须是多个字符
		'vue/multi-word-component-names': 0,
		// 禁止变量声明覆盖外层作用域的变量
		'no-shadow': ['error', { allow: ['state', 'getters'] }],
	},
}
  • 添加.eslintignore
# ESLint 总是忽略 /node_modules/ 和 /bower_components/ 中的文件;
# 因此对于一些目前解决不了的规则报错,但是如果又急于打包上线,在不影响运行的情况下,我们就可以利用 .eslintignore 文件将其暂时忽略。

#本地查看dist时,eslint不需要检查
dist
  • 添加.vscode/setting.json
{
	"editor.formatOnSave": false,
	"editor.defaultFormatter": "esbenp.prettier-vscode",
	"editor.codeActionsOnSave": {
		"source.fixAll.eslint": true
	},
	"eslint.alwaysShowStatus": true
}
  • 添加.prettierignore
**/*.svg
node_modules
dist
  • 添加.prettierrc.js
//Prettier 支持可以配置参数不多,2022.12.1日查看时,总共才 21 个,这里是所有参数的说明 prettier options
//参考地址:https://prettier.io/docs/en/options.html#print-width
//该文件修改,要重新启动vscode才能生效,否则不生效
//解决代码风格的的问题
module.exports = {
	printWidth: 120, //(默认值)单行代码超出 80 个字符自动换行
	tabWidth: 4, //默认一个 tab 键缩进相当于 2 个空格
	useTabs: true, // 行缩进使用 tab 键代替空格
	semi: false, //(默认值)语句的末尾加上分号
	singleQuote: true, // 使用单引号
	quoteProps: 'as-needed', //(默认值)仅仅当必须的时候才会加上双引号
	jsxSingleQuote: true, // 在 JSX 中使用单引号
	trailingComma: 'all', // 不用在多行的逗号分隔的句法结构的最后一行的末尾加上逗号
	bracketSpacing: true, //(默认值)在括号和对象的文字之间加上一个空格
	bracketSameLine: false, // (默认值)元素的 > 单独占一行
	arrowParens: 'avoid', // 当箭头函数中只有一个参数的时候可以忽略括弧
	htmlWhitespaceSensitivity: 'ignore', // vue template 中的结束标签结尾尖括号掉到了下一行
	vueIndentScriptAndStyle: false, //(默认值)对于 .vue 文件,不缩进 <script> 和 <style> 里的内容
	endOfLine: 'lf', //(默认值) Linux and macOS as well as inside git repos
	// endOfLine:'crlf',                     //(默认值) Linux and macOS as well as inside git repos
	embeddedLanguageFormatting: 'auto', //(默认值)允许自动格式化内嵌的代码块
}
  • 此时发现会有eslint报错提示了,但是会有报错:Unable to resolve path to module ‘vue’ 等…
npm i eslint-import-resolver-webpack -D
  • 此时很多文件第一行还会有一个报错:Resolve error: unable to load resolver “alias”
npm install eslint-import-resolver-alias -D

十八、工程目录结构整理,webpack.config整理

1、目录整理

在这里插入图片描述

2、webpack.config.js整理

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

const appHtml = path.resolve(__dirname, '../public/index.html')
// 本插件会提取css到单独的文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 此插件用于优化和压缩CSS
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')

// const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { VueLoaderPlugin } = require('vue-loader')

// elementPlus自动导入相关配置
// eslint-disable-next-line import/no-unresolved
const AutoImport = require('unplugin-auto-import/webpack')
// eslint-disable-next-line import/no-unresolved
const Components = require('unplugin-vue-components/webpack')
// eslint-disable-next-line import/no-unresolved
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

let isProd = true
function getWebpackConfig() {
	isProd = process.env.NODE_ENV === 'production'
	return {
		mode: isProd ? 'production' : 'development',
		devtool: isProd ? 'source-map' : 'eval-source-map', // 可用选项参考:https://webpack.docschina.org/configuration/devtool
		entry: {
			index: './src/main.js',
		},
		output: {
			filename: '[name].[contenthash:8].js',
			path: path.resolve(__dirname, '../dist'),
			publicPath: '/', // 发送到 output.path 目录的每个文件,都将从 output.publicPath 位置引用
			clean: true, // 每次构建前清理 /dist 文件夹
		},
		// 如果想要在一个 HTML 页面上使用多个入口,还需设置 optimization.runtimeChunk: 'single'
		// // 将包含chunks映射关系的list单独从入口文件里提取出来,方便浏览器缓存,否则会在入口文件js中每次都会发生变化,所有的入口文件一起共同生成一个runtimeChunk。
		optimization: {
			minimize: isProd, // 如果是ture会进行js、css压缩,会产生LICENSE.txt文件,把注释提取到单独的txt文件中
			// 压缩CSS
			// 这将仅在生产环境开启 CSS 优化。如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize 设置为 true
			minimizer: [
				// js的压缩全靠这三个...,webpack认为,如果配置了minimizer,就表示开发者在自定义压缩插件,无论配置minimizer是true还是false,内部的JS压缩器都会被覆盖掉。所以我们这里要手动把它加回来,webpack内部使用的JS压缩器是terser-webpack-plugin.
				`...`,
				new CssMinimizerPlugin(),
			],
			// chunk分离
			splitChunks: {
				chunks: 'all',
				cacheGroups: {
					vendor: {
						test: /[\\/]node_modules[\\/]/,
						name: 'vendors',
						// 告诉 webpack 忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 选项,并始终为此缓存组创建 chunk。
						enforce: true,
					},
				},
			},
			runtimeChunk: 'single',
		},
		resolve: {
			// import引入文件的时候不用加后缀,webpack会自动按顺序尝试解析
			extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
			// 路径别名
			alias: { '@': path.resolve(__dirname, '../src') },
		},
		// 更多配置文档:https://webpack.docschina.org/configuration/dev-server
		devServer: {
			static: {
				directory: path.join(__dirname, '../dist'),
			},
			client: {
				progress: true,
			},
			compress: true, // 启用 gzip compression:
			historyApiFallback: true,
			hot: true,
			open: true, // 自动打开浏览器
			port: 'auto', // 自动使用一个可用端口
			proxy: {
				'/api': {
					target: 'http://cf-pc-dev.wti-xa.com:7777',
					changeOrigin: true, // 设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求,主要解决跨域问题
					pathRewrite: {
						// '^/api': ''
					},
				},
			},
			// 默认情况下,开发服务器将通过 HTTP 提供服务。可以选择使用 HTTPS 提供服务:
			// https: true,
		},
		module: {
			rules: [
				{
					test: /\.css$/i,
					use: [
						// 不要同时使用* `style-loader` *与* `mini-css-extract-plugin`*。*
						isProd ? MiniCssExtractPlugin.loader : 'style-loader',
						'css-loader',
						'postcss-loader',
					],
				},
				{
					test: /\.less$/i,
					use: [
						// compiles Less to CSS
						isProd ? MiniCssExtractPlugin.loader : 'style-loader',
						'css-loader',
						'postcss-loader',
						'less-loader',
					],
				},
				{
					test: /\.(png|svg|jpg|jpeg|gif|webp)$/i,
					type: 'asset',
					// 不配置的情况下,webpack会把8k以下的文件转换成base64的URL
					parser: {
						dataUrlCondition: {
							maxSize: 10 * 1024, // 4kb
						},
					},
				},
				{
					test: /\.(woff|woff2|eot|ttf|otf)$/i,
					type: 'asset/resource',
				},
				{
					test: /\.(js|mjs|jsx|ts|tsx)$/,
					exclude: /(node_modules)/,
					// include: path.resolve(__dirname, '../src'),
					use: {
						loader: 'babel-loader',
						options: {
							cacheDirectory: !isProd, // 使用 cacheDirectory 选项,将 babel-loader 提速至少两倍。这会将转译的结果缓存到文件系统中。
							// presets: [["@babel/preset-env", { targets: "ie 11" }]],
							// plugins: ["@babel/plugin-transform-runtime"],
							// 上边两项配置移动到babel.config.js
						},
					},
				},
				{
					test: /\.vue$/,
					use: 'vue-loader',
				},
			],
		},
		plugins: [
			new HtmlWebpackPlugin({
				title: 'Vue3+webpack5系统框架',
				template: appHtml,
				favicon: path.resolve(__dirname, '../public/favicon.ico'),
				// 附加一个唯一的webpack编译散列到所有包含的脚本和CSS文件。这对缓存破坏很有用
				// 这里所有的引用都用一个hash值每次都会所有的都发生改变,所以用output的[contenthash]hash来替换这里。
				// hash: true,
				xhtml: true, // 如果为true,则将链接标记呈现为自关闭(符合XHTML)
			}),
			// 打包分析工具
			isProd &&
				new BundleAnalyzerPlugin({
					analyzerMode: 'static', // 生成一个分析报告的html文件(默认生成一个 report.html)
					openAnalyzer: false, // 默认值为true,是否在浏览器中自动打开报表
				}),
			// 提取CSS文件
			new MiniCssExtractPlugin({
				filename: 'css/[name].[contenthash:8].css',
			}),
			new VueLoaderPlugin(),
			// elementPlus自动导入相关配置
			AutoImport({
				resolvers: [ElementPlusResolver()],
			}),
			Components({
				resolvers: [ElementPlusResolver()],
			}),
			new webpack.DefinePlugin({
				// 解决浏览器报的warning
				__VUE_OPTIONS_API__: true, // 是否支持vue2的optionsAPI
				__VUE_PROD_DEVTOOLS__: false, // 开发阶段tree shaking
			}),
		].filter(Boolean), // 去掉假值
	}
}
module.exports = getWebpackConfig()

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值