VUE|模板语法

1 模板

1) 什么是模板

什么是模板

由Vue解析的HTML字符串

Vue的主要工作

  1. 编译模板
  2. 挂载

2) 如何确定模板

确定模板有几种方式

  1. 没有指定template选项, 以容器的innerHTML做为模板
  2. 指定template选项, 以template选项做为模板
  3. 指定render选项, 以render函数做为模板

优先级: render函数 > template > 容器

render函数在后续有专门的章节介绍, 这里我们先介绍template选项

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 容器指定的innerHTML模板 < template指定模板 -->
    <div id="app">由容器指定的模板</div>

    <script>
      const { createApp, h } = Vue

      const app = createApp({
        template: `<h1>由template指定的模板</h1>`,
        render: () => h('div', '由render函数指定的模板'),
      })

      app.mount('#app')
    </script>
  </body>
</html>

2 模板语法

1) 什么是模板语法

什么是模板语法

在模板字符串中, 具有特殊意义的语法

2) 模板语法分类

模板语法分为

  • 插值语法: 在{{}}中书写的语法
  • 指令语法: 以v-开头的语法

插值语法

语法

{{}}中书写的语法

{{}}里书写js表达式,叫做 插值表达式

应用

主要应用于文本节点

示例

<div id="app">
  {{msg}}
</div>

指令语法

语法

v-开头的指令.

在属性值中书写js表达式, 叫做 指令表达式

应用

主要应用于属性节点

示例

<a v-bind:href="url">百度</a>

3 插值语法细节

1) 直接使用实例上的属性

{{}}内, 可以直接访问当前实例上的属性

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 直接使用instance实例上的属性 -->
      姓名: {{name}} <br />
      女友: {{gf.name}} <br />
      喜欢的书: {{books[0].name}} <br />
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            name: 'xiaoming',
            age: 20,
            gf: {
              name: 'xiaomei',
              age: 18,
            },
            books: [{ name: 'Vue' }, { name: 'React' }],
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

2) 使用表达式

{{}}内, 可以书写JS表达式, 但是不能书写语句

示例

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}

<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}

4 属性绑定指令

如果要将某一个属性的值和data中的状态绑定, 需要使用绑定指令

属性绑定

一个元素 的一个属性值和data中的一个变量绑定

<!-- 完整的写法 -->
<a v-bind:href="url">百度</a>
<!-- 简写(推荐) -->
<a :href="xzd">新中地</a>

vue官方更推荐简写的方式

img
const vm = createApp({
  data() {
    return {
      url: 'http://www.baidu.com',
    }
  },
}).mount('#app')
  • 将a标签href属性和data中的url变量绑定
  • 相当于<a href="http://baidu.com"></a>

特别说明

在指令后面的引号中, 可以使用类似于{{}}中一样的语法.

  • 可以使用 js表达式
  • 直接访问vm实例上的属性和方法
  • 不能js语句

5 事件绑定指令

1) 什么是事件绑定

将JS事件和对应的处理函数绑定

2) 语法

v-on:事件名="函数名"
// 简写
@事件名="函数名"

示例

给一个按钮绑定点击事件

<button @click="handleClick">点击</button>
methods: {
  handleClick() {
    alert('hello')
  }
}
  • methods中定义的函数会挂载到vm实例对象上
  • methods中定义的普通函数内部this指向vm
  • methods中, 不推荐使用箭头函数

3) 事件修饰符

在绑定事件的时候, 可以添加事件修饰符, 常用的事件修饰符如下:

  • .prevent: 阻止默认行为
  • .stop: 阻止冒泡
  • .once: 事件只会触发一次

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <a @click.prevent="" href="http://www.baidu.com">baidu</a>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({}).mount('#app')

      // 原生js方式
      // document.querySelector('a').addEventListener('click', function (e) {
      //   // 调用事件对象的preventDefault方法, 阻止默认行为
      //   // 对于a元素而言, 默认行为就是跳转页面
      //   e.preventDefault()
      // })
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <ul @click="handleUl">
        <!-- 
          1. `.stop` 阻止事件向上冒泡
          2. 事件修饰符可以连用
         -->
        <li @click.stop="handleLi">1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        methods: {
          handleUl() {
            console.log('ul被点击了...')
          },
          handleLi() {
            console.log('li被点击了...')
          },
        },
      }).mount('#app')
    </script>
  </body>
</html>

4) 按键修饰符

主要针对 键盘事件

常用按键修饰符

  • .enter: 回车键
  • .up: 上
  • .down: 下
  • .left: 左
  • .right: 右

系统功能键

  • .ctrl
  • .alt
  • .shift

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <input type="text" @keyup.enter="submit" />
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        methods: {
          submit() {
            console.log('输入了数据, 按下回车键')
          },
        },
      }).mount('#app')
    </script>
  </body>
</html>

指令语法的完整示意图:

img

6 双向绑定指令

1) 什么是双向绑定

双向绑定指令 通常应用于 表单元素

  • 数据的改变 会 影响视图
  • 视图的改变 会 影响数据

2) 原理

表单元素, 如input``select``textarea是具有交互性的: 用户可以操作

当用户操作时, 会触发特定的事件如: input事件change事件

在事件处理的回调中, 可以对数据进行修改

示例

双向绑定原理

  1. 通过绑定input框的value属性和data中的一个变量, 实现了数据->视图的绑定
  2. 监听input框的input事件, 当事件触发时, 更新data中的变量, 实现了视图->数据的绑定

3) 简写指令

v-model: 双向绑定指令

将表单元素的 值 和 data中定义的状态绑定

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <input type="text" v-model="username" />
    </div>

    <script>
      const { createApp } = Vue

      createApp({
        data() {
          return {
            username: '',
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

** 注意**

不同的表单元素, 绑定的属性和监听的事件不同

img

实例:实现一个Form表单

image-20230713102135249
<template>
  <div class="form">
    <div class="form-container">
      <el-form>
        <el-form-item label="用户名">
          <el-input v-model="ruleForm.name" />
        </el-form-item>
        <el-form-item label="密码">
          <el-input v-model="ruleForm.password" />
        </el-form-item>
        <el-form-item label="出生日期">
          <el-date-picker
            v-model="ruleForm.date"
            type="date"
            placeholder="Pick a date"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="性别" prop="resource">
          <el-radio-group v-model="ruleForm.gender">
            <el-radio label="男" />
            <el-radio label="女" />
          </el-radio-group>
        </el-form-item>
        <el-form-item label="爱好">
          <el-checkbox-group v-model="ruleForm.type">
            <el-checkbox label="吃饭" name="type" />
            <el-checkbox label="睡觉" name="type" />
            <el-checkbox label="听歌" name="type" />
            <el-checkbox label="打游戏" name="type" />
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="所在区域">
          <el-select v-model="ruleForm.region" p>
            <el-option label="上海" value="上海" />
            <el-option label="武汉" value="武汉" />
          </el-select>
        </el-form-item>
        <el-form-item label="个人签名">
          <el-input v-model="ruleForm.desc" type="textarea" />
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      ruleForm: {
        name: '',
        password: '',
        region: '',
        date: '',
        type: [],
        desc: '',
      },
    }
  },
}
</script>

<style scoped>
.form {
  all: initial;
}

.form-container {
  margin-left: 30%;
  margin-right: 30%;
}
</style>

7 条件渲染

1) 什么是条件渲染

当条件满足时, 渲染到页面

主要指令: v-ifv-show

2) 示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 当一个内容有复杂逻辑时, 使用v-if -->
      <div v-if="flag">我是通过v-if控制的</div>
      <div v-else>我是通过v-else控制的</div>
      <!-- 当一个内容需要切换显示时, 使用v-show -->
      <div v-show="flag">我是通过v-show控制的</div>
      <div v-show="!flag">我是通过v-show控制的</div>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            flag: true,
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

  • 当flag为true时, 两个元素都可以显示

  • 当flag为false时, 两个元素都不显示, 区别

    • v-if: 不会创建元素
    • v-show: 创建元素, 但是display=none

3) 使用表达式

在指令表达式中, 除了使用变量外, 也可以使用表达式

<div v-if="flag == true">这是用v-if渲染的元素</div>

案例

需求

通过按钮控制元素的显示/隐藏

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click="flag = !flag">切换显示/隐藏</button>
      <div v-show="flag">div显示的内容</div>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            flag: true,
          }
        },
        methods: {
          toggle() {
            // if (this.flag == true) {
            //   this.flag = false
            // } else {
            //   this.flag = true
            // }
            this.flag = !this.flag
          },
        },
      }).mount('#app')
    </script>
  </body>
</html>
  • 绑定按钮的点击事件

    • 当flag==true时点击, flag先取反, 再保存, 此时flag为false
    • 当flag==false时点击, flag先取反, 再保存, 此时flag为true

8 列表渲染

1) 什么是列表渲染

列表渲染也称"循环渲染", 通过v-for遍历数组或者对象

2) 遍历数组

获取元素

如果只希望得到每个数组元素的值, 不需要得到下标

语法

v-for="item in items"

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <ul>
        <!-- 在指令表达式中, in前面的参数表示数组元素 -->
        <!-- <li v-for="item in items">{{item}}</li> -->
        <!-- (元素, 下标) in 数组 -->
        <li v-for="(value, key) in items">{{key}} -- {{value}}</li>
      </ul>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            items: ['test1', 'test2', 'test3'],
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

获取元素和下标

如果只希望得到每个数组元素的值和下标

语法

v-for="(item, index) in items"

3) 遍历对象

  • 只取值: v-for="value in obj"
  • 取键和值: v-for="(value, key) in obj"
  • 取键和值和索引: v-for="(value, key, index) in obj"

9 样式绑定

1) 什么是样式绑定

通过绑定class属性 或者 style属性 修改样式

2) 绑定class属性

常见有两种语法

  • 数组写法
  • 对象写法

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
    <style>
      .red {
        color: red;
      }
      .blue {
        color: skyblue;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 原生的写法 -->
      <span class="red blue">一段文字</span>

      <!-- 绑定class属性 -- 对象的写法 -->
      <span :class="obj">对象的写法</span>

      <!-- 绑定class属性 -- 数组的写法(推荐) -->
      <span :class="arr">数组的写法</span>

      <span :class="foo">绑定一个变量</span>

      <span :class="flag ? 'red' : 'blue'">使用表达式</span>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            obj: {
              red: true,
              blue: true,
            },
            arr: ['red', 'blue'],
            foo: 'red',
            flag: true,
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

3) 绑定style属性

对象写法

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- 原生的写法 -->
      <div style="font-size: 32px; color: red">原生的写法</div>
      <!-- 绑定style属性 -- 对象写法 -->
      <div :style="obj">对象的写法</div>
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            obj: {
              // 'font-size': '32px',
              fontSize: '32px',
              color: 'red',
            },
          }
        },
      }).mount('#app')
    </script>
  </body>
</html>

4) 作业

需求

实现tab栏切换

img

参考答案

工程化后的结果

<template>
  <body>
    <div class="body">
      <nav>
        <ul class="menu-tab">
          <li
            v-for="(item, index) in items"
            :key="index"
            class="menu-item"
            :class="index == active ? 'current' : ''"
            @click="handleClick(index)"
          >
            <a>{{ item.name }}</a>
          </li>
        </ul>
      </nav>
      <div class="panel">
        <div class="view-container">
          <router-view />
        </div>
      </div>
    </div>
  </body>
</template>

<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
if (router.currentRoute.value.path !== '/') router.replace('/')

const items = ref([
  { name: 'Home', route: '/' },
  { name: 'About', route: '/about' },
  { name: 'Todo', route: '/todo' },
  { name: 'Form', route: '/form' },
])

const active = ref(0)

function handleClick(i) {
  this.active = i
  router.replace(this.items[i].route)
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

li {
  list-style: none;
}

.menu-tab {
  display: flex;
  justify-content: space-between;
  margin: 0 auto;
  height: 60px;
  border: 1px solid #fff;
  background-color: #fff;
  box-sizing: border-box;
}

.menu-tab .menu-item {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: bold;
  color: #666;
  background-color: #f9f9f9;
  cursor: pointer;
  border-bottom: 2px solid var(--el-border-color);
}
.menu-tab .menu-item:hover {
  color: #2c3e50;
}
.menu-tab .menu-item.current {
  color: #2c3e50;
  background-color: #fff;
  border-left: 2px solid var(--el-border-color);
  border-right: 2px solid var(--el-border-color);
  border-top: 2px solid var(--el-border-color);
  border-bottom: none;
}

.body {
  width: 90%;
  margin: 0 auto;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: #fff;
}

nav a {
  font-weight: bold;
}

.panel {
  flex: 1;
  border-left: 2px solid var(--el-border-color);
  border-right: 2px solid var(--el-border-color);
  border-bottom: 2px solid var(--el-border-color);
  margin-bottom: 20px;
}

.view-container {
  padding: 40px;
}

body {
  background-color: #f9f9f9;
}
</style>
posted @ 2023-07-21 11:31  Weltㅤ  阅读(14)  评论(0编辑  收藏  举报