文章目录
知识回顾
前言
Vue项目介绍及其核心语法
源码分析
1. 项目结构介绍(单页面应用程序)
VUE-DEMO
│─node_modules 第三包文件夹
├─public 放html文件的地方
| └─favicon.ico 网站图标
├─src 源代码目录 → 以后写代码的文件夹
│ └─assets 静态资源目录 → 存放图片、字体等
│ └─components 组件目录 → 存放通用组件
│ └─App.vue App根组件 → 项目运行看到的内容就在这里编写
│ └─main.ts 入口文件 → 打包或运行,第一个执行的文件
└─.eslintrc.cjs eslint配置文件
└─.gitignore git忽视文件
└─.prettierrc.json prettierrc配置文件
└─env.d.ts ts代码智能提示使用
└─index.html index.html 模板文件
└─package.json 项目配置文件 → 包含项目名、版本号、scripts、依赖包等
└─pnpm-lock.yaml pnpm锁文件,由pnpm自动生成的,锁定安装版本
└─README.md 项目说明文档
└─tsconfig.app.json ts项目配置文件
└─tsconfig.json ts配置文件
└─tsconfig.app.json ts的node环境配置文件
└─vite.config.js create-vue配置文件
2. 项目运行流程图(单页面应用程序)
项目运行后执行main.ts
main.ts中会执行app.vue文件,导入createapp方法,导入app.vue , 创建app实例对象
最后渲染到index.html容器中
3. 选项式和组合式api
- 选项式
<template>
<button @click="toggle">显示隐藏图片</button>
<img v-show="show" alt="Vue logo" src="./assets/logo.png" />
<hr />
计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script>
export default {
data() {
return {
show: true,
count: 0
}
},
methods: {
toggle() {
this.show = !this.show
},
increment() {
this.count++
}
}
}
</script>
- 组合式
<template>
<button @click="toggle">显示隐藏图片</button>
<img v-show="show" alt="Vue logo" src="./assets/logo.svg" />
<hr />
计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const show = ref(true)
const toggle = () => {
show.value = !show.value
}
const count = ref(0)
const increment = () => {
count.value++
}
</script>
4. 插值表达式 {{}} 胡子语法
插值表达式是一种Vue的模板语法, 我们可以用插值表达式渲染出Vue提供的数据
- 作用:利用表达式进行插值,渲染到页面中
- 表达式:是可以被求值的代码,JS引擎会讲其计算出一个结果
注意事项:
- 在插值表达式中使用的数据 必须在setup函数中进行了提供
- 支持的是表达式,而非语句,比如:if for …
- 不能在标签属性中使用 {{ }} 插值 (插值表达式只能标签中间使用)
<template>
<div>{{ title }}</div>
<div>{{ sum(100, 200) }}</div>
<p>{{ array[1] }}</p>
</template>
<script setup lang="ts">
const title = 'test'
const sum = (num1: number, num2: number) => {
return num1 + num2
}
const array = [1, 2, 3]
</script>
5. reactive函数
<template>
<button @click="onClick">点击我</button>
<button @click="addAge">增加年龄</button>
<button @click="addHobbies">新增爱好</button>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const person = reactive({
name: '张三',
age: 18,
friend: {
name: '李四',
age: 20,
hobbies: ['打游戏', '看电影']
}
})
const onClick = () => {
console.log('点击了')
alert('点击了我')
}
const addAge = () => {
person.age++
console.log(person.age)
}
const addHobbies = () => {
person.friend.hobbies.push('看书')
console.log(person.friend.hobbies)
}
</script>
6. ref表达式
<template>
<div>年龄{{ age }}</div>
<button @click="changeAge">修改年龄</button>
<div>{{ person }}</div>
<button @click="changePersonAge">修改person中年龄</button>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
const age = ref(18)
const changeAge = () => {
age.value++
}
const person = ref({ name: '张三', age: 18 })
const changePersonAge = () => {
person.value.age++
}
</script>
7. vue中常用指令
Vue 提供的带有 v- 前缀 的 特殊 标签属性
- v-text v-html
- v-if v-show
<script setup lang="ts">
import { ref } from 'vue'
const flag = ref(false)
const toggle = () => {
flag.value = !flag.value
}
</script>
<template>
<div v-show="flag" class="box">我是v-show控制的盒子</div>
<div v-if="flag" class="box">我是v-if控制的盒子</div>
<button @click="toggle">显示/隐藏</button>
</template>
<style>
.box {
width: 100px;
height: 100px;
background-color: red;
margin-bottom: 20px;
}
</style>
- v-else 和 v-else-if
<script setup lang="ts">
import { ref } from 'vue'
// 1. 性别
const gender = ref(0) // 0-男 1-女
// 2. 成绩
const score = ref(95)
</script>
<template>
<div id="app">
<p v-if="gender === 0">性别:♂ 男</p>
<p v-else>性别:♀ 女</p>
<hr>
<p v-if="score >= 90">成绩评定A:奖励电脑一台</p>
<p v-else-if="score >= 80">成绩评定B:奖励周末郊游</p>
<p v-else-if="score >= 60">成绩评定C:奖励零食礼包</p>
<p v-else>成绩评定D:惩罚一周不能玩手机</p>
</div>
</template>
- 事件绑定
<template>
<div>
<button @click="count--">-</button>
<span>{{ count }}</span>
<button v-on:click="count++">+</button>
<p></p>
<div class="box">
<h3>黑马自动售货机</h3>
<button @click="buy(5)">可乐5元</button>
<button @click="buy(10)">咖啡10元</button>
<button @click="buy(8)">牛奶8元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(100)
const money = ref(100)
const buy = (price: number) => {
money.value -= price
}
</script>
<style></style>
8. 事件绑定获取事件对象
<template>
<div>
<button @click="count--">-</button>
<span>{{ count }}</span>
<button v-on:click="count++">+</button>
<button @click="clickMe">点击了我</button>
<button @click="clickMeTwo(100, $event)">点击了我</button>
<p></p>
<div class="box">
<h3>黑马自动售货机</h3>
<button @click="buy(5)">可乐5元</button>
<button @click="buy(10)">咖啡10元</button>
<button @click="buy(8)">牛奶8元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(100)
const money = ref(100)
const buy = (price: number) => {
money.value -= price
}
const clickMe = (event: Event) => {
console.log(event)
}
const clickMeTwo = (num: number, event: Event) => {
console.log(num)
console.log(event)
}
</script>
<style></style>
9. v-for遍历
Vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。
v-for 指令需要使用 (item, index) in arr 形式的特殊语法,其中:
● item 是数组中的每一项
● index 是每一项的索引,不需要可以省略
● arr 是被遍历的数组
v-for中的key:
● 语法: key=“唯一值”
● 作用:给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用。
● 为什么加key:Vue 的默认行为会尝试原地修改元素(就地复用)
<template>
<div id="app">
<div class="box">
<span
v-for="(item, index) in array"
:key="index"
:class="{ active: item === array[activeIndex] }"
@click="activeIndex = index"
>{{ item }}</span
>
<!-- 列表遍历 -->
<ul>
<li v-for="item in goods" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const array = ['首页', '内容', '我的']
const goods = [
{ name: 'apple', id: 1 },
{ name: 'banana', id: 2 },
{ name: 'orange', id: 3 },
{ name: 'pear', id: 4 }
]
const activeIndex = ref(0)
</script>
<style></style>
<style>
.box {
display: flex;
}
.box span {
width: 50px;
height: 60px;
}
.active {
background-color: red;
}
</style>
10. 遍历对象和删除
- {{ key }} : {{ value }}
{{ item }}
<script setup lang="ts">
import { ref } from 'vue'
const list = ref([
{ id: 1, name: '《红楼梦》', author: '曹雪芹' },
{ id: 2, name: '《西游记》', author: '吴承恩' },
{ id: 3, name: '《水浒传》', author: '施耐庵' },
{ id: 4, name: '《三国演义》', author: '罗贯中' }
])
const del = (id: number) => {
// console.log('删除', id)
// 通过 id 进行删除数组中的 对应项 → filter(不会改变原数组)
// filter: 根据条件,保留满足条件的对应项,得到一个新数组。
// console.log(list.value.filter(item => item.id !== id))
list.value = list.value.filter((item) => item.id !== id)
}
</script>
<template>
<div id="app">
<h3>小黑的书架</h3>
<ul>
<li v-for="item in list" :key="item.id">
<span>{{ item.name }}</span>
<span></span>
<button @click="del(item.id)">删除</button>
</li>
</ul>
</div>
</template>
<style></style>
11. 双向绑定指令
所谓双向绑定就是:
● 数据改变后,呈现的页面结果会更新
● 页面结果更新后,数据也会随之而变
作用: 给表单元素(input、radio、select)使用,双向绑定数据,可以快速 获取 或 设置 表单元素内容
语法:v-model=“变量”
v-model 双向绑定的底层逻辑是 在input组件上是 value属性和input事件的结合
<script setup lang="ts">
import { ref } from 'vue'
const username = ref('')
const password = ref('')
const onChange = (event: Event) => {
username.value = (event.target as HTMLInputElement).value
console.log(username.value)
}
const login = () => {
console.log(username.value, password.value)
}
const reset = () => {
username.value = ''
password.value = ''
}
</script>
<template>
<div id="app">
<!-- 账户:<input v-model="username" type="text" /> <br /><br /> -->
<!-- v-model的底层逻辑写法 -->
<!--底层逻辑是value属性和input事件的结合 -->
账户:<input :value="username" type="text" @input="onChange" /> <br /><br />
密码:<input v-model="password" type="password" /> <br /><br />
<button @click="login">登录</button>
<button @click="reset">重置</button>
</div>
</template>
<style></style>
12. 计算属性
概念
● 基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。语法
● 声明在 computed 函数中,一个计算属性对应一个函数
● 使用起来和普通属性一样使用 {{ 计算属性名}}
● computed中的计算属性虽然是函数的写法,但它依然是个属性使用步骤
● 从 vue 中导出 computed 函数
● 在 setup 函数中,使用 computed 函数,传入一个函数,函数返回计算好的数据
<script setup lang="ts">
import { computed, ref } from 'vue'
const list = ref([
{ id: 1, name: '篮球', num: 1 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 }
])
// 使用计算属性,有缓存只执行一次,后面的取缓存数值
const total = computed(() => {
return list.value.reduce((sum, item) => sum + item.num, 0)
})
// 使用函数没有缓存
const totalNum = () => {
return list.value.reduce((sum, item) => sum + item.num, 0)
}
</script>
<template>
<div id="app">
<h3>小黑的礼物清单</h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="item in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<!-- 目标:统计求和,求得礼物总数 -->
<p>礼物总数:{{ total }} 个</p>
</div>
</template>
<style>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,
td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
13. 侦听器
- 作用
● 监视数据变化,执行一些业务逻辑或异步操作
- 主要使用方式
● 使用 watch 监听一个响应式数据 watch(数据, 改变后回调函数)
● 使用 watch 监听多个响应式数据 watch([数据1, 数据2, …], 改变后回调函数)
● 使用 watch 监听响应式对象数据中的一个属性(简单) watch(()=>数据, 改变后回调函数)
● 使用 watch 监听响应式对象数据中的一个属性(复杂),配置深度监听
● 使用 watch 监听,配置默认执行
一个和多个响应数据变化
<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
// 1. 使用 watch 监听一个响应式数据
const count = ref(100)
// 只监听一个
// watch(count, (newValue, oldValue) => {
// console.log(newValue, oldValue)
// })
const user = reactive({
name: 'tom',
info: {
gender: '男',
age: 18
}
})
// 监听两个
watch([count, user], (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 2s后count加一
setTimeout(() => {
count.value++
}, 2000)
// 4s后user.info.age加一
setTimeout(() => {
user.info.age++
}, 4000)
</script>
<template>
<div class="app-page">
<div>{{ count }}</div>
<button @click="count++">变</button>
</div>
</template>
<style lang="css" scoped></style>
深度监听
<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
// 1. 使用 watch 监听一个响应式数据
const count = ref(100)
// 只监听一个
// watch(count, (newValue, oldValue) => {
// console.log(newValue, oldValue)
// })
const user = reactive({
name: 'tom',
info: {
gender: '男',
age: 18
}
})
// 监听两个
watch([count, user], (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 2s后count加一
setTimeout(() => {
count.value++
}, 2000)
// 4s后user.info.age加一
setTimeout(() => {
user.info.age++
}, 4000)
// 3. 监听响应式对象数据的一个数据,简单类型
// watch(()=>数据, 改变后回调函数)
watch(
() => user.info.age,
(newValue, oldValue) => {
console.log(newValue, oldValue)
}
)
// 深度监听deep:true
watch(
() => user.info,
(newValue) => {
console.log(newValue)
},
{ deep: true }
)
// 新版已经不需要设置,直接对其进行监听不需要箭头函数
watch(user.info, (newValue) => {
console.log(newValue)
})
</script>
<template>
<div class="app-page">
<div>{{ count }}</div>
<button @click="count++">变</button>
<p>姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}</p>
</div>
</template>
<style lang="css" scoped></style>
- 小结
● watch(需要监听的数据,数据改变执行函数,配置对象) 来进行数据的侦听
● 数据:单个数据,多个数据,函数返回对象属性,属性复杂需要开启深度监听
● 配置对象:deep 深度监听 immediate 默认执行
拓展知识
reactive和ref的选择
- 开始分析
● reactive 可以转换对象成为响应式数据对象,但是不支持简单数据类型。
● ref 可以转换简单数据类型为响应式数据对象,也支持复杂数据类型,但是操作的时候需要 .value 。
● 它们各有特点,现在也没有最佳实践,没有明显的界限,所有大家可以自由选择。 - 推荐用法
● 如果能确定数据是对象且字段名称也确定,可使用 reactive 转成响应式数据,其他一概使用 ref 。这样就没有心智负担 。