Skip to content
001

01_HBuilder 编辑器 + 内置组件 + Vue3 基础语法

01_HBuilder 编辑器

01_HBuilder 创建 Vue3 项目

01_HBuilder 下载安装

HBuilder 官网

解压安装包

  1. 首先, 选中下载的 zip 包, 点击右键菜单, 点击解压到当前文件夹
  2. 进入解压后的文件夹, 找到 HBuilderX.exe, 直接点击打开

02_HBuilder 创建 Vue3 项目

  • 文件 ==> 新建 ==> 项目 ==> uni-app ==> 项目名称 uniappV3Demo1 ==> 位置 C:/Users/18123/Desktop ==> 选择模板 默认模板 ==> Vue 版本选择 3 ==> 创建
002

02_目录结构

bash
┌─pages                 业务页面文件存放的目录
  ├─index
  └─index.vue       index 页面
  └─list
     └─list.vue        list 页面
├─static                存放应用引用的本地静态资源 (如图片、视频等) 的目录
├─App.vue               应用配置, 用来配置 App 全局样式以及监听 应用生命周期
├─main.js               Vue 初始化入口文件
├─pages.json            配置页面路由、导航条、选项卡等页面类信息
└─uni.scss              内置的常用样式变量

02_运行到浏览器 + 运行到小程序

01_运行到 Chrome 浏览器

浏览器运行配置

  • 工具 ==> 设置 ==> 运行配置 ==> 浏览器运行配置 ==> Chrome 浏览器安装路径 C:/Program Files/Google/Chrome/Application/chrome.exe
003

运行到 Chrome 浏览器

  • 项目 ==> 运行 ==> 运行到浏览器 ==> Chrome
004

02_运行到微信开发者工具

微信开发者工具官网

微信开发者工具 打开 服务端口

  • 设置 ==> 安全 ==> 打开 服务端口
005

小程序运行配置

  • 工具 ==> 设置 ==> 运行配置 ==> 小程序运行配置 ==> 微信开发者工具路径 D:/develop/wechar_devtools/start
006

运行到小程序模拟器

  • 运行 ==> 运行到小程序模拟器 ==> 微信开发者工具
007

03_新建 uniapp 页面

01_页面的结构

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
	<!-- 1.多个根节点 -->
	<view class="layout">
		<div class="box1">box1</div>
		<div class="box2">box2</div>
	</view>
</template>

<!-- 2.单文件语法糖 -->
<script setup>

</script>

<!-- 3.scss 预处理 -->
<style lang="scss">
.layout {
	border: 1px solid red;
	.box1 {
		border: 1px solid green;
	}
	.box2 {
		border: 1px solid blue;
	}
}
</style>

02_新建 uniapp 页面

  • pages ==> 新建页面 ==> 请输入页面名称 demo1 ==> 勾选 创建同名目录 ==> 选择模板 空页面模板(组合式) ==> 勾选 在 pages.json 中注册 ==> "navigationBarTitleText": "demo" ==> 创建
008

03_pages 设置页面路径及窗口表现

json
// /uniappV3Demo1/pages.json
{
	// 1.设置页面路径及窗口表现
	"pages": [ // pages 数组中第一项表示应用启动页, 参考: https://uniapp.dcloud.io/collocation/pages
		{
			// 2.配置页面路径
			"path": "pages/demo1/demo1",
			// 3.配置页面窗口表现, 配置项
			"style": {
				// 4.导航栏标题文字内容
				"navigationBarTitleText": "demo"
			}
		},
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "uni-app"
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	"uniIdRouter": {}
}

04_自定义模板

01_自定义模板

  • pages ==> 新建页面 ==> 自定义模板 ==> 在打开的目录中 创建 Vue3Setupo.vue 文件, 并填写下面内容
html
<template>
  <view class="">

  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

02_内置组件

01_view 视图容器 + text 文本

01_view 视图容器

它类似于传统 html 中的 div, 用于包裹各种元素内容

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
	<!-- 1.hover-class 指定按下去的样式类 -->
	<view class="box" hover-class="boxHover">
		<!-- 2.hover-stop-propagation 指定是否阻止本节点的祖先节点出现点击状态 -->
		<view class="inner" hover-class="innerHover" hover-stop-propagation>内部元素</view>
	</view>
</template>

<script setup>

</script>

<style lang="scss" scoped>
.box{
    width: 200px;
    height: 200px;
    background: #ccc;
}
.boxHover{
    background: orange;
    width: 300px;
}
.inner{
    width: 150px;
    height: 150px;
    background: green;
}
.innerHover{
    background: greenyellow;
}
</style>

02_text 文本

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.selectable 文本是否可选; space 显示连续空格, emsp 中文字符空格一半大小 -->
  <text selectable space="emsp">text 文    本标签</text>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

02_scroll-view 可滚动视图区域

01_scroll-view 可滚动视图区域

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.scroll-x 允许横向滚动 -->
  <scroll-view scroll-x class="scrollView">
    <view class="box">scroll 子元素</view>
    <view class="box">scroll 子元素</view>
    <view class="box">scroll 子元素</view>
    <view class="box">scroll 子元素</view>
    <view class="box">scroll 子元素</view>
    <view class="box">scroll 子元素</view>
  </scroll-view>
</template>

<script setup>

</script>

<style lang="scss" scoped>
.scrollView {
  width: 80%;
  height: 220px;
  border: 1px solid red;
  white-space: nowrap;

  .box {
    width: 100px;
    height: 100px;
    background: green;
    display: inline-block;
    margin: 5px;
  }
}
</style>

03_swiper 滑块视图容器

01_swiper 滑块视图容器

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- indicator-dots 显示面板指示点; indicator-color 指示点颜色; indicator-active-color 当前选中的指示点颜色 -->
    <!-- circular 采用衔接滑动; autoplay 自动切换; interval 自动切换时间间隔; vertical 滑动方向为纵向 -->
    <swiper indicator-dots indicator-color="rgba(255,255,255,0.3)" indicator-active-color="#ffffff" 
    circular autoplay interval="2000" vertical>
      <swiper-item>11111</swiper-item>
      <swiper-item>2222</swiper-item>
      <swiper-item>333</swiper-item>
      <swiper-item>44444</swiper-item>
    </swiper>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped>
swiper {
  width: 100vw;
  height: 200px;
  border: 1px solid green;

  swiper-item {
    width: 100%;
    height: 100%;
    background: pink;
  }

  swiper-item:nth-child(2n) {
    background: orange;
  }
}
</style>

04_image 图片 + swiper 滑块视图容器

01_image 图片

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- 1.未设置宽高时, 默认宽度 320px, 高度 240px; mode 图片裁剪, 缩放的模式 -->
    <image src="../../static/logo.png" mode=""></image>

    <!-- 2.scaleToFill(默认) 不保持纵横比缩放图片, 使图片的宽高完全拉伸至填满 image 元素 -->
    <image src="../../static/pic3.webp" mode="" class="pic2"></image>

    <!-- 3.aspectFit 保持纵横比缩放图片, 使图片的长边能完全显示出来; 也就是说, 可以完整地将图片显示出来 -->
    <image src="../../static/pic3.webp" mode="aspectFit" class="pic2"></image>

    <!-- 4.aspectFill 保持纵横比缩放图片, 只保证图片的短边能完全显示出来; 也就是说, 图片通常只在水平或垂直方向是完整的, 另一个方向将会发生截取 -->
    <image src="../../static/pic3.webp" mode="aspectFill" class="pic2"></image>

    <!-- 5.widthFix 宽度不变, 高度自动变化, 保持原图宽高比不变 -->
    <image src="../../static/pic4.jpg" mode="widthFix" class="pic3"></image>

    <!-- 6.heightFix 高度不变, 宽度自动变化, 保持原图宽高比不变 -->
    <image src="../../static/pic4.jpg" mode="heightFix" class="pic4"></image>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped>
.pic2 {
  width: 200px;
  height: 200px;
}

.pic3 {
  width: 300px;
}

.pic4 {
  height: 300px;
}
</style>

02_image 图片 + swiper 滑块视图容器

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- indicator-dots 显示面板指示点; indicator-color 指示点颜色; indicator-active-color 当前选中的指示点颜色 -->
    <!-- circular 采用衔接滑动; autoplay 自动切换; interval 自动切换时间间隔; vertical 滑动方向为纵向 -->
    <swiper indicator-dots indicator-color="rgba(255,255,255,0.3)" indicator-active-color="#ffffff"
    circular autoplay interval="2000" vertical>
      <!-- aspectFill 保持纵横比缩放图片, 只保证图片的短边能完全显示出来; 也就是说, 图片通常只在水平或垂直方向是完整的, 另一个方向将会发生截取 -->
      <swiper-item><image src="../../static/pic1.png" mode="aspectFill"></image></swiper-item>
      <swiper-item><image src="../../static/pic2.png" mode="aspectFill"></image></swiper-item>
      <swiper-item><image src="../../static/pic3.webp" mode="aspectFill"></image></swiper-item>
      <swiper-item><image src="../../static/pic4.jpg" mode="aspectFill"></image></swiper-item>
    </swiper>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped>
swiper {
  width: 100vw;
  height: 200px;
  border: 1px solid green;

  swiper-item {
    width: 100%;
    height: 100%;
    background: pink;

    image {
      width: 100%;
      height: 100%;
    }
  }

  swiper-item:nth-child(2n) {
    background: orange;
  }
}
</style>

05_navigator 页面跳转

01_navigator 页面跳转

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- 1.open-type 跳转方式; navigate(默认) 保留当前页面, 跳转到应用内的某个页面 -->
    <navigator url="/pages/demo1/demo1" open-type="navigate">
      跳转到 demo1
    </navigator>

    <!-- 2.reLaunch 关闭所有页面, 打开到应用内的某个页面 -->
    <navigator url="/pages/demo1/demo1" open-type="reLaunch">
      跳转到 demo1
    </navigator>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

06_button 按钮 + input 单行输入框

01_button 按钮

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- 1.size 按钮的大小; default 默认大小; type 按钮的样式类型; primary 绿色, 蓝色, 浅蓝色; loading 名称前是否带 loading 图标 -->
    <button size="default" type="primary" loading>按钮</button>

    <!-- 2.mini 小尺寸; warn 红色; disabled 是否禁用 -->
    <button size="mini" type="warn" disabled>按钮</button>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

02_input 单行输入框

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- type input 的类型; text 文本输入键盘; placeholder 输入框为空时占位符; placeholder-style 指定 placeholder 的样式 -->
    <!-- confirm-type: 设置键盘右下角按钮的文字, 仅在 type="text" 时生效; search 右下角按钮为 "搜索" -->
    <input type="text" placeholder="请输入搜索内容" placeholder-style="color: orange" confirm-type="search" />

    <!-- tel 电话输入键盘; maxlength 最大输入长度, 设置为 -1 的时候不限制最大长度 -->
    <input type="tel" placeholder="请输入搜索内容" maxlength="11"  />
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

03_Vue3 基础语法

01_ 插值语法

01_ 插值语法

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.文本插值 -->
  <view>
    姓名: 张三
  </view>

  <!-- 2.JavaScript 表达式 -->
  <view>
    {{ 2 + 3 }}
  </view>
  <view>
    {{ a + 5 }}
  </view>
  <view>{{ Date.now() }}</view>
  <view>{{ Math.random() }}</view>
  <view>{{ 1 < 2 ? '张三' : "李四" }}</view>

  <!-- 3.调用函数 -->
  <view>{{ fn() }}</view>
</template>

<script setup>
const a = 6;
function fn() {
  return "Vue3 学习"
}
</script>

<style lang="scss" scoped></style>

02_ref() 响应式数据

01_ref() 响应式数据

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>{{ num1 }}</view>

  <!-- 3.在模板中使用 ref 时, 我们不需要附加 .value  -->
  <view>{{ num2 }}</view>
  <view>{{ test }}</view>
  <view>{{ arr[2] }}</view>
  <view>{{ obj }}</view>
</template>

<script setup>
import { ref } from "vue";

// 1.num1 变量并不是响应式的
let num1 = 6
// setInterval(()=>{
//  num1++;
//  console.log(num1);
// },1000)

// 2.使用 ref 函数创建响应式变量, 返回一个包含 value 属性的对象
let num2 = ref(10);
setInterval(() => {
  num2.value++;
  console.log(num2.value);
}, 1000)

// 4.字符串, 数组, 对象... 都可以使用 ref 函数创建响应式变量
let test = ref("咸虾米");

let arr = ref([1, 2, 3, 4]);

let obj = ref({ name: "王五", age: 18 })
obj.value.name = "李四"
</script>

<style lang="scss" scoped></style>

03_v-bind 属性绑定

01_v-bind 属性绑定

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <!-- 1.想要响应式地绑定一个属性, 应该使用 v-bind -->
    <image v-bind:src="picurl" mode=""></image>

    <!-- 2.v-bind 简写语法 :  -->
    <image v-bind:src="picurl" mode=""></image>
  </view>
</template>

<script setup>
import { ref } from "vue";
const picurl = ref("../../static/pic1.png");
</script>

<style lang="scss" scoped></style>

02_图片切换案例

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view>
    <image v-bind:src="picurl" mode=""></image>
  </view>
</template>

<script setup>
import { ref } from "vue";

// 1.定义图片数组
const arrs = ref([
  "../../static/pic1.png",
  "../../static/pic2.png",
  "../../static/pic3.webp",
  "../../static/pic4.jpg"
]);

// 2.默认显示第一张图片
const picurl = ref("../../static/pic1.png");

// 3.定时器, 每 1 秒切换一次图片
let i= 0;
setInterval(()=>{
  i++;
  picurl.value = arrs.value[i%4];
}, 1000);
</script>

<style lang="scss" scoped></style>

04_class 绑定类 + style 绑定样式

01_class 绑定类

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.传递一个对象来动态切换 class -->
  <view class="box" :class="{ active: isActive }">
    v-bind指令
  </view>

  <!-- 2.使用三元表达式, 来动态切换 class -->
  <view class="box" :class="isActive ? 'active' : ''"></view>
</template>

<script setup>
import { ref } from "vue";

const isActive = ref(true);

// 3.使用定时器来动态切换 class
setInterval(() => {
  isActive.value = !isActive.value;
}, 1000)
</script>

<style lang="scss" scoped>
.box {
  width: 200px;
  height: 200px;
  background: orange;
  font-size: 20px;
}

.active {
  background: green;
  color: #fff;
}
</style>

02_style 绑定样式

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.绑定内联样式 -->
  <view class="box" :style="{width: '300px',height:260+'px',fontSize: size +'px'}">内联样式</view>
</template>

<script setup>
import { ref } from "vue";

const size = ref(30);

// 2.修改 size 的值
let i = 0;
setInterval(() => {
  i++;
  size.value += i
}, 1000)
</script>

<style lang="scss" scoped>
.box {
  width: 200px;
  height: 200px;
  background: orange;
  font-size: 20px;
}
</style>

05_方法事件 + 内置组件事件

01_方法事件

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.使用 v-on 指令(简写为 @) 来监听 DOM 事件 -->
  <view class="box" @click="onClick">
    {{ num }}
  </view>
</template>

<script setup>
import { ref } from "vue";
const num = ref(1);

// 2.定义一个方法
function onClick() {
  num.value++;
}
</script>

<style lang="scss" scoped>
.box {
  width: 200px;
  height: 200px;
  background: orange;
}
</style>

02_更改颜色案例

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.点击事件, 点击时调用 onClick 方法 -->
  <view class="box" @click="onClick" :style="{ background: color }"></view>
</template>

<script setup>
import { ref } from "vue";
const color = ref('#fc359a');

// 2.点击事件处理函数, 用于生成随机颜色并赋值给 color 变量
function onClick() {
  color.value = "#" + String(Math.random()).substring(3, 9)
}
</script>

<style lang="scss" scoped>
.box {
  width: 200px;
  height: 200px;
  background: orange;
}
</style>

03_内置组件事件

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.switch 开关选择器; checked 改变时触发 change 事件 -->
  <switch @change="onChange" />
  <button type="primary" :loading="isLoading">普通按钮</button>
</template>

<script setup>
import { ref } from "vue";

const isLoading = ref(false);

// 2.e.detail.value 为开关的选中状态
function onChange(e) {
  isLoading.value = e.detail.value
}
</script>

<style lang="scss" scoped></style>

06_v-if 和 v-show 条件渲染

01_v-if 条件渲染

条件性地渲染一块内容

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="">
    <!-- 1.v-if v-else -->
    <view v-if="shop">京东</view>
    <view v-else>淘宝网</view>

    <!-- 2.v-if v-else-if v-else -->
    <view v-if="day === 1">星期1</view>
    <view v-else-if="day === 2">星期2</view>
    <view v-else-if="day === 3">星期3</view>
    <view v-else-if="day === 4">星期4</view>
    <view v-else-if="day === 5">星期5</view>
    <view v-else-if="day === 6">星期6</view>
    <view v-else-if="day === 7">周末</view>
    <view v-else>格式错误</view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const shop = ref(true);
const day = ref(3);
</script>

<style lang="scss" scoped></style>

02_v-if 对比 v-show

v-show 条件渲染

  • 另一个按条件显示一个元素
html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.v-if 如果在初次渲染时条件值为 false, 则不会做任何事; 条件区块只有当条件首次变为 true 时才被渲染 -->
  <view class="box1" v-if="false">
    <image src="../../static/pic1.png" mode=""></image>
  </view>

  <!-- 2.v-show 元素无论初始条件如何, 始终会被渲染, 只有 CSS display 属性会被切换 -->
  <view class="box2" v-show="false">
    <image src="../../static/pic2.png" mode=""></image>
  </view>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

03_template 包装器

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <!-- 1.template 不可见的包装器元素, 最后渲染的结果并不会包含这个 template 元素 -->
  <template v-if="true">
    <image src="../../static/logo.png" mode=""></image>
    <view>logo</view>
  </template>

  <template v-else>
    <image src="../../static/pic4.jpg" mode=""></image>
    <view>pic4</view>
  </template>
</template>

<script setup>

</script>

<style lang="scss" scoped></style>

07_v-for 列表渲染

01_v-for 列表渲染

渲染一个列表

  • 提供一个唯一的 key, 从而重用和重新排序现有的元素
html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="out">
    <!-- 1.使用 v-for 指令循环渲染 10 个 box 模块; 提供一个唯一的 key, 从而重用和重新排序现有的元素 -->
    <view class="box" v-for="(item, index) in 10" :key="index">box 模块 - {{ index + 1 }}</view>

    <!-- 2.遍历 nba 数组, 将每个元素赋值给 item -->
    <view v-for="item in nba" :key="item.id">
      球星: {{ item.name }} - 球衣: {{ item.num }}
    </view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const nba = ref([
  { id: 1, name: "乔丹", num: 23 },
  { id: 2, name: "詹姆斯", num: 6 },
  { id: 3, name: "科比", num: 24 },
])
</script>

<style lang="scss" scoped></style>

08_购物车案例

01_购物车案例

  • 提供一个唯一的 key, 以便它可以跟踪每个节点的标识, 从而重用和重新排序现有的元素
html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="out">
    <!-- 1.提供一个唯一的 key, 以便它可以跟踪每个节点的标识, 从而重用和重新排序现有的元素 -->
    <view class="item" v-for="(item, index) in goods" :key="item.id">
      <checkbox></checkbox>
      <text text class=" title">{{ item.name }}</text>
      <text class="del" @click="remove(index)">删除</text>
    </view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const goods = ref([
  { id: 11, name: "小米" },
  { id: 22, name: "华为" },
  { id: 33, name: "oppo" },
  { id: 44, name: "苹果" }
])

function remove(index) {
  goods.value.splice(index, 1)
}
</script>

<style lang="scss" scoped>
.out {
  padding: 10px;

  .item {
    padding: 10px 0;

    .del {
      color: #c00;
      margin-left: 30px;
    }
  }
}
</style>

09_表单 @focus 和 @blur 事件

01_表单 @focus 和 @blur 事件

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="out">
    <!-- 1.@focus 输入框聚焦时触发; @blur 输入框失去焦点时触发 -->
    <input type="text" :value="iptValue" @focus="onFocus" @blur="onBlur" />

    <!-- 2.设置图片的 class 属性, 当 isActive 为 true 时, 添加 active 类 -->
    <image src="../../static/chicken.gif" mode="" class="pic" :class="isActive?'active':''" />
  </view>
</template>

<script setup>
import { ref } from "vue";
const iptValue = ref("");
const isActive = ref(false);

// 3.@输入框聚焦时触发
function onFocus(e) {
  isActive.value = true;
}

// 4.输入框失去焦点时触发
function onBlur(e) {
  isActive.value = false;
}
</script>

<style lang="scss" scoped>
.out {
  padding: 0 20px;
  margin-top: 40px;
  position: relative;

  input {
    border: 1px solid #ccc;
    height: 40px;
    position: relative;
    z-index: 2;
    background: #fff;
    padding: 0 10px;
  }

  .pic {
    width: 24px;
    height: 24px;
    z-index: 1;
    position: absolute;
    top: 0px;
    left: calc(50% - 12px);
    transition: top 0.3s;
  }

  .pic.active {
    top: -24px;
  }
}
</style>

10_v-model 双向绑定

01_@input 获取输入值

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="out">
    <!-- 1.@input 当键盘输入时, 触发 input 事件 -->
    <input type="text" :value="iptValue" @input="onInput" />

    <view>预览: {{ iptValue }}</view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const iptValue = ref("");

// 2.当键盘输入时, 触发 input 事件
function onInput(e) {
  iptValue.value = e.detail.value;
}
</script>

<style lang="scss" scoped></style>

02_v-model 双向绑定

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="out">
    <!-- 1.v-model 双向绑定 -->
    <input type="text" :value="iptValue" v-model="iptValue" />

    <view>预览: {{ iptValue }}</view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const iptValue = ref("");
</script>

<style lang="scss" scoped></style>

04_综合案例

01_热梗案例

01_热梗案例

html
<!-- /uniappV3Demo1/pages/index/index.vue -->

<template>
  <view class="title">近期热梗</view>

  <view class="out">
    <view class="list">
      <!-- 1.v-for 列表渲染 -->
      <view v-for="(item, index) in lists" :key="item.id" class="row">
        <view class="text">{{ index + 1 }}. {{ item.title }}</view>
        <!-- 2.onclose 点击删除 -->
        <view @click="onclose(index)" class="close"><icon type="clear" size="26"/></view>
      </view>
    </view>
    <view class="count">共 {{ lists.length }} 条梗</view>

    <view class="comment">
      <!-- 4.v-model 双向绑定; @confirm 回车发布热梗 -->
      <input v-model="iptValut" @confirm="onSubmit" type="text" placeholder="请输入热梗..." />
      <!-- 5.disabled 禁用按钮; onSubmit 发布热梗 -->
      <button :disabled="iptValut.length < 3" @click="onSubmit" size="mini" type="primary">发布</button>
    </view>
  </view>
</template>

<script setup>
import { ref } from "vue";
const lists = ref([
  { id: 111, title: "刚满 18 岁" },
  { id: 222, title: "我不吃牛肉" },
  { id: 333, title: "遥遥领先" },
  { id: 444, title: "哪里贵了" }
])

const iptValut = ref("")

// 3.点击删除
const onclose = function (index){
  lists.value.splice(index, 1)
}

// 6.发布热梗
const onSubmit = function (){
  lists.value.push({ id: Date.now(), title: iptValut.value })
  iptValut.value = ""
}
</script>

<style lang="scss" scoped>
.title {
  font-size: 26px;
  text-align: center;
  color: #3c3c3c;
  padding: 30px 0 15px;
}

.out {
  width: 90vw;
  margin: 15px auto;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
  border-radius: 5px;
  padding: 15px;
  box-sizing: border-box;

  .list {
    .row {
      padding: 10px 0;
      border-bottom: 1px solid #e8e8e8;
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 18px;
      color: #333;

      .text {
        padding-right: 5px;
        box-sizing: border-box;
      }
    }
  }

  .count {
    padding: 10px 0;
    font-size: 15px;
    color: #888;
    text-align: center;
  }

  .comment {
    display: flex;
    margin-top: 10px;

    input {
      flex: 4;
      background: #f4f4f4;
      margin-right: 5px;
      height: 100%;
      height: 32px;
      border-radius: 4px;
      padding: 0 10px;
      color: #333;
    }

    button {
      flex: 1;
    }
  }
}
</style>

更新时间: