目录
一、 引言
- 渐进式框架是指那些允许开发者根据项目需求逐步引入和使用其功能的框架,而不需要一次性全部使用框架的特性。常见的渐进式框架有:Vue、React、Angular 等。
- Vue3的特点 : 性能提升,源码升级,新的特性,新的生命周期等等
二、介绍
记录本人对于Vue3的学习! 不定时补充!
学习地址 : Vue.js
三、Vue3的学习
(一) 使用 VSCode 开发 Vue
创建Vue项目
npm create vue@latest
后会有一些选项,可以根据自己的需求选择,或者一路回车:
Vue.js - The Progressive JavaScript Framework
> 请输入项目名称: … runoob-vue3-app
> 是否使用 TypeScript 语法? … 否 / 是
> 是否启用 JSX 支持? … 否 / 是
> 是否引入 Vue Router 进行单页面应用开发? … 否 / 是
> 是否引入 Pinia 用于状态管理? … 否 / 是
> 是否引入 Vitest 用于单元测试? … 否 / 是
> 是否要引入一款端到端(End to End)测试工具? › 不需要
> 是否引入 ESLint 用于代码质量检测? › 否
项目初始化完成,可执行以下命令:
cd runoob-vue3-app
npm install
npm run dev
(二) Vue组件的基本结构
Vue 组件是 Vue 应用的核心构建块。
一个组件通常包含以下部分:
一个 .vue
文件包含三个部分:<template>
、<script>
和 <style>
。
<template>模版 :
- 使用 HTML 和 Vue 模板语法定义组件的 UI。
- 支持插值(
{{ }}
)、指令(如v-if
、v-for
)和事件绑定(如@click
)
<script> 脚本 :
- 定义组件的逻辑,包括数据、方法、生命周期钩子等。
- 使用
export default
导出组件选项。
<style>样式 :
- 定义组件的样式。
- 使用
scoped
属性可以将样式限制在当前组件内。
(三) 项目结构
一个 Vue 3 项目通常包含以下文件和文件夹:
my-vue-app/
├── node_modules/ # 项目依赖的第三方库
├── public/ # 静态资源文件夹
│ ├── favicon.ico # 应用的 图标
│ ├── index.html # 应用的 HTML 模板
├── src/ # 项目源代码
│ ├── assets/ # 静态资源(如图片、字体等)
│ │ └── logo.png
│ ├── components/ # 可复用的 Vue 组件
│ │ └── HelloWorld.vue
│ ├── store/ # 页面级组件
│ │ └── counter.ts # Vuex 状态管理配置
│ ├── views/ # 页面级组件
│ │ └── Home.vue
│ ├── App.vue # 根组件
│ ├── main.ts # 项目入口文件
│ └── router/ # 路由配置
│ └── index.js
├── .gitignore
├── babel.config.js
├── package.json # 项目配置和依赖管理
├── README.md
├── index.html
├── vue.config.js
└── yarn.lock or package-lock.json # 依赖的精确版本锁定文件
1. 核心文件解析
public/index.html 是 Vue 应用的 HTML 模板文件。
-
- Vue 会将应用挂载到
<div id="app"></div>
中。
- Vue 会将应用挂载到
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
src/main.ts 是 Vue 应用的入口文件。
负责创建 Vue 应用实例,并将根组件(通常是 App.vue)挂载到 index.html 中的 div#app 中
import './assets/main.css'
// 创建 Vue 应用实例
import { createApp } from 'vue'
// 创建 Pinia 状态管理实例
import { createPinia } from 'pinia'
// 引入根组件
import App from './App.vue'
// 引入路由配置
import router from './router'
// 创建 Vue 应用实例
const app = createApp(App)
// 使用 Pinia 状态管理库
app.use(createPinia())
// 使用路由配置
app.use(router)
// 将应用挂载到 DOM 中的 #app 元素上
app.mount('#app')
src/App.vue 是 Vue 应用的根组件。
src/App.vue 通常包含应用的主要布局和路由视图。
<script setup lang="ts">
// 引入 Vue Router 的 RouterLink 和 RouterView 组件
import { RouterLink, RouterView } from 'vue-router'
// 引入 HelloWorld 组件
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<header>
<!-- 显示 Vue 的 logo 图片 -->
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<!-- 包含 HelloWorld 组件和导航栏的容器 -->
<div class="wrapper">
<!-- 引入 HelloWorld 组件,并传递一个消息参数 -->
<HelloWorld msg="Hello Vue3!" />
<!-- 导航栏,包含 Home 和 About 页面的链接 -->
<nav>
<!-- 链接到首页 -->
<RouterLink to="/">Home</RouterLink>
<!-- 链接到关于页面 -->
<RouterLink to="/about">About</RouterLink>
</nav>
<!-- wrapper 结束标签 -->
</div>
<!-- header 结束标签 -->
</header>
<!-- 显示当前路由对应的组件内容 -->
<RouterView />
</template>
<style scoped>
/* 设置 header 的基础样式 */
header {
line-height: 1.5;
max-height: 100vh;
}
/* 设置 logo 的样式 */
.logo {
display: block;
margin: 0 auto 2rem;
}
/* 设置导航栏的基本样式 */
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
/* 设置导航栏中激活链接的颜色 */
nav a.router-link-exact-active {
color: var(--color-text);
}
/* 设置导航栏中激活链接在悬停时的背景颜色 */
nav a.router-link-exact-active:hover {
background-color: transparent;
}
/* 设置导航栏中链接的样式 */
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
/* 移除导航栏中第一个链接的左侧边框 */
nav a:first-of-type {
border: 0;
}
/* 针对屏幕宽度大于等于 1024px 的响应式设计 */
@media (min-width: 1024px) {
/* 设置 header 的布局方式为 flex 并居中显示 */
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
/* 设置 logo 的左边距为 2rem,右边距为 0 */
.logo {
margin: 0 2rem 0 0;
}
/* 设置 wrapper 的布局方式为 flex 并左对齐显示 */
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
/* 设置导航栏在大屏幕上的样式 */
nav {
text-align: left;
margin-left: -1rem;
font-size: 1rem;
padding: 1rem 0;
margin-top: 1rem;
}
}
</style>
src/components/ 文件夹包含可复用的 Vue 组件。如HelloWorld.vue
<script setup lang="ts">
// 定义组件的属性,这里接收一个名为 msg 的字符串类型的属性
defineProps<{
msg: string
}>()
</script>
<template>
<!-- 主要的问候内容区域 -->
<div class="greetings">
<!-- 显示从父组件传递过来的消息,使用绿色的样式 -->
<h1 class="green">{{ msg }}</h1>
<!-- 介绍项目使用的技术栈,并包含指向 Vite 和 Vue 3 官方网站的链接 -->
<h3>
你已经成功创建了一个使用
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a> 的项目。接下来该做什么呢?
</h3>
</div>
</template>
<style scoped>
/*设置 h1 标签的样式,包括字体粗细、大小和相对位置*/
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
/*设置 h3 标签的字体大小*/
h3 {
font-size: 1.2rem;
}
/*在 .greetings 类下的 h1 和 h3 标签默认居中显示*/
.greetings h1,
.greetings h3 {
text-align: center;
}
/*当屏幕宽度大于等于 1024px 时,改变 .greetings 类下的 h1 和 h3 标签的对齐方式为左对齐*/
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
src/views/ 文件夹包含页面级组件,通常与路由配置一起使用。如AboutView.vue
<template>
<!-- 定义了一个名为 "about" 的容器,用于包含关于页面的内容 -->
<div class="about">
<!-- 显示一个标题,内容为 "This is an about page" -->
<h1>This is an about page</h1>
</div>
</template>
<style>
/* 针对屏幕宽度大于或等于 1024px 的媒体查询 */
@media (min-width: 1024px) {
/* 为 "about" 类设置最小高度为视口高度的 100% */
.about {
min-height: 100vh;
/* 使用 Flexbox 布局,使子元素在主轴和交叉轴上居中对齐 */
display: flex;
align-items: center;
}
}
</style>
src/router/ 是 Vue Router 的配置文件,用于定义路由;如index.ts
// 导入 createRouter 和 createWebHistory 方法,用于创建 Vue Router 实例
import { createRouter, createWebHistory } from 'vue-router'
// 导入 HomeView 组件
import HomeView from '../views/HomeView.vue'
// 创建 Vue Router 实例
const router = createRouter({
// 使用 HTML5 History 模式,设置基础 URL 为应用的环境变量 BASE_URL
history: createWebHistory(import.meta.env.BASE_URL),
// 定义路由表
routes: [
{
// 定义首页路由路径
path: '/',
// 定义路由名称为 'home'
name: 'home',
// 指定路由对应的组件为 HomeView
component: HomeView,
},
{
// 定义关于页面的路由路径
path: '/about',
// 定义路由名称为 'about'
name: 'about',
// 路由级别的代码分割
// 这将为该路由生成一个单独的代码块 (About.[hash].js)
// 并且在访问该路由时按需加载
component: () => import('../views/AboutView.vue'),
},
],
})
// 导出默认的 router 实例,供应用程序使用
export default router
src/store/ 是 Vuex 状态管理的配置文件(如果使用 Vuex);如counter.ts
import { ref, computed } from 'vue' // 引入 Vue 的 ref 和 computed 函数
import { defineStore } from 'pinia' // 引入 Pinia 的 defineStore 函数
// 定义一个名为 'counter' 的 store
export const useCounterStore = defineStore('counter', () => {
// 使用 ref 创建一个响应式变量 count,初始值为 0
const count = ref(0)
// 使用 computed 创建一个计算属性 doubleCount,其值为 count 的两倍
const doubleCount = computed(() => count.value * 2)
// 定义一个名为 increment 的函数,用于增加 count 的值
function increment() {
count.value++
}
// 返回 store 中的 state 和 actions
return { count, doubleCount, increment }
})
package.json 是项目的配置文件,包含项目的元数据、依赖和脚本
vite.config.js 是 Vite 项目的核心配置文件,通过配置 vite.config.js,你可以自定义 Vite 的各种行为,例如开发服务器、构建选项、插件等。
// 导入 defineConfig 函数,用于定义 Vite 配置
import { defineConfig } from 'vite';
// 导入 Vue 插件,用于支持 Vue 项目
import vue from '@vitejs/plugin-vue';
// 导入 path 模块,用于处理路径
import path from 'path';
// 使用 defineConfig 定义 Vite 配置
export default defineConfig({
// 指定项目的根目录,默认为当前工作目录。这里设置为 ./src,表示项目源代码放在 src 目录下。
root: path.resolve(__dirname, './src'),
// 基础路径,用于部署在子路径时使用
base: '/my-app/',
// 开发服务器配置
server: {
// 指定开发服务器端口
port: 3000,
// 是否自动打开浏览器
open: true,
// 配置代理服务器,用于解决跨域问题
proxy: {
'/api': {
target: 'http://localhost:8080', // 目标服务器地址
changeOrigin: true, // 是否改变请求源
rewrite: (path) => path.replace(/^\/api/, ''), // 重写请求路径
},
},
},
// 构建配置
build: {
// 指定输出目录
outDir: path.resolve(__dirname, '../dist'),
// 指定静态资源目录
assetsDir: 'static',
// 是否生成 sourcemap 文件
sourcemap: true,
// 是否压缩代码
minify: 'terser', // 使用 terser 进行代码压缩
// 配置 Rollup 选项
rollupOptions: {
// 配置外部依赖
external: ['lodash'],
// 配置输出格式
output: {
manualChunks: {
// 将 lodash 单独打包
lodash: ['lodash'],
},
},
},
},
// 插件配置
plugins: [
// 使用 Vue 插件
vue(),
],
// 模块解析配置
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'), // 将 @ 映射到 src 目录
},
},
// CSS 配置
css: {
// 配置 CSS 预处理器选项
preprocessorOptions: {
scss: {
// 全局注入 SCSS 变量
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
// 环境变量配置
envPrefix: 'VITE_', // 环境变量前缀,默认为 VITE_
});
(四) 声明式响应
1. ref()
ref 函数
作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)
创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
JS中操作数据: xxx.value
模板中读取数据: 不需要.value,直接:{{xxx}}
备注:
接收的数据可以是:基本类型、也可以是对象类型。
基本类型的数据:响应式依然是靠Object.defineProperty()的getter与setter完成的。
对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数
2. setup
Vue3.0中一个新的配置项,值为一个函数。
setup是所有Composition API(组合API)“ 表演的舞台 ”。
组件中所用到的:数据、方法等等,均要配置在setup中。
setup函数的返回值是一个对象,则对象中的属性、方法, 在模板中均可以直接使用。
注意点:
尽量不要与Vue2.x配置混用
Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
如果有重名, setup优先。
setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
3. reactive 函数
一般不使用它,使用ref()
reactive对比ref
- ref
-
- ref用来定义:基本类型数据。
- ref通过类中的getter与setter来实现响应式(数据劫持)
- ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
- reactive
-
- reactive用来定义:对象(或数组)类型数据。
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- reactive定义的数据:操作数据与读取数据均不要.value。
(五) 基础使用
1. 条件渲染
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染- 你也可以使用
v-else
为v-if
添加一个“else 区块”
-
- 一个
v-else
元素必须跟在一个v-if
或者v-else-if
元素后面,否则它将不会被识别
- 一个
v-else-if
提供的是相应于v-if
的“else if 区块”。它可以连续多次重复使用;
-
v-else
类似,一个使用v-else-if
的元素必须紧跟在一个v-if
或一个v-else-if
元素后面
- 另一个可以用来按条件显示一个元素的指令是
v-show
。其用法基本一样;
-
- 不同之处在于
v-show
会在 DOM 渲染中保留该元素;v-show
仅切换了该元素上名为display
的 CSS 属性。
- 不同之处在于
<script setup>
import { reactive, computed, ref } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
const awesome = ref(true);
const type = ref('A');
const ok = true;
</script>
<template>
<div>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</div>
<div>
<button @click="awesome = !awesome">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</div>
<div>
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
</div>
<div>
<template v-if="!awesome">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
</div>
<div>
<h1 v-show="awesome">Hello!</h1>
</div>
</template>
- 在一个
<template>
元素上使用v-if
,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个<template>
元素;v-if
v-else
和v-else-if
也可以在<template>
上使用。 v-show
不支持在<template>
元素上使用,也不能和v-else
搭配使用。
v-if
vs. v-show
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if
也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display
属性会被切换。
总的来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show
较好;如果在运行时绑定条件很少改变,则 v-if
会更合适。
2. 列表渲染
v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用item in items
形式的特殊语法,其中items
是源数据的数组,而item
是迭代项的别名;- 注意 v-for 是如何对应 forEach 回调的函数签名的。实际上,你也可以在定义 v-for 的变量别名时使用解构,和解构函数参数类似
- 对于多层嵌套的 v-for,作用域的工作方式和函数的作用域很类似。每个 v-for 作用域都可以访问到父级作用域
<script setup>
import { ref } from 'vue'
const parentMessage = ref('Parent')
const items = ref([
{ message: 'Foo' },
{ message: 'Bar' }
])
</script>
<template>
<!-- v-for 指令基于一个数组来渲染一个列表。
v-for 指令的值需要使用 item in items 形式的特殊语法,
其中 items 是源数据的数组,而 item 是迭代项的别名; -->
<div>
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</div>
<!-- 注意 v-for 是如何对应 forEach 回调的函数签名的。
实际上,你也可以在定义 v-for 的变量别名时使用解构,
和解构函数参数类似 -->
<div>
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
</div>
<!-- 对于多层嵌套的 v-for,
作用域的工作方式和函数的作用域很类似。
每个 v-for 作用域都可以访问到父级作用域: -->
<div>
<li v-for="item in items">
<span v-for="(messageItem , messageIndex) in item.message">
{{ item.message }} {{ messageIndex }} {{ messageItem }}
</span>
</li>
</div>
<!-- 你也可以使用 of 作为分隔符来替代 in,这更接近 JavaScript 的迭代器语法: -->
<div>
<li v-for="item of items">
{{ item.message }}
</li>
</div>
</template>
v-for
来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用Object.values()
的返回值来决定。v-for
可以直接接受一个整数值。在这种用例中,会将该模板基于1...n
的取值范围重复多次。
-
- 注意此处
n
的初值是从1
开始而非0
- 注意此处
v-if
类似,你也可以在<template>
标签上使用v-for
来渲染一个包含多个元素的块。v-if
VSv-for
-
- 它们同时存在于一个节点上时,
v-if
比v-for
的优先级更高。这意味着v-if
的条件将无法访问到v-for
作用域内定义的变量别名; - 注意 : 同时使用
v-if
和v-for
是不推荐的,因为这样二者的优先级不明显。
- 它们同时存在于一个节点上时,
<script setup>
import { ref } from 'vue'
const myObject = ref({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
})
const items = ref([
{ message: 'Foo' },
{ message: 'Bar' }
])
const todos = ref([
{ name: 'Buy groceries', isComplete: false },
{ name: 'Finish work', isComplete: true },
{ name: 'Cook dinner', isComplete: false }
])
</script>
<template>
<!-- v-for 来遍历一个对象的所有属性。-->
<div>
<ul>
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
</div>
<!-- v-for 可以直接接受一个整数值。在这种用例中,会将该模板基于 1...n 的取值范围重复多次。
-->
<div>
<span v-for="n in 10">{{ n }}</span>
</div>
<!-- v-if 类似,你也可以在 <template> 标签上使用 v-for 来渲染一个包含多个元素的块。 -->
<div>
<ul>
<template v-for="item in items">
<li>{{ item.message }}</li>
</template>
</ul>
</div>
<!-- 它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名: -->
<div>
<ul>
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
</ul>
</div>
</template>
四、结尾
(一) 对于Vue2和Vue3的区别
个人对于Vue2和Vue3的区别的直观感觉是script标签中的处理的简化(也可以理解为JS的处理更具有一定的规则)
使用vue2实现
<script>
export default {
name: 'MyDemo01',
data() {
return {
name : "张三",
age : 20,
count : 12,
isVisible: true,
// 如果使用选项式API,将url定义在这里
url: "https://www.runoob.com",
items: [
{ id: 1, name: 'Vue 3' },
{ id: 2, name: 'JavaScript' },
{ id: 3, name: 'HTML' }
],
message: '数据的双向绑定',
nums: 0
}
},
methods: {
toggleVisibility() {
this.isVisible = !this.isVisible
},
sayHello(num) {
// 使用this.count来访问在data中定义的count
alert(`Hello ${this.name}, 你今年 ${this.age} 岁了,你说话 ${num} 次`)
},
increment() {
this.count += 1;
}
},
props: {
msg: String
}
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<!-- 使用this.count来访问在data中定义的count -->
<button @click="sayHello(count)">
{{count}}
</button>
</div>
<div>
<p v-if="isVisible">这段文本是可见的</p>
<button @click="toggleVisibility">切换可见性</button>
</div>
<div>
<a :href="url">跳转到 runoob.com</a>
</div>
<div>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
<div>
<input v-model="message" placeholder="输入一些文本" />
<p>你输入了:{{ message }}</p>
</div>
<div>
<button v-on:click="increment">点击我</button>
<p>点击次数:{{ nums }}</p>
</div>
</template>
使用vue3实现 两种
<script>
import { ref } from 'vue';
export default {
name: 'MyDemo02',
setup() {
const url = ref('https://www.runoob.com')
const msg = ref('Hello Vue 3!')
const count = ref(0)
const isVisible = ref(true)
const items = ref([
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'orange' },
])
const message = ref('')
const nums = ref(0)
const sayHello = (count) => {
console.log(`Hello ${count} times!`)
}
const toggleVisibility = () => {
isVisible.value = !isVisible.value
}
const increment = () => {
nums.value++
}
}
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<!-- 使用this.count来访问在data中定义的count -->
<button @click="sayHello(count)">
{{count}}
</button>
</div>
<div>
<p v-if="isVisible">这段文本是可见的</p>
<button @click="toggleVisibility">切换可见性</button>
</div>
<div>
<a :href="url">跳转到 runoob.com</a>
</div>
<div>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
<div>
<input v-model="message" placeholder="输入一些文本" />
<p>你输入了:{{ message }}</p>
</div>
<div>
<button v-on:click="increment">点击我</button>
<p>点击次数:{{ nums }}</p>
</div>
</template>
官方推荐的写法: 在script标签中加上setup
<script setup>
</script>
<script setup>
import { ref } from 'vue';
const url = ref('https://www.runoob.com')
const msg = ref('Hello Vue 3!')
const count = ref(0)
const isVisible = ref(true)
const items = ref([
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'orange' },
])
const message = ref('数据的双向绑定')
const nums = ref(0)
const sayHello = (count) => {
console.log(`Hello ${count} times!`)
}
const toggleVisibility = () => {
isVisible.value = !isVisible.value
}
const increment = () => {
nums.value++
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<!-- 使用this.count来访问在data中定义的count -->
<button @click="sayHello(count)">
{{count}}
<button v-on:click="increment">点击我</button>
</button>
</div>
<div>
<p v-if="isVisible">这段文本是可见的</p>
<button @click="toggleVisibility">切换可见性</button>
</div>
<div>
<a :href="url">跳转到 runoob.com</a>
</div>
<div>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
<div>
<input v-model="message" placeholder="输入一些文本" />
<p>你输入了:{{ message }}</p>
</div>
<div>
</div>
</template>