Vue-模板和选项式API

模板

确定模板有几种方式

  1. 以容器的innerHTML做为模板
  2. template选项做为模板
  3. render函数做为模板

优先级: 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>

模板语法

  • 插值语法: 在{{}}中书写的语法
    主要应用于文本节点
<div id="app">
  {{msg}}
</div>
  • 指令语法: 以v-开头的语法
    主要应用于属性节点
<a v-bind:href="url">百度</a>

插值语法细节

  1. 直接访问实例上的属性
<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>
  1. 使用表达式
    赋值表达式, 运算表达式, 条件表达式, 函数表达式
{{ number + 1 }}

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

{{ message.split('').reverse().join('') }}

<!-- 这是一个语句,而非表达式 -->
<!-- {{ var a = 1 }} -->

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

属性绑定指令

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

同样可以使用js表达式

<div id="app">
  <!-- 完整的写法 -->
  <a v-bind:href="url">百度</a>
  <!-- 简写(推荐) -->
  <a :href="url">百度</a>
</div>

const { createApp } = Vue
const vm = createApp({
  data() {
    return {
      url: 'http://www.baidu.com',
    }
  },
}).mount('#app')

事件绑定指令

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

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

<button @click="handleClick">点击</button>

methods: {
  handleClick() {
    alert('hello')
  }
}

事件修饰符:

  • .prevent: 阻止默认行为
  • .stop: 阻止冒泡
  • .once: 事件只会触发一次
<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>
<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>

按键修饰符:

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

系统功能键

  • .ctrl
  • .alt
  • .shift
<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>

双向绑定指令

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

  • 数据 的改变影响 视图
  • 视图 的改变影响 数据
<div id="app">
  <input type="text" v-model="username" />
  <p>{{username}}</p>
</div>

<script>
  const { createApp } = Vue
  const vm = createApp({
    data() {
      return {
        username: '',
      }
    },
  }).mount('#app')
</script>

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

  • texttextarea 使用 value属性和 input事件
  • checkboxradio 使用 checked属性和 change事件
  • select 字段将 value作为prop并将 change作为事件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vue.js Form Example</title>
  <script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    <form @submit.prevent="submitForm">
      <label for="username">用户名: </label>
      <input type="text" id="username" v-model="form.username"><br>
      
      <label for="password">密码: </label>
      <input type="password" id="password" v-model="form.password"><br>

      <label for="age">年龄: </label>
      <input type="text" id="age" v-model="form.age"><br>

      <label for="bio">个人签名: </label>
      <textarea v-model="form.bio"></textarea><br><br>

      <label>性别: </label>
      <input type="radio" id="male" value="male" v-model="form.sex">
      <label for="male">男</label>
      <input type="radio" id="female" value="female" v-model="form.sex">
      <label for="female">女</label><br>

      <label>爱好: </label>
      <input type="checkbox" id="eat" value="eat" v-model="form.hobbies">
      <label for="eat">吃</label>
      <input type="checkbox" id="sleep" value="sleep" v-model="form.hobbies">
      <label for="sleep">睡</label><br>

      <label for="city">城市: </label>
      <select v-model="form.city">
        <option value="nanjing">南京</option>
        <option value="wuxi">无锡</option>
        <option value="taizhou">泰州</option>
      </select><br>

      <input type="submit" value="提交">
    </form>
  </div>

  <script>
    const { createApp } = Vue;

    createApp({
      data() {
        return {
          form: {
            username: '',
            password: '',
            age: '',
            sex: '',
            hobbies: [],
            city: '',
            bio: ''
          }
        };
      },
      methods: {
        submitForm() {
          // 在这里处理表单提交,例如发送到服务器
          console.log(this.form);
        }
      }
    }).mount('#app');
  </script>
</body>
</html>

计算属性

计算属性用于对原始数据的再次加工

  • 有缓存
  • 调试方便
  <body>
    <!-- 需求: 实现字符串的反转
          eg. abc 转换成  cba 
    -->
    <div id="app">
      请输入一个字符串: <input type="text" v-model="msg" /> <br />
      <h3>反转后的字符串: {{reverseMsg}}</h3>
    </div>

    <script>
      const { createApp } = Vue
      const app = createApp({
        data: function() {
          return {msg: '',}
        },
        computed: {
          // 编写一个函数, 这个函数会被做为该计算属性的getter
          reverseMsg() {
            console.log('计算属性被执行了...')
            // 该函数的返回值, 做为访问计算属性的结果
            return this.msg.split('').reverse().join('')
          },
        },
      })
      const vm = app.mount('#app')
    </script>
  </body>

侦听器

通过watch配置项, 监听已经存在的属性的改变

watch: {
  // 监听data中的firstName属性
  firstName() {
   // 执行一系列的操作
  },
},
  <body>
    <div id="app">
      姓: <input type="text" v-model="lastName" /> <br />
      名: <input type="text" v-model="firstName" /> <br />
      全名(侦听器实现): {{fullName}}
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            lastName: '',
            firstName: '',
            fullName: '',
          }
        },
        watch: {
          // 侦听lastName的变化, 当lastName变化时, 执行该函数
          lastName() {
            this.fullName = this.lastName + this.firstName
          },
          // 侦听firstName的变化, 当firstName变化时, 执行该函数
          firstName() {
            this.fullName = this.lastName + this.firstName
          },
        },
      }).mount('#app')
    </script>
  </body>

在watch对应的回调函数中, 可以获取到新值旧值

  watch: {
    // 在watch对应的回调函数中, 可以得到新值和旧值
    // 对于简单数据类型, 可以获取新旧值
    // 对于引用数据类型, 不能获取旧值
    firstName(newValue, oldValue) {
      // 一对多: 监听某一个属性的改变, 做一系列的操作
      console.log('firstName改变了...')
      console.log('新的值:', newValue)
      console.log('旧的值:', oldValue)
    },
  },

访问响应式引用(对象, 数组, 函数)的旧值和新值:

const { createApp, ref } = Vue

const app = createApp({
  setup() {
    const someData = ref('initial value')

    // 使用 watch 函数
    watch(someData, (newVal, oldVal) => {
      console.log('新值:', newVal);
      console.log('旧值:', oldVal);
    })

    // ...其他逻辑

    return {
      someData
    }
  }
}).mount('#app')

计算属性 VS 侦听器

  1. 是否会在vm实例中挂载新属性?

    • computed会
    • watch不会
  2. 对应关系

    • computed是多对一, 可以同时监听多个值改变, 最终计算得到一个新的属性
    • watch是一对多, 主要监听一个属性的变化, 执行多种逻辑
  3. 能否获取新旧值?

    • computed不能
    • watch能

侦听器的高级用法

  • immediate: true => 立即执行watch的回调
  • flush: 'post' => 让watch的回调在DOM更新之后执行
  • 对watch值类型属性, 可以在watch的回调中获取到新旧值

条件渲染

当条件满足时, 渲染到页面, 主要指令: v-ifv-show

当flag为false时:

  • v-if: 不会创建元素
  • v-show: 创建元素, 但是display=none
  <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>

使用表达式:
<div v-if="flag == true">这是用v-if渲染的元素</div>

  <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() {
            this.flag = !this.flag
          },
        },
      }).mount('#app')
    </script>
  </body>

列表渲染

通过v-for遍历数组或者对象

  1. 获取元素

v-for="item in items"

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

  <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>
  1. 遍历对象

    • 只取值: v-for="value in obj"

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

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

样式绑定

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

  1. 绑定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>
  1. 绑定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>
<!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>
    <!-- 1.1 引入vue.js -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
    <!-- 2.2 实例静态页面(CSS部分) -->
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      li {
        list-style: none;
      }

      .menu-tab {
        display: flex;
        justify-content: space-between;
        margin: 50px auto;
        height: 40px;
        width: 700px;
        border: 1px solid #eee;
        border-bottom: 1px solid #e4393c;
        background-color: #f7f7f7;
        box-sizing: border-box;
      }
      .menu-tab .menu-item {
        flex: 1;
        display: flex;
        justify-content: center;
        align-items: center;

        font-size: 14px;
        color: #666;
        cursor: pointer;
      }
      .menu-tab .menu-item:hover {
        color: #e4393c;
      }
      .menu-tab .menu-item.current {
        color: #fff;
        background-color: #e4393c;
      }
    </style>
  </head>
  <body>
    <!-- 1.2 编写页面容器 -->
    <div id="app">
      <!-- 2.1 实例静态页面(HTML部分) -->
      <ul class="menu-tab">
        <li
          v-for="(item, index) in items"
          class="menu-item"
          :class="index == active ? 'current' : '' "
          @click="handleClick(index)"
        >
          {{item}}
        </li>
      </ul>
    </div>

    <!-- 1.3 创建vue实例对象 -->
    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            items: ['商品介绍', '规格与包装', '售后保障', '商品评价(2000+)', '手机社区'],
            active: 0,
          }
        },
        methods: {
          handleClick(i) {
            console.log(i)
            this.active = i
          },
        },
      }).mount('#app')
    </script>
  </body>
</html>
posted @ 2024-11-27 22:49  Khru  阅读(11)  评论(0编辑  收藏  举报