一口气速通Vue3.0💨
🧑💻🧑💻 终于学完了Vue2.0=>、神清气爽,吃嘛嘛香,一口气能上18层楼🏙️
都说前端技术更新迭代快,这不现在都Vue3.0了,且Vue2.0已经停止更新/维护了,经典:白学🥲
No、No、No: 虽然现实如此,但影响我觉得不大,Vue3.0 是2.0 进行的一个升级🆙,完全兼容Vue2.0的语法;
且就算你要学习Vue3,也离不开学习它的语法,很多的思想、概念:都是2.0=过渡=>3.0, 虽然有些语法变动挺大的;
🧻🧻卷卷卷、🌊🌊与其纠结内耗的时间,就已经掌握了,🤨不信,这篇Blog带你速通Vue3.0🚀🚀🚀
Vue3.0 核心升级🆙
响应式机制: Vue3
使用Proxy
来实现响应式,而Vue2
使用的是getter/setter
组合式API: 引入了setup()
函数和Composition API
,允许开发者以函数式的方式组织组件逻辑,提高代码复用性;
生命周期钩子的变化: 移除了beforeCreate
和created
等,引入了setup
阶段,以及新的生命周期钩子;
更好的TypeScript支持: 在设计时就考虑到了TypeScript
的集成,提供了更好的类型定义和推断;
移除和新增特性: 移除了filter
等较少使用功能,增加了:Suspense
、Teleport
等新特性;
生态系统更新: 虽然生态仍在发展中,但主要的UI库和工具链已经或正在更新以支持Vue3
;
组合式API
组合式API是Vue3引入的一种新的编程模式,它彻底改变了Vue组件的编写方式:
与Vue2中的选项式API相比,组合式API不再依赖于传统的data、methods等选项对象结构,
而:组合式API: 通过一系列函数式的API来构建组件的逻辑,更灵活、高效的方法来组织和复用代码逻辑;
使用Vue2选项式开发过程中,随着业务复杂,经常出现一个函数和变量,相差几十行代码的情况,非常不利于开发;
组合式API通过将逻辑相关部分组合在一起,解决了选项式API在复杂组件中逻辑分散的问题;
对于大型项目和需要高度复用逻辑的场景,组合式API提供了更优的解决方案;
提供了更细粒度的响应性控制,使得组件更新更加精确;
创建Vue3项目:
创建Vue3项目可以通过多种方式完成,主要两种方法:Vite
、Vue-cli
Vue CLI 基于成熟的 Webpack 构建,它是一个全面的脚手架系统,提供了丰富的插件和配置选项,适合复杂和大型项目;
Vite 则是基于 ES6 模块的即时加载机制,利用浏览器原生的模块导入功能,通过HTTP服务器直接提供未经打包的源代码;
Vite 在开发时通过esbuild快速预编译依赖,并在运行时按需编译和加载模块,这大大加快了启动速度📈
-
启动速度:
Vite
因为避免了完整的打包过程,在开发环境中启动速度远快于Vue CLI
,以及快速迭代的开发阶段;但生产环境构建时,两者依赖的工具
Vite
使用Rollup、Vue CLI
默认Webpack各有侧重,具体速度取决于项目结构配置; -
热模块替换 (HMR): Vite 提供了更快的HMR体验,因为它直接在浏览器中编译和替换模块,而不需要重新构建整个应用;
总结:适用场景=>
Vue CLI: 更适合大型项目,需要高度定制化的构建流程,以及对成熟生态依赖较高的情况;
Vite: 适合快速原型开发、小型项目或对启动速度有高要求的项目,尤其是Vue 3项目,是Vue 3官方推荐开发工具;
create-vue 脚手架:
🆗,既然是官方推荐:那么就免为其难的浅浅尝试一下吧: 感受一下Vite速度🚤
首先,确保Node.js版本: Node.js版本在14.18+
,推荐使用16+
版本,以兼容Vite
和Vue3
的要求;
创建项目: 打开终端,使用命令、输入项目名称、选择框架依赖,确认选择Vue、使用TypeScript
、JavaScript
;
npm create vite@latest #或
yarn create vite #或
pnpm create vite
如此简单,就快速的构建了一个Vue3的项目!! 相比VueCli确实快了不少!!
Vue3项目目录:
VsCode配置:禁用Vu2插件—Vetur、安装Vue3插进volar:、核心配置文件:
- vite.config.js: 项目的配置文件,基于
vite
的配置; - package.json: 项目包文件,核心依赖项变成了
Vue3
、vite
- main.js - 入口文件:
createApp
创建Vue实例、初始化Vue应用,配置全局插件,引入根组件;
import './style.css'
import App from './App.vue'
import { createApp } from 'vue'
createApp(App).mount('#app');
//原始new Vue()创建一个应用实例 =升级=> createApp() 将创建实例进行了封装,保证每个实例的独立封闭性
-
app.vue - 根组件:
SFC
单文件组件:script - templa - style
,为方便操作JS
、CSS
、将templa
放中间;变化一: 脚本
script
和模板template
顺序调整、script
添加setup
标识支持组合式API
变化二: 模板
template
不再要求唯一根元素;
组合式API-setup函数
组合式API是Vue 3引入的核心特性之一: 它通过setup
选项提供一种新的组件内部结构,以更模块化的方式组织代码;
setup() 函数\使用:
setup
是Vue 3组件中的一个新选项,它在组件实例被创建之前执行=> 生命周期: setup
执行早于任何生命周期钩子,
这意味着不能在setup
中直接访问如beforeCreate
、created
传统生命周期钩子中的数据;
上下文隔离: setup
内部,this
是不可用的,因为:它不绑定到组件实例,
这促使开发者使用响应式API,如:ref
、reactive
,来管理状态;
<script>
export default {
setup(){
console.log(this);
console.log('setup 执行中...');
},
beforeCreate(){ console.log('beforeCreate 执行中...'); }
}
</script>
<template></template>
<style scoped></style>
setup 中返回值与模板进行绑定: setup
中变量不能直接在模板中使用;
属性暴露: setup
函数可以返回一个对象,该对象中的属性和方法将被添加到组件实例上;
响应式数据: 通常在setup
中使用ref
或reactive
来创建响应式数据,这些数据的变化会触发视图更新;
<script>
export default {
setup(){
console.log(this);
console.log('setup 执行中...');
//属性暴露:setup 中返回值与模板进行绑定,
const message = 'this is message'
const logMessage = ()=>{ console.log(message) }
return { message, logMessage } //setup return返回的对象,可以在模板中使用数据、方法等;
},
beforeCreate(){ console.log('beforeCreate 执行中...'); }
}
</script>
<!-- setup与模板的交互: 可以直接获取setup return的函数\属性 -->
<template>
{{message}}
<button @click="logMessage" >点击log</button>
</template>
<style scoped></style>
<script setup>
语法糖🍬
<script setup>语法糖
是Vue 3中引入的一项重要特性:
它极简了单文件组件(SFC)中组合式API的使用,提高了代码的可读性和简洁性;有多极简?
<!-- <script setup>它极简了单文件组件(SFC)中组合式API的使用 -->
<script setup>
const message = 'this is message'
const logMessage = ()=>{ console.log(message) }
</script>
<template>
{{message}}
<button @click="logMessage" >点击log</button>
</template>
这这这,简直少了一大半代码,编译时优化: <script setup>
在编译阶段处理,减少了运行时的模板编译负担;
直接声明使用: <script setup>
内,直接声明变量、方法和导入模块,无需通过return
语句暴露给模板;
组合式API入口: setup
函数,在生命周期的早期执行,允许访问和处理props
,但内部不应使用this
关键字;
不支持name属性直接设置: 若需为组件命名,需在外部<script>
标签中设置export default
对象的name
属性;
组合式API-reactive\ref函数:
setup中返回的对象: <script setup >
只是普通对象、属性,并不是响应式的:数据修改,模板自动更新;
通常都需要通过: reactive、ref(推荐)
函数进行响应式处理;
reactive函数:
对象与数组的响应式: reactive
用于创建复杂数据结构如:对象、数组)的响应式版本、基本语法:
reactive({对象}); 接受一个对象类型数据,参数传入并返回一个响应式的对象;
<!-- <script setup>中定义的数据,并不是响应式对象 -->
<script setup>
// const obj = { count : 1 }
// 在setup中可以通过: reactive\ref函数创建响应式数据\返回;
import { reactive } from 'vue';
const obj = reactive({
count : 1,
});
const countAdd = ()=>{ obj.count++ }
</script>
<template>
{{obj.count}}
<button @click="countAdd" >+</button>
</template>
注意:reactive
是浅层响应性: reactive
只对最外层对象的属性变更做出响应,如果修改了对象内部的嵌套对象或数组;
需要确保这些嵌套结构也是通过ref
或reactive
创建的响应式;
ref函数:
因为:reactive仅支持对象,所以通常使用最多的是ref
:
ref
支持基本数据、对象类型如:Number
、String
、Boolean
的响应式包装,
但,它返回一个Ref
对象,该对象有一个.value
属性,需要使用.value
访问ref
包装的值;
Vue 3.2版本后,部分情况可以不加.value
,深层响应性: ref
用于对象或数组时,其内部值的深层属性也是响应式的;
<!-- <script setup>中定义的数据,并不是响应式对象 -->
<script setup>
// const obj = { count : 1 }
// 在setup中可以通过: reactive\ref函数创建响应式数据\返回;
// ref注意事项:
// 1. 脚本中访问数据,需要通过 .value
// 2. 在template中,.value不需要加 (帮我们扒了一层)
import { reactive, ref } from 'vue';
const count = ref(0);
const countAdd = ()=>{ count.value++ } //需要使用.value访问ref包装的值
</script>
<template>
{{count}}
<button @click="countAdd" >+</button>
</template>
注意: ref
创建的响应式性是针对其内部值,而不是引用本身,即使原.value
发生变化,也不会触发Vue响应式更新;
ref的底层原理: ref
函数内部的实现依赖于reactive
函数;
组合式API - computed函数
组合式API—computed
函数特性: 允许你创建基于其他数据的计算属性,相当于Vue2的,computed计算属性
和:传统使用类似,仅修改了写法: 导入computed函数
、在computed函数中return基于响应式数据做计算的值,用变量接收
<script setup>
// 导入依赖函数,声明响应式数据;
import { computed, ref } from 'vue'
const list = ref([1, 2, 3, 4, 5, 6, 7, 8,2,31,4,51,1,-1,-1])
// 基于list派生计算属性:从list中过滤出 > 2
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
</script>
<template>
<div>
<div>原始数据: {{ list }}</div>
<div>计算后的数据: {{ computedList }}</div>
</div>
</template>
<style scoped></style>
注意事项: 确保计算属性的依赖是响应式的,即通过ref
、reactive
的响应式对象,否则Vue将无法追踪变化;
异步计算: Vue3的计算属性不直接支持异步操作,可以考虑使用watchEffect或在外部管理异步逻辑;
类型定义: 在TypeScript项目中,为计算属性提供正确的类型定义,以利用类型检查和自动补全;
返回值: 计算属性函数应该返回一个值,不返回值\返回undefined
可能会导致模板渲染错误;
组合式API - watch函数
Vue3的组合式API中的watch函数: 提供了一种监听=>响应式数据
变化并执行相应操作的强大机制;
和Vue2-watch属性类似: 不过了换了一种声明方式,语法如下: Vue3支持监听一个、多个,响应式变量;
//watch函数: 监听单个属性
import { ref, watch } from 'vue';
watch('ref对象',(oldValue,newValue) => { ... });
//函数接收两个参数: 监听对象、操作函数('历史数据','新数据')
//watch函数: 监听多个属性,和单属性监听类型,不同的是ref对象为一个数组[Array];
watch(['ref对象1','ref对象2'],(oldArray,newArray) => { ... }); //操作函数参数也是,修改前后数组;
watch函数,参数选项: 👁️🗨️watch监听函数除了,必须的:监听对象
、操作函数
,还有一个[可选]的,参数选项:
- immediate: 设置为
true
时,监听器会在初始运行时立即触发一次; - deep: 如果需要深度监听对象或数组内的变化,可以设置为
true
,默认:false
仅监听基本值、对象地址改变)
watch('ref对象',(oldValue,newValue) => { ... },{
immediate : true,
deep : true,
});
deep 深度监听—对象属性:
组合式API - 生命周期:
Vue3的组合式API引入了一套新的生命周期钩子:
这些钩子以函数的形式提供,使得开发者能够更清晰、更灵活地组织和管理组件的生命周期;
Vue2和Vue3的生命周期钩子在概念上相似,以下是Vue2与Vue3生命周期钩子的主要对比:
beforeCreate -> setup()
Created -> setup()
类似于Vue2的beforeCreate\created,但更早,且不直接等同,用于设置组件的响应式数据和副作用
beforeMount -> onBeforeMount 在挂载开始之前被调用,此时还没有真实的DOM
mounted -> onMounted 组件被挂载到DOM之后调用,可以访问和操作DOM
beforeUpdate -> onBeforeUpdate 数据更新之前调用,但DOM尚未更新
updated -> onUpdated 数据更新并完成DOM更新之后调用
beforeDestroyed -> onBeforeUnmount 组件销毁之前调用,此时组件仍然可用
destroyed -> onUnmounted 组件销毁之后调用,所有指令和事件监听器已被移除
activated -> onActivated 当一个被<keep-alive>包裹的组件被激活时调用
deactivated -> onDeactivated 当<keep-alive>包裹的组件被切换出去,不再显示时调用
组合式API - 父子通信:
在Vue3中,组合式API为父子组件通信提供了更加灵活和现代的手段,以下是几种主要的父子通信方式:
注意事项: Vue3,使用:@
作为 src/下目录
,引用组件、依赖,需要配置:vue.config.js
路径别名;
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
父组件引用子组件:
Vue3中使用组件两种方式:全局注入、局部导入👇👇
- 局部导入: 导入子组件的地址,定义组件名、直接在父组件
template
中使用;
父传子 通信:
Vue3中,父组件向子组件传递数据主要通过Props
进行,这一机制与Vue2相似: 但有一些细节上的更新和改进;
父组件传递数据: 在父组件的模板中,通过属性绑定v-bind
、简写:属性名
将数据传递给子组件;
<script setup>
//Vue3中使用组件两种方式: 全局注入、局部导入
//局部导入、导入子组件的地址、直接在template中使用;
import { ref } from 'vue'
import SonCom from '@/components/son-com.vue'
//父组件定义响应式数据
const count = ref(0);
const message = ref("父组件发送消息")
const getMessage = ()=>{ count.value++; }
</script>
<template>
<div>
<p>App父组件</p>
<button @click="getMessage" >父组件发送消息按钮</button>
<SonCom :message="message" :count="count"></SonCom>
<!-- 父组件给子组件 :添加属性的方式传值 -->
</div>
</template>
<style scoped></style>
子组件通过:defineProps来定义接收的属性: 可以是类型注解的形式,以提供更好的类型安全;
<!-- son子组件 -->
<script setup>
// 注意:由于写了setup 所以无法直接配置 props 接收数据;
// 所以:借助于 “编译器宏” 函数接收子组件传递的数据;
const props = defineProps({
count: Number,
message: String
})
console.log("子组件获取父组件参数: "+props.message);
</script>
<template>
<div class="son" >
<p>我是子组件,接收:{{ message+count }}</p>
<!-- 对于props传递过来的数据,模板中可以直接使用 -->
</div>
</template>
<style scoped>
.son { border: 1px solid #000; padding: 30px; }
</style>
子传父 通信:
Vue3中子组件向父组件传递数据主要通过自定义事件emit
来实现:
子组件 定义自定义事件: 在子组件的<script setup>
标签内使用defineEmits
函数来定义监听事件;
<!-- son子组件 -->
<script setup>
// 注意:由于写了setup 所以无法直接配置 props 接收数据;
// 所以:借助于 “编译器宏” 函数接收子组件传递的数据;
const props = defineProps({
count: Number,
message: String
})
console.log("子组件获取父组件参数: "+props.message+props.count);
//子组件传递父组件数据: 通过 defineEmits 定义父组件监听事件;
import { defineEmits,ref } from 'vue';
const scount = ref(0);
const emit = defineEmits(['sonMessage']);
//子组件中定义函数, 触发函数,给父组件发送数据;
const sendMsg = () =>{
emit('sonMessage',"子组件发送消息"+(scount.value++))
}
</script>
<template>
<div class="son" >
<p>我是子组件,接收:{{ message+count }}</p>
<!-- 对于props传递过来的数据,模板中可以直接使用 -->
<button @click="sendMsg" >子组件给父组件发送消息</button>
</div>
</template>
<style scoped>
.son { border: 1px solid #000; padding: 30px; }
</style>
父组件监听事件: 通过监听子组件触发的事件来接收数据,注意:事件名称与子组件中定义的一致;
<script setup>
//Vue3中使用组件两种方式: 全局注入、局部导入
//局部导入、导入子组件的地址、直接在template中使用;
import { ref } from 'vue'
import SonCom from '@/components/son-com.vue'
//父组件定义响应式数据
const count = ref(0);
const message = ref("父组件发送消息")
const getMessage = ()=>{ count.value++; }
//父组件: 监听子组件触发的事件来接收数据,事件名称与子组件中定义的一致;
const smessage = ref();
function sonMessage(data) {
smessage.value = data;
console.log('从子组件收到的数据:', data);
}
</script>
<template>
<div>
<p>App父组件: {{smessage}}</p>
<button @click="getMessage" >父组件发送消息按钮</button>
<!-- 父组件给子组件 :添加属性的方式传值、通过 @绑定监听子组件传递数据函数;-->
<SonCom :message="message" :count="count" @sonMessage="sonMessage" ></SonCom>
</div>
</template>
<style scoped></style>
模板引用 \ 宏函数
模板引用
组合式API—模板引用: 模板引用的概念与Vue 2有所不同,Vue2 ref、$ref使用、
Vue 2中,我们通常使用ref、v-ref获取DOM元素引用: Vue 3中,这一过程被统一和简化;
Vue3 使用ref进行模板引用:ref不仅用于创建响应式数据,也可以用来获取DOM元素的引用
<script setup>
// 模板引用:可以用于获取dom,也可以获取组件)
import { onBeforeMount, onMounted, ref } from 'vue'
const inp = ref(null); //1.首先通过 ref(null); 定义一个空属性;
//3.如此即可在 JS中直接获取到对应的Dom,对其进行操作,如: 进行焦点获取,
//同时注意:要保证Dom已成功渲染,否则返回undefined
//生命周期钩子 onMounted
onMounted(()=>{ console.log(inp.value) }); //Dom已渲染完成
onBeforeMount(()=>{ console.log(inp.value) }); //Dom未渲染完成
//定义函数,点击让输入框聚焦:
const clickFn = ()=>{ inp.value.focus() };
</script>
<template>
<div>
<input ref="inp" type="text"> <!-- 2.在模板要获取的标签中定义同名的的 ref="属性值" -->
<button @click="clickFn">点击让输入框聚焦</button>
</div>
</template>
注意事项:
返回null的初始值: 在组件挂载之前,ref值.value
会是null
,因为:Dom并未加载完成;
生命周期钩子: 通常在onMounted
或适当的生命周期钩子中使用这些引用,确保DOM已经渲染完成;
模板引用+宏 操作组件
ref 模板引用: 强大之处,在于它可以获取Dom元素、组件元素、并操作,没错:可以获取操作组件;
默认情况下在,组件<script setup>
语法糖下组件内部的属性和方法是,不开放给父组件访问的:
子组件定义: 需要通过:defineExpose
编译宏指定哪些属性和方法容许访问;
<script setup>
import { ref } from 'vue'
//创建定义组件内部:属性、函数
const sayHi = () => { console.log('打招呼')};
const count = ref(999);
//通过:defineExpose编译宏指定哪些属性和方法容许访问
defineExpose({
count,
sayHi
})
</script>
<template>
<div> 我是用于测试的组件 - {{ count }} </div>
</template>
父组件: 使用ref
来创建一个对子组件实例的引用,获取子组件defineExpose
暴露的响应式数据或方法;
<script setup>
// 模板引用:可以用于获取dom,也可以获取组件)
import TestCom from '@/components/test-com.vue'
import { ref,onMounted } from 'vue'
const testRef = ref(null); //1.首先定义ref null对象属性;
onMounted(()=>{ console.log(testRef.value) }); //3.Dom已渲染完成,操作获取组件;
//4.定义函数操作组件:数据、函数
const getCom = () => {
testRef.value.sayHi();
testRef.value.count += 1;
console.log(testRef.value.count);
}
</script>
<template>
<div>
<p>父组件: 模板引用获取操作子组件</p>
<TestCom ref="testRef" ></TestCom> <!-- 2.要获取的组件、Dom标签上绑定ref="对象属性名" -->
<button @click="getCom">获取组件: 减少子组件数据;</button>
</div>
</template>
组件跨层传递provide、inject
组合式API中的provide
和inject
特性允许在Vue
组件树中进行依赖注入:
这是一种跨组件通信的方式,尤其适用于:祖先组件
=向其=> 子孙组件
传递数据的问题,无需通过中间组件逐层传递props
//在父组件的setup函数中,你可以使用provide来注入数据或对象;
import { provide } from 'vue';
provide('传递对象|属性|名', '对象|属性|值');
//子\后代组件: 使用inject接收父组件提供数据;
const 自定义变量名 = inject('对应的对象|属性|名'); //接收父组件匹配传递的值;
//根据各个组件管理自己数据原则,一般不允许跨组件修改数据,
//所以修改组件数据,一般直接暴漏一个: 修改函数 => 子孙后代提供函数修改数据的方法;
Vue3.3 新特性:
defineOptions
defineOptions 特性概述: defineOptions
宏的引入是为了在Vue 3.3
中提供一个更简洁和统一的方式来定义组件选项;
Vue3开始, 大家都在使用<script setup>
但,很多时候我们还需要使用:Vue2
的一些特定的属性:组件名
…之类的;
如何解决? Vue3.3之前为了使用,只能定义一个新的<script>
专门用来写原始的配置;
<script>
//vue2 定义配置属性
export default{
name : "组件名",
}
</script>
<script setup>/* vue3 setup配置 */</script>
<template> <!-- 页面主体 --></template>
<style scoped>/* css */</style>
defineOptions
允许开发者在保持<script setup>
简洁性的同时,定义更复杂的组件选项;
<script setup>
import { defineOptions } from 'vue';
defineOptions({
name: 'MyComponent',
inheritAttrs: false,
});
</script>
defineModel
在Vue3中,自定义组件上使用v-model
: 相当于传递一个modelValue
属性,同时触发 update:modelValue
事件
//父组件在子组件上定义v-model
<Child v-model="isVisible">
/* 相当于 :modelValue 属性、 匹配一个 @update:modelValue 事件对象 */
<Child :modelValue="值" @update:modelValue="更新同步值,函数">
//子组件: 定义与父组件通信
defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
我们需要先定义 props
,再定义 emits
其中有许多重复的代码,如果需要修改此值,还需要手动调用 emit
函数;
于是乎 defineModel
诞生了: 生效需要配置 vite.config.js
设置 defineModel: true
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue({
script: {
defineModel: true
}
})],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
子组件设置:defineModel, 给需要进行父子通信的表单元素设置,无需手动使用ref
或reactive
来创建响应式状态;
<script setup>
//子组件导入 defineModel 并创建对象, 并在模板表单元素中使用
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>
<template>
<div>
<!-- :value="modelValue" 匹配defineModel -->
<!-- @input 给表单绑定input 输入事件处理,父组件给子组件绑定v-model 自动匹配其数据、函数 -->
<input
type="text"
:value="modelValue"
@input="e => modelValue = e.target.value"
>
</div>
</template>
父组件: 直接通过 v-model
绑定至,子组件匹配的defineModel()
函数返回对象上,通常用于表单;
<script setup>
import MyInput from '@/components/my-input.vue'
import { ref } from 'vue'
const txt = ref('123456')
</script>
<template>
<div>
<!-- 直接在子组件上使用 v-model='响应式数据' -->
<MyInput v-model="txt"></MyInput>
{{ txt }}
</div>
</template>
代码管理:
本代码已经使用Git进行管理: 公众号回复:Vue项目工程化