vue3常用功能

152 阅读6分钟

博客内容整理自b站up主技术蛋老师

引入

// cdn 引入 嵌入在 html中 <head>标签里
<script src="https://unpkg.com/vue@3"></script>

创建Vue实例与挂载

Vue.createApp({data(){}, computed, template, components, props})

// Vue 2创建实例
const vm = new Vue();    

每次使用Vue都需要创建新的实例,只需调用vue里的createApp()函数就行,此函数有个可选参数,是个对象{}

新的应用实例需要挂载到DOM的指定位置,即应用实例调用mount()方法,这样就挂载到DOM的一个标签上了,没有挂载实例的标签不受影响,原生HTML、CSS、JS可与Vue并存。

实现以上步骤的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>01</title>
    <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
    <div id="app">
        <h1>临时清单</h1>
    </div>
    <script>
        Vue.createApp().mount("#app");
    </script>
</body>
</html>

插值表达式

<!-- 插值表达式:用两个花括号直接引用data里的数据 -->
<h1> {{ title }} </h1>  

Vue.createApp({
    data(){
        return{
            title:'零食清单'
        }
    }
}).mount("#app");

v-for 循环遍历数组-基础语法

<li v-for="item in items">{{item}}</li>
// items 是data中的属性名,item 自定义名字,代表进行遍历的子元素
// 再把 item 用插值表达式的方式加入到 <li> 标签里
  • 举个栗子:
Vue.createApp({
  data() {
    return {
      title: '零食清单',
      foods:
      [
        { id: 1, name: '原味', image: './images/原味.png', purchased: false },
        { id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
        { id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
       ]
    }
  }
}).mount('#app');
  • 上例可写为:
<ul>
    <li v-for="food in foods">
        <span>{{ food.name }}</sapn>
    </li>
</ul>    

v-bind 单向绑定属性

img中的src属性如何绑定数据?

v-bind:src=""来为HTML属性和数据进行绑定,在属性前加v-bind:,然后在传入属性值的时候就是vue里的数据了。

<ul>
    <li v-for="food in foods">
        <img v-bind:src="food.image">
        <span>{{ food.name }}</sapn>
    </li>
</ul> 

v-model 双向绑定

<ul>
    <li v-for="food in foods">
        <span>{{ food.name }}</sapn>
        <input type="checkbox" v-model="food.purchased">
        <span>{{ food.purchased }}</sapn>
    </li>
</ul>  
标题内容备注
单向绑定
v-bind
属性值和Vue数据绑定Vue数据变动,DOM就会变动
双向绑定
v-model
input输入框的value和vue数据绑定Vue数据变动,DOM跟着变动
DOM变动,Vue数据跟着变动

v-for 循环遍历数组-引申用法

// index 为数组的序号
<li v-for="(item, index)in items">

// key 是Vue的特殊属性,用于跟踪和更新元素
<li v-for="(item, index)in items" v-bind:key="index">
  • 举个栗子:
<div id="app">
    <section>
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in foods.filter(item =>!item.purchased)"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>

    <section>
        <h2>已购零食</h2>
        <ul>
            <li v-for="food in foods.filter(item =>item.purchased)"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
</div>

v-show 显示隐藏

// v-show 接收的值为true就显示,false就不显示
<img v-show="true"/> 
  • 举例:
<div id="app">
    // 若数组无元素,长度为0,就是false,则不显示 
    <section v-show="foods.filter(item=>!item.purchased).length">
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in foods.filter(item =>!item.purchased)"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>

    <section v-show="foods.filter(item=>item.purchased).length">
        <h2>已购零食</h2>
        <ul>
            <li v-for="food in foods.filter(item =>item.purchased)"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
</div>

image.png image.png image.png

computed 计算属性

  • 也是createApp对象参数里的一个属性,计算属性在计算时才更新,提升性能。
computed:{
    beforeBuy(){
        return this.foods.filter(item =>!item.purchased);
    },
    afterBuy(){
        return this.foods.filter(item =>item.purchased);
    }
}
  • 重复的filter(item =>item.purchased)部分,用computed计算属性,上面的栗子就可以简写如下,函数名后面不需加括号:
<div id="app">
    <section v-show="beforeBuy.length">  
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in beforeBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>

    <section v-show="afterBuy.length">
        <h2>已购零食</h2>
        <ul>
            <li v-for="afterBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
</div>

template 属性

html直接写在template属性里,为Vue实例挂载到对应DOM节点上,解决了每个页面都有同样的元素,不用多次书写HTML,而是用Vue一对多地挂载上去,也就是组件的概念。

es6-string-html这个插件,可以解析出template里的html格式。

// App.js
export default{
  template:/*html*/`
    <section v-show="beforeBuy.length">  //函数名后面不需加括号
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in beforeBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>

    <section v-show="afterBuy.length">
        <h2>已购零食</h2>
        <ul>
            <li v-for="afterBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `,
    data() {
        return {
            foods: [
                { id: 1, name: '原味', image: './images/原味.png', purchased: false },
                { id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
                { id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
            ]
        }
    },
    computed:{
        beforeBuy(){
            return this.foods.filter(item =>!item.purchased);
        },
        afterBuy(){
            return this.foods.filter(item =>item.purchased);
        }
    }
}
// index.html
<div id="app"></div>
<script type="module">
    import App from './components/App.js';
    Vue.createApp(App).mount('#app');
</script>    
  • 深度拆组件
// AppSection.js
export default{
  template:/*html*/`
    <section v-show="beforeBuy.length">  //函数名后面不需加括号
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in befforeBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>

    <section v-show="afterBuy.length">
        <h2>已购零食</h2>
        <ul>
            <li v-for="afterBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `,
    data() {
        return {
            foods: [
                { id: 1, name: '原味', image: './images/原味.png', purchased: false },
                { id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
                { id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
            ]
        }
    },
    computed:{
        beforeBuy(){
            return this.foods.filter(item =>!item.purchased);
        },
        afterBuy(){
            return this.foods.filter(item =>item.purchased);
        }
    }
}    

components 属性

// App.js
import AppSections from "./AppSections.js"  // 引入 AppSections 对象
export default{
    // 注册 AppSections 子组件
    components:{AppSections},   // 用components属性,接收一个对象,就是组件名
    template:/*html*/` 
        <app-sections></app-sections> // vue会把此 标签名 与 组件名 对应起来
    `
}
  • 继续拆组件,因为拆组件是vue逻辑流程,把上下两个列表拆出来
// AppSectionsList.js
export default {
    template:/*html*/`
     <section v-show="beforeBuy.length">  
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in beforeBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `,
    data() {
        return {
            foods: [
                { id: 1, name: '原味', image: './images/原味.png', purchased: false },
                { id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
                { id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
            ]
        }
    },
    computed:{
        beforeBuy(){
            return this.foods.filter(item =>!item.purchased);
        },
        afterBuy(){
            return this.foods.filter(item =>item.purchased);
        }
    }
}
// AppSection.js
import AppSectionsList from "./AppSectionsList.js"  // 引入 AppSectionsList 对象
export default{
  components:{ AppSectionsList }  
  template:/*html*/`
    <app-section-list></app-section-list>
    <section v-show="afterBuy.length">
        <h2>已购零食</h2>
        <ul>
            <li v-for="afterBuy"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `

props 属性

全称为properties属性

  • 每个子组件都可以调用props属性,这个属性接受一个对象:
// 在子组件里用 props 接收这个 headline 里面的数据 并注明接收数据的类型
props: {
  headline:String
}
  • 在父组件AppSection直接输入headline="未购零食"
// 在父组件标签里 显式 定义一个 headline 属性 并给属性传一个字符 未购零食
<app-section-list headline="未购零食"></app-section-list>
  • 整理为如下:
// 父组件(使用层) AppSection.js
template:/*html*/`
    <app-section-list headline="未购零食"></app-section-list>
// 子组件(底层) AppSectionsList.js
export default {   
    props:{
        headline: String
    }
}
  • 如果用上父组件的数据,用v-bind绑定一个叫beforeBuyChild的属性,可简写为:beforeBuyChild,接着传入beforeBuy数据:
// 父组件(使用层) AppSection.js    
<app-section-list headline="未购零食" :beforeBuyChild="beforeBuy">
</app-section-list>
  • 在子组件上用props接收,接收的属性名为beforeBuyChild,接收的数据是数组,并把原来的beforeBuy都统一为beforeBuyChild,使其对应自己组件的数据名。
// 子组件(底层) AppSectionsList.js
export default {
    template:/*html*/`
     <section v-show="beforeBuyChild.length">  
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in beforeBuyChild"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `,
    props:{
        headline: String,
        beforeBuyChild: Array        
    }
}

props 用法小结

  1. 先在父组件标签里定义特殊属性,并且在属性值这里传入父组件的数据
  2. 子组件用props接收就可以使用了
  • 因为已购和未购零食的结构是一样的,复用子组件AppSectionsList,但是在复用之前需要把计算属性合并:
// AppSection.js    
computed:{
  // 创建一个新的计算属性函数 fillters
  fillters(){
    return{
      beforeBuy: this.foods.filter(item =>!item.purchased),
      afterBuy: this.foods.filter(item =>item.purchased)
    }
  }
}
  • 用对象属性值的方式访问:
// AppSection.js    
import AppSectionsList from "./AppSectionsList.js"  
export default{
  components:{ AppSectionsList }  
  template:`
    <app-section-list
        headline="未购零食" 
        :BuyChild="fillters.beforeBuy">
    </app-section-list>
    <app-section-list
        headline="已购零食" 
        :BuyChild="fillters.afterBuy">
    </app-section-list>
    `,
    data() {
        return {
            foods: [
                { id: 1, name: '原味', image: './images/原味.png', purchased: false },
                { id: 2, name: '辣味', image: './images/辣味.png', purchased: false },
                { id: 3, name: '炭烧味', image: './images/炭烧味.png', purchased: false }
            ]
        }
    },
    computed:{
        fillters(){
            return{
                beforeBuy: this.foods.filter(item =>!item.purchased),
                afterBuy: this.foods.filter(item =>item.purchased)
            }
        }
    }
}    
  • 基于父组件计算属性的合并,并用对象属性值的方式进行访问,数组类型变为对象类型;子组件接收的是对象Object,把原来的beforeBuyChild都改为buyChild
// AppSectionsList.js
export default {
    template:/*html*/`
     <section v-show="beforeBuyChild.length">  
        <h2>未购零食</h2>
        <ul>
            <li v-for="food in beforeBuyChild"
                :key="food.id">
                <img v-bind:src="food.image" />
                <span>{{ food.name }}</span>
                <input type="checkbox" v-model="food.purchased" />
            </li>
        </ul>
    </section>
    `,
    props:{
        headline: String,
        buyChild: Object        
    }
}
  • 小结:

未购清单设置buyChild属性,并且传入beforeBuy的返回内容,已购清单也设置buyChild属性,但传入afterBuy的返回内容,在子组件里用buyChild一次性把两个内容都接收过来,完成复用。

v-on 监听事件

// AppSection.js    
import AppSectionsList from "./AppSectionsList.js"  
export default{
  components:{ AppSectionsList }  
  template:`
    <app-section-list
        headline="未购零食" 
        :BuyChild="fillters.beforeBuy">
    </app-section-list>
    <app-section-list
        headline="已购零食" 
        :BuyChild="fillters.afterBuy">
    </app-section-list>
    <form>   // 加入表单元素
        <input type="text" placeholder="输入爱吃的"/>
        <button type="submit">添加</button>
    </form>    
    `,
  • 在需要监听的元素上添加v-on:click=""
<button v-on:click="add" type="submit">添加</button>

methods属性

// .prevent去掉默认的刷新
<form v-on:submit.prevent="add">   // 加入表单元素
<form @submit.prevent="add">  // 缩写形式
    <input type="text" placeholder="输入爱吃的"/>
    <button type="submit">添加</button>
</form> 

methods:{
    add(){
        this.foods.push({
            id:this.foods.length + 1,
            name:'',
            image:'../images/youyu.png',
            purchased:false
        });
    }
}
  • 如何获取到input里面的内容?用v-model双向数据绑定,先设置一个临时的空字符newFood,用于临时存放v-model的数据
data(){
    return{
        foods:[
            {id:1, name:'原味'},
            {id:2, name:'辣味'},
            {id:3, name:'炭烧'}
        ],
        newFood:‘’
    }
}
  • input里设置v-model,并传入这个newFood
<input type="text" placeholder="输入爱吃的" v-model="newFood"/>

methods:{
    add(){
        this.foods.push({
            id:this.foods.length + 1,
            name:this.newFood, // 和input里的value绑定的
            image:'../images/youyu.png',
            purchased:false
        });
        this.newFood = '';
    }
}

v-if 指令判断

判断传入的值是true(显示元素)还是false(调到下一个条件)

  • button上添加条件判断:
// AppSections.js
<form @submit.prevent="add"> 
    <input type="text" placeholder="输入爱吃的" v-model="newFood"/>
    <button type="submit" v-if="foods.length <=3">添加</button>
    <button type="submit" v-else-if="foods.length >3 && foods.length <5 ">再加</button>
    <button type="submit" v-else>继续加</button> 
  • 添加样式:
// AppSections.js
<form @submit.prevent="add"> 
    <input type="text" placeholder="输入爱吃的" v-model="newFood"/>
    <button type="submit" v-if="foods.length <=3">添加</button>
    <button type="submit" :style="{backgroundColor:aquamarine}"
    v-else-if="foods.length >3 && foods.length <5 ">再加</button>
    <button type="submit" v-else>继续加</button> 
  • v-bind:class="{类名:布尔值}"
.buttonColor{
    background-color:aquamarine;
}
<button type="submit" :class="{backgroundColor:true}"
    v-else-if="foods.length >3 && foods.length <5 ">再加</button>
<button type="submit" :class="{backgroundColor:true} 
    v-else>继续加</button> 
  • form表单拆成一个单独组件
// AppSectionsForm.js
export default{
    template:`
      <form @submit.prevent="add"> 
          <input type="text" placeholder="输入爱吃的" v-model="newFood"/>
          <button type="submit">添加</button>
      </form>    
    `
}
// AppSections.js
import AppSectionsList from "./AppSectionsList.js";
import AppSectionsForm from "./AppSectionsForm.js";

export default{
    components:{AppSectionsList, AppSectionsForm},
    template:`
        <app-sections-list headline="未购零食" :buyChild="filter.beforeBuy"></app-sections-list>
        <app-sections-list headline="已购零食" :buyChild="filter.afterBuy"></app-sections-list>
        <app-sections-form></app-sections-form>
     `   
}
  • 分析缺少的数据:
// AppSectionsForm.js
export default{
    template:`
      <form @submit.prevent="add"> 
          <input type="text" placeholder="输入爱吃的" v-model="newFood"/>
          <button type="submit">添加</button>
      </form>    
    `,
    data(){
        return{
            newFood:''
        } 
    },
    methods:{
        add(){
            // 用到的 foods 子组件中没有,不能直接使用
            this.foods.push({  
                id:this.foods.length + 1,
                name:this.newFood, // 和input里的value绑定的
                image:'../images/youyu.png',
                purchased:false
            });
        this.newFood = '';
        }
    }
}

$emit

  • 上例中用到的foods子组件中没有,不能直接使用;用props传值也不方便,就可以用$emit向全局暴露自己的位置,让父组件使用,上例修改add写法:
// AppSectionsForm.js
methods:{
        add(){
            this.$emit('add', this.newFood)
            this.newFood = '';
        }
}
  • 修改父组件的add方法,并监听:
// AppSections.js
<app-sections-form 
    @add=“fatherAdd”></app-sections-form>

methods:{
        fatherAdd(youyusi){
              id:this.foods.length + 1,
              name:youyusi
              image:'../images/youyu.png',
              purchased:false
        }
}

父子通信小结

  • 父传子,子用props接收
  • 子传父,子用$emit向外暴露