webpack5 从0搭建vue3开发配置基础

149 阅读4分钟

webpack版本

  • webpack-cli 4.10
  • webpack 5.73

搭建项目基础

在命令行中输入

mkdir webpack-vue3
cd webpack-vue3
npm init -y
npm install webpack webpack-cli -S
npm install webpack-merge -D
mkdir config src

创建入口文件和webpack配置文件

touch src/index.js config/webpack.common.js config/webpack.dev.js config/webpack.prod.js

# 基础目录
├── config
│   ├── webpack.common.js
│   ├── webpack.dev.js
│   └── webpack.prod.js
├── package.json
└── src
    └── index.js

添加config基础配置

/config/webpack.common.js

module.export = {
	entry: {
		index: "./src/index.js", //入口文件
	},
};

在/config/webpack.dev.js /config/webpack.prod.js 下合并 common项

const { merge } = require("webpack-merge");
const common = require("./webpack.common");
module.export = merge({

	
}, common);

安装HtmlWebpackPlugin

npm install html-webpack-plugin -D
mkdir index.html

在common配置中添加HtmlWebpackPlugin

...  
plugins: [
    // 生成html,自动引入所有bundle
    new HtmlWebpackPlugin({
      title: "injectTitle",
      filename: "index.html",
      template: path.resolveApp("./index.html"),
    })
  ],
...

index.html

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>
		<%= htmlWebpackPlugin.options.title %>
	</title>
</head>

<body>
//挂载的根节点
	<div id="root"></div>
</body>
</html>

创建 /config/path.js 文件 用来解析文件路径

const fs = require("fs");
const path = require("path");

const appDirectory = fs.realpathSync(process.cwd());

const resolveApp = (realitivePath) => path.resolve(appDirectory, realitivePath);

module.exports = {
  resolveApp,
  appPublic: resolveApp("public"),
  appHtml: resolveApp("public/index.html"),
  appSrc: resolveApp("src"),
  appDist: resolveApp("dist"),
};

  • 安装cross-env (注入环境变量 区分环境) 和webpack-dev-server
npm install webpack-dev-server cross-env -D
  • 完善dev 配置文件
const { merge } = require("webpack-merge");
const common = require("./webpack.common");
const { resolveApp } = require("./path.js");
module.exports = merge(
  {
    //webapck在指定mode下有相应默认的优化配置 化繁为简
    mode: "development",
    //打开调试source-map 方便debugger
    devtool: "eval-cheap-module-source-map",
    //输出目录
    output: {
      //dev环境下 不需要加content-hash
      filename: "[name].bundle.js",
      path: resolveApp("dist"),
      //重新编译清空上一次的目录
      clean: true,
    },
    devServer: {
      static: {
        directory: resolveApp("dist"),
      },
      compress: true,
      port: 8889,
    },
  },
  common
);

  • 完善prod 配置文件
const { merge } = require("webpack-merge");
const common = require("./webpack.common");
const { resolveApp } = require("./path.js");
module.exports = merge(
  {
    mode: "production",
    output: {
			//prod环境下 需要加content-hash 避免版本缓存
      filename: "[name].[contenthash].bundle.js",
      path: resolveApp("dist"),
      clean: true,
    },
  },
  common
);

  • 在package.json中添加脚本命令
...
"scripts": {
	"dev": "cross-env NODE_ENV=development webpack server --open --config config/webpack.dev.js",
	"build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"
},
...
  • 输入npm run dev 页面load正常

处理本地图片和字体

在 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,并且配置资源体积限制实现。

在common配置中添加

...
 module: {
 ...
  rules: [
      // 图片
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        include: path.appSrc,
        type: "asset/resource",
      },
      // 字体
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        include: path.appSrc,
        type: "asset/resource",
      },
  ]
  }
...

处理CSS样式

推荐使用postcss + postnext来书写样式 现在css的功能已经够强大了 以前常用的网状级联和变量 postcss也已经有对应插件了 对预处理有需要的也可以安装对应的loader

npm install style-loader css-loader postcss-loader postcss-preset-env -D

在common配置中添加

配置babel

babel是一个JavaScript编译器,用来将ESnext 新的语法转换成浏览器兼容的代码,我们需要使用babel相关的几个包

npm install babel-loader -D
### babel的核心库
npm install @babel/core -D 
### 将新语法转换成浏览器版本支持的语句
npm install @babel/preset-env -D
### API转换,对内置对象进行重命名,以防止污染全局环境 替代babel-polyfill
npm install @babel/runtime -S
npm install @babel/plugin-transform-runtime -D

在common配置下添加

...
module: {
 ...
rules: [
		...
	     {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      }
  ]
  }

在根目录下添加babel.config.js

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: {
          browsers: ["last 2 versions"], //最近两个版本的浏览器
        },
      },
    ],
  ],
  plugins: ["@babel/plugin-transform-runtime"],
};

其他配置项

别名

在common配置中添加

...
  resolve: {
    alias: {
      "@": path.appSrc,
			//...按需添加
    },
  },
...

开启文件缓存

缓存生成的 webpack 模块和 chunk,来改善构建速度;二次构建时能大幅提高构建速度,亲测比首测构建速度提高60%以上~
在common配置中添加

...
  cache: {
    type: "filesystem",//使用文件缓存
  },
...

Vue 相关配置

首先来安装vue3相关的几个包

### vue3包 sfc解析器
npm install vue@next @vue/compiler-sfc -S
### 提取vue模板
npm install vue-loader -D

在common配置中继续添加

const { VueLoaderPlugin } = require("vue-loader");

...

plugins: [
...
	new VueLoaderPlugin(),
],
module: {
rules: [
...
	{
	test: /\.vue$/,
	loader: "vue-loader",
	},
]
}
...

挂载vue组件

在src目录下添加App.vue文件

<template>
 Hello World
</template>

在index.js中引入并挂载

import { createApp } from "vue";
import App from "@/App.vue";

const app = createApp(App);

app.mount("#root");

跑一下npm run dev 屏幕输出Hello world 成功~

按需添加 vue-router

npm install vue-router@4 -S 
mkdir src/router src/views
touch src/router/index.js src/views/page1.vue src/views/page2.vue

src/router/index.js

import { createRouter, createWebHashHistory } from "vue-router";

const routes = [
  { path: "/", component: () => import("@/views/page1.vue") },
  { path: "/page2", component: () => import("@/views/page2.vue") },
];

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

在src/index.js引入

import { createApp } from "vue";
import App from "../src/App.vue";
import { router } from "@/router";
const app = createApp(App);

app.use(router).mount("#root");

App.vue

<template>
	<router-view />
</template>

按需添加pinia

npm install pinia -S  
mkdir src/store
touch src/store/index.js

src/store/index.js

import { createPinia } from "pinia";
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => {
    return { count: 0 };
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++;
    },
  },
});

export const pinia = createPinia();

在src/index.js引入

import { createApp } from "vue";
import App from "@/App.vue";
import { router } from "@/router";
import { pinia } from "@/store";
const app = createApp(App);

app.use(router).use(pinia).mount("#root");

在page1.vue中添加

<template>
	<div>
		{{ counter.count }}
		<button @click="add">ADD</button>
	</div>
</template>
<script setup>
import { useCounterStore } from '@/store'

const counter = useCounterStore()

function add() {
	// 三种用法
	counter.count++
	// with autocompletion ✨
	// counter.$patch({ count: counter.count + 1 })
	// or using an action instead
	// counter.increment()
}

</script>

npm run dev 测试一下 没毛病~
基础的搭建就完成了~