vue3语法

我眼中的vue3

将网页组件化,提高代码可复用性
将数据渲染到网页非常方便
响应式数据,做到数据与展示的数据双向绑定或单向绑定
方法只有纯粹的数据逻辑,而不再去处理 DOM 事件细节。

重点难点

父子组件通信的运用
生命周期钩子函数的理解和运用
配套的vue-cli(脚手架) vue-router(路由) vuex(全局状态管理) axios(替代Ajax异步请求)等的使用
vue3的组件式api的应用,新版本内嵌setup理解
渲染模板与虚拟DOM VNode

主要内容

1.v-bind 属性绑定(简写 ':')

   最常用,主要用于把数据绑定到DOMattribute

<a :href="url"></a>
<div :[a?b:c]="data"></div> 绑定动态参数 即可以选择绑定的参数

2.v-on 事件绑定(简写 '@')

    用于绑定事件

<botton @click="function"></botton>
<div @[a?b:c]="function"></div>绑定动态事件 即可以选择绑定的参数

3.{{}}

用于绑定文本


4.v-if  v-for

用于绑定DOM结构


5.v-html

用于将传递的语言转化为html(内容全部替换掉)


6.computed计算属性

计算属性是一种属性而非函数,监听响应式数据的变化,并随着响应式数据变化而变化,

注意:计算属性的值的变化是因为内部响应式数据的变化而引起的!

computed: {
  a_squrt: {
    reuturn Math.sqrt(this.a)
  }}
  1. 与函数比较:

结果完全一样,但是计算属性在原始响应式数据改变的情况下,不在此计算
而函数则调用一次计算一次,计算属性主要用于响应式数据,如Date.now则不行
建议vue中多使用计算属性,以减少计算次数

  1. 与属性比较:

默认计算属性只有getter,没有setter,即不能对计算属性赋值,可以自定义set

computed: {
 fullName: {
   get() {
     return this.firstName + ' ' + this.lastName
   },
   set(newValue) {
     const names = newValue.split(' ')
     this.firstName = names[0]
     this.lastName = names[names.length - 1]
   }
 }
}

7.watch侦听器

相对于计算属性,数据变化需要做出更复杂的操作,如执行异步操作 (访问一个API),并设置一个执行该操作的条件可以在watch内部写

const nums = ref(9)
watch(nums, (newValue, oldValue) => {
    console.log(`watch 已触发,nums由${oldValues}更新为${newValues}`)
})

8.class的绑定

  1. 对象语法

class的名称加不加引号都一样,(class有短横线时需要加引号)最终isActivehasError转换为boolean值为真时,
此DOM元素的class内如下,且:classclass可共存,取元素并集
且class的有无也会随着isActivehasError的更新而更新

// 1
<div :class="{ active: bool1, 'text-danger': bool2 }">
<div :class="computedClass"></div>
data(){
    return {
        bool1:true,
        bool2:true
    }
}
 computed: {
  computedClass: {
    return {
    active: this.bool1,
    'text-danger':this.bool2
    }
  }
 }

最终结果都是

<div class="active text-danger"></div>
  1. 数组语法

数组语法与对象语法不同的是,会将这里的activeClasserrorClass作为属性(响应式属性和计算属性)获取其值,而不是类本身

<div :class="[activeClass, errorClass]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>
data() {
  return {
    activeClass: 'active',
    errorClass: 'text-danger'
  }}
  1. 混合语法
<div :class="[{ active: isActive }, errorClass]"></div>

语法同样适用于自定义组件

自定义组件含有多个根元素时:

<template>
    <p :class="$attrs.class">Hi!</p>
    <span>This is a child component</span>
</template>

9.style绑定

  1. 对象语法

看着非常像 CSS,但其实是一个JavaScript对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名,即fontSize === 'font-size'

<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
//或者绑定到一个对象上去
<div :style="styleObject"></div>
data() {
  return {
    styleObject: {
      color: 'red',
      fontSize: '13px'
    }
  }}
  1. 数组语法

将所有对象绑定到此DOM,去并集,一些样式会被覆盖,如paddingheightmarginletf等等

<div :style="[baseStyles, overridingStyles]"></div>

10.v-if v-show条件渲染

大坑!!!!

使用v-if进行切换时,条件块内的事件监听器和子组件会被适当地被销毁和重建。

这个时候往往会出一些问题,比如一开始运行的时候,如果v-iffalseJavaScript会找不到组件(如d3.select('#test'),而此时组件不存在)


解决办法是在v-iffalse变为true时,且等待组件加载完毕,再进行JavaScript操作,那么更好的办法是将此段要显示的html与需要操作其的JavaScript封装为一个组件,这样可以通过子组件内部的钩子函数来调控JavaScript启动时间,或者直接使用v-show进行操作,因为v-show仅仅只是简单地将css样式内的display设置为none


简单来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好

11.v-for列表渲染

这个也是vue最爽的地方之一,可以很轻松的把成千上万条数据渲染成列表或表格

xuan数组

<ul id="array-with-index">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
data() {
    return {
      parentMessage: 'Parent',
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }

xuan对象

<li v-for="(value, name, index) in myObject">
  {{ index }}. {{ name }}: {{ value }}
</li>
data() {
    return {
      myObject: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2020-03-22'
      }
    }
  }

注意渲染对象是前面的是值,后面的才是key

维护状态

当xaun的对象或者数组改变时,相比大家已经想到,绑定的DOM数据也会动态更新,那么问题来了,怎么更新呢,假如把所有的数据全部换掉,vue该怎么去更新DOM呢,默认情况下,是进行就地更新,即位置顺序,假如后端给过来的数据突然不按照顺序来了,第一名排在的第二个位置,这显然是不合理的,所以v-for可以进行key的设置,以确保按此规则更新.

<div v-for="item in items" :key="item.id"></div>

更新原数组的函数:push() pop() shift() unshift() splice() sort() reverse()
不会更新原数组的函数:filter()concat() 和 slice()

也可以直接更新替换原来数组,vue依然能够动态更新,而且并不是重新渲染数组,据说是一种智能的启发式方法

官方不推荐v-forv-if在同一元素上使用,可以考虑如下方案

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

同样,自定义组件也可以使用v-for,但是数值的传递仍然要通过props

12.v-modol双向绑定

主要用于input select等表单元素,将页面变化的值同时传递给响应式属性
input使用value,:value,checked等确定需要绑定的内容

//message与输入框文字绑定
<input v-model="message" placeholder="edit me" />
//selected值与option内的文字绑定
<select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>

多个复选框或者,一个多选框绑定元素时,初始化为数组
组件的v-model双向绑定见下方

13.props组件传值

父组件传递值给子组件

父组件通过v-bind:绑定函数传递给子组件
子组件通过props接收
注意 组件传值不适用v-bind只能传递字符串,不会传递其他任何类型如boolean,Number,Array等

//父,value为父组件响应式属性
    <blog-post  :data="value"></blog-post>
    <blog-post  v-bind="post"></blog-post> //post为对象,这样做将其所有property 都作为 prop 传入
//子,
<template>
    <div class="blog-post">
      {{data}}
    </div>
</template>
//...props接收方式1
 props:['data']
//...
//...props接收方式2
 props:{
    data:{
        type:Number,
        required:true,
        default:0
    }
 }
//...

注意 对象的default值需要用工厂函数进行定义

default() {
        return { message: 'hello' }
      }

type类型有String Number Boolean Array Object Date Function Symbol
如果子组件需要监听父组件传递的prop的变化,又要对该prop进行修改,可以使计算属性动态修改和监听

在父组件定义了应该点击事件函数,希望在子组件调用,且该函数调用了父组件的响应式数据

父组件通过v-on:绑定函数传递给子组件
子组件通过emit接收

//父,postFontSize为父组件属性
    <blog-post  @enlarge-text="postFontSize += 0.1"></blog-post>
//子,click绑定父组件传递的函数
<template>
    <div class="blog-post">
      <button @click="$emit('enlargeText')">
        hello world
      </button>
    </div>
</template>

注意短横线命名法会转化为驼峰命名法,二者等价,上文已经提到过
也可以在javaScrip内接收

emits: ['enlargeText']
子组件属性传递给父组件--通过v-model的方式

以子组件输入为例,子组件inputvalue绑定的父组件

<input :value="inputValue" @input="$emit('update:inputValue', $event.target.value)"/>

或定义计算属性代替,(这里计算属性需要设置setter,否则无法更新)

template:'<input v-model="getValue">'
props: ['inputValue'],
emits: ['update:inputValue']
getValue: {
      get() {
        return this.inputValue
      },
      set(value) { 
        this.$emit('update:inputValue', value)
      }
    }

或者在js处抛出事件

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName">
</user-name>
app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['update:firstName', 'update:lastName'],
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `})

故想要在父子组件绑定双向响应数据,需要额外做的事情是将子组件更新的值返回到父组件,故需要自己定义update更新父组件事件

v-model的修饰符

除了内置修饰符之外,v-model还可以自定义修饰符

如何在子组件捕获修饰符

自定义的修饰符会产生一个新的prop,若v-model未绑定名称,新的prop名称为modelModifiers,否则生成的 prop 名称将为 arg + "Modifiers"

<my-component v-model.capitalize="myText"></my-component>
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  }

<slot>插槽

很好理解,我们需要在子组件中间插入一段别的组件或者html

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
所以插槽内的代码无法直接访问子组件,子组件也无法调用插槽内的属性

//父组件
<todo-button>
  <div>
  //...
  </div>
</todo-button>
//子组件
<button class="btn-primary">
  <slot></slot>
</button>

当有多个插槽时,可以使用name和v-slot的方式确定父组件插入的内容放到哪个插槽。v-slot的值不需要加引号

//父组件
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer></div>
  //子组件
<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template></base-layout>

通过插槽slot绑定可以把子组件的值传递给父组件调用
父组件定义一个template通过v-slot:来接收子组件slot的一个插槽 prop
通过这个插槽prop调用值,同理,多个插槽也是一样

//子组件
<ul>
  <li v-for="( item, index ) in items">
    <slot :item="item" :index="index" :another-attribute="anotherAttribute"></slot>
  </li>
</ul>
//父组件
<todo-list>
  <template v-slot:default="slotProps">
    <i class="fas fa-check"></i>
    <span class="green">{{ slotProps.item }}</span>
  </template>
</todo-list>

<transition> 过渡

vue也配备了良好的交互过渡封装

在我们开始使用vue时可以不用过多的考虑过渡,毕竟按钮什么的动效可以用各种UI框架
Element-UI LAyui等等,专注于样式

<transition> 会自动引入六个class (只要有)

默认:v-enter-from v-enter-active v-enter-to v-leave-from v-leave-active v-leave-to
如果使用了 <transition name="my-transition">,那么 v-enter-from 会替换为 my-transition-enter-from
以此类推
可以通过 <transition :duration="1000">...</transition>设置时间

ref模板引用

ref本来只是将数值变成响应式属性,方便赋值继续引用而不是拷贝值
但以如下方式定义ref变量且在DOM中引用
则该ref便成为模板实例,(因为用户难免还是要去操作DOM,比如我需要在vue中使用d3)

<template> 
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)
      onMounted(() => {
        // DOM 元素将在初始渲染后分配给 ref
        console.log(root.value) // <div>This is a root element</div>
      })
      return {
        root
      }
    }
  }</script>

生命周期函数

这块是重点,也放到了后面

最后修改:2022 年 04 月 27 日
来过的证明就是打钱