Vue项目手动搭建指南
目录
- 基础环境搭建
- 项目初始化
- webpack配置
- 核心功能配置
- 项目优化
一、基础环境搭建
1.1 Node.js环境准备
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 14.21.3
nvm install 16.20.0
1.2 项目基础结构
mkdir my-vue-project
cd my-vue-project
npm init -y
mkdir -p src/{assets,components,views,router,store,styles,utils}
mkdir public
touch src/main.js src/App.vue
touch public/index.html
二、项目初始化
2.1 安装基础依赖
Vue2项目依赖
{
"dependencies": {
"vue": "^2.6.14",
"vue-router": "^3.5.3",
"vuex": "^3.6.2",
"axios": "^0.27.2"
},
"devDependencies": {
"webpack": "^4.46.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4",
"webpack-merge": "^5.8.0",
"babel-loader": "^8.2.5",
"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.14",
"css-loader": "^6.7.1",
"sass-loader": "^13.0.2",
"sass": "^1.53.0",
"file-loader": "^6.2.0",
"url-loader": "^4.1.1",
"html-webpack-plugin": "^5.5.0",
"clean-webpack-plugin": "^4.0.0"
}
}
Vue3项目依赖
{
"dependencies": {
"vue": "^3.3.4",
"vue-router": "^4.2.4",
"pinia": "^2.1.6",
"axios": "^1.4.0"
},
"devDependencies": {
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0",
"@babel/core": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"@babel/preset-typescript": "^7.22.5",
"@vue/compiler-sfc": "^3.3.4",
"babel-loader": "^9.1.3",
"vue-loader": "^17.2.2",
"css-loader": "^6.8.1",
"sass-loader": "^13.3.2",
"sass": "^1.65.1",
"file-loader": "^6.2.0",
"url-loader": "^4.1.1",
"html-webpack-plugin": "^5.5.3",
"clean-webpack-plugin": "^4.0.0",
"typescript": "^5.1.6"
}
}
2.2 基础文件配置
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Project</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
src/App.vue (Vue2)
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style lang="scss">
#app {
font-family: Arial, sans-serif;
}
</style>
src/App.vue (Vue3)
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App'
})
</script>
<style lang="scss">
#app {
font-family: Arial, sans-serif;
}
</style>
src/main.js (Vue2)
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
src/main.ts (Vue3)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.use(router)
app.use(pinia)
app.mount('#app')
三、webpack配置
3.1 基础配置文件
webpack.base.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
},
resolve: {
extensions: ['.js', '.vue', '.json', '.ts'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/]
}
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[contenthash].[ext]'
}
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new VueLoaderPlugin()
]
};
webpack.dev.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
hot: true,
open: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
});
webpack.prod.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true
}
}
})
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
}
});
3.2 TypeScript配置
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"exclude": ["node_modules"]
}
3.3 Babel配置
.babelrc
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3
}],
"@babel/preset-typescript"
]
}
四、核心功能配置
4.1 路由配置
Vue2路由
import Vue from 'vue';
import VueRouter, { RouteConfig } from 'vue-router';
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
}
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
export default router;
Vue3路由
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
4.2 状态管理配置
Vue2 (Vuex)
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {}
});
Vue3 (Pinia)
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
});
五、项目优化
5.1 性能优化配置
代码分割
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
缓存优化
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
},
cache: {
type: 'filesystem'
}
};
5.2 打包命令配置
package.json
{
"scripts": {
"dev": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"build:report": "webpack --config webpack.prod.js --profile --json > stats.json && webpack-bundle-analyzer stats.json"
}
}
5.3 环境变量配置
.env.development
NODE_ENV=development
VUE_APP_API_URL=http://localhost:3000
.env.production
NODE_ENV=production
VUE_APP_API_URL=https://api.example.com