[TOC]

模板

  • 绑定一个变量到dom(mount),返回的信息会替换模板里的字符串(多个返回使用逗号
    • 模板字符串要对应
  • 如果要一次性设置内容,不再响应式变化,添加v-once指令
  • 模板支持复杂类型,如{{data[0]}}
  • 对于html内容的模板,为了输出真正的 HTML,你需要使用v-html 指令
  • 模板与指令都支持js表达式,见下面
    • 注意必须是表达式,且只能有一个表达式。
  • 很多时候我们绑定组件需要一个父级容器,我们经常使用div做为容器。vue提供了一个不可见的容器<template></template>
  • {{}}只能用于html代码里,如页面上的静态html,或者是js里拼接的html ```javascript
    第{{ counter }}秒 :值为{{ counter }}

const Counter = { data() { return { counter: 0 } }, mounted() { //表示绑定后发生变化,每1000ms变化一次 setInterval(() => { this.counter++ }, 1000) } }

Vue.createApp(Counter).mount('#hello-vue')  //绑定一个变量到dom,返回的信息会替换模板里的字符串

// 第3秒:值为3 …

```javascript
return {
      rawHtml: '<span style="color: red">This should be red.</span>'
    }

<span v-html="rawHtml"></span>   //最终输出红色的一行字
{{ xx + 1 }}   //如果xx为布尔值,则计算时true为1,false为0,字符串则直接拼接'1'
{{ message.split('').reverse().join('') }}   将message反转

this

  • this指代的是绑定的dom元素,如·Vue.createApp(a).mount('#a')this.message就是指#adom元素里的模板message

    mount()

  • mount可以理解为挂载,将一个vue实例挂到一个dom元素上

  • 组件挂载最好挂载在更大一级上,否则很容易导致失效 。如我要

    原生dom事件

  • 例如键盘事件,鼠标事件,表单事件等

    修饰符

  • 修饰符一般的作用是对指令进行增强,修饰符都可以串联。

    *参数名称

  • HTML attribute 名不区分大小写,因此浏览器将所有大写字符解释为小写。这意味着当你在 DOM 模板中使用时,驼峰 prop 名称和 event 处理器参数需要使用它们的 kebab-cased (横线字符分隔) 等效值:

    • postTitlepost-title等效。但是aaaAAA不等效
    • 反正最好保持一致

      指令

  • 指令也支持表达式

    <div id="app"><div v-bind:id="'div-' + id"></div></div>
    
    return {id:5}
    Vue.createApp(ComponentsApp).mount('#app')  //会生成一个id为div5的div
    

    动态参数

    ```javascript 如果你的组件实例有一个 data property attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。

如果eventName 的值为 “focus” 时,v-on:[eventName] 将等价于 v-on:focus

<a name="RcAep"></a>
## 指令缩写

- Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写
```javascript
<!-- 完整语法 -->
<a v-bind:href="url"> ... </a>
//缩写
<a :href="url"> ... </a>
<!-- 动态参数的缩写 -->
<a :[key]="url"> ... </a>

<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>

<!-- 缩写 -->
<a @click="doSomething"> ... </a>

<!-- 动态参数的缩写 -->
<a @[event]="doSomething"> ... </a>

v-bind 属性

  • v-bind 将html属性与变量进行绑定
  • v-bind可以设置一个html属性的子属性,如<div :style="{ fontSize: fontsize+ 'em' }"> fontsize是绑定的字体大小的变量

    <input v-bind:value="message" />
    
    const AttributeBindingApp = {
          data() {
              return {
                  message: "rzd"
              }
          }
      }
      Vue.createApp(AttributeBindingApp).mount('#bind-attribute')
    

    v-on 事件监听

  • v-on用于绑定些dom事件,如click事件,多个事件使用,分割

    • 有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法。event即相当于所有原始dom事件对象。.可以获取个具体的事件
  • 有如下事件:
    • click
    • keyup键盘事件 一般用于input,其他标签很难捕获键盘事件。@keyup.a只有为点击a时才触发
    • scroll 滚动事件
    • submit表单提交事件 ```javascript

      {{ message }}

const EventHandlingApp = { data() { return { message: ‘Hello Vue.js!’ } }, methods: { reverseMessage() { this.message = this.message //获取调用该方法的元素的message模板数据,即#event-handlin。而非按钮 .split(‘’).reverse().join(‘’) //该行代码为字符串反转 } } } Vue.createApp(EventHandlingApp).mount(‘#event-handlin’)

```javascript
<button @click="say('hi')">Say hi</button>

 methods: {
    say(message) {
      alert(message)
    }
  }
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

methods: {
  warn(message, event) {
    // 现在可以访问到原生事件
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}

事件修饰符

  • .once这个事件只会被触发一次,仅限于自身事件,引发的父级事件不影响
  • @scroll.passive发生滚动事件时才触发
  • .stop阻止所有单击事件继续触发,如点击一个元素,父级元素也有点击事件,则相当于触发了2个点击事件,当前元素自身的事件先执行,然后stop阻止父级执行

    按键修饰符

  • 可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

  • 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。vue为常用按键提供了别名链接

    • @keyup.enter为enter按键时触发

      v-model 双向绑定

  • 上面的都是js里的return变量改变dom里的模板字符串。而通过v-model可以让dom里模板改变时,js里的变量也跟着变化

    文本输入表单

    ```javascript

    {{ message }}

const TwoWayBinding = { data() { return { message: ‘Hello Vue!’ } } } Vue.createApp(TwoWayBinding).mount(‘#two-way-binding’)

<a name="FzBhO"></a>
### 单选框
```java
<div id="app" >
            单复选框:
            <input type="checkbox" id="checkbox" v-model="checked">
            &nbsp;&nbsp;
            <label for="checkbox">{{checked}}</label>
        </div>

data() {
return {
        checked:true  //为true即表示默认选中
}
}

多选框

  • 给多个复选框绑定一个数组,则选中一个,数组值就增加一个,增加的值为复选框的value
    • 暂不清楚如何默认选中
  • image.png ```javascript


    选中的值:{{checkedNames}}

return { checkedNames:[] }

<a name="d146P"></a>
### 单选按钮组

- 给多个单选按钮绑定同一个变量,变量设置的值即初始选中的按钮value。若变量初始值与任何一个按钮值都不同,则默认都不选中
```javascript
<input type="radio" id="one" value="One" v-model="picked">
        <label for="one">One</label>
        <input type="radio" id="two" value="Two" v-model="picked">
        <label for="two">Two</label>
        <span>选中的值:{{picked}}</span>

return {
                        picked: 'One'  //默认选中one按钮
                }

下拉框

  • 下拉框与单选按钮组逻辑一样,也是变量初始值即默认选择地option。只不过不是option绑定变量,而是由select标签绑定
  • 有一个要注意地,当**option**不存在value时,变量与option文本对应。存在value时,与value对应。所以要避免一个option的value与另一个option的内容相同造成歧义
  • 变量设置的初始选中优先级大于html属性设置的默认选中
  • ios中vue设置select默认都不选择可能会出现特殊问题,所以默认都不选中应该采用一个值为空的禁用选项。 ```javascript
    下拉框: value:{{pan}}

return { pan: ‘B’ //默认选中B选项,优先级大于html属性设置的默认选中0选项 }

<a name="sy741"></a>
### v-model修饰符

- 修饰符用于增强`v-model`。有如下:
   - .`lazy`  只有内容变化时才会与绑定变量进行数据同步
   - `.number` 会尝试将输入的内容转为数值型,如输入025,则同步为25.但是输入xx025,还是xx025,不会变为xx25
      - 如果input本身就是`type=number`,则无需再添加`.number`
   - `.trim`  **过滤首尾空格,不过貌似不加**`**.trim**`**时,vue也会将首尾的多个空格合并为一个**
```javascript
<input type="text" v-model.number="message">{{message}}

v-if 是否显示

  • 绑定一个变量,如果变量为true,就显示。为false,就隐藏。而且这种隐藏不是动态修改display的显示隐藏,而是直接像注释一样抹除
  • v-if,v-else-if,v-else 。也可以直接接表达式
  • **v-show****v-if**相同,不同的是**v-show**是类似display的隐藏显示,仍旧需要创建dom,因此开销更大
    • 如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。 ```javascript

      {{ message }}

const TwoWayBinding = { data() { return { message: ‘Hello Vue!’,
isShow:true } } }

```javascript
<div v-if="type === 'A'">type 等于 A</div>
<div v-else-if="type === 'B'">type 等于 B</div>
<div v-else-if="type === 'C'">type 等于  C</div>
<div v-else> Not A/B/C </div>    //相当于if(type==a){} else if(type==b){} ...

data () {
  return {
    type: 'C'
  }
}

v-for 循环

  • v-for遍历的

image.png

    <table id="list-rendering">
            <tr v-for="todo in todos" >
                <td   style="border: 1px  solid  black">
          {{ todo.text }}
            </td>
            </tr>
        </table>

//还可以
<table id="app" >
            <tr v-for="(item,index) in items" >   //index即索引值
                <td   style="border: 1px  solid  black">
                    {{item.message}}---{{index}}
                </td>
            </tr>
        </table>

    const ListRendering = {
        data() {
            return {
                todos: [
                    {message:'狂神说Java'},
                        {message:'狂神说前端'},
                        {message:'狂神说运维'}
                ]
            }
        }
    }
    Vue.createApp(ListRendering).mount('#list-rendering')

v-for与v-if同时使用

  • v-for和v-if一起使用时,v-if优先级大于v-for。这就表示v-for遍历的每一个子数据v-if不能直接访问到,但是能访问被遍历的数据

    • 可以采用多套一层template,将v-for用在template上,v-if用于子标签上
    • 其他方法不怎么懂,不探讨

      <div id="vuexx" style="padding: 10%">
      <div class="layui-row layui-col-space15">
         <template   v-if="info[1].sid==3" v-for="item in info">    //能通过这种方式访问数据,嵌套template避免与v-if 
         <list   v-bind:resdata="item"   v-if="item.sname.length>2">
      
         </list></template>
      </div>
      </div>
      <script>
      const Counter = Vue.createApp({
         data() {
             return {
                 info: null
             }
         },
         created() {
             axios.get('http://localhost:8081/test').then(res => {
                 this.info = res.data;
             });
         }
      });
      Counter.component("list", {
         props: ['resdata'],
         template: `
           <div class="layui-col-md6">
           <div class="layui-panel">
             <div style="padding: 30px;">
               姓名:{{ resdata.sname }}{{resdata}}
               <a style="float: right" class="layui-btn layui-btn-normal" v-bind:href="resdata.classId+'.html?classId='+resdata.classId">详情</a>
             </div>
           </div>
           </div>`
      })
      Counter.mount('#vuexx')
      </script>
      

      组件详解

  • 我们可以看到其实上面的那种创建vue实例的最后也是调用**createApp**,也是生成组件进行挂载

  • 组件是可复用的vue实例。如table是一个组件,a链接也是一个组件。table里可以使用多个a链接
  • **.component()**的第一个参数即组件的名字,即标签的名称
    • 命名规范:全部小写,不同词之间使用**-**拆分
  • image.png ```javascript
      {{key}}

//只创建一个组件

    {{key}}

const ComponentsApp = { data() { return { groceryList: [ { id: 0, text: ‘Vegetables’ }, { id: 1, text: ‘Cheese’ }, { id: 2, text: ‘Whatever else humans are supposed to eat’ } ] } } }

    const app = Vue.createApp(ComponentsApp)   //实例做为组件参数传入,以此传入实际数据

    app.component('todo-item', {  //组件标签名称
        props: ['todox'],       //相当于形参,prop:专门用于给子组件传值
        template: `<li>{{todox.id}}:{{ todox.text }}</li>`
    })

    app.mount('#components-app')  //挂载组件
<a name="vIPTF"></a>
## 组件的功能

- 侦听器
- `computed`计算属性,相当于专门处理data的一个函数
- `Data Property`即data函数
- `methods`:Vue 自动为 methods 绑定 this,以便于它始终指向组件实例。这将确保方法在用作事件监听或回调时保持正确的 this 指向。在定义 methods 时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的 this 指向。
   - methods可以像普通方法一样被模板与指令访问
   - 从模板调用的方法不应该有任何副作用,比如更改数据或触发异步进程。如果你想这么做,应该使用生命周期钩子来替换。
   - 防抖与节流:[略](https://v3.cn.vuejs.org/guide/data-methods.html#%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81)
- **template使用**`**``**`**包裹**
- `components`要引用其他模板,组件时可以使用该属性
```javascript
const loginForm={template:`...`} 
const registerForm={template:`...`}
const app=Vue.createApp({
        components: {
            loginForm,
            registerForm
        }
    });
<button @click="increment">Up vote</button>   //increment是methods里的一个方法
<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

组件实例信息

  • 每创建一个组件,就创建一个新实例,不同实例的内容是不同的。如一个组件定义点击则count+1。创建3个组件,点击一个组件不会让另外2个的count+1 ```javascript const app = Vue.createApp({ data() { return { count: 4 } }, methods: { increment() {
    // `this` 指向该组件实例
    this.count++
    
    } } })

const vm = app.mount(‘#app’)

console.log(vm.count) // => 4

vm.increment()

console.log(vm.count) // => 5

<a name="yEIYE"></a>
## Prop:向子组件传递数据

- 通过prop可以在组件上注册的一些自定义 attribute.即相当于给标签添加了自定义属性,我们可以直接像普通html参数一样传值(注意不能使用`v-bind:参数`,`:参数`进行传值,这样就只会去寻找后台绑定数据,找不到就不渲染显示,**没有v-bind则相当于插槽一样直接html给js传值**)
- 不同数据的prop例子见vue3的深入组件:[链接](https://v3.cn.vuejs.org/guide/component-props.html#prop-%E7%B1%BB%E5%9E%8B)
```java
<div id="blog-post-demo" class="demo">
  <blog-post title="My journey with Vue"></blog-post>
  <blog-post title="Blogging with Vue"></blog-post>  //title的参数值传给了模板的title,类似插槽
  </div>

const app = Vue.createApp({})

app.component('blog-post', {
  props: ['title'],
  template: `<h4>{{ title }}</h4>`
})
app.mount('#blog-post-demo')

计算属性

  • 我们可以将同样的函数定义为一个方法,而不是一个计算属性。从最终结果来说,这两种实现方式确实是完全相同的。然而,不同的是计算属性将基于它们的响应依赖关系缓存。计算属性只会在相关响应式依赖发生改变时重新求值。这就意味着只要 author.books 还没有发生改变,多次访问 publishedBookMessage 时计算属性会立即返回之前的计算结果,而不必再次执行函数。
    • 总之就是对于计算的东西写个计算属性比函数好,对于实时变化的数据则应该使用函数
  • 计算属性还可以设置set,get一样的方法 ```javascript

    长方形的面积: {{ area }}

data() { return { width: 0, height: 0 } }, computed: { // 当 width 和 height 发生变化时,area 也会同时变化, // 并且这总变化会体现在模板上。 area() { return this.width * this.height; }, }

```javascript
const s={
            data() {
                return {
                    author: {
                        name: 'John Doe',
                        books: [
                            'Vue 2 - Advanced Guide',
                            'Vue 3 - Basic Guide',
                            'Vue 4 - The Mystery'
                        ]
                    }
                }
            },
            computed: {
                // 计算属性的 getter
                publishedBooksMessage() {
                    // `this` 指向 vm 实例
                    return this.author.books.length > 0 ? 'Yes' : 'No'
                }
            }
        }
    Vue.createApp(s).mount('#computed-basics')

<span>{{ publishedBooksMessage }}</span>  //结果为yes
computed: {
  now() {
    return Date.now()
  }
}  //下面的计算属性将永远不会更新,因为 Date.now () 不是响应式依赖:
computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

含入参的计算属性

 <div id="app" >
        <p v-for="item  in jiage">原价格:{{item}}  打折后:{{zhe(item)}}</p>
    </div>
<script>
    const app={
        data()
        {
            return { jiage: [1000,200,50 ]}
        },
        computed:{  
            zhe(){   //zhe(res){return res*0.7;}是无效的
                return function (res){   //这里就是vue3中实现过滤器一样的效果的实现
                    return res*0.7;
                }
            }
        }
    }
    Vue.createApp(app).mount("#app");

监听子组件事件

  • 组件内部绑定事件:this.$emit("事件名",参数);

image.png

<body id="app">
  <div id="blog-posts-events-demo" class="demo">
    <div :style="{ fontSize: postFontSize + 'em' }">
      <blog-post
                 v-for="post in posts"
                 :key="post.id"
                 :title="post.title"
                 @enlarge-text="postFontSize += 0.1"
                 ></blog-post>
    </div>
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          posts: [
            { id: 1, title: 'My journey with Vue'},
            { id: 2, title: 'Blogging with Vue'},
            { id: 3, title: 'Why Vue is so fun'}
          ],
          postFontSize: 1
        }
      }
    })

    app.component('blog-post', {
      props: ['title'],
      template: `
      <div class="blog-post">
      <h4>{{ title }}</h4>
      <button @click="$emit('enlargeText')">  <!--可以增加一个参数,使得能让blog-post自己决定放大多少。$emit('enlargeText', 0.1)-->  
      //$emit用于绑定一个内联的方法,vue看来aB和a-B是一样的,但是我们自己写最好还是同名
      Enlarge text
    </button>
    </div>
    `
    })
   app.mount('#blog-posts-events-demo')

    //改写为方法,
    blog-post ... @enlarge-text="onEnlargeText"></blog-post>
    methods: {
  onEnlargeText(enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}
  • 可以在组件的 emits 选项中列出已抛出的事件.这将允许我们检查组件抛出的所有事件

    app.component('blog-post', {
    props: ['title'],
    emits: ['enlargeText']
    })
    

    侦听器

  • 侦听器类似计算属性,在数据变化时执行函数。侦听器能侦听变量与计算属性的变化

    • 侦听对象子属性可以直接使用**.**一样访问,如 watch: { 'tom.username'() { },
  • Vue 通过 watch 选项提供了一个更通用的方法来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。(即如果能使用计算属性实现,就不要使用侦听器)
    • 下面的例子中,使用 watch 选项允许我们执行异步操作 (访问一个 API),并设置一个执行该操作的条件。这些都是计算属性无法做到的。 ```javascript

      Ask a yes/no question:

      {{ answer }}

<a name="ssPEv"></a>
## 组件生命周期

- 即一个组件的生命周期,例如一个生命周期需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。
- 一个生命周期期间的不同时候会执行不同函数。**这些特定时候执行的函数即生命周期钩子  **如`created``mounted`、`updated `和 `unmounted`
```javascript
Vue.createApp({
  data() {
    return { count: 1}
  },
  created() {  //创建一个created钩子,该函数会在组件创建后自动执行
    // `this` 指向 vm 实例
    console.log('count is: ' + this.count) // => "count is: 1"
  }
})

自定义组件使用v-model

组件的作用域

  • 这个二级区域之外的所有例子都是全局组件,全局组件的不好是不需要使用到的地方也会加载组件的js代码
  • ·对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字(component-a、component-b),其属性值就是这个组件的选项对象(ComponentA、ComponentB)。

我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用:

var runoobA = {
  template: '<h1>自定义组件!</h1>'
}

const app = Vue.createApp({
  components: {
    'runoob-a': runoobA
  }
})

app.mount('#app')

*过滤器

  • vue3不支持,这里提下只是为了让你知道有这么个东西

    插槽

  • 组件可以定义一个标签,使用标签就相当于替换为组件的模板。还有一个插槽也有类似功能,且更加灵活,如插槽可以是任何模板代码,如字符串,html代码,或者是其他组件

    • <slot>是内容的占位符,即插槽。即组件中输入的内容会替换插槽的内容。即模板的相反作用。模板是js中的替换html中的占位,插槽是html中的替换js中的占位
    • 如果模板中一个插槽都没有,则html中组件中输入的内容会直接丢弃
    • html的组件内容中的**{{}}**的模板功能仍旧生效

      <div id="slots-demo" class="demo">
         <alert-box>
             Something bad happened.
         </alert-box>
      </div>
      <script>
         const app = Vue.createApp({})
      
         app.component('alert-box', {
             template: `
      <div class="demo-alert-box">
       <strong>Error!</strong>
       <slot></slot>  //
      </div>
      `
         })
      
         app.mount('#slots-demo')
      

      闪烁问题

  • vue在dom生成时,是直接创建模板数据的,再根据绑定数据进行渲染。这就导致速度慢时可能看到的是模板。如{{message}}对应数据是hello,速度慢时可能先看到{{message}},然后再看到hello。

    • 可以通过v-cloak解决,速度慢时直接看不到任何东西,然后再看到正确信息
  • v-cloak再加载完会被移除,借助这个特性可以配合display:none实现未加载完时含有v-bloak的元素不显示

    <style type="text/css">
    [v-cloak] {   
      display: none;
    }
    </style>
    

    响应式失效问题

  • 这里是看的vue2版本的教程了解的,vue3中不一定存在这些问题

  • 常见有 3 种情况,你改动了 vue 的 data,但是 vue 没有重新渲染页面,即,响应式失效:
    • 为对象添加新属性;
    • 通过数组下标索引操作数组;
    • 通过 length 属性为数组扩容或缩容。
  • 详见响应式失效 _ Java Note.pdf