1. 三种写模板template的方式 §

  1. Vue完整版,写在HTML
  2. Vue完整版,写在选项options
  3. Vue非完整版,配合*.vue文件

Vue完整版,写在HTML文件中

  • Vue2.6写法

    1
    2
    3
    4
    
    <div id=xxx>
    {{n}}
    <button @click="add">+1</button>
    </div>
    1
    2
    3
    4
    5
    
    new Vue({
    el: '#xxx',
    data: {n: 0}, // data 可改写成函数
    methods: {add() {...}}
    })
    
  • Vue3.x写法

    1
    2
    3
    
    import { createApp } from 'vue'
    createApp(App).use(router).use(store).mount('#app')
    // el 属性不再适用,而挂载时的 mount 也无需再加上 `$`
    

Vue完整版,写在选项options

1
<div id=app></div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
new Vue({
    template: `
        <div>
            {{n}}
            <button @click="add">+1</button>
        </div>
    `,
    data: {n: 0}, // data 可改写成函数
    methods: {add() { this.n += 1}}
}).$mount('#app')
  • 注意一个细节:div#app会被替代,即替换后,就不存在#appdiv

Vue非完整版,配合*.vue文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<template>
    <div>
        {{n}}
        <button @click="add">+1</button>
    </div>
</template>
<script>
    export default {
        data() {return {n: 0}}, // data 必须为函数
        methods: {add() {this.n += 1}}
    }
</script>
<style>这里写CSS</style>
  • *.js中引入使用

    1
    2
    3
    4
    
    import Xxx from './xxx.vue' // Xxx 是一个 options 对象
    new Vue({
    render: h => h(Xxx)
    }).$mount('#app')
    

2. 模板语法v-html v-on v-bind v-if v-for v-show §

  1. 展示内容
  2. 绑定属性
  3. 绑定事件
  4. 条件判断
  5. 循环操作
  6. 显示/隐藏

展示内容

表达式

  • {{object.a}}表达式
  • {{n + 1}}可以写任何运算(只支持运算,不支持条件判断 if 或循环等)
  • {{fn(n)}}可以调用函数,在methods中声明fn
  • 如果值为undefinednull,空值就不显示
  • 另一种写法为<div v-text="表达式"></div>{{}}

HTML 富文本内容

  • 假设data.x值为<strong>hi</strong>
  • <div v-html="x"></div>即可显示粗体的hi

展示源代码文本{{n}}

  • <div v-pre>{{n}}</div>
  • v-pre不会对模板进行编译

绑定属性

绑定src或其它任意属性

  • <img v-bind:src="x" />

v-bind:可简写为:

  • <img :src="x" />

绑定对象

  • <div :style="{border: '1px solid red', height:100}"> 可以省略默认单位px</div>

绑定事件

v-on:事件名

  • 函数名
    • <button v-on:click="add">+1</button>
    • 点击之后,Vue会运行add()
    • methods中定义
  • 函数名加参数
    • <button v-on:click="fn(1)">xxx</button>
    • 点击之后,Vue会运行fn(1)(不会立即执行)
    • methods中定义
  • 表达式(可执行代码不支持if..else 支持三目运算符)
    • <button v-on:click="n += 1">xxx</button>
    • 点击之后,Vue会运行n += 1
  • 发现函数就加括号调用,否则就直接运行代码
  • Vue不会处理运行fn(1)后返回的函数

缩写

  • <button @click="add">+1</button>

条件判断

if..else

1
2
3
4
5
6
7
8
9
<div v-if="x > 0">
    x大于0
</div>
<div v-else-if="x === 0">
    x为0
</div>
<div v-else>
    x小于0
</div>

循环操作

  • for(value, key) in (obj || array)对象或数组
  • 先写value,再写key
  • 必须绑定:key=""

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    <template>
    <div class="container">
    <!-- 遍历数组 -->
    <ul>
        <li v-for="(value, index) in usersArray" :key="index">
            索引:{{index}} 值:{{value}}
        </li>
    </ul>
    <!-- 遍历对象 -->
    <ul>
        <li v-for="(value, keyName) in usersObj" :key="keyName">
            属性名:{{keyName}} 属性值:{{value}}
        </li>
    </ul>
    <!-- 遍历单标签 -->
    <div class="wrapper">
        <div v-for="(value, keyName) in keys" :key="keyName">
            <input type="button" v-for="(value, index) in keys[keyName]" :key="index" :value="value" />
        </div>
    </div>
    </div>
    </template>
    <script>
    export default {
    data() {
    return {
      usersArray: [],
      usersObj: {},
      keys: {
        0: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
        1: ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
        2: ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
        3: ["Z", "X", "C", "V", "B", "N", "M"],
        4: ["SPACE"],
      },
    };
    },
    };
    </script>
  • 坑预警:key = "index"bug

  • :key一般用不会重合的值

显示/隐藏

v-show

  • <div v-show="n%2 === 0"> n 是偶数</div>

近似等于

  • <div :style="{display:n%2===0?'block':'none'}"> n 是偶数</div>
  • 注意可以显示在页面中的元素,display不只有block
  • tabledisplay: table
  • lidisplay: list-item
  • DOM解耦,高内聚,低耦合 jQuery 虚拟DOM

模板语法小结

Vue模板主要特点有

  • 使用XML语法,注意template中的不是HTML
    • XML里与有闭合标签的(更容易写编译器,体积小),如
    • <input name="userName">or<div></div>- HTML
    • <input name="userName" /> - XML or HTML
    • <div /> - XML自闭合标签
  • 使用{{}}插入表达式
  • 使用v-htmlv-onv-bind等指令操作DOMVue声明式编程代替原生 JS 的命令式编程div.innerHTML = x
  • 使用v-ifv-for等指令实现条件判断和循环

其他指令

  • v-modelVue表单里说明
  • v-slot在插槽里说明
  • v-cloak文档
  • v-once文档

v-if V.S. v-show

  • v-if 是否存在于 DOM 中
  • v-show CSS

3. 指令Directive与修饰符.* §

同义词:命令、指示

指令

什么是指令

  • <div v-text="x"></div>
  • <div v-html="x"></div>
  • v-开头的就是指令

语法

  • v-指令明:参数=值,如v-on:click=add
  • 如果里没有特殊字符,则可以不加引号
  • 有些指令没有参数和值,如v-pre
  • 有些指令可以没有值
    • <a v-on:click.prevent href="https://www.baidu.com">Baidu</a>
  • 也可以有值
    • <a v-on:click.prevent="xx" href="https://www.baidu.com">Baidu</a>
    • 点击 a 标签不会跳转链接

修饰符

什么是修饰符

  • 修饰符 (modifier) 是以半角句号.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定
  • Vue #修饰符

支持修饰符的指令

  • @click.stop="add"表示阻止事件传播/冒泡
  • @click.prevent="add"表示阻止默认动作
  • @click.stop.prevent="add"表示以上两种操作同时执行

修饰符的种类

修饰符的种类:按指令支持分

  • v-on支持的主要有 .{keycode | keyAlias}.stop.prevent
    • 其他.captureselfoncepassivenative
    • 快捷键相关:.ctrl.alt.shift.meta.exact
    • 鼠标相关:.left.right.middle
  • v-bind支持的有.prop.camel.sync
  • v-model支持的有 .lazynumbertrim

内置修饰符的种类:按作用分

  • 事件修饰符
  • 按键修饰符
  • 系统修饰符
  • 鼠标按钮修饰符

事件修饰符

vue 官方解释 :在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。

尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节

  • .stop 阻止事件的传播或者冒泡
  • .prevent 阻止事件的默认事件
  • .capture 事件为捕获方式
  • .self 触发事件的是自己这个元素,而不是内部元素
  • .once 事件只会触发一次
  • .passive 用来告诉事件监听器,没有调用preventDefault方法,可以提高滚动、滑动性能
  • .native 我们不能在组件上监听原生dom事件
  • 例如在router-link上不能监听click
  • 因此要加上.native修饰符,意为本地的,将他作为原来的html5标签来监听的意思
  • 注意:preventpassive不能一起使用,例如@click.prevent.passive会报错

    1
    2
    3
    4
    5
    
    <!--当点击提交按钮时阻止对表单的提交-->
    <!--提交事件不再重载页面-->
    <form v-on:submit.prevent="onSubmit"></form>
    <!--点击a标签不会跳转链接-->
    <a @click.prevent href="baidu.com"></a>

按键修饰符

KeyBoardEvent.key的有效按键

  • 可以传入键盘上的有效按键键名(F1 等不可以,需自定义)
  • 可以写ASCII码,也可以写字母等等,实际上是KeyBoardEvent.key的有效按键,已被弃用,Vue3.x不再支持
  • ASCII码是keyCode的值
  • Vue.config.keyCodes已弃用,将不再支持

别名(alias)

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

    1
    2
    
    <!-- 当用户在键盘 敲下enter回车键时,会触发xxx事件 -->
    <input type="text" @keypress.enter="xxx">

Vue3 迁移策略 —— 按键修饰符(破坏性改动)

  • vue 3 官方文档原文—— keycode modifiers

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    new Vue({
    template: `
    <div>
        {{n}}
        <a v-on:click.prevent="xx" href="https://www.baidu.com">Baidu</a>
        <input @keypress.13="y" />
        <input @keypress.enter="y" />
    </div>
    `,
    data: {n: 0},
    methods: {
        x() {
            console.log("xxx")
        },
        /*
        y(e) {
            if(e.keycode === 13) {
                console.log("用户敲了回车")
            }
        },
        */
        y(e) {
          console.log("用户敲了回车")
        },
    }
    })
    

系统修饰符

  • .ctrl
  • .alt
  • .shift
  • .meta
  • .exact

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <!-- Alt + C -->
    <input v-on:keyup.alt.67="clear">
    
    <!-- Ctrl + Click -->
    <div v-on:click.ctrl="doSomething">Do something</div>
    
    <!-- 有且只有 Ctrl 被按下的时候才触发 -->
    <button v-on:click.ctrl.exact="onCtrlClick">A</button>
    <!-- 不加exact的话可以一起按下别的键-->

鼠标按钮修饰符

  • .left
  • .right
  • .middle

4. .sync 修饰符 §

场景描述

  • 爸爸给儿子钱,儿子要钱花,示例
  • 儿子打电话(触发事件)想爸爸要钱

需求抽象

  • 当一个子组件改变了一外部传入的属性时,此变化也会同步到父组件中所绑定的数据中
  • 子组件要“通知”父组件,才可以改变

Vue 规则

  • 子组件不可修改props外部数据,否则会出现警告,数据会被父组件的重新覆盖
  • 子组件中使用发布(发出)$emit(eventName, fn | 单个 JavaScript 表达式),可以触发事件,并传参(eventBus
    • 即事件回传机制明确通知父组件,真正的更新还是父组件自己实现
  • 注意子组件发布(发出) $emit('update:money ', money - 100), 在事件后传入的应该是一个值,而不是一个其他表达式等,如 money = money - 100 返回的是undefined
  • 父组件中在插入的子组件标签上使用$event(订阅)可以获取(监听)子组件的$emit发布(发出)的数据,重新赋值给数据,实现同步更新
  • 即事件的“发布订阅”
  • 事件名eventName的命名规则为'update:myPropName',注意是有带冒号的字符串

Child.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<div class="child">
  {{money}}
  <!--
  <button @click="spendMoney">
  -->
  <button @click="$emit('update:money', money - 100)">
    <span>花钱</span>
  </button>
</div>
</template>

<script>
export default {
  props: ["money"],
  /*
  methods: {
      spendMoney() {
          this.$emit("update:money", this.money - 100)
      },
  },
  */
};
</script>

<style scoped>
.child {
  border: 3px solid yellowgreen;
}
</style>
  • 子组件需要修改从实例引入的 props 数据 money,不直接改,而是使用了 $emitmoney 处理并且传参

由于这种场景很常见

  • .sync可实现以上场景,Vue内置了EventBus事件总线传值方法,.syncEventBus的语法糖,封装为修饰符,代替了 $event 的使用
  • 父组件中插入的子组件标签上使用:money.sync="total"等价于:money="total" v-on:update:money="total=$event"
  • 父组件中对属性使用.sync修饰符,同步子组件中相应数据的修改

Father.vue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template>
<div id="app" class="app">
  App.vue 我现在有{{total}}
  <hr />
  <Child :money.sync="total" />
  <!--
    <Child :money.sync="total" @update:money="total = $event" />
  -->
  <!--
    <Child :money="total" @update:money="total = $event" />
  -->
  <!--
    <Child :money="total" v-on:update:money="total = $event" />
  -->
  <!--
    <Child :money="total"/>
  -->
</div>
</template>

<script>
import Child from "./components/Child.vue";

export default {
  name: "App",
  components: {
    // Child: Child,
    // 简写
    Child,
  },
  data() {
    return {
      total: 10000,
    };
  },
};
</script>

<style lang="scss">
#app {
  border: 3px solid orange;
  padding: 10px;
}
</style>
  • 插入的子组件标签,添加监听,事件名update:money,意图是更新赋值total = $event,即拿到捕获到的参数,也就是子组件发过来的 money - 100 的值

注意

  • 带有 .sync 修饰符的 v-bind 不能和 JS 表达式一起使用
  • 例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的
  • 取而代之的是,只能提供要绑定的 property 名,类似 v-model
  • 当子组件需要更新 myProp 的值时,可以显式地触发一个更新事件this.$emit('update:myProp', newValue),注意是newValue
  • 用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用
    • <text-document v-bind.sync="doc"></text-document>
    • 这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器
  • .sync只是作为一个编译时的语法糖。它会被扩展为一个自动更新父组件属性的 v-on 监听器,实现表面的“数据双向绑定”
  • .sync避免真正的“双向绑定”所带来的维护上的问题,会引起父子组件都以更改数据容易混乱,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    <template>
    <div>
    <p>{{bar}}</p>
    <p>
        <ChildInsertToFather :foo.sync="bar"/>
    </p>
    <!-- 会被扩展为(等价、语法糖) -->
    <p>
        <ChildInsertToFather :foo="bar" @update:foo="val => bar = val"/>
    <p>
        <ChildInsertToFather :foo="bar" @update:foo="bar = $event"/>
    </p>
    </div>
    </template>
    <script>
    Vue.component('my-comp', {
    template: '<div @click="increment">+1s</div>',
    data() {
        return {copyFoo: this.foo}
    },
    props: ['foo'],
    methods: {
        increment: function() {
        // 当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件
            this.$emit('update:foo', ++this.copyFoo)
            // this.$emit('update:foo', newValue)
        }
    }
    })
    new Vue({
    data() {
        return {
            bar: 0
        }
    }
    })
    </script>
  • 小结:vue2.x 修饰符.sync的功能是:当一个子组件改变了一个 prop 的值时,这变化也会同步到父组件中所绑定的对应属性

  • 父组件中在插入的子组件标签上,绑定的属性加后缀.sync,并指向父组件中响应的数据<Child :childProp.sync="dadData" />

  • 子组件发布(主动通知)给外部$emit('update:childProp', changeDadData)


Vue3.0 移除 v-bind 中的 .sync 修饰符

  • vue 3.x 又去掉了 .sync ,合并到了 v-model

    1
    2
    3
    4
    5
    
    <!-- vue 2.x -->
    <MyComponent v-bind:title.sync="title" />
    
    <!-- vue 3.x -->
    <MyComponent v-model:title="title" />
  • :xxx.sync 将被 v-model:xxx 取代


总结

四个常用修饰符

  • @click.stop="xxx"
  • @click.prevent="xxx"
  • @keypress.enter="xxx"
  • :money.sync="total"

·未完待续·

参考文章

相关文章