摘要

简单学习一下Vue

正文

文档

  1. 介绍 — Vue.js
  2. 风格指南 — Vue.js
  3. hexo中对占位符的解析 | TonyStudio

Vue是一套用于构建用户界面的渐进式JavaScript框架。

何为渐进式?

Vue可以创建自底向上逐层的应用,从简单应用到复杂应用。

如果是简单应用,只需要一个轻量的核心库。

如果是复杂应用,可以引入各式各样的vue插件。

Vue特点

  1. 采用组件化模式,可复用、好维护。一个.vue就可以作为一个组件,其中包含html、css、js
  2. 声明式编码,无需直接操作dom。像直接操作dom的就叫做命令式编码,有点像面向对象与面向过程的区别,具体区别可以看图。
  3. 使用虚拟dom和diff算法,尽量复用dom。也就是数据发生变化,只进行增量更新。

1.jpg

一、上手

像vue下面这样的语法,就叫做模板语法,模板语法又分很多类。

html
1
2
3
<div id="test">
    Hello {{message}}
</div>

1.1 占位语法

遇到了点hexo的冲突问题,hexo中的占位符敲好也是{% raw %}{{}}{% endraw %}或者{% raw %}{%%}{% endraw %}

所以如果想要展示原内容的话,语法需要像下面这样使用

{% raw %}{% raw %}文章内容{% endraw %}{% endraw %}

{% raw %}{{content}}{% endraw %}表示占位符,content内容支持js表达式。

占位语法一般用于标签体内容

一般真实开发也只会有一个Vue示例,并且是配合组件一起用的。

html
 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
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<div id="root">
    Hello {{message.toUpperCase()}},My name is {{name}},now timestamp is {{Date.now()}}, I will calculate 1+1 for you, the result is
    {{1+1}},
</div>
<div id="test">
    Hello {{message}}
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    Vue.config.productionTip = false;
    const app = new Vue({
        el: '#root',
        data: {
            message: 'Vue',
            name: "ccc"
        }
    });
    

</script>
</body>
</html>

1.2 指令语法

vue有很多指令,格式都是v-xxx

v-bind这种就是指令语法,v-bind可以简写为:

指令语法用于标签,作用范围包含并大于占位语法

html
 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
<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<div id="app">
    <a v-bind:href="link">{{blogName}}</a>
    <a :href="link">{{blogName}}</a>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    Vue.config.productionTip = false;
    const app = new Vue({
        el: '#app',
        data: {
            blogName: '博客',
            link:"https://meethigher.top"
        }
    });


</script>
</body>
</html>

1.3 数据绑定

v-bind是单向数据绑定,也就是绑定的值,如果通过页面进行了修改,原本的值不会变化。

v-model支持双向数据绑定,如果页面的值发生变化,绑定的值也会变化。

v-model只能用在输入类元素,也就是内容可编辑的元素。

html
 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
<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<div id="root">
    单向数据绑定:<input type="text" v-bind:value="name">
    双向数据绑定:<input type="text" v-model:value="name">
</div>
<script src="js/vue.js"></script>
<script>
    new Vue({
        el:'#root',
        data: {
            name:'test'
        }
    })
</script>
</body>
</html>

1.4 el与data的两种写法

el两种写法

  1. new对象时配置el属性
  2. 通过创建的对象使用$mount指定el的值

data两种写法

  1. 对象式
  2. 函数式

使用组件时,data必须使用函数式

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
    /*第一种写法*/
    // new Vue({
    //     el:'#root',
    //     data: {
    //         name:'test'
    //     }
    // });
    /*第二种写法,el使用mount,data使用函数*/
    const app = new Vue({
        //data: function ()可以简写为data()
        data: function () {
            //this是vue对象
            console.log(this);
            return {
                name: "test"
            }
        }
    });
    app.$mount("#root");
</script>

1.5 vue中的mvvm模型

M:model模型,对应data中数据

V:view视图,对应vue模板

VM:viewModel视图模板,对应vue对象

对使用者,跟后端的MVC没啥区别,只不过控制器换成了视图模板罢了。

视图模板本身和控制器作用也是一样的,调用数据model,将数据model返回给view视图使用。

1.6 数据代理

通过Object.defineProperty添加属性值,该属性值的key在遍历时,是不会被遍历到的

2.jpg

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>
    let person={
        name:"test",
        age:18
    };
    /*这样定义的不会参与遍历*/
    Object.defineProperty(person,"sex",{
        value: "男"
    });
    console.log(person);
    //遍历key
    console.log(Object.keys(person))
</script>

当然了,我们也可以给他们配置属性,允许该操作。

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/*允许被遍历*/
Object.defineProperty(person,"sex",{
    value: "男",
    //控制属性可以被遍历,默认false
    enumerable:true,
    //控制属性可以被修改,默认false
    writable:true,
    //控制属性可以被删除,默认false。通过控制台delete person.sex测试
    configurable:true
});

如果,我们想要动态修改对象中的值,可以这么玩

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
    let sex="男";
    let person={
        name:"test",
        age:18
    };

    //让值为动态的
    Object.defineProperty(person,"sex",{
        //读取值
        get() {
            console.log("读取sex");
            return sex;
        },
        //修改值
        set(value) {
            console.log("修改sex");
            sex=value;
        }
    });
    console.log(person);
</script>

1.7 事件与事件修饰符

注意事项

  1. 使用v-on:xxx或者@xxx绑定事件,其中xxx是事件名,比如click
  2. 事件如果放在data中,会自动生成getter与setter,建议放在methods中
  3. methods中如果用箭头函数,this就表示window。不用箭头函数,this表示vue对象
  4. @click="function"与@click="function($event)"一样,前者是后者省略写法。
html
 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
<div id="root">
   <button v-on:click="showInfo
   <button @click="showInfo">@简
   <button @click="showInfoWith
</div>
<script>
    const app=new Vue({
        el:"#root",
        data:{
            name:"meethigher",
            address:"china"
        },
        methods: {
            // showInfo() {
            //     alert("test"
            // }
            showInfo(a,b,c,d) {
                //只有第一个参数a有值
                console.log(a,b
                //此时this是vue对象
                console.log(thi
            },
            showInfoWithParam(p
                console.log(eve
                alert(param);
            }
        }
    });
</script>

对于事件修饰符,拿阻止默认事件来说,我们从js的语法写vue可以这样

javascript
1
2
3
4
5
showInfo(e) {
    //阻止默认行为,比如a标签点击跳转,阻止后就不用跳转了
    e.preventDefault();
    alert("test");
}

但是vue给提供了更方便的写法

html
1
2
3
<div id="root">
    <a href="https://meethigher.top" @click.prevent="showInfo">test</a>
</div>

像这种还有很多,vue中常用的事件修饰符

  1. prevent:阻止默认事件,常用
  2. stop:阻止事件冒泡,常用
  3. once:事件只触发一次,常用
  4. capture:使用事件的捕获模式
  5. self:只有event.target是当前操作的元素才会触发
  6. passive:事件的默认行为立即执行,不需要等待事件回调完成

修饰符可以连写

.prevent.stop,先阻止默认事件,再阻止冒泡

html
 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<style>
    .test {
        margin: 10px auto;
        background-color: #42b983;
    }
    .aaa {
        height:250px;
        overflow: auto;
    }
    .aaa>li {
        margin: 10px auto;
        height: 200px;
        background-color: green;
    }
</style>

<div id="root">
    <!--阻止默认事件,不阻止会进行跳转-->
    <a href="https://meethigher.top" @click.prevent="showInfo">test</a>
    <!--阻止冒泡,不阻止会执行方法两次-->
    <div class="test" @click="showInfo">
        <button @click.stop="showInfo">测试冒泡</button>
    </div>
    <!--设置事件只触发一次-->
    <button @click.once="showInfo">事件只触发一次</button>
    <!--使用事件的捕获模式,就是在捕获阶段就开始执行。
    js点击事件的触发,分两个阶段,
    一个是捕获阶段,从dom树最外层找到最内层,
    然后才是冒泡阶段,冒泡阶段是从内向外。
    点击div2正常log顺序是2,1,如果对div1进行捕获,输出就成了1,2
    -->
    <div class="test" @click.capture="showMsg('div1')">
        div1
        <div class="test" @click="showMsg('div2')">
            div2
        </div>
    </div>
    <!--只有event.target是当前操作的元素才会触发-->
    <div class="test" @click.self="showTarget">
        <button @click="showTarget">测试只有当前元素才能触发</button>
    </div>
    <!--事件的默认行为立即执行,无须等待事件回调执行完毕
    滚动事件有scroll,还有wheel
    scroll指滚动条移动,先移动再执行事件
    wheel指鼠标滚轮移动,执行完事件再移动
    -->
    <ul class="aaa" @wheel.passive="calculate">
        <li>向晚</li>
        <li>贝拉</li>
        <li>珈乐</li>
        <li>嘉然</li>
        <li>乃琳</li>
    </ul>
</div>

<script>
    const app=new Vue({
        el:"#root",
        data:{
            name:"meethigher",
            address:"china"
        },
        methods: {
            showInfo(e) {
                // e.preventDefault();//阻止默认事件
                //e.stopPropagation();//阻止冒泡
                alert("test");
            },
            showMsg(param) {
                console.log(param);
            },
            showTarget(e) {
                console.log(e.target);
            },
            calculate() {
                for (let i=0;i<100000;i++) {
                    console.log("a");
                }
                console.log("算完了");
            }
        }
    });
</script>

1.8 键盘事件

先看一个例子

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<div id="root">
    <input type="text" placeholder="按下回车提示输入" @keydown="showInfo">
</div>

<script>
    new Vue({
        el:"#root",
        methods: {
            showInfo(e) {
                if(e.keyCode!==13) return;
                console.log(e.target.value);
            }
        }
    })
</script>

一般操作是上面这样进行的,但是在Vue中,提供了常用的按键别名

  1. 回车:enter
  2. 删除:delete
  3. 退出:esc
  4. 空格:space
  5. 换行:tab,必须与keydown使用
  6. 上:up
  7. 下:down
  8. 左:left
  9. 右:right
  10. ctrl
  11. alt
  12. shift
  13. meta:对应windows的win键
html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<div id="root">
    <input type="text" placeholder="按下ctrl+enter提示输入" @keydown.ctrl.enter="showMsg">
    <input type="text" placeholder="按下ctrl+enter提示输入" @keydown.ctrl.e="showMsg">
</div>

<script>
    // 自定义全局按键修饰符
    Vue.config.keyCodes.e = 69;
    new Vue({
        el:"#root",
        methods: {
            showInfo(e) {
                console.log(e.target.value);
            }
        }
    })
</script>

1.9 计算属性

需求

两个输入框,一个姓,一个名,下面文本框动态展示姓名。

占位符实现

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div id="root">
    姓:<input type="text" v-model="xing"><br>
    名:<input type="text" v-model="ming"><br>
    全名:{{xing.slice(0,3)}}{{ming}}
    全名:{{xing.slice(0,3)}}{{ming}}
</div>
<script src="js/vue.js"></script>
<script>
    new Vue({
        el:"#root",
        data: {
            xing: '',
            ming:''
        }
    })
</script>

修改一次,调用两次。

方法实现

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="root">
    姓:<input type="text" v-model="xing"><br>
    名:<input type="text" v-model="ming"><br>
    全名:{{getFullName()}}
    全名:{{getFullName()}}
</div>
<script src="js/vue.js"></script>
<script>
    new Vue({
        el:"#root",
        data: {
            xing: '',
            ming:''
        },
        methods: {
            getFullName() {
                console.log("方法被调用");
                return this.xing.slice(0,3)+this.ming;
            }
        }
    })
</script>

修改一次,方法调用两次。

计算属性实现

很明显,不管是占位符或是方法,都是在只要数据发生变化后,都要重新读取一下,效率不高。

Vue提供了计算属性,为了解决这个问题,计算属性里面用到了缓存,将数据缓存了起来,便于后续的复用。

何为属性,vue对象中的data里面的可以都是属性(属性名)。

html
 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
<div id="root">
    姓:<input type="text" v-model="xing"><br>
    名:<input type="text" v-model="ming"><br>
    全名:{{getFullName}}<br>
    全名:{{getFullName}}
</div>
<script src="js/vue.js"></script>
<script>
    const app=new Vue({
        el:"#root",
        data: {
            xing: '',
            ming:''
        },
        computed: {
            getFullName: {
                //有人读取fullName时,get就会被调用,并将其返回值作为getFullName的值
                get(){
                    console.log("被调用了");
                    return this.xing+this.ming;
                },
                /*控制台通过app.getFullName=xxx即可执行*/
                set(value) {
                    console.log("set被调用");
                    this.xing=value;
                }
            }
        }
    })
</script>

修改一次,只调用一次。

计算方法实现

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script>
    const app=new Vue({
        el:"#root",
        data: {
            xing: '',
            ming:''
        },
        computed: {
            getFullName() {
                console.log("被调用了");
                return this.xing+this.ming;
            }
        }
    })
</script>

1.10 监视属性

需求

实现一个切换天气按钮,获取当前天气和上一次天气。

常规实现

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="root">
    <h2>今天天气很{{weather}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script src="js/vue.js"></script>
<script>
    const app = new Vue({
        el: "#root",
        data: {
            isHot: true
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            weather() {
                return this.isHot ? "炎热" : "凉爽";
            }
        }
    });
</script>

监视属性实现

监视属性的两种实现

  • 实例化Vue对象时,初始化配置watch
  • 通过已有的Vue对象,obj.$watch监视
html
 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
45
46
47
48
<div id="root">
    <h2>今天天气很{{weather}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script src="js/vue.js"></script>
<script>
    const app = new Vue({
        el: "#root",
        data: {
            isHot: true
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            weather() {
                return this.isHot ? "炎热" : "凉爽";
            }
        },
        // 实例化Vue对象时,初始化配置watch
        // watch: {
        //     isHot: {
        //         //初始化时,令handler调用一次
        //         immediate:false,
        //         //当isHot这个值发生变化时,自动true
        //         handler(newValue,oldValue) {
        //             console.log("isHot被修改了",newValue,oldValue);
        //         }
        //     },
        //     weather: {
        //         immediate:true,
        //         handler(newValue, oldValue) {
        //             console.log("监测weather方法",newValue,oldValue);
        //         }
        //     }
        // }
    });

    // 通过已有的Vue对象,obj.$watch监视
    app.$watch("weather",{
        immediate:true,
        handler(newValue, oldValue) {
            console.log("监测weather方法",newValue,oldValue);
        }
    })
</script>

vue默认不监视多层级数据的变化,如果想要监视多层级监视,就要配置deep。

html
 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
<div id="root">
    <button @click="number.a++">a++</button>
</div>

<script>
    const app = new Vue({
        el: "#root",
        data: {
            isHot: true,
            number: {
                a:1,
                b:2
            }
        },
        watch: {
            number: {
                deep: true,
                 /*多层次数据监视,vue默认不监视下面层次数据的变化*/
                handler() {
                    console.log("number发生变化");
                }
            }
        }
    });
</script>

1.11 绑定样式

绑定class

vue绑定样式需要使用:class,参数内容支持字符串、数组、对象(key的值需要是boolean类型)。

html
 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
<div id="root">
    <div id="weather" class="basic" :class="[weatherClass,weatherValid]" >今天的天气:{{weather}}</div>
    <button @click="changeWeather">查询当前天气</button>
    <button @click="addFont">添加字体样式</button>
</div>
<script src="js/vue.js"></script>
<script>
    const app = new Vue({
        el: "#root",
        data: {
            // weatherClass: ["basic","sunny"],//多样式时的写法
            weatherClass: "sunny",
            weather: "晴天",
            /*为false时,表示:class的引用不生效,true表示生效*/
            weatherValid: {
                isValid: false
            }
        },
        methods: {
            changeWeather() {
                const arr=["sunny","cloudy","rain"];
                const arrDesc=["晴天","多云","雨天"];
                const index=Math.floor(Math.random()*3);
                this.weatherClass=arr[index];
                this.weather=arrDesc[index];
            },
            addFont() {
                let weatherClass = this.weatherClass;
                if(weatherClass instanceof Array) {
                    console.log("是数组,追加");
                    this.weatherClass.push("font");
                }else {
                    console.log("不是数组,拼接");
                    this.weatherClass=this.weatherClass+" font";
                }
            }
        }
    })
</script>