【小兔鲜】day01 项目、Vue3介绍、组合式API、小案例
0. 市场上Vue前端工程师用到的技术
Vue3 + TS + Element Plus + Vite + CSS3
Node的版本 16~18
Element Plus https://cn.element-plus.org/zh-CN/
0.1 📁 Vue 3 项目目录结构(Vite 模板)
my-vue-app/
├── node_modules/ # 项目依赖(自动生成)
├── public/ # 公共静态资源目录(不会被打包)
│ └── favicon.ico # 网站图标等资源
├── src/ # 源码目录(主要开发都在这里)
│ ├── assets/ # 图片、字体等静态资源
│ ├── components/ # 可复用组件(按钮、卡片等)
│ ├── views/ # 页面级组件(路由对应页面)
│ ├── router/ # 路由配置(如果有使用 vue-router)
│ ├── store/ # 状态管理(使用 Pinia/Vuex 时)
│ ├── App.vue # 根组件
│ └── main.js # 应用入口文件
├── index.html # 项目主页模板
├── vite.config.js # Vite 配置文件
├── package.json # 项目配置和依赖管理
├── .gitignore # Git 忽略文件配置
└── README.md # 项目说明文档
1. Vue3小兔鲜先导课
1.1 技术栈
1.2 项目规模
1.3 项目亮点
1.4 课程安排
2. 认识Vue3
2.1 Vue3组合式API体验
通过一个Counter案例体验Vue3新引入的组合式API
首先是Vue2的书写方式
<template>
<button @click="addCount">{{ count }}</button>
</template>
<script>
// Vue2的代码规范
export default {
data() {
return {
count: 0
}
},
methods: {
addCount() {
this.count++
}
}
}
</script>
<style lang="less">
</style>
Vue3的书写方式
<template>
<button @click="addCount">{{ count }}</button>
</template>
<script setup>
// vue3组合式api实现
import {ref} from 'vue'
const count = ref(0)
const addCount = () => count.value++
</script>
<style lang="less">
</style>
Vue3更多的优势
3. create-vue创建Vue3项目
create-vue
是Vue官方新的脚手架工具,底层切换到了vite
(下一代前端工具链),为开发提供极速响应。
(base) ➜ ~ nvm list
-> v12.22.12
v14.21.3
v18.20.3
system
default -> 12 (-> v12.22.12)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v18.20.3) (default)
stable -> 18.20 (-> v18.20.3) (default)
lts/* -> lts/iron (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12
lts/fermium -> v14.21.3
lts/gallium -> v16.20.2 (-> N/A)
lts/hydrogen -> v18.20.3
lts/iron -> v20.14.0 (-> N/A)
(base) ➜ ~ nvm use v18.20.3
Now using node v18.20.3 (npm v10.8.3)
(base) ➜ ~ node -v
v18.20.3
使用create-vue创建项目
- node的版本要求是16.0或者更高
npm init vue@latest
这个命令会安装并执行crate-vue
(base) ➜ ~ npm init vue@latest
Need to install the following packages:
create-vue@3.15.1
Ok to proceed? (y) y
> npx
> create-vue
┌ Vue.js - The Progressive JavaScript Framework
│
◇ 请输入项目名称:
│ vue-project
│
◇ 请选择要包含的功能: (↑/↓ 切换,空格选择,a 全选,回车确认)
│ none
正在初始化项目 /Users/fanzhen/vue-project...
│
└ 项目初始化完成,可执行以下命令:
cd vue-project
npm install
npm run dev
| 可选:使用以下命令在项目目录中初始化 Git:
git init && git add -A && git commit -m "initial commit"
npm notice
npm notice New major version of npm available! 10.8.3 -> 11.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.2.0
npm notice To update run: npm install -g npm@11.2.0
npm notice
(base) ➜ vue-project npm run dev
> vue-project@0.0.0 dev
> vite
VITE v6.2.3 ready in 699 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ Vue DevTools: Open http://localhost:5173/__devtools__/ as a separate window
➜ Vue DevTools: Press Option(⌥)+Shift(⇧)+D in App to toggle the Vue DevTools
➜ press h + enter to show help
3.1 新建项目结构
package.json
{
"name": "vue-project",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.2.1",
"vite-plugin-vue-devtools": "^7.7.2"
}
}
main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
app.vue
// setup就是一个开关,容许在script中写组合式API
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
3.2 小节
3.3 补充说明npm init vue@latest
npm init vue@latest
是一个用于创建 Vue.js 项目的命令行指令,它使用 npm(Node Package Manager) 来初始化一个基于 Vue 3 的最新版本的项目。下面详细解释这个命令的各个部分:
3.3.1 npm init
- 作用:npm init 是 npm 的一个命令,用于初始化一个新的 Node.js 项目(生成 package.json 文件)。
- 通常用法:npm init 会交互式地询问项目信息(如名称、版本等),但结合 vue@latest 后,它会直接调用 Vue 的脚手架工具来创建项目。
3.3.2 vue@latest
- 含义:vue 是 Vue.js 的官方脚手架工具(create-vue),@latest 表示使用最新版本。
- 作用:这个包会提供一个交互式命令行界面(CLI),引导你配置 Vue 项目的基本选项(如是否支持 TypeScript、Pinia、Router 等)。
- 替代写法:你也可以用 npm create vue@latest(npm create 是 npm init 的别名)。
3.3.3 执行流程
- 检查环境:npm 会检查本地是否安装了 create-vue,如果没有,会自动下载最新版本。
- 启动向导:运行后,会进入交互式配置界面,询问:
- 项目名称(默认 vue-project)
- 是否支持 TypeScript?
- 是否启用 JSX?
- 是否安装 Vue Router(路由)?
- 是否安装 Pinia(状态管理)?
- 是否添加 Vitest 或 Cypress 测试工具?
- 是否启用 ESLint(代码规范检查)?
3. 生成项目:根据你的选择,生成项目结构并安装依赖。
3.3.4 与 vue-cli 的区别
- Vue 2 时代:旧版使用 vue-cli(全局安装 npm install -g @vue/cli,然后运行 vue create my-app)。
- Vue 3 推荐:现在官方推荐使用 create-vue(即 npm init vue@latest),它基于 Vite(更快、更轻量),而不是 Webpack。
3.3.5 使用 create-vue 指定 Vue 版本(Vue 3)
npm init vue@latest
默认使用最新的 Vue 3 版本,但你可以通过以下方式间接控制 Vue 的版本:
npm init vue@latest my-vue-app
cd my-vue-app
npm install vue@3.2.47 # 安装指定版本的 Vue 3
4. 组合式API - setup选项
setup选项的写法和执行时机
在beforeCreate之前执行
验证
setup选项中写代码的特点
这里的变量messgae、方法logMessage都需要return,但是这样真的很麻烦!
但是vue给了一个语法糖,如下所示
4.1 小结
5. 组合式API-reactive和ref函数
5.1 reactive()函数
作用:接受对象类型数据的参数传入并返回一个响应式的对象。
核心步骤:
- 从 vue 包中导入 reactive 函数
- 在
<script setup>
中执行 reactive 函数并传入类型为对象的初始值,并使用变量接收返回值
🌰
<script setup>
// 导入
import { reactive } from 'vue'
// 执行函数 传入参数 变量接收
const state = reactive({
msg:'this is msg'
})
const setSate = ()=>{
// 修改数据更新视图
state.msg = 'this is new msg'
}
</script>
<template>
{{ state.msg }}
<button @click="setState">change msg</button>
</template>
🌰
5.2 ref()函数
作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象。
核心步骤:
- 从 vue 包中导入 ref 函数
- 在
<script setup>
中执行 ref 函数并传入初始值,使用变量接收 ref 函数的返回值
🌰
5.3 小结
6. 组合式API-computed
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
核心步骤:
- 导入computed函数
- 执行函数 在回调参数中return基于响应式数据做计算的值,用变量接收
🌰
6.1 小节
7. 组合式API-watch-基本使用和立即执行
watch函数的作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数。
俩个额外参数:1. immediate(立即执行) 2. deep(深度侦听)
7.1 侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
7.2 侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
7.3 immediate
说明:在侦听器创建时立即触发回调, 响应式数据变化之后继续执行回调。
语法:
8. 组合式API-watch-深度侦听和精确侦听
8.1 deep
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep
选项
🌰
🌰
8.2 精确侦听对象的某个属性
需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调
8.3 小结
9. 组合式API-生命周期函数
Vue3的生命周期API (选项式 VS 组合式)
9.1 生命周期函数基本使用
- 导入生命周期函数
- 执行生命周期函数 传入回调
9.2 生命周期函数执行多次
生命周期函数是可以执行多次的,多次执行时传入的回调会在时机成熟时依次执行。
9.3 小结
10. 组合式API下的父子通信-父传子
基本思想
- 父组件中给子组件绑定属性
- 子组件内部通过props选项接收
🌰
🌰
🌰
在Vue2中:
11. 组合式API下的父子通信-子传父
基本思想
- 父组件中给子组件标签通过
@
绑定事件 - 子组件内部通过
$emit
方法触发事件
11.1 小结
12. 组合式API-模版引用
概念:
12.1 使用
如何使用(以获取dom为例 组件同理)
🌰
12.2 defineExpose()
默认情况下在<script setup>
语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose
编译宏指定哪些属性和方法允许访问。
🌰
13. 组合式API-provide和inject
作用与场景:
顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信。
13.1 跨层传递普通数据
🌰
🌰
13.2 跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据。
🌰
14. Vue3综合小案例
案例演示:
项目地址:git clone http://git.itcast.cn/heimaqianduan/vue3-basic-project.git
- 模版已经配置好了案例必须的安装包
- 案例用到的接口在 README.MD文件 中
- 案例项目有俩个分支,main主分支为开发分支,complete分支为完成版分支供开发完参考
APP.vue
<script setup>
import Edit from './components/Edit.vue'
import { ref, onMounted } from 'vue'
import axios from 'axios'
// TODO:列表渲染
const list = ref([])
const getList = async () => {
const res = await axios.get("/list")
list.value = res.data
}
onMounted(() => getList())
// TODO:删除功能
const onDelete = async (id) => {
await axios.delete(`/del/${id}`)
getList()
}
// TODO:编辑功能
const editRef = ref(null)
const openDialog = (item) => {
editRef.value.open(item)
}
// 更新列表
const updateList = () => {
getList()
}
</script>
<template>
<div class="app">
<el-table :data="list">
<el-table-column label="ID" prop="id"></el-table-column>
<el-table-column label="姓名" prop="name" width="150"></el-table-column>
<el-table-column label="籍贯" prop="place"></el-table-column>
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button type="primary" link @click="openDialog(row)">编辑</el-button>
<el-button type="danger" link @click="onDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<Edit ref="editRef" @update-list="updateList" />
</template>
<style scoped>
.app {
width: 980px;
margin: 100px auto 0;
}
</style>
main.js
import { createApp } from "vue"
// 导入 ElementPlus 和 样式文件
import ElementPlus from "element-plus"
import "element-plus/dist/index.css"
import App from "./App.vue"
const app = createApp(App)
// 使用 ElementPlus
app.use(ElementPlus)
app.mount("#app")
Edit.vue
<script setup>
import { ref } from 'vue'
import axios from 'axios'
// TODO: 编辑
// 控制弹框打开关闭
const dialogVisible = ref(false)
// 表单数据
const form = ref({
name: '',
place: '',
})
// 打开弹框
const open = (item) => {
const { name, place, id } = item
dialogVisible.value = true
form.value.name = name
form.value.place = place
form.value.id = id
}
// 提交表单
const emit = defineEmits(['update-list'])
const onSubmit = async () => {
if (form.value.name && form.value.place) {
// 提交接口
await axios.patch(`/edit/${form.value.id}`, form.value)
// 关闭弹框
dialogVisible.value = false
// 通知父组件拉取最新列表
emit('update-list')
}
}
defineExpose({
open
})
</script>
<template>
<el-dialog v-model="dialogVisible" title="编辑" width="400px">
<el-form label-width="50px">
<el-form-item label="姓名">
<el-input placeholder="请输入姓名" v-model="form.name" />
</el-form-item>
<el-form-item label="籍贯">
<el-input placeholder="请输入籍贯" v-model="form.place" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="onSubmit">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.el-input {
width: 290px;
}
</style>
package.json
{
"name": "vue3-demo-template",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"vue": "^3.2.41"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.1.2",
"mockjs": "^1.1.0",
"vite": "^3.1.8",
"vite-plugin-mock": "^2.9.6"
}
}