VUE框架

129 阅读12分钟

前端框架

框架和库

库:库更侧重于某一个功能点,库与库之间没有排他性,一个项目中,跨域引入多个库混合使用。
jQuery、lodash
框架:框架给项目提供一套完整的解决方案,框架和框架之间一般不会混合使用。
VUEjs\React\Angular

Angular

诞生于2009年

React

诞生于2013年,起源于facebook的内部项目--Instagram。

VUEjs

诞生于2014年,尤雨溪开发。

vue基础语法

vue实例

const vm = new Vue({});

1.data数据

data用于设置当前vue实例中所有需要用到的数据。
data属性的值只有两种情况:对象、返回对象的函数。
const vm = new Vue({
    data:{
        //数据
    }
    });

const vm = new Vue({
    data:function(){
        return {
            //数据
        }
    }
});

注意:在后续学习完组件以后,组件中的data就只能返回对象的函数了。

el

el 属性用于指定当前vue实例的挂载目标。换句话说el属性就是用来指定vue实例的作用范围。
el的属性值是一个字符串,用于设置挂载目标节点的选择器。
注意:不要将body设置为挂载目标,因为vue最终渲染成功后会将挂载目标元素给替换掉。

模板语法(数据渲染)

在挂载目标范围内,通过{{}}双大括号的语法,可以将vue实例中的数据渲染到页面上。
html
<div id="app">
    <h1>{{msg + student.name}}</h1>
    <h3>{{flag ? 'hello':'world'}}</h3>
    {{num}}
</div>
vue
const vm = new Vue({
    el:'#app',
    data:function(){
        return {
            msg:'hello world',
            num:100,
            student:{
                name:'starnight',
                age:20
                },
                flag:true
            }
        }
    });

事件处理

事件监听
vue中提供了一个v-on指令,用来设置事件监听
<button v-on:click="handleClick">按钮</button>

事件处理函数
vue中提供了一个methods属性,用来设置vue中所有需要用到的方法。

const vm = new Vue({
    methods:{
        handleClick(){
            console.log('123123');
            }
        }
    });

响应式

当一个vue实例创建时,它将data对象中的所有的属性(数据)加入到vue的响应式系统中。
当data中的数据值发生改变时,视图(页面)会自动去“响应”数据的变化,立即更新为最新的数据。

指令

vue中提供了一系列以v-on开头的属性,我们把这些属性称之为“指令”。

v-text

v-text也可以用于数据渲染,该指令所对应的数据,会将标签中原本的数据给覆盖掉。
<h2 v-text="msg">100</h2>
//渲染之后显示的时msg的值

v-html

v-html也可以用于数据渲染,它和v-text、双大括号的区别在于,v-html可以识别数据中的HTML代码。
html
<div id="app">
<h1 v-html="msg"></h1>
</div>
new Vue({
    el:'#app',
    data(){
        return{
            msg:'<a href="#">hello</a>'
            }
        }
    })

v-show

v-show指令通过属性值的真或假,来设置元素的display属性。
html
<div id="app">
<h1 v-show="isShow">{{msg}}</h1>
<button v-on:click="isShow = !isShow">显示/隐藏</button>
</div>
new Vue({
    el:'#app',
    data(){
        return{
            msg:'<a href="#">hello</a>',
            isShow:true
            }
        }
    })

v-on

用于绑定事件监听器
简写@
<button @click="isShow = !isShow">显示/隐藏</button>

v-bind

当我们需要给标签身上的属性设置动态的数据时,就需要用过v-bind来设置该属性。
简写:
<a v-bind:href="link">百度</a>
//简写<a :href="link">百度</a>
new Vue({
    el:'#app',
    data(){
        return{
            msg:'<a href="#">hello</a>',
            isShow:true
            link:'https://www.baidu.com'
            }
        }
    })

条件渲染

vue中提供了v-if、v-else、v-else-if来实现条件渲染。
v-if和v-show的区别:
v-if是通过控制元素是否在DOM节点中渲染,来决定元素的显示或隐藏;
v-show是通过控制元素的display的css样式,来决定元素的显示或隐藏。
当我们需要频繁的进行显示/隐藏的切换时,用v-show,否则用v-if。
template标签
其内容隐藏在客户端之外,该内容在加载页面时是不会呈现、不会渲染出任何信息的。相当于自带属性:template{display:none;}

列表渲染

vue中提供了v-for来实现列表渲染。
根据源数据多次呈现元素或模板块。
<div v-for="item in items">
  {{ item.text }}
</div>

v-for注意点:
1.使用对象
可以针对于数组和对象;
2.key属性
为了跟踪到v-for循环出来的每个节点,则需要为v-for所在的节点提供一个唯一的key属性。key属性的类型只能为string类型或number类型。
3.应用场景
在项目中,会遇到多层v-for嵌套的情况,这时,key的值不能全部取相应的index,不然会报错。由于key的属性可以为string或number,所以可以取对象或数组中为string类型的值作为key属性。
通过v-for渲染出来每一个元素身上都需要添加一个唯一的key属性。
注意:不推荐在同一元素上使用v-if和v-for。

练习

购物车
 <div id="app">
        <table>
            <thead>
                <tr>
                    <th>商品编号</th>
                    <th>商品名称</th>
                    <th>商品单价</th>
                    <th>商品数量</th>
                    <th>商品总价</th>
                    <th>商品操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-if="goodsData.every(item => item.count == 0)">
                    <td>购物车为空</td>
                </tr>
                <tr v-for="item in goodsData" :key="item._id">
                <td>{{item._id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.price}}</td>
                <td>
                    <button @click="item.count--">-</button>
                    <span>{{item.count}}</span>
                    <button @click="item.count++">+</button>
                </td>
                <td>{{item.price * item.count}}</td>
                <!-- 删除 -->
                <td><button @click="goodsData.splice(index,1)">删除</button></td> 
                </tr>
                 </tbody>
        </table>
        <p>合计:{{totalPrice()}}</p>
    </div>
    
    <script src="./js/vue-2.4.0.js"></script>

    <script>
        new Vue({
            el:'#app',
            data(){
                return {
                    goodsData:[
                        { _id:1,name:'苹果',price:'10',count:20},
                        { _id:2,name:'香蕉',price:'20',count:10},
                        { _id:3,name:'梨',price:'15',count:22},
                    ]
                }
            },
            methods:{
                //计算总价
                totalPrice(){
                    const totalPrice = 0;
                    return this.goodsData.reduce((sum,item) => {
                        return sum + item.price * item.count; 
                    },0);
                }
            }
        })
    </script>            

计算属性和侦听器

计算属性

computed
当我们需要对原数据(data)进行一些额外的操作,希望在原数据的基础之上得到一些新数据,但同时又不希望改变原数据。这种情况下,我们会选择用计算属性。
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  }
}
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
计算属性
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // a computed getter
    publishedBooksMessage() {
      // `this` points to the component instance
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>

计算属性的缓存
计算属性的结果会在第一次计算完成后缓存下来,在后续的使用中,只要原数据没有发生变化,计算属性就不会再重新计算。每一次的使用都是读取缓存中的结果。
一旦计算属性中依赖的原数据发生变化,计算属性就会重新计算,也就是说,计算属性所对应的方法会重新调用。

计算属性的getters和setters

该选项接收一个对象,其中键是计算属性的名称,值是一个计算属性 getter,或一个具有 get 和 set 方法的对象 (用于声明可写的计算属性)。
所有的 getters 和 setters 会将它们的 this 上下文自动绑定为组件实例。
<div id="app">
        <h1>{{num}}</h1>
        <button @click="num ='star night'">修改</button>
    </div>
    <script src="./js/vue-2.4.0.js"></script>
    <script>
        new Vue({
            el:'#app',
            data(){
                return{
                    xing: 'st',
                    ming: 'ni'
                }
            },
            //计算属性
            computed:{
                num:{
                    get(){
                        return this.xing + ' ' + this.ming;
                    },
                    set(val){
                        this.xing = val.split(' ')[0];
                        this.ming = val.split(' ')[1];
                    }
                }
            }
        })
    </script>

todolist练习

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .done{
            color: red;
            text-decoration: line-through;
        }
        .btns>* {
            margin-right: 10px;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul>
            <!-- <li>html(false)</li>
            <li>css(true)</li>
            <li>js(false)</li> -->
            <!-- <li class="done" v-for="(item,index) in listData" :key="index">
                {{item.text}}
                <span v-if="item.done">(完成)</span>
                <span v-else>(未完成)</span>
            </li> -->
            <template v-for="(item,index) in filterListData">
                <li class="done" v-if="item.done" @click="item.done = !item.done">{{item.text}}</li>
                <li v-else @click="item.done = !item.done">{{item.text}}</li>
                <!-- @click="item.done = false/true" -->
            </template>
        </ul>
        <div class="btns">
            <template v-for="item in btns">
                <span v-if="item == current" >{{item}}</span>
                <a href="#" v-else @click="current = item">{{item}}</a>
            </template>
            <span>{{doneTotal}}/{{listData.length}}</span>
        </div>
    </div>
    <script src="./js/vue-2.4.0.js"></script>

    <script>
        new Vue({
            el:'#app',
            data(){
                return {
                    listData:[
                        { text:'html',done:false},
                        { text:'css',done:true},
                        { text:'js',done:false},
                    ],
                    btns:['all','done','active'],
                    current:'all'
                }
            },
            computed:{
                filterListData(){
                    switch(this.current){
                        case 'done':return this.listData.filter(item =>item.done);
                        case 'active':return this.listData.filter(item =>!item.done);
                        default:return this.listData;
                    }
                },
                doneTotal(){
                    return this.listData.filter(item => item.done).length;
                }
            }
        })
    </script>

</body>
</html>

侦听器

vue中提供了watch属性,用来侦听data或computed中数据的变化。一旦侦听发生了改变,就会执行对应的侦听方法。
        new Vue({
            el:'#app',
            data(){
                return {
                    currentPage:1
                }
            },
            //侦听器(侦听属性)
            watch:{
                currentPage(newValue,oldValue){
                    //当curentPage发生变化时,该函数会被调用
                }
            }
        })

侦听引用数据类型

new Vue({
            el:'#app',
            data(){
                return {
                    pageData:{
                    currentPage:1
                    }
                }
            },
            //侦听器(侦听属性)
            watch:{
                'pageData.currentPage'(newValue,oldValue){
                    //当curentPage发生变化时,该函数会被调用
                },
                pageData:{
                handler(newValue,oldValue){
                    //当pageData中的任意属性发生变化时,该函数会被调用
                },
                //深度侦听
                deep:true
              }
            }
        })

数据首次渲染时的侦听
默认情况下,watch只有在数据发生改变时才会触发侦听。但是,某些情况下,我们需要数据首次渲染时,也能够触发侦听函数。

 new Vue({
            el:'#app',
            data(){
                return {
                    currentPage:1
                }
            },
            //侦听器(侦听属性)
            watch:{
                currentPage:{
                hanler(newValue,oldValue){
                    //当curentPage发生变化时,该函数会被调用
                },
                //立即执行侦听
                immediate:true
            }
           }
        })

事件处理扩展语法(事件修饰符)
.stop

阻止单击事件继续传播
<a v-on:click.stop='doThis'></a>

.prevent

提交事件不再重载页面
<form v-on:submit.prevent='onSubmit'></form>

修饰符可以串联
<a v-on:click.stop.prevent="doThat"></a>

.capture

添加事件监听器时使用事件捕获模式
即内部元素触发的事件先在此处理,然后才交由内部元素进行处理
<div v-on:click.capture='doThis'>...</div>

class与style绑定

操作元素的class列表和内联样式是数据绑定的一个常见需求。我们可以用v-bind处理:只需要通过表达式计算出字符串结果即可。但是字符串拼接麻烦且容易出错。因此,在将v-bind用于class和style是,vue.js做了增强,表达式的结果类型除了字符串之外,还可以是对象或数组。
对象语法
我们可以传给v-bind:class一个对象,动态地切换class
<div v-bind:class="{active:isActive}"></div>

数组语法

我们可以把一个数组传给v-bind:class,以应用一个class列表
<div v-bind:class="[activeClass,erroeClass]"></div>
vue中datadata:{
    cativeClass:'active',
    erroeClass:'text-danger'
    }
渲染为:
<div class="active text-danger"></div>
如果想根据条件切换列表中的class,可以用三元表达式
<div v-bind:class="[isActive ? activeClass:'',erroeClass]"></div>

绑定内联样式

对象语法
v-bind:style的对象语法十分直观,看着非常像css,但其实是一个javascript对象。css属性名可以用驼峰或短横线(需要引号)分隔来命名
<div v-bind:style="{color:activeColor,fontSize:fontSize+'px'}"></div>
data:{
    activeColor:'red',
    fontSize:30
    }

数组语法

v-bind:style的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles,overridingStyles]"></div>
自动添加前缀
当v-bind:style使用需要添加浏览器引擎前缀的css属性时,如transform,vue.js会自动侦测并添加相应的前缀。

表单输入绑定

vue中提供了v-model指令用于表单元素。
双向数据绑定
通过v-model绑定的元素身上,都具有双向数据绑定的功能。
双向数据绑定,指的是:页面由data中的数据渲染而来,当data中的数据发生改变时,页面会自动更新;当操作页面时,data中的数据也会随之改变。

v-model的使用

1.输入框
在输入框中绑定v-model属性,就可以通过v-model的属性值,实时获取到用户输入的内容。
<input type="text" v-model="name">
data(){
    return{
        name:'starnight',
        }
    }

2.复选框
单个复选框
在单个复选框上绑定v-model属性,可以通过v-model属性值的true或false,来控制复选框的选中或未选中,同时,操作复选框的选中或未选中,也会同步更新v-model属性值的true或false。

<input type="checkbox" v-model="isChecked">
data(){
    return{
        isChecked:true
        }
    },

多个复选框
在多个复选框上绑定同一个v-model属性,通过判断复选框自己的value值是否在v-model的数组里面,来控制复选框的选中或未选中。同时,操作复选框的选中或未选中,也会同步更新复选框的value值在数组中添加或删除。

<input type="checkbox" value="eat" v-model="checkeds">吃饭
<input type="checkbox" value="sleep" v-model="checkeds">睡觉
<input type="checkbox" value="hit" v-model="checkeds">打豆豆
data(){
    return{
        checkeds:['sleep'],
        }
    },

3.单选框
在单选框上绑定v-model属性,通过判断单选框自己的value值是否和v-model的值是否相等,来控制单选框的选中或未选中。

<input type="radio" value="男" v-model="gender">男
<input type="radio" value="女" v-model="gender">女
data(){
    return {
        gender:'男',
        }
    },

4.下拉列表
在select身上绑定v-model属性,通过判断option的value值和v-model的值是否相等,来决定被选中的option。

<select v-model="city">
    <option value="四川">四川</option>
    <option value="上海">上海</option>
    <option value="北京">北京</option>
    <option value="云南">云南</option>
</select>
data(){
    return{
        city:'北京'
    }
},

组件

组件的概念

通常一个应用会以一棵嵌套的组件树的形式来组织
例如,可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为了能在模板中使用,这些组件必须先注册以便vue能够识别。这里有两种组件的注册类型:全局注册和局部注册。
至此,我们的组件都只是通过vue.component全局注册的。
<body>
    <div id="app">
        <hello></hello>
        <world></world>
    </div>
    <div id="root">
        <hello></hello>
        <my></my>
    </div>

    <script src="./js/vue-2.4.0.js"></script>

    <script>
        //全局注册组件(创建组件)(可以嵌套局部)

        Vue.component('hello',{ 
            //组件的模板(组件要显示的标签)
            template:`
                <div>
                    <h1>hello</h1>
                    <h2>hello</h2>
                    <world2></world2> //嵌套
                    <h3>hello</h3>
                </div>
            `
        });
        Vue.component('world2',{
            template:`
                <div>
                    <h1>star</h1>
                </div>
            `
        })
        Vue.component('my',{
            template:`
                <div>
                    <h1 @click='clickEvent'>{{newmsg}}</h1>
                </div>
            `,
            data(){
                return {
                    msg:'night'
                }
            },
            computed:{
                newmsg(){
                    return this.msg+' love';
                }
            },
            methods:{
                clickEvent(){
                    console.log('zyyzyy');
                }
            }
        });

        //局部注册组件(不能嵌套进全局)

        new Vue({
            el:'#app',
            components:{
                'world':{
                    template:`<p>star night</p>`
                }
            }
        })
        new Vue({
            el:'#root'
        })
    </script>
</body>

prop

vue中为组件提供了一个props的属性,用于父组件向子组件传值。
父组件传值
父组件中,可以通过给子组件设置属性的方式,来给子组件进行传值。
Vue.component('father',{
    template:`
        <div>
            <child name="xinwan"></child>
        </div>
    `
})
Vue.component('child',{
    props:['name'],
    template:`<span>{{name}}</span>`
})

子组件接收值
子组件通过props属性,在数组中设置对应的属性名,来接收父组件传递的数据。(可以接收多个传递多个)

Vue.component('child',{
    props:['name'],
    template:`<span>{{name}}</span>`
})

传递的数据类型
当父组件向子组件传递数据时,除了传递字符串以外,其他所有数据类型都需要通过v-bind来进行传递。

Vue.component('father',{
    template:`
        <div>
            <child name="xinwan" :age="20" :flag="true"></child>
        </div>
    `
})

对象形式的props

props可以通过设置成对象的形式,来进行props的验证。
Vue.component('child',{
    props:{
        name:String,    //name接收的数据类型为String
        age:[Number,String],  //age接收的数据类型为Number 或者 String
        gender:{
            type:String,  //gender 接收的数据类型为String
            dafault:'nv' // 当没有接收到gender属性值时,采用default的默认值‘nv’
        },
        friends:{
           type:Object,
           //default:() => {return () }
           default:() => ({}) //当默认值为数组或对象时,要以函数返回值的形式来设置默认值。
           }
       }
   })

自定义事件

基础语法

在父组件的methods属性中定义事件的方法,然后在调用子组件时,通过@自定义名称=方法名来向子组件传递自定义事件。
//父组件
Vue.component('father',{
    template:`
        <div>
            <child @hello="hello"></child>
        </div>
        `,
        methods:{
            hello() {
                console.log('hello');
                },
            }
        })
子组件通过@事件类型="$emit('自定义事件名称')"来触发父组件传递的自定义事件。
//子组件
Vue.component('child',{
    template:`
        <div>
            <h1 @click="$emit('hello')">child</h1>
        </div>
        `
    })

自定义事件的使用场景

1.子组件修改父组件的数据
在父组件中定义一个修改父组件数据的方法,然后通过自定义事件将该方法传递给子组件,让子组件来触发该方法。
Vue.component('father',{
    template:`
        <div>
            <child @add="add"></child>
        </div>
        `,
        data() {
            return {num:100}
        },
        methods:{
            add(){
                this.num++;
            },
        }
    })
//子组件
Vue.component('child',{
    template:`
        <div>
            <button @click="$emit('add')">+1</button>
        </div>
        `
    })

2.子组件传值给父组件
当子组件想要将数据传递给父组件时,通过调用父组件的方法,来进行数据的传递。

Vue.component('father',{
    template:`
        <div>
            <child @add="add"></child>
        </div>
        `,
        data() {
            return {num:100}
        },
        methods:{
            add(newNum1,newNum2){
                this.num += newNum1 + newNum2;
            },
        }
    })
$emit()的第二个参数,用来向父组件传递数据
//子组件
Vue.component('child',{
    template:`
        <div>
            <button @click="$emit('add',200,300)">+1</button>
        </div>
        `
    })

插槽

vue中提供了slot来作为插槽使用。

插槽内容

从组件外部往组件内部的插槽添加内容。
<cpn><h2>插槽内容</h2></cpn>
Vue.component('cpn',{
    tempalte:`
        <div>
            <slot></slot>
        </div>
        `
    });

后备内容(插槽的默认值)

当我们使用组件时,没有给插槽传递内容,默认情况下,插槽内容为空。但是我们也可以自己手动去给插槽设置默认值,即当没有给插槽传递数据时,页面就显示插槽的默认值。
<cpn></cpn>
Vue.component('cpn',{
    template:`
        <div>
            <slot>插槽的默认值</slot>
        </div>
        `
    });

具名插槽

当一个组件中,有多个插槽时,我们可以通过name属性给插槽设置名字。
<cpn>
    <template v-slot:header>
        <h1>头部</h1>
    </template>
    <template v-slot:footer>
        <h1>尾部</h1>
    </template>
</cpn>
Vue.component('cpn',{
    template:`
        <div>
            <slot name="header"></slot>
            <slot name="footer"></slot>
        </div>
        `
    });

作用域插槽

当插槽中渲染的数据,是由插槽所在的子组件提供,但是插槽显示的标签是由父组件提供,这种情况下我们就需要用到作用域插槽。
Vue.component('father',{
    template:`
        <div>
            <child v-slot="slotProps">
                <ul>
                    <li v-for="item in slotProps.students" :key="item">{{item}}</h1>
                </ul>
            </child>
            <chile v-slot="slotProps">
                <ol>
                    <li v-for="item in slotProps.students" :key="item">{{item}}</li>
                </ol>
            </child>
        </div>
    `
});

Vue.component('child',{
    data(){
        return {
            students:['张三','李四','王五']
            }
        },
        template:`
            <div>
                <h3>学生列表</h3>
                <slot :students="students"></slot>
            </div>
        `
    });

生命周期函数

new Vue({
            el:'#app',
            data(){
                return {
                    msg:'star'
                }
            },
            beforeCreate(){
                console.log('创建前');
            },
            created(){
                console.log('创建完成');
            },
            beforeMount(){
                console.log('挂载前');
            },
            mounted(){
                console.log('挂载完成');
            },
            beforeUpdate(){
                console.log('数据更新前');
            },
            updated(){
                console.log('更新完成');
            },
            beforeDestroy(){
                console.log('销毁前');
            },
            destroyed(){
                console.log('销毁完成');
            }
        })

created\mounted:页面初始化时向服务端发起请求。

动态组件

Vue.component('tabs',{
    data(){
        return {
            current:'home'
        }
    },
    template:`
        <div>
            <div>
                <button @click="current = 'home'">home</button>
                <button @click="current = 'about'">about</button>
                <button @click="current = 'psots'">posts</button>
            </div>
            <div>
            <keep-alive>
                <component :is="current"></component>
            </keep-alive>
            </div>
        </div>
    `
})

Vue.component('home',{
    template:`
        <h1>home的组件内容</h1>
        `
    })
Vue.component('about',{
    template:`
        <h1>about的组件内容</h1>
        <input type="text"/>
        `
    })
Vue.component('psots',{
    template:`
        <h1>psots的组件内容</h1>
        `
    })