基础语法

mustache语法

{{msg}}
<!-- 可计算 -->
<!-- 可以是表达式,但不能是语句 -->
{{msg + site}}
{{msg}} {{site}}

双大括号只能作用于标签的内容,不能作为标签的属性

插值语法

v-once

<body>
    <div id="app">
        <h1>会变化:{{ msg }}</h1>
        <h1 v-once>不会变化:{{ msg }}</h1>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            }
        }).mount("#app");
    </script>
</body>

还没想到什么常用的应用场景

v-text

<body>
    <div id="app">
        <!-- 这个更灵活 -->
        <h1>{{ msg }},你好。</h1>
        <p>--------------</p>
        <h1 v-text="msg">默认内容</h1>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            }
        }).mount("#app");
    </script>
</body>

v-html

<body>
    <div id="app">
        {{site}}
        <p>---------------</p>
        <p>适用于展示性信息,业务型尽量不用这个</p>
        <div v-html="site"></div>
        <div v-html="intro"></div> 
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    site: '<a href="http://www.sdu.edu.cn">sdu</a>',
                    intro: '<button>click</button>'
                }
            }
        }).mount("#app");
    </script>
</body>

v-pre

<body>
    <div id="app">
        <p v-pre>希望不产生编译:{{msg}}</p>
        <h1>{{msg}}</h1>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            }
        }).mount("#app");
    </script>
</body>

v-cloak

<style>
    [v-cloak] {
        display: none;
    }
</style>
<body>
    <div id="app">
        <h1>无v-cloak:{{msg}}</h1>
        <h1 v-cloak>有v-cloak:{{msg}}</h1>
    </div>
    <script src="js/vue.js"></script>
    <script>
        // vue实例解析之前,h1标签有一个v-cloak属性
        // vue实例解析之后,h1标签的v-cloak属性自动消失
        setTimeout(()=>{
            const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            }
        }).mount("#app");
        },1000);
    </script>
</body>

绑定属性

v-bind

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- 不显示 -->
        <img src="imgUrl" alt="">
        <!-- 动态绑定 -->
        <!-- 针对标签里的属性 -->
        <img v-bind:src="imgUrl" v-bind:alt="alt">
        <a v-bind:href="site">SDU</a>
        <!-- 简写 -->
        <img :src="imgUrl" :alt="alt">
        <a   :href="site">SDU</a>

    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu",
                    imgUrl: "https://v3.cn.vuejs.org/logo.png",
                    alt: "Vue Logo",
                    site: "http://www.sdu.edu.cn"
                }
            }
        }).mount("#app");
    </script>
</body>

动态绑定

<style>
    .yellow {
        color: yellow;
    }
    .blue {
        color: blue;
    }
    .f60 {
        font-size: 60px;
    }
</style>
<body>
    <div id="app">
        <h1 class="yellow">{{msg}}</h1>
        <h1 :class="yellow">{{msg}}</h1>
        <p>---------------</p>
        <!-- <p :class="red f60">{{ msg }}</p> -->
        <!-- 对象赋值 -->
        <p :class="{yellow: isBlue, f60: isF60}">{{ msg }}</p>
        <button @click="change">toggle</button>

        <p style="background-color:wheat;" :class="{yellow: isBlue, f60: isF60}">{{ msg }}</p>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu",
                    yellow: "blue",
                    f60: "f60",
                    isBlue: true,
                    isF60: true
                }
            },
            methods: {
                change(){
                    this.isBlue = !this.isBlue;
                    this.isF60  = !this.isF60;
                }
            }
        }).mount("#app");
    </script>
</body>

数组赋值class

<body>
    <div id="app">
       <h3 class="red f60">{{ msg }}</h3>
       <h3 :class="[red, f60]">{{ msg }}</h3>
       <h3 :class="getClass()">{{ msg }}</h3>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() { 
                return {
                    msg: "sdu",
                    red: "red",
                    f60: "f60"
                }
            },
            methods: {
                getClass() {
                    return [this.red,this.f60];
                }
            }
        }).mount("#app");
    </script>
</body>

绑定style

以对象的方式为主

<body>
    <div id="app">
       <p style="font-size: 50px;">{{ msg }}</p>
       <!-- <p :style="font-size: 50px;">{{ msg }}</p> -->
       <p :style="{fontSize: '75px'}">{{ msg }}</p>
       <p :style="{fontSize: fs}">{{ msg }}</p>
       <p :style="{fontSize: fs, backgroundColor: bColor}">{{ msg }}</p>
       <p :style="getStyle()">{{ msg }}</p>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() { 
                return {
                    msg: "sdu",
                    fs:"100px",
                    bColor:"wheat"
                }
            },
            methods: {
                getStyle(){
                    return {fontSize: "125px", backgroundColor: this.bColor};
                }
            }
        }).mount("#app");
    </script>
</body>

事件监听

v-on

<body>
    <div id="app">
       <h1>运算结果:{{ count }}</h1>
       <button v-on:click="sub()">-</button>
       <!-- <button @click="add()">+</button> -->
       <button @click="add">+</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() { 
                return {
                    count: 0
                }
            },
            methods: {
                add(){
                    this.count++;
                },
                sub(){
                    this.count--;
                }
            }
        }).mount("#app");
    </script>
</body>
<body>
    <div id="app">
        <!-- 1. 不带参数 -->
       <button @click="btnClick1()">btn1</button>
       <button @click="btnClick2()">btn2</button>
       <!-- 2. 带参数 -->
       <button @click="btnClick3(123,'heihei',true,msg)">btn3</button>
       <!-- 3. 带参数又需要事件对象 -->
       <!-- $event -->
       <button @click="btnClick4(123,'heihei',true,msg, $event)">btn4</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            },
            methods: {
                btnClick1(args){
                    console.log(args);
                },
                btnClick2(args){
                    console.log(args);
                },
                btnClick3(arg1, arg2, arg3, arg4){
                    console.log(arg1, arg2, arg3, arg4);
                },
                btnClick4(arg1, arg2, arg3, arg4,event){
                    console.log(arg1, arg2, arg3, arg4,event);
                }
            }
        }).mount("#app");
    </script>
</body>

$event 传递事件对象

  • 事件冒泡取消
  • 默认事件取消
  • 响应一次事件
  • 键盘事件
<style>
    .box {
        background-color: red;
        width: 100px;
        height: 100px;
    }
</style>
<body>
    <div id="app">
        <!-- 1. 阻止冒泡 -->
        <div class="box" @click="boxClick">
            <button @click.stop="btnClick">click</button>
        </div>

        <!-- 2. 阻止默认事件 -->
        <form action="http://www.sdu.edu.cn">
            <input type="submit" value="提交" @click.prevent="doSubmit">
        </form>
        <a href="http://www.sdu.edu.cn" @click.prevent="aClick">SDU</a>
        <br>
        <!-- 3. 响应一次事件 -->
        <button @click.once="btnClick">click</button>
        <br>

        <!-- 4. 键盘事件修饰符 -->
        <input type="text" @keyup="getMsg"><br>
        <input type="text" @keyup.enter="getMsg">
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "sdu"
                }
            },
            methods: {
                boxClick() {
                    console.log("Click box");
                },
                btnClick(event) {
                    //   js中的事件默认是冒泡方式,逐层往上传播,
                    // 可以通过stopPropagation()函数停止事件在DOM层次中的传播,
                    // 通过dojo中的event.stop(evt);效果是相同的。
                    // event.stopPropagation();
                    console.log("Click btn");
                },
                doSubmit(event) {
                    // preventDefault它是事件对象(Event)的一个方法,
                    // 作用是取消一个目标元素的默认行为。既然是说默认行为,
                    // 当然是元素必须有默认行为才能被取消,
                    // 如果元素本身就没有默认行为,调用当然就无效了。
                    // 阻止默认事件
                    // event.preventDefault();
                    console.log("form submit");
                },
                aClick(){
                    console.log("Click a");
                },
                getMsg(event){
                    console.log(event);
                }
            }
        }).mount("#app");
    </script>
</body>

计算属性

<body>
    <div id="app">
        <p>SDU · Honglou</p>
        <p>------1. 常规用法-------</p>
        <p>{{ msg + ' · ' + campus }}</p>
        <p>------2. 函数-----------</p>
        <p>{{ getPosi() }}</p>
        <p>------3. 计算属性-------</p>
        <p>{{ fullPosi }}</p>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "SDU",
                    campus: "Honglou"
                }
            },
            computed: {
                fullPosi() {
                    return this.msg + " · " + this.campus;
                }
            },
            methods: {
                getPosi() {
                    return this.msg + " · " + this.campus;
                }
            }
        }).mount("#app");
    </script>
</body>

复杂一点的计算属性

<body>
    <div id="app">
        <p>------------------------</p>
        <p>TotalArea:{{ campuses[0].area + campuses[1].area + campuses[2].area + campuses[3].area }}</p>
        <p>------------------------</p>
        <p>TotalArea:{{ totalArea }}</p>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    campuses: [
                        {name: "Honglou", area: 300},
                        {name: "Zhongxin", area: 200},
                        {name: "Ruanjian", area: 400},
                        {name: "xinglong", area: 1300}
                    ]
                }
            },
            computed: {
                totalArea() {
                    total = 0;
                    for(let campus of this.campuses){
                        total += campus.area;
                    }
                    return total;
                }
            }
        }).mount("#app");
    </script>
</body>

计算属性的内部实现

<script>
    const app = Vue.createApp({
        data() {
            return {
                msg: "SDU",
                campus: "Honglou"
            }
        },
        computed: {
            fullPosi: {
                set(newValue) {
                    console.log("-----set()-----");
                    const arr = newValue.split("·");
                    this.msg = arr[0];
                    this.campus = arr[1];
                },

                get() {
                    console.log("-----get()-----");
                    return this.msg + " · " + this.campus;
                }
            }
        },
        methods: {
            getPosi() {
                return this.msg + " · " + this.campus;
            }
        }
    }).mount("#app");
</script>

使用计算属性只会计算一次,内部会缓存,函数的话每调用一次就会重算一次.

结构

v-if

<body>
    <div id="app">
        <div v-if="flag">解封</div>
        <div v-else>不解封</div>
        <p>------------------------------</p>
        <div v-show="flag">不想出去捏</div>
        <div v-show="!flag">想出去捏</div>
        <!-- v-show 和 v-if 的区别 -->
        <button @click="flag=!flag">在干嘛捏</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "SDU",
                    flag: true
                }
            }
        }).mount("#app");
    </script>
</body>

v-if 是反复销毁创建元素.
v-show 是通过css的 display:none; 实现的.

v-show 有点类似于switch-case

<body>
    <!-- v-if 和 v-show 分别实现,看切换前后的区别 -->
    <div id="app">
        <div v-if="loginType === 'phone'">
            <label for="">Phone:</label>
            <input type="text" placeholder="请输入手机号"><br>
            <label for="">Pwd:</label>
            <input type="password" placeholder="请输入密码">
        </div>
        <div v-else-if=" loginType === 'email'">
            <label for="">Email:</label>
            <input type="text" placeholder="请输入邮箱"><br>
            <label for="">Pwd:</label>
            <input type="password" placeholder="请输入密码">
        </div>
        <button @click="change">切换登陆方式</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    loginType: "phone"
                }
            },
            methods: {
                change() {
                    this.loginType === "phone"?this.loginType="email":this.loginType="phone";
                }
            }
        }).mount("#app");
    </script>
</body>

v-for

<body>
    <div id="app">
        <ul>
            <li v-for="p in personArr">{{ p }}</li>
        </ul>
        <p>----------------------------------------</p>
        <ul>
            <li v-for="(p, index) in personArr">{{ index }} -- {{ p }}</li>
        </ul>
        <p>----------------------------------------</p>
        <ul>
            <li v-for="(p, index) in persons">{{ index }} -- {{ p.name }} - {{ p.age }} - {{ p.address }}</li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    personArr: ["SDU",300,"Math"],
                    persons: [
                        {name:"Luo7",age:22,address:"SDU"},
                        {name:"Luo8",age:27,address:"SEU"},
                        {name:"Luo9",age:28,address:"SFU"}

                    ]
                }
            }
        }).mount("#app");
    </script>
</body>

例子2:

<body>
    <div id="app">
        <ul>
            <li v-for="item in person">{{ item }}</li>
        </ul>
        <p>----------------------------</p>
        <ul>
            <li v-for="(item, key) in person">{{key}}: {{ item }}</li>
        </ul>
        <ul>
            <li v-for="(item, key, index) in person">{{ index }} -- {{ key }}: {{ item }}</li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    person: {
                        name: "Luo",
                        age: 22,
                        friends: ["刘大海", "洛建国"]
                    }
                }
            }
        }).mount("#app");
    </script>
</body>

2.x中,如果在一个元素上同时使用 v-ifv-for 时,v-for 优先级更高.

3.x 中,v-if 优先级高于 v-for.

因此尽量避免在一个元素上同时使用v-forv-if.

小例子

<body>
    <div id="app">
        <h3>搜索列表</h3>
        <label for=""><input type="text" placeholder="请输入搜索的姓名" v-model="searchStr"></label>
        <div>
            <h3>排序</h3>
            <button @click="setOrderType(2)">年龄升序</button>
            <button @click="setOrderType(1)">年龄降序</button>
            <button @click="setOrderType(0)">换原</button>
        </div>
        <ul>
            <li v-for="(p, index) in filterPersons">
                {{ index + 1 }} - 姓名: {{ p.name }}, 性别: {{ p.gender }}, 年龄: {{p.age}}, 手机: {{p.phone}}
            </li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    persons: [
                        {name: "Luo", gender: "M", age: 22, phone: "3357"},
                        {name: "Luo7", gender: "M", age: 23, phone: "9527"},
                        {name: "胖大海", gender: "M", age: 9, phone: "113357"},
                        {name: "张福兴", gender: "F", age: 66, phone: "13357"},
                        {name: "刘保健", gender: "F", age: 91, phone: "3231357"},
                        {name: "牛德华", gender: "M", age: 17, phone: "6663357"},
                    ],
                    // 关联输入框
                    searchStr: "",
                    // flag: 0 默认, 1 降序, 2 升序
                    orderType: 0
                }
            },
            computed: {
                filterPersons() {
                    // 1. 取出相关的属性
                    const {persons, searchStr, orderType} = this;
                    // 2. 定义过滤数组
                    let arr = [...persons];
                    // 3. 根据条件过滤
                    if(searchStr.trim()){
                        arr = persons.filter((p)=>{
                            return p.name.indexOf(searchStr) != -1;
                        });
                    }
                    // 4. 排序
                    if(orderType) {
                        arr.sort((p1,p2)=>{
                            if(orderType === 1){
                                return p2.age - p1.age;
                            }else {
                                return p1.age - p2.age;
                            }
                        });
                    }
                    // 5. 返回过滤后的数组
                    return arr;
                }
            },
            methods: {
                setOrderType(orderType) {
                    this.orderType = orderType;
                }
            }
        }).mount("#app");
    </script>
</body>

为什么要绑定key?

  • vue内部采用的是DOM
    • 运用了Different算法
    • key的作用主要是为了高效的更新虚拟DOM
    • 运行过程
      • 当页面中某一层级有许多相同的节点时(列表展示)
      • 如果在头部或者中间插入一个新节点
        • 默认流程效率低
        • 绑定一个key作为唯一标识符,Diff算法可以正确识别节点,找到争取的位置插入新的节点
        • key 最好使用字符串或数值类型的值
        • key 可以避免潜在的bug
  • key是给每个vnode的唯一id,可以依靠key,更准确/更快地拿到oldVnode中对应的vnode节点
<body>
    <div id="app">
        <div>
            <label for="">ID: <input type="text" v-model="id"></label>
            <label for="">Name: <input type="text" v-model="name"></label>
            <button @click="add">add</button>
        </div>
        <p>----------------------------------</p>
        <ul>
            <li v-for="item in listArr" :key="item.id">
                <input type="checkbox">
                {{item.id}} --- {{item.name}}
            </li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    id: "",
                    name: "",
                    listArr: [
                        {id: 1, name: "张大炮"},
                        {id: 2, name: "张萝卜"},
                        {id: 3, name: "钱大富"},
                        {id: 4, name: "严丽"},
                        {id: 5, name: "刘大海"},
                        {id: 6, name: "金鑫鑫"},
                    ]
                }
            },
            methods: {
                add() {
                    // 尾部追加
                    // this.listArr.push({id:this.id, name: this.name});
                    // 头部追加
                    // this.listArr.unshift({id:this.id, name: this.name});
                    // 中间追加
                    this.listArr.splice(2, 0, {id:this.id, name: this.name})
                }
            }
        }).mount("#app");
    </script>
</body>

表单绑定

v-model

实现方式

<body>
    <div id="app">
        <!-- <input type="text" v-model="msg"> -->
        <h1>{{ msg }}</h1>
        <!-- <input type="text" :value="msg" @input="valueChange"> -->
        <input type="text" :value="msg" @input="msg=$event.target.value;">
    </div>
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "SDU"
                }
            },
            methods: {
                valueChange(event){
                    this.msg = event.target.value;
                }
            }
        }).mount("#app");
    </script>
</body>

v-model 单选

<body>
    <div id="app">
        <label for=""><input type="radio" value="男" name="gender" v-model="gender">男</label>
        <label for=""><input type="radio" value="女" name="gender" v-model="gender">女</label>
        <p>-----------------------</p>
        <h2>选择的性别是:{{ gender }}</h2>
    </div>
    
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    msg: "SDU",
                    gender: "男"
                }
            }
        }).mount("#app");
    </script>
</body>

v-model 进行值绑定

<body>
    <div id="app">
        <!-- 1. 只选一个 -->
        <label for="">
            <input type="checkbox" name="" v-model="isAgree">同意霸王条款
        </label>
        <h2>是否同意协议: {{ isAgree ? "同意":"未同意" }}</h2>
        <button :disabled="!isAgree">下一步</button>

        <p>---------------------------</p>
        <!-- 2. 多个选择 -->
        <label for="">爱好</label>
        <input type="checkbox" name="hobbies" v-model="hobbies" value="JAVA">JAVA
        <input type="checkbox" name="hobbies" v-model="hobbies" value="C">C
        <input type="checkbox" name="hobbies" v-model="hobbies" value="Python">Python
        <input type="checkbox" name="hobbies" v-model="hobbies" value="JavaScript">JavaScript
        <h2>用户画像:{{hobbies}}</h2>

        <!-- 3. 值绑定 -->
        <label for="" v-for="star in stars">
            <input type="checkbox" name="" :value="star" v-model="likes">{{star}}
        </label>
        <h2>用户画像:{{likes}}</h2>
    </div>
    
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    isAgree: false,
                    hobbies: [],
                    stars: ["牛德华","周木伦","成尤"],
                    likes: []
                }
            }
        }).mount("#app");
    </script>
</body>

结合多选,单选下拉菜单

<body>
    <div id="app">
        <p>选择校区:</p>
        <select name="campus" v-model="campus">
            <option value="中心">中心</option>
            <option value="洪楼">洪楼</option>
            <option value="软件">软件</option>
        </select>
        <p>选择的校区: {{ campus }}</p>
        
        <p>----------------------------------</p>
        <p>选择校区:</p>
        <select name="campus" v-model="campuses" multiple>
            <option value="中心">中心</option>
            <option value="洪楼">洪楼</option>
            <option value="软件">软件</option>
        </select>

        <p>选择的校区: {{ campuses }}</p>
    </div>  
    
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    campus: "中心",
                    campuses: []
                }
            }
        }).mount("#app");
    </script>
</body>

修饰符

<body>
    <div id="app">
        <!-- 1. 懒加载 -->
        <input type="text" v-model.lazy="msg">
        <h1>{{ msg }}</h1>

        <!-- 2. number -->
        <!-- <input type="number" v-model="age"> -->
        <input type="number" v-model.number="age">
        <h1>{{ age }}--- {{ typeof age }} </h1>

        <!-- 3.trim -->
        <input type="text" v-model.trim="name">
        <h1>xxx{{ name }}yyy</h1>
    </div>  
    
    <script src="js/vue.js"></script>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                   msg: "sdu",
                   age: 0,
                   name: ""
                }
            }
        }).mount("#app");
    </script>
</body>