用webpack从零开始配置vue3

187 阅读4分钟

实现步骤

  1. 实现能打包js的项目;
  2. 处理HTML文件并引用js的功能
  3. 处理css
  4. 处理vue文件
  5. 处理ts
  6. 完整实例
  7. 优化和兼容(后续在其它笔记)

环境说明

  1. 编辑器:vscode
  2. node版本:v22.11.0
  3. 安装包工具: npm

实现能打包js的项目

  1. 初始化项目:新建一个文件夹在终端中运行npm init -y,会生成一个package.json文件;

  2. 下载webpack相关插件npm i webpack webpack-cli webpack-dev-server -D

  3. 创建webpack配置文件: 在根目录创建webpack.config.jswebpack的相关配置都会在这里;

  4. 新建打包的入口文件: 在新建src文件夹,以及在src文件夹内新建main.js文件(作为入口文件),新增index.html(用来调用main.js),文件结构如下:image.png

  5. 配置webpack的入口、出口以及开发环境信息: 在webpack.config.js中配置如下

    const path = require("path");
    
    module.exports = {
      entry: { // webpack的入口
                    main: {
                            import: path.resolve(__dirname, 'src/main.js'),	
                    }
            },
      output: {
                    path: path.resolve(__dirname, 'dist'), // 打包在那个文件夹
                    filename: '[name].js', // 打包生成的文件名,name会转化为入口entry的key值
            },
      devServer: {
                    static: { // 静态文件在那个文件夹,一般在根目录的public
                            directory: path.resolve(__dirname, 'public'), 
                    },
                    port: 9001, // 使用的端口  
                    hot: true,  // 是否热更新
                    open: true, // 启动后自动打开浏览器,支持指定浏览器
                    compress: true, // 启用GZIP压缩
            },
    }
    
  6. 创建webpack命令; 在package.json设置scriptdev为和build属性

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack server --mode development",
        "build": "webpack --mode production"
      },
    
  7. 打包生成文件:执行npm run build,会在根目录生成dist文件,并在dist文件夹中生成main.js

  8. 每次打包前都清除dist文件夹的文件

    1. 安装: npm install --save-dev clean-webpack-plugin

    2. 引入:在webpack.config.js引入 const { CleanWebpackPlugin } = require('clean-webpack-plugin');

    3. 在插件处配置:

       ```js
       plugins: [
               new CleanWebpackPlugin(), // 清除dist文件夹下的文件
       ]
       ```
      
  9. 为了方便查看打包后的代码,我们不将js压缩

module.exports = {
  optimization: {
    minimize: false
  },
}

处理HTML并自动引入js

  1. 让HTML调用main.js并且打包到dist文件中
    1. 下载生成HTML文件的插件 npm i html-webpack-plugin --save-dev

    2. webpack.config.js引入插件const HtmlWebpackPlugin = require("html-webpack-plugin");

    3. webpack.config.js使用该插件

      const path = require("path");
      const HtmlWebpackPlugin = require("html-webpack-plugin");
      
      module.exports = {
       plugins: [
                     new CleanWebpackPlugin(), // 清除dist文件夹下的文件
                     new HtmlWebpackPlugin({
                             template: path.resolve(__dirname, 'src/index.html'), // 入口文件
                             filename: 'index.html', 
                             chunks: ['main'] // 引用对应的js
                     }),
             ]
      }
      
    4. 执行npm run build会在dist文件夹下多一个index.html,执行npm run dev会打开一个浏览器页签

处理css

  1. 安装loadernpm i style-loader css-loader -D ,其中style-loader是将css代码注入到DOM中,css是将css代码处理成js的格式;
  2. 配置webpack.config.js
    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/, // 匹配 CSS 文件
            use: ['style-loader', 'css-loader'], // 使用 style-loader 和 css-loader
          },
        ],
      },
    
  3. 新建css文件:在src文件夹下创建assets/css/style.css文件;
  4. 引用css文件: 在main.js中引用css,import "./assets/css/main.css";
  5. 执行webpacknpm run build或者npm run dev看一下css有没有加载成功;

处理图片等文件

  1. 安装插件:安装file-loaderurl-loader都行,建议使用url-loader,可以将小图片转化为DATA URL,可以减少http请求,并且url-loader包含file-loader npm install url-loader -D
  2. 配置webpack.config.js:
module: {
    rules: [
      {
            test: /\.(png|jpe?g|gif|svg)$/i,
            use: [
                    {
                            loader: 'url-loader',
                            options: {
                                    limit: 8192, // 小于 8KB 的文件将被转换为 Data URL
                                    name: '[path][name].[ext]',
                            },
                    },
            ],
    },
    ],
  },

处理vue文件

  1. 安装插件:vue要打包到项目里的 npm i vue, 两个打包用的vue-loadervue-template-compiler(处理模板语法),npm i vue-loader vue-template-compiler -D
  2. 配置文件:
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      }
    ],
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}
  1. src目录下创建App.vue
  2. main.js引用,并把App挂载到body
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App); // 生成一个app组件
app.mount('#app'); // 用这个组件替换id为app的标签

处理ts(组合API)

  1. 安装npm i typescript ts-loader -D;
  2. 新增tyconfig.json文件:
{
  "compilerOptions": {
    "target": "ES5", // 可以兼容巨大部分的浏览器了
    "module": "ESNext", // 用来支持较新的模板语法
    "sourceMap": true, // 方便调试
    "strict": true,
    "jsx": "preserve", // 处理JSX语法,没有的话可以移除
    "importHelpers": true, // 
    "moduleResolution": "node",
    "esModuleInterop": true, // experimentalDecorators和esModuleInterop用于支持装饰器和更好的模块互操作性
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  },
  "include": [ // 编译那些文件的ts
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue"
  ],
  "exclude": [
    "node_modules"
  ]
}
  1. 配置webpack.config.js
module.exports = {

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/], // 允许在 .vue 文件中使用 TypeScript
        },
				exclude: /node_modules/
      },
    ],
  },
  resolve: {
		extensions: ['.tsx', '.ts', '.vue', '.js'],
		alias: { // 给固定路径起别名
			'@': path.resolve(__dirname, 'src')
		}
	},
}
  1. 使用ts:在vue中设置<script lang="ts">,里面就可以写ts语法了
  2. 注意:如果项目中只有vue使用了ts,tsconfig.json会报错,找不到include下的ts文件

完整实例

  1. 目录结构

image.png

  1. package.json文件
{
  "name": "webpack1",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack server --mode development",
    "build": "webpack --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "css-loader": "^7.1.2",
    "html-webpack-plugin": "^5.6.3",
    "style-loader": "^4.0.0",
    "ts-loader": "^9.5.1",
    "typescript": "^5.6.3",
    "vue-loader": "^17.4.2",
    "vue-template-compiler": "^2.7.16",
    "webpack": "^5.96.1",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^5.1.0"
  },
  "dependencies": {
    "vue": "^3.5.13"
  }
}

  1. webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");

module.exports = {
  entry: { // webpack的入口
		main: {
			import: path.resolve(__dirname, 'src/main.js'),	
		}
	},
  output: {
		path: path.resolve(__dirname, 'dist'), // 打包在那个文件夹
		filename: '[name].js', // 打包生成的文件名,name会转化为入口entry的key值
	},
  devServer: {
		static: { // 静态文件在那个文件夹,一般在根目录的public
			directory: path.resolve(__dirname, 'public'), 
		},
		port: 9002, // 使用的端口  
		hot: true,  // 是否热更新
		open: true, // 启动后自动打开浏览器,支持指定浏览器
		compress: true, // 启用GZIP压缩
	},
  optimization: {
    minimize: false
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
				test: /\.(png|jpe?g|gif|svg)$/i,
				use: [
					{
						loader: 'url-loader',
						options: {
							limit: 8192, // 小于 8KB 的文件将被转换为 Data URL
							name: '[path][name].[ext]',
						},
					},
				],
			},
      {
        test: /\.css$/, // 匹配 CSS 文件
        use: ['style-loader', 'css-loader'], // 使用 style-loader 和 css-loader
      },
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/], // 允许在 .vue 文件中使用 TypeScript
        },
				exclude: /node_modules/
      },
    ],
  },
  resolve: {
		extensions: ['.tsx', '.ts', '.vue', '.js'],
		alias: { // 给固定路径起别名
			'@': path.resolve(__dirname, 'src')
		}
	},
  plugins: [
    new VueLoaderPlugin(),
		new HtmlWebpackPlugin({
			template: path.resolve(__dirname, 'src/index.html'), // 入口文件
			filename: 'index.html', 
			chunks: ['main'] // 引用对应的js
		}),
	]
}
  1. tsconfig.json
{
  "compilerOptions": {
    "target": "ES5", // 可以兼容巨大部分的浏览器了
    "module": "ESNext", // 用来支持较新的模板语法
    "sourceMap": true, // 方便调试
    "strict": true,
    "jsx": "preserve", // 处理JSX语法,没有的话可以移除
    "importHelpers": true, // 
    "moduleResolution": "node",
    "esModuleInterop": true, // experimentalDecorators和esModuleInterop用于支持装饰器和更好的模块互操作性
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  },
  "include": [ // 编译那些文件的ts
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue"
  ],
  "exclude": [
    "node_modules"
  ]
}
  1. App.vue
<template>
  <!-- 在这里编写模板内容,比如可以添加一些按钮、文本等元素来与方法进行交互 -->
  <div>
    <button @click="handleClick">点击我</button>
    <p>{{ message }}</p>
  </div>
</template>

<script setup lang="ts">
import { Ref, ref } from 'vue';
// 定义响应式数据
const message: Ref<string> = ref('初始消息');

// 定义方法
const handleClick = (): void => {
  message.value = '按钮被点击后更新的消息';
};
</script>

<style scoped>
button {
  color: skyblue;
  border: 1px solid gray;
  border-radius: 4px;
  &:hover {
    color: blue;
  }
}
</style>
  1. main.js
<template>
  <!-- 在这里编写模板内容,比如可以添加一些按钮、文本等元素来与方法进行交互 -->
  <div>
    <button @click="handleClick">点击我</button>
    <p>{{ message }}</p>
  </div>
</template>

<script setup lang="ts">
import { Ref, ref } from 'vue';
// 定义响应式数据
const message: Ref<string> = ref('初始消息');

// 定义方法
const handleClick = (): void => {
  message.value = '按钮被点击后更新的消息';
};
</script>

<style scoped>
button {
  color: skyblue;
  border: 1px solid gray;
  border-radius: 4px;
  &:hover {
    color: blue;
  }
}
</style>
  1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

浏览器兼容与优化(后续其它笔记)

  1. 兼容
    1. js:需要通过babel兼容
    2. css:需要postcssautoprefixer
  2. 性能优化
    1. 压缩,包拆分,将静态代码抽到一个包,删除无用代码等
  3. 打包优化
    1. 多线程打包,不同环境配置不同的配置文件,开发环境css不需要到css文件和不需要压缩,而生产环境要