02.Vue3 基础语法:常用语法、列表渲染、事件绑定和表单双向绑定

Vue3 基础语法

1. Vue 中应用和组件的基础概念

createApp

createApp 表示创建一个 Vue 应用,存储在 app 变量中。

传入的参数表示,这个应用最外层的组件,应该如何展示。

const app = Vue.createApp({
    data() {
        return {
            message: "hello world"
        }
    },
    template: "<div>{{ message }}</div>"
})

const vm = app.mount("#root");

vm 代表的就是 vue 应用的根组件。

MVVM 设计模式

M:model 数据模型,数据和业务逻辑在这层定义,如 data

V:view 视图,负责数据的展示,如 template

VM: 视图数据连接层,负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作。

VM 是 vue 组件帮我们做的。

获取 vm 里的data

使用 $data 属性即可

vm.$data

2. 生命周期函数

生命周期好比人生命中几个重要阶段,例如出生,上学,结婚,生孩子,死亡等,到了这些阶段都会做些事情,例如,出生后办出生证明,上学第一次带红领巾,死前立遗嘱等等。

Vue 组件同样有几个重要的生命周期,这些生命周期有对应的生命周期钩子(函数),即组件到达某个生命周期就会自动执行相应的生命周期函数的内容。

  • beforeCreate (创建前):在实例初始化之后、进行数据侦听和事件/侦听器的配置之前同步调用。此时无法访问到 data、computed、watch、methods 上的方法和数据

  • created (创建后):在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el property 目前尚不可用。

  • 进入判断,判断实例里边是否有 template 选项,如果存在就把模板变成一个 render 函数后,和数据结合并执行。如果不存在,就把 el 的 innerHtml 作为 template。即,template 的模板内容可以直接写在 root 节点下面。

  • beforeMount (挂载前):模板已经变成函数后被首次调用,已经生成了 DOM 树,但是此时还未挂载在 root 节点上,页面显示的仍是 vue 占位符。

  • Mounted (挂载后):在实例挂载完成后被调用,这时候传递给 app.mount 的元素已经被新创建的 vm.$el 替换了。如果根实例被挂载到了一个文档内的元素上,当 mounted 被调用时, vm.$el 也会在文档内。

    mounted 不会保证所有的子组件也都被挂载完成。如果希望等待整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick

    mounted() {
      this.$nextTick(function () {
        // 仅在整个视图都被渲染之后才会运行的代码
      })
    }
    
  • beforeUpdate (更新前):当 data 中的数据发生变化时会自动执行的函数,此时真实 DOM 还未被渲染。

  • updated (更新后):由于数据更改导致虚拟 DOM 重新渲染和打补丁之后自动调用。

  • beforeUnmount (销毁前):在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。

  • unmounted (销毁后):卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。

3. 常用模板语法

3.1 常规用法

插值表达式,用两个大括号括起来。

  const app = Vue.createApp({
    data() {
      return {
        message: "hello world"
      }
    },
    template: "<div>{{ message }}</div>"
  })
  app.mount("#root");

3.2 v-html

插值表达式会将标签转义,如果不想要标签被转义,就使用 v-html:

  const app = Vue.createApp({
    data() {
      return {
        message: "<strong>hello world</strong>"
      }
    },
    template: "<div v-html='message'></div>"
  })
  app.mount("#root");

3.3 v-bind

如果要给标签属性赋值,就需要用 v-bind,而不是插值语法。

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world"
      }
    },
    template: "<div v-bind:title='message'>{{ message }}</div>"
  })
  app.mount("#root");
</script>

v-bind 内容,包括插值语法,里面可以放 js 表达式。简写形式,标签名前面加冒号即可。

3.4 v-once

v-once 表示数据只渲染一次

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world"
      }
    },
    template: "<div v-once>{{ message }}</div>"
  })
  app.mount("#root");
</script>

上面的代码的意思是,渲染的时候使用了 data 里的 message,后边数据更新,v-once 包裹的内容也不会更新。

3.5 v-if 和 v-show

这两个都是控制内容的显示与否,但是又有所差距

<div id="conditional-rendering">
  <span v-if="seen">现在你看到我了</span>
</div>
  • v-show 本质就是标签 display 设置为 none,控制隐藏
  • v-if 是动态的向 DOM 树内添加或者移除 DOM 元素

因此,如果包裹的内容显示和不显示的切换频繁,用 v-show 更合理,因为避免了不停的销毁和创建。不频繁切换的话就用 v-if,减少渲染消耗。

v-if、v-else-if 和 v-else:这些如果要使用的话,就需要要紧紧贴在一起,否则不生效。

<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>

3.6 标签名赋值

如果标签名不确定,那么用中括号包裹,动态获取即可。

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world",
        name: "title",
        event: "click",
      }
    },
    methods: {
      handleClick() {
        console.log("click")
      }
    },
    template: `
      <div @[event]="handleClick" :[name]="message">
        {{ message }}
      </div>
    `
  })
  app.mount("#root");
</script>

3.7 阻止默认行为

如,阻止表单的默认提交行为,使他不跳转。使用 e.preventDefault()

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world",
        name: "title",
        event: "click",
      }
    },
    methods: {
      handleClick(e) {
        e.preventDefault();
      }
    },
    template: `
      <form action="https://www.baidu.com">
        <button type="submit" @click="handleClick">提交</button>
      </form>
    `
  })
  app.mount("#root");
</script>

更简单的写法,使用修饰符 .prevent 简化代码:

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: "hello world",
        name: "title",
        event: "click",
      }
    },
    methods: {
      handleClick() {
        console.log("click")
      }
    },
    template: `
      <form action="https://www.baidu.com">
        <button type="submit" @click.prevent="handleClick">提交</button>
      </form>
    `
  })
  const vm = app.mount("#root");
</script>

4. 数据、方法、计算属性和侦听器

4.1 数据 data

数据,也就是 data

如果要更改 data 里的数据,例如 message,修改 vm.$data.message 属性即可。如果是根数据,直接修改 vm.message 即可。

4.2 方法 methods

比如上边的 handleClick,就是其中一个方法。方法的 this 指向 vue 实例。

注意:不要使用箭头函数来定义函数!箭头函数会使 this 指向丢失!

4.3 计算属性 computed

计算属性 computed,用来放置计算后的数据。

<script>
  const app = Vue.createApp({
    data() {
      return {
        count: 2,
        price: 5,
      }
    },
    computed: {
      total() {
        return this.count * this.price
      }
    },
    template: `
      <div>
        {{ total }}
      </div>
    `
  })
  app.mount("#root");
</script>

如上面的代码,如果 count 或者 price 发生改变,total 也会跟着改变。计算属性使模板更轻量的同时,可读性更好。

就会有人问了,为什么不用 methods 呢?computed 的优势在于,只有相关的依赖的内容发生改变的时候才会重新求值,即 computed 使用了缓存。methods 每次页面调用都会去执行。

4.4 侦听器 watch

虽然大部分的情况下,computed 更合适,但是有些特殊情况,比如异步操作或开销较大的操作,使用侦听器是更合适的。

watch 用于监听 data 里的某个数据发生改变时候需要做的操作。

watch 可以接收两个参数,当前的数值和修改前的数值。

比如我监听 price 里的数据改变,使用如下代码:

    watch: {
      price(current, prev) {
        console.log(prev, current);
      }
    },

更改 price 数据后,输出如下:

和 computed 相比,watch 是一个更为底层的方式,能够使用 computed 的尽量使用 computed,因为写起来更简洁。

5. 样式绑定语法

5.1 class 常规使用

简单的写法就是用 :class 动态赋予类名。

<script>
  const app = Vue.createApp({
    data() {
      return {
        classString: "red"
      }
    },
    computed: {
      total() {
        return this.count * this.price
      }
    },
    template: `
      <div :class="classString">hello world</div>
    `
  })
  const vm = app.mount("#root");
</script>

注:red 和 green 都写了相应的 css 颜色样式

更复杂的情况,比如要用到多个类,就得使用对象格式。

<script>
  const app = Vue.createApp({
    data() {
      return {
        classObj: {
          red: true,
          bold: true,
        }
      }
    },
    computed: {
      total() {
        return this.count * this.price
      }
    },
    template: `
      <div :class="classObj">hello world</div>
    `
  })
  const vm = app.mount("#root");
</script>

里边的两个属性,如果值为 true,表示这个类的样式附加在当前的 DOM 上,值为 false 就不附加样式。这种的优势是在于可以控制多个样式。

当然,上面看着有两个 true,因为不控制类显示与否,因此显得有点鸡肋。单纯的想要添加多个类的样式,用数组即可。

<script>
  const app = Vue.createApp({
    data() {
      return {
        classArray: ["red", "bold"],
      }
    },
    computed: {
      total() {
        return this.count * this.price
      }
    },
    template: `
      <div :class="classArray">hello world</div>
    `
  })
  const vm = app.mount("#root");
</script>

后面重量级的来了,数组组合对象使用:

<script>
  const app = Vue.createApp({
    data() {
      return {
        classArray: ["red", "bold", {big: true}],
      }
    },
    computed: {
      total() {
        return this.count * this.price
      }
    },
    template: `
      <div :class="classArray">hello world</div>
    `
  })
  const vm = app.mount("#root");
</script>

5.2 class 包含子组件的情况

如果有子组件,且子组件的 template 只有一个根节点,可以在组件上写类名或者直接在组件上写或者在子组件根节点上写。

直接在组件上写类名:

<script>
  const app = Vue.createApp({
    template: `
      <Children class="green" />
    `
  })
  app.component("Children", {
    template: `<div>Children</div>`
  })
  const vm = app.mount("#root");
</script>

在子组件根节点上写类名:

<script>
  const app = Vue.createApp({
    template: `
      <Children/>
    `
  })
  app.component("Children", {
    template: `<div class="green">Children</div>`
  })
  const vm = app.mount("#root");
</script>

如果有两个及以上根节点,且在组件上写类名(在子组件上写,该咋写咋写,没歧义),vue 不清楚你这个类要赋予子组件的哪一个根节点,则需要使用下面的方法:

<script>
  const app = Vue.createApp({
    template: `
      <Children class="green" />
    `
  })
  app.component("Children", {
    template: `
      <div :class="$attrs.class">one</div>
      <div>two</div>
    `
  })
  const vm = app.mount("#root");
</script>

用 $attrs 获取组件的 property 然后按需赋予即可。

在子组件上写类名:

<script>
  const app = Vue.createApp({
    template: `
      <Children/>
    `
  })
  app.component("Children", {
    template: `
      <div class="green">one</div>
      <div>two</div>
    `
  })
  const vm = app.mount("#root");
</script>

5.3 style 内联样式

有两种方法,一种是常规的内联样式写法,另一种是用对象形式来写,这种方法相比于常规写法更好:

<script>
  const app = Vue.createApp({
    data() {
      return {
        styleObject: {
          color: "orange",
          background: "yellow",
        },
      }
    },
    template: `
      <div :style="styleObject">hello world</div>
    `
  })
  const vm = app.mount("#root");
</script>

6. 列表渲染

循环可以可以批量渲染出列表或者对象里的值。但是有一个重点在于,添加或删除的时候,只有要添加和删除的部分需要改动,其他的部分并不需要进行改动,因此用 key 值这个唯一值来标定每个循环生成的内容,就可以减少不必要的开销(如果没写 key 值,列表会全部给你重新渲染一遍)。

6.1 数组循环渲染

第一个参数为 item,第二个参数是对应的下标 (可选)

<script>
  const app = Vue.createApp({
    data() {
      return {
        list: ["sjh", "student", "vue"]
      }
    },
    template: `
      <div>
        <div v-for="(item, index) in list">
          {{ item }} -- {{ index }}
        </div>
      </div>
    `
  })
  const vm = app.mount("#root");
</script>

还可以直接循环数字,下面的代码是,从 1 循环到 10

<div v-for="item in 10" :key="item">
    {{ item }}
</div>

6.2 对象循环渲染

第一个参数为 value,第二个参数为 key (可选),第三个参数为 index (可选)

<script>
  const app = Vue.createApp({
    data() {
      return {
        userObj: {
          name: "sjh",
          learning: "vue",
          gender: "male",
        }
      }
    },
    template: `
      <div>
        <div v-for="(value, key, index) in userObj" :key="key">
          {{ index }} -- {{ key }} -- {{ value }}
        </div>
      </div>
    `
  })
  const vm = app.mount("#root");
</script>

6.3 数组变更方法和替换方法

变更方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。

变更方法就是非函数式的方法,即直接修改数组本身内容。

这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
替换方法

替换方法就是函数式编程返回新数组后,替换原来的数组。

filter, concat, map 等。

直接修改

通过 index 修改,或者直接一个新数组覆盖原来的数组,都是可行的,没有像 React 限制比较多。同样的,vue 可以直接往对象里添加内容。

注意:

vue2 版本直接修改对象内容或者通过 index 来修改数组内容,vue 是监听不到的。

6.4 v-if 和 v-for 优先级问题

不要在同一个节点上使用 v-ifv-for!当它们处于同一节点,v-if 的优先级比 v-for 更高,这意味着 v-if 将没有权限访问 v-for 里的变量。

<!-- 这将抛出一个错误,因为“todo” property 没有在实例上定义 -->

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

可以把 v-for 移动到 <template> 标签中来修正。template 相当于占位符,在真正渲染的时候不做任何渲染:

<template v-for="todo in todos" :key="todo.name">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

7. 事件绑定

7.1 传参规则与 event

刚才的内容已经展示了基本的事件绑定方法,下面展示一下示例,便可以很清楚的展示了其他细节。

<script>
  const app = Vue.createApp({
    data() {
      return {
        counter: 0
      }
    },
    methods: {
      handleClick(num, event) {
        this.counter += num
        console.log(event)
      }
    },
    template: `
      <div>
        {{ counter }}
        <button @click="handleClick(2, $event)">点击</button>
      </div>
    `
  })
  const vm = app.mount("#root");
</script>

事件可以附带参数(和正常的 js 方法一样),如果又要参数又要 event,参数用 $event 填充即可。

7.2 一个事件同时绑定多个方法

这写法比较奇葩,但还是得记一下

<div>
    {{ counter }}
    <button @click="handleClick1(), handleClick2()">点击</button>
</div>

7.3 事件修饰符

事件修饰符可以很方便地实现一些常见的需求,例如阻止冒泡行为,阻止默认行为等。事件修饰符可以串联使用

stop 阻止事件冒泡行为

事件有冒泡行为,因此需要进行捕获来避免冒到不必要的地方去。

<div @click="handleDivClick">
    {{ counter }}
    <button @click.stop="handleBtnClick">点击</button>
</div>
self 保证自身触发的事件

只有点击自身才触发事件,点击子元素不行。上面想要的效果,用 self 也可以实现,但差别在于,这种写法没有阻止冒泡。

<div @click.self="handleDivClick">
    {{ counter }}
    <button @click="handleBtnClick">点击</button>
</div>
其他修饰符
  • .prevent 阻止默认行为

  • .capture 捕获事件,内部触发的事件优先处理

  • .once 只执行一次

  • .passive 提升性能使用

    <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发,   -->
    <!-- 而不会等待 `onScroll` 完成,                    -->
    <!-- 以防止其中包含 `event.preventDefault()` 的情况  -->
    <div @scroll.passive="onScroll">...</div>
    

7.4 按键修饰符

@keyup@keydown 当按键松开 / 按下时触发。

但是更常用的是,按下特定的按钮触发事件,因此得使用按键修饰符。

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input @keyup.enter="submit" />
常用按键修饰符
  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
组合键修饰符
  • .ctrl
  • .alt
  • .shift
  • .meta
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
鼠标按钮修饰符
  • .left
  • .right
  • .middle
.exact 修饰符
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

精确地按 ctrl 和鼠标点击才会触发,按 ctrl 附加上其他键不会触发。

8. 表单双向绑定

可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。表单内容改变 data 里的值的同时,data 里的值改变也会影响表单内容。

v-model 是语法糖,底层是通过事件绑定和表单的 property 赋值实现的。

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

8.1 常规用法

文本 text
<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
多行文本 textarea
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br />
<textarea v-model="message" placeholder="add multiple lines"></textarea>

textarea 不支持插值语法,用 v-model 代替。

复选框 checkbox

当可以多选的时候,v-model 需要绑定一个数组

<div id="v-model-multiple-checkboxes">
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
  <label for="mike">Mike</label>
  <br />
  <span>Checked names: {{ checkedNames }}</span>
</div>
Vue.createApp({
  data() {
    return {
      checkedNames: []
    }
  }
}).mount('#v-model-multiple-checkboxes')
选择框 select

单选时:

<div id="v-model-select" class="demo">
  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>

第一行选项不可选,且内容为空是推荐写法,v-model 没有值的时候,初始值指向第一项。

Vue.createApp({
  data() {
    return {
      selected: ''
    }
  }
}).mount('#v-model-select')

若选择框多选时,绑定到一个数组上即可。

<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

8.2 值绑定

复选框 checkbox

选中的值和未选中的值想要自定义的时候,使用 true-value 和 false-value 属性值

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
单选框 radio
<input type="radio" v-model="pick" v-bind:value="a" />

当选中的时候,值为 a

8.3 修饰符

.lazy

.lazy v-model 默认 input 触发事件,用 lazy 修饰符则改为失去焦点的时候 blur 触发。

.number

将输入的值转化为数值类型

.trim

输入的值截去首尾空白字符

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值