Vue.js第2课-基础

作者: e20a12f8855d | 来源:发表于2019-05-11 07:49 被阅读20次

    一、Vue.js 实例

    一个 Vue.js 的项目,是由很多个组件组成的,Vue.js 组件也是一个实例,也可以说一个 Vue.js 的项目是由很多很多 Vue.js 实例拼装成的。

    <div id="app">
        <div v-on:click="clickFun">{{msg}}</div>
        <test></test>
    </div>
    <script>
        // 创建一个全局组件
        Vue.component('test', {
            template: "<h1>hello</h1>"
        })
    
        var app = new Vue({
            el: "#app",
            data: {
                msg: "hello world"
            },
            methods: {
                clickFun() {
                    alert("hello");
                }
            }
        })
        console.log(app); // wn {_uid: 0, _isVue: true, $options: {…}, _renderProxy: wn, _self: wn, …}
        console.log(app.$el); // <div id="app"><div>hello world</div></div>
        console.log(app.$data); // {__ob__: we}
        console.log(app.$data.msg); // hello world
    </script>
    

    二、Vue.js 实例生命周期函数

    生命周期函数就是 Vue.js 实例在某一个时间点会自动执行的函数,这些函数并不是放到 methods 中,而是直接放到 Vue.js 的实例中。

    <div id="app"></div>
    <script>
        var app = new Vue({
            el: "#app",
            template: "<div>{{msg}}</div>",
            data: {
                msg: "hello world"
            },
            // Vue 实例部分初始化的时候执行
            beforeCreate: function () {
                console.log("beforeCreate");
            },
            created: function () {
                console.log("create");
            },
            beforeMount: function () {
                console.log(this.$el); // <div id="app"></div>
                console.log("beforeMount");
            },
            mounted: function () {
                console.log(this.$el); // <div>hello word</div>
                console.log("mounted");
            },
            // beforeDestroy 和 destroyed 需要先销毁,才会执行,在控制台执行 app.$destroy
            beforeDestroy: function () {
                console.log("beforeDestroy");
            },
            destroyed: function () {
                console.log("destroyed");
            },
            // 当数据发生改变时,beforeUpdate 和 updated 才会执行
            beforeUpdate: function () {
                console.log("beforeUpdate");
            },
            updated: function () {
                console.log("updated");
            }
        })
    </script>
    

    下图是 Vue.js 官网上生命周期图示:

    三、Vue.js 的模板语法

    1、插值表达式

    <div id="app">{{msg}}</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                msg: "Hello World!"
            }
        })
    </script>
    

    2、v-text

    v-text 指令,让标签内的 innerText 和 msg 绑定,后面的值是一个 js 表达式:

    <div id="app">
        <div id="Vtext" v-text="msg"></div>
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                msg: "Hello World!"
            }
        })
        var Vtext = document.getElementById('Vtext');
        console.log(Vtext.innerText); // Hello World!
    </script>
    

    3、v-html

    v-html 让标签内的 innerHTML 和 msg 绑定:

    <div id="app">
        <div id="Vhtml" v-html="msg"></div>
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                msg: "Hello World!"
            }
        })
        var Vhtml = document.getElementById('Vhtml');
        console.log(Vhtml.innerHTML); // Hello World!
    </script>
    

    目前来看,v-text 和 v-html 没有区别,我们把 msg 中的值变成一个标签,例如放到 h1 标签中,v-text 渲染的依然是 “< h1>hello</ h1>”,而 v-html 渲染出的就是 h1 字号的 “hello”。

    <div id="app">
        <div v-text="msg"></div>
        <div v-html="msg"></div>
        ---
        <div v-text="msg2"></div>
        <div v-html="msg2"></div>
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                msg: "Hello World!",
                msg2: "<h1>Hello World!</h1>"
            }
        })
    </script>
    

    通过以上的例子得出,插值表达式和 v-text 的渲染结果是一样的。

    凡是 “v-***” 后面的内容都是一个 js 表达式,在表达式中,不仅可以写一个变量,还可以往后面加一个字符串,插值表达式中也可以写 js 表达式:

    <div id="app">
        {{msg + ' world1'}}
        <div v-text="msg + ' world!'"></div>
        <div v-html="msg + ' world!'"></div>
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                msg: "Hello"
            }
        })
    </script>
    

    四、计算属性,方法和侦听器

    fullName 的计算。

    方式一:computed 计算属性(有缓存机制,会提高性能)

    <div id="app">{{fullName}} {{age}}</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                firstName: "Liu",
                lastName: "Zhenghe",
                age: 26
            },
            computed: {
                fullName: function () {
                    console.log("计算了一次!");
                    return this.firstName + this.lastName;
                }
            }
        })
    </script>
    

    可以看到页面渲染出 LiuZhenghe 26。

    如何证明 computed 具有缓存机制?在 fullName 方法中打印一句话“计算了一次!”,在控制台改变 age 的值,再改变 fullName 中计算过的属性值。

    可以看到,页面一刷新的时候,打印一遍 “计算了一次!”,改变 age,没有打印这句话,改变 lastName,又打印一遍 “计算了一次!”。

    方式二:methods

    注意:因为 fullName 这次写到了 methods 中,所以在模板引擎中需要加括号。

    <div id="app">{{fullName()}} {{age}}</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                firstName: "Liu",
                lastName: "Zhenghe",
                age: 26
            },
            methods: {
                fullName: function () {
                    console.log("计算了一次!");
                    return this.firstName + this.lastName;
                }
            }
        })
    </script>
    

    可以看到,页面一刷新的时候,打印一遍 “计算了一次!”,改变 age,也打印这句话,改变 firstName 和 lastName,又都打印一遍 “计算了一次!”。

    所以,说明 methods 方法是没有缓存机制的,只要改变了数据,就会重新计算一次。

    方式三:watch 侦听器

    <div id="app">{{fullName}} {{age}}</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                firstName: "Liu",
                lastName: "Zhenghe",
                fullName: "LiuZhenghe",
                age: 26
            },
            watch: {
                firstName: function () {
                    console.log("计算了一次!");
                    this.fullName = this.firstName + this.lastName;
                },
                lastName: function () {
                    console.log("计算了一次!");
                    this.fullName = this.firstName + this.lastName;
                }
            }
        })
    </script>
    

    在控制台,改变 age,并没有打印出 “计算了一次!”这句话,改变 firstName 和 lastName 的值,发现打印出了这句话,说明 watch 和 computed 一样具有缓存机制,但和 computed 相比,watch 逻辑代码多出很多,所以,当这三种计算方式都能实现计算功能的时候,优先选择 computed。

    五、计算属性的 setter 和 getter

    computed 有这样一个特性:当他依赖的值发生变化的时候,它就会去重新的计算。

    <div id="app">{{fullName}}</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                firstName: "Liu",
                lastName: "Zhenghe",
            },
            computed: {
                fullName: {
                    get: function () {
                        return this.firstName + " " + this.lastName
                    },
                    // set 方法可以接收一个设置后的值,例如在控制台改变 fullName 的值 “app.fullName="Li Zheng"”,将会打印出 “Li Zheng”。
                    set: function (value) {
                        console.log(value);
                        // 将设置后的 fullName 值通过空格分开,并存放到数组 arr 中,按照上面给 fullName 设置的值,下边 arr 的值将会打印出 “(2) ["Li", "Zheng"]”
                        var arr = value.split(" ");
                        console.log(arr);
                        this.firstName = arr[0];
                        this.lastName = arr[1];
                    }
                }
            }
        })
    </script>
    

    通过这个例子,可以知道,在 computed 上,不仅可以通过 get 方法,通过其他的值算出一个新值,同时可以写 set 方法,通过设置一个值,来改变他相关联的一个值,而改变了相关的值后,又使 fullName 的值重新计算。

    六、Vue.js 中的样式绑定

    样式和数据绑定,实现:点击一次,元素变色,再点击一次,元素颜色变回来。

    方式一、class 的对象绑定,借助 class 和对象的形式做样式和数据的绑定。

    <div id="app" :class="{activted:isActivted}" v-on:click="changeColor">Hello World</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                isActivted: false,
            },
            methods: {
                changeColor: function () {
                    this.isActivted = !this.isActivted;
                }
            }
        })
    </script>
    <style>
        .activted {
            color: red
        }
    </style>
    

    上面代码中,通过给元素绑定点击事件,在点击事件中,改变 isActivted 的值,来确定是否给元素的 class 加 activted,然后给 activted 设置一个样式,来实现点击切换颜色效果。

    方式二、:class 中不再写入一个对象,而是写一个数组,在数组中写一个 activted。

    <div id="app" :class="[activted]" v-on:click="changeColor">Hello World</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                activted: "",
            },
            methods: {
                changeColor: function () {
                    // 用 if 判断实现点击切换效果:
                    // if (this.activted === "activted") {
                    //     this.activted = "";
                    // } else {
                    //     this.activted = "activted";
                    // };
    
                    // 用三元运算符实现点击切换效果:
                    this.active = this.activted === "activted" ? this.activted = "" : this.activted =
                        "activted";
                }
            }
        })
    </script>
    <style>
        .activted {
            color: red
        }
    </style>
    

    在上面代码中,首先将 activted 设置为空,然后在点击事件中通过 if 或三元运算符来判断,当 data 中的 activted 为空时,就设置为 “activted”,当是“activted”时,再设置为空,以此来实现点击切换颜色效果。

    方式三、通过 style 来改变样式,:style 中写 js 字符串。

    <div id="app" :style="styleObj" v-on:click="changeColor">Hello World</div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                styleObj: {
                    color: "black"
                }
            },
            methods: {
                changeColor: function () {
                    this.styleObj.color = this.styleObj.color === "black" ? "red" : "black";
                }
            }
        })
    </script>
    

    上面代码中,将 data 中的样式对象 styleObj 传到 :style 中,在点击事件中,通过改变 styleObj 中的 color 值来实现点击切换颜色效果。

    方式四:通过 style 来改变样式,:style 中写数组,数组中还可以挂载多个样式。

    <div id="app" :style="[styleObj,{fontSize : '24px'},{fontWeight : '900'}]" v-on:click="changeColor">Hello World
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                styleObj: {
                    color: "black"
                }
            },
            methods: {
                changeColor: function () {
                    this.styleObj.color = this.styleObj.color === "black" ? "red" : "black";
                }
            }
        })
    </script>
    

    无论是绑定 class 还是 style,都有两种方式,一种是通过对象的方式绑定,一种是通过数组的方式绑定。

    七、Vue.js 中的条件渲染

    1、条件渲染 v-if 和 v-show 对比

    v-if 后面跟一个 js 表达式,它的返回值(true, false)决定了这个元素是否真实的被挂载到页面上。

    <div id="app">
        <div v-if="show">Hello World!</div>
        <div v-show="show">Hello World!</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                show: true
            }
        })
    </script>
    

    在 data 中 show 默认为 true,是显示的,可以在控制台中修改一下 show 的值:“app.show = false”,发现两个 dom 不存在了,改为 true,这两个 dom 又显示了。

    打开网页,可以看到,无论是 v-if 还是 v-show,如果 data 中的 show 值是 false,那么两个元素都不显示,我们在控制台中查看一下两个元素:

    发现,v-if 在页面上就不显示了,而 v-show 元素是存在的,只是样式被加了 “display: none;” ,所以 v-show 的性能更会高一些,因为它不会频繁的去把有个 dom 从页面上删再添加。

    2、更复杂的使用 v-if

    v-if 和 v-else
    <div id="app">
        <div v-if="show">Hello World!</div>
        <!-- 注意,v-if 和 v-else 两个元素中间不能加其他元素,否则条件判断不能生效。 -->
        <div v-else>Bye World!</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                show: false
            }
        })
    </script>
    

    上面代码中,如果 show 的值是 false 的话,就显示 v-else 中的值,需要注意的是 v-if 和 v-else 两个元素中间不能加其他元素,否则条件判断不能生效。

    多条件(v-if、v-else-if、v-else)
    <div id="app">
        <div v-if="show === 'a'">This is a!</div>
        <div v-else-if="show === 'b'">This is b!</div>
        <div v-else>This is other</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                show: a
            }
        })
    </script>
    

    可以在控制台中改变一下 show 的值,当时 show 的值为 a 时,显示 “This is a”,为 b 时,显示 “This is b”,为其他时,显示 “This is other。”

    3、key 值

    <div id="app">
        <div v-if="show">
            用户名:<input type="text">
        </div>
        <div v-else>
            邮箱:<input type="text">
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                show: true
            }
        })
    </script>
    

    上面代码,因为 show 的值是 true,所以此时页面上显示的是用户名输入框,我们在输入框中输入内容,然后在控制台中改变 show 的值为 flase,发现输入框更换为邮箱了,但是输入框里的内容并没有变,这是因为 Vue 在重新渲染页面的时候,会尽量的复用页面上已存在的 dom,当显示用户名的时候,已经有了一个 input,当切换邮箱名的时候,Vue 会发现以前页面上有一个 input,所以他的机制会尽量的帮你复用页面上的 dom,回去尝试复用这个 input,但是内容并没有被清空。

    解决方法:在 input 中加 key 值,例如给用户名加一个 key 值 “key="username"”,邮箱加一个 “key="email"”,此时再改变 show 的值,发现输入框内容被清空了功能也没有任何的 bug 了。当给元素加一个 key 值的时候,Vue 会知道他是页面上唯一的元素,如果两个元素的 key 值不一样,Vue 就不会尝试复用以前的 input 标签了。

    <div id="app">
        <div v-if="show">
            用户名:<input type="text" key="username">
        </div>
        <div v-else>
            邮箱:<input type="text" key="email">
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                show: true
            }
        })
    </script>
    

    八、Vue.js 中的列表渲染

    先来看一下列表循环最基础的内容:

    <div id="app">
        <div v-for="item in list">{{item}}</div>
        <div v-for="item of list">{{item}}</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [
                    "one", "two", "three", "four", "five"
                ]
            }
        })
    </script>
    

    在 v-for 中,in 和 of 的效果是一样的,可以在 item 后再加一个参数 index,代表索引下标:

    <div id="app">
        <div v-for="(item, index) of list">{{item}} --- {{index}}</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [
                    "one", "two", "three", "four", "five"
                ]
            }
        })
    </script>
    

    在实际的开发中,为了提升循环显示的性能,我们会给每一个循环项上加唯一的 key 值,可以在循环向上加一个 key 值,因为目前 index 的值是唯一的,所以使用 index 作为 key 值。

    <div id="app">
        <div v-for="(item, index) of list" :key="index">{{item}} --- {{index}}</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [
                    "one", "two", "three", "four", "five"
                ]
            }
        })
    </script>
    

    但是不推荐这样使用 index 的,因为使用 index 作为 key 值,在平凡操作 DOM 元素相对应的数据的时候,是比较费性能的,可能让 Vue 没法充分的复用 dom 节点。如果不用 index 做 key 值。那用什么呢?

    一般在真正的项目中,后端向前端返回数据的时候,list 并不是写死的一个数据,后端反数据的时候,一般会携带一个数据相关的唯一标识符,一般是 id,可能是数据对应的一个随机的数据段,例如:

    <div id="app">
        <div v-for="(item, index) of list" :key="item.id">{{item.text}} --- {{index}}</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [{
                    id: "10001",
                    text: "one"
                }, {
                    id: "10002",
                    text: "two"
                }, {
                    id: "10003",
                    text: "three"
                }],
            }
        })
    </script>
    

    这个时候,key 值就没必要是 index 了,可以用 item.id,我们可以参考之前 TodoList 在控制台中给 list 添加数据,可以看到数据添加成功,页面更新了:

    如果通过下标添加数据呢?可以在控制台看到数据添加成功,但是页面却没有更新。

    到这里,需要讲一个内容,当我们去尝试修改数组里的内容时,不能直接通过下标的形式,只能通过 Vue 提供的几个数组变异方法来操作数组,才能够实现数据发生变化,页面也跟着变这种响应式效果。在 Vue 中一共提供了 7 个数组变异方法来帮助我们操作数组,他们分别是:

    • pop:把数组最后一项删除掉。
    • push:往数组里增加一条。
    • shift:把数组的第一项删除掉。
    • unshift:往数组的第一项里加点儿内容。
    • splice:数组的截取。
    • sort:对驻足进行排序。
    • reverse:对数组取反。

    回到页面上,例如将第二项做一个替换,可以在控制台做如下操作:

    除了变异方法可以改变页面数据的显示,还有一种方法,就是改变引用:

    上边数组循环只循环了一个数据,假设有这样一个情况:不仅要根据 list2 循环一个 div,还要根据 list 循环一个 span 标签:

    <div id="app">
        <div v-for="(item, index) of list">
            <div>{{item.text}} --- {{index}}</div>
            <span>{{item.text}} --- {{index}}</span>
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [{
                    id: "10001",
                    text: "one"
                }, {
                    id: "10002",
                    text: "two"
                }, {
                    id: "10003",
                    text: "three"
                }],
            }
        })
    </script>
    

    如果要想显示一个 span 一个 div 就必须在外层包裹一个 div,否则就会先显示完 div 再显示 span, 假设在需求里就不想让这个 div 存在,有什么办法么?template 占位符,可以把外层 div 改为 template,也可以理解为一个模板占位符,回到页面上,可以看到依然是 div,span 的显示方式,但是最外层的 div 就消失了。

    <div id="app">
        <template v-for="(item, index) of list">
            <div>{{item.text}} --- {{index}}</div>
            <span>{{item.text}} --- {{index}}</span>
        </template>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [{
                    id: "10001",
                    text: "one"
                }, {
                    id: "10002",
                    text: "two"
                }, {
                    id: "10003",
                    text: "three"
                }],
            }
        })
    </script>
    

    其实这个 template 模板占位符可以帮我们去包裹一些元素,但是在循环的时候并不会被真正的渲染到页面上。

    除了数组可以做循环之外,其实还可以对对象做一个循环,接下来看一下对象怎么做循环:

    <div id="app">
        <div v-for="item of userInfo">{{item}}</div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                userInfo: {
                    name: "liu",
                    age: "26",
                    gender: "male",
                    salary: "secret"
                }
            }
        })
    </script>
    

    item 还可以接收其他内容 key,index:

    <div id="app">
        <div v-for="(item, key, index) of userInfo">
            {{item}} --- {{key}} --- {{index}}
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                userInfo: {
                    name: "liu",
                    age: "26",
                    gender: "male",
                    salary: "secret"
                }
            }
        })
    </script>
    

    我们在控制台改变一下 userInfo 中的值:

    可以看到,改变 userInfo 中的某个值,页面会更新,但是新添加一个 address 值,数据变了,但页面不会更新,所以当你去便历对象的时候,如果直接动态的往里加值是不好用的,但是如果就是相加那怎么加呢?

    可以和数组的方式一样,直接改变他的引用:

    通过改变引用这种方式,数据变化了,页面也就变化了。

    九、Vue.js 中的 set 方法

    先来回顾上一节我们说过的,如果要改变对象 userInfo 中的属性和值,就要通过它的引用来改变,这样才会让页面也更新。

    <div id="app">
        <div v-for="(item, key, index) of userInfo">
            {{item}} --- {{key}} --- {{index}}
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                userInfo: {
                    name: "liu",
                    age: "26",
                    gender: "male",
                    salary: "secret"
                }
            }
        })
    </script>
    

    那除了改变引用是否还有其他方法能在改变数据后页面也更新呢?下面看一下 Vue.js 中的 set 方法。

    1、对象中的 set 方法

    还是用上的代码,可以通过构造函数来更新 userInfo 中的属性值,例如:“Vue.set(app2.userInfo,"address","Beijing")”,也可以通过实例对象来更新 userInfo 中的属性值,例如 “app2.$set(app2.userInfo,"tel","1234567")”,这两种方法在更新数据后,页面也都会同步更新。

    2、数组中的 set 方法

    <div id="app">
        <div v-for="item of list">
            {{item}}
        </div>
    </div>
    
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                list: [1, 2, 3, 4]
            }
        })
    </script>
    

    在控制台中,分别通过全局对象(构造函数)和实例对象来更新 list 中的数据,可以看到这两种方法在更新数据后,页面也都会同步更新。


    长得好看的都会关注我的 o(≧v≦)o~~

    相关文章

      网友评论

        本文标题:Vue.js第2课-基础

        本文链接:https://www.haomeiwen.com/subject/yvptaqtx.html