Vue.js

172 阅读2分钟

1. 双向数据绑定

input框中的文字可以即时显示在<h1>标签中:

    <div id="app">
        <input type="text" :value="str" @input="change">
        <h1>{{str}}</h1>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                str: '我爱Vue'
            },
            methods: {
                change: function (e) {
                    let event = e || event;
                    this.str = event.target.value
                }               
            }
        })
    </script>

v-model就类似于上面的写法,是一种语法糖,来实现双向数据绑定

@keyup后面可以接修饰符 .enter .keyCode值 , 当按下回车键时,alert 出 input框中的文字:

    <div id="app">
        <input type="text" v-model="str" @keyup.13="keyFn">
        <h1>{{str}}</h1>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                str: '我爱Vue'
            },
            methods: {
                keyFn(e) {
                    alert(this.str)
                }
            }
        })
    </script>

2. v-if 与 v-show 的区别

<h1 v-if="false">pineapple</h1>
v-if 的元素只会在指令的表达式返回 true 时被渲染。如返回 false ,在DOM文档中找不到对应的元素,变成了注释。
<span v-show="false">apple</span>
v-show 的元素始终会被渲染并保留在 DOM 中。 只是简单地基于 CSS display进行切换。

v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show 较好;
如果在运行时条件很少改变(例如页面一开始加载的时候进行判断显示),则使用 v-if 较好。
在实际的开发过程中,使用v-if比较多。

3. v-if 与 v-for 一起使用

不推荐同时使用 v-if 和 v-for
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。

    <div id="app">
        <ul>
            <!--  <li v-for="(item,index) in list" v-if="item>1"> -->
            //不推荐上面的写法
            <li v-for="(item,index) in list">
                <span v-if="item>1">
                    {{item}}
                </span>
            </li>
        </ul>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                list: [1, 2, 3]
            }
        })
    </script>

❤️解决思路:使用computed计算属性来过滤数据,之后再渲染过滤好的的数据。

    <div id="app">
        <ul>
            <li v-for="(item,index) in arr">
                {{item}}
            </li>
        </ul>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                list: [1, 2, 3]
            },
            computed: {
                arr: function () {
                    return this.list.filter(r => r > 1)
                }
            }
        })
    </script>

4. 监听器 watch

监听器watch, 只要data中的值被改变了就会被触发。
基本数据类型可以使用简写的方式。
引用数据类型使用简写的方式无效,需改用对象的方式,加deep:true深度监听。

    <div id="app">
        <h1>¥{{price}}</h1>
        <button @click="up">涨价10元</button>
        <h1>¥{{car.price}}w</h1>
        <button @click="carUp">涨价1w</button>
        <h2>您的爱车相比原价涨了{{car.expensive}}w</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                price: 100,
                car: {
                    price: 100,
                    price0: 100,
                    expensive: 0
                }
            },
            watch: {
             /*属性需要和data中的属性相对应 */
             /* 第一个参数a表示最新的值 第二个参数b表示之前的值 */
                price: function (a, b) {
                    console.log('相比原价贵了' + (a - 100) + '元');
                },
                car: {
                    // 进入页面会立即执行监听器里面的handler方法
                    immediate: true,
                    // 深度监听
                    deep: true,
                    //handler方法名是固定的不可以被篡改 
                    handler: function () {
                        this.car.expensive = this.car.price - this.car.price0
                    }
                }
            },
            methods: {
                carUp: function () { this.car.price++ },
                up: function () {
                    this.price = this.price + 10
                }
            }
        })
    </script>

5. 周期函数/钩子函数

    <div id="app">
        <h1>{{msg}}</h1>
        <button @click="change">改变msg</button>
        <button @click="destroy">销毁Vue实例</button>
        <h1>{{time}}</h1>
    </div>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        /* Vue的八大生命周期钩子函数 */
        /* 区别之一:执行顺序的问题 beforeCreate>created>beforeMount>mounted */
        new Vue({
            el: "#app",
            data: {
                msg: '我爱Vue',
                time: 0,
                timeId: null
            },
            /* Vue实例化对象创建之前 */
            beforeCreate() {
                /* 实例化对象创建之前是获取不到data里面的数据的 */
                console.log('beforeCreate', this.msg)
            },
            /* Vue实例化对象创建之后 */
            created() {
                /* 实例化对象创建之后可以获取data里面的数据 */
                /* 实例化对象创建之后不可以获取到dom包括根节点 */
                console.log('created', this.msg, this.$el)
                /* ★一般在created里面调用接口把接口里面的数据赋值给到Vue的data中 */
                this.timeId = setInterval(() => {
                    this.time++;
                    console.log(this.time)
                }, 500)
            },
            /* Vue的dom挂载之前 */
            beforeMount() {
                /* dom挂载之前可以获取到根节点 */
                /* beforeMount还没有把data中的数据渲染到dom节点上 */
                console.log('beforeMount', this.$el)
            },
            /* Vue的dom挂载之后 */
            mounted() {
                /* mounted已经把data中的数据渲染到了dom节点上 */
                console.log('mounted', this.$el)
                /* ★一般在获取dom节点操作要放在mounted中执行,例如echarts中获取根元素 */
            },
            /* Vue的data值更新前 */
            /* 当我把Vue实例中的data中的值改变了会触发beforeUpdate和updated */
            /* 顺序上 beforeUpdate执行顺序优先于updated  */
            beforeUpdate() {
                console.log('beforeUpdate', this.msg, this.$el)
            },
            /* Vue的data值更新后 */
            updated() {
                console.log('updated', this.msg, this.$el)
            },
            /* Vue组件销毁前 */
            /* 在调用$destroy()方法的时候 会执行下面的两个钩子函数 */
            /* 执行顺序上beforeDestroy优先于destroyed执行  */
            /* ★beforeDestroy和destroyed的一个使用场景是在销毁定时器节约内存的时候都可以使用 */
            beforeDestroy() {
                console.log('beforeDestroy', this.msg, this.$el)
            },
            /* Vue组件销毁后 */
            destroyed() {
                console.log('destroyed', this.msg, this.$el)
                clearInterval(this.timeId)
            },
            methods: {
                change() {
                    this.msg = '我爱React'
                },
                destroy() {
                    this.$destroy();
                }
            }
        })
    </script>

6. 自定义指令

    <div id="app">
        <input type="text" v-focus="{background:'yellow'}">
        <p 
        v-fontsize="{fontSize:'50px'}" 
        v-sty="{background:'red',color:'yellow'}">hello</p>
    </div>
    <script src="./vue.min.js"></script>
    <script>
        /* 全局自定义指令 全面的写法 */
        Vue.directive('focus', {
            /*  当绑定元素插入到 DOM 中 */
            inserted: function (el, binding) {
                console.log(binding)
                el.focus();
                el.style.background = binding.value.background;
            }
        })
        new Vue({
            el: "#app",
            /* 局部的自定义指令 */
            directives: {
                fontsize: {
                    inserted: function (el, binding) {
                        console.log(binding)
                        el.style.fontSize = binding.value.fontSize;
                    }
                },
                sty(el, binding) {
                    el.style.color = binding.value.color;
                    el.style.background = binding.value.background;
                }
            }
        })
    </script>

7. 过滤器+渲染优化

<head>
    <style>
        [v-cloak] {
            display: none;
        }
    </style>
</head>

<body>
    <!-- 使用v-属性 style设置display:none 实现渲染优化 -->
    <div id="app" v-cloak>
        <!-- | 过滤器的管道符 -->
        <h1>{{'春如四季'|fn('天气')|fn('多变')}}</h1>
        <h1>{{'宝贝'|str('快逃')}}</h1>
        <h1>{{msg|dao}}</h1>
    </div>
    <script src="./vue.min.js"></script>
    <script>
        /* 全局过滤器 */
        Vue.filter('fn', function (v, s) {
            /* v就是管道符左边的数据 s为传过来的参数*/
            return v + s;
        })
        new Vue({
            el: "#app",
            data: {
                msg: 'hello'
            },
            /* 局部过滤器 */
            filters: {
                str: function (v, s) {
                    return v + s;
                },
                //字符串倒序
                dao: function (v) {
                    return v.split('').reverse().join('')
                }
            }
        })
    </script>
</body>

8. 组件

组件
概念组件即自定义控件
用途组件能够封装可重用代码,扩展HTML标签功能
本质自定义标签
分类全局组件:不同作用域内均可使用
局部组件:只在定义该组件的作用域内可以使用

❗注意:组件和Vue实例类似,需要注册后才可以使用。

<body>
    <div id="app">
        <!-- 组件名中含有大写字母,浏览器编译的时候会转成小写,导致找不到相应的组件,
        可以全变成小写,中间加-来解决 -->
        <!-- 所有的组件都需要使用一个div来包裹 -->
        <div>
            <child-b />
        </div>
        <div>
            <son />
        </div>
    </div>
    <!-- 模板的第二种写法 使用template加id -->
    <template id="ChildB">
        <!-- template里面也需要使用div来包裹一下 -->
        <div>
            <h1>我是ChildB</h1>
            <h1>我是ChildB</h1>
            <div>
                <child-a />
            </div>
        </div>
    </template>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        /* 一定要写在new Vue的前面 */
        /* 全局组件 */
        Vue.component('ChildA', {
            /* 模板的第一种写法 直接写 */
            template: '<h1>我是ChildA</h1>',
            created: function () {
                console.log('我是childA')
            }
        })
        Vue.component('ChildB', {
            template: '#ChildB'
        })
        new Vue({
            el: "#app",
            //局部组件
            components: {
                son: {
                    template: `<h1>我是局部组件son</h1>`,
                    created() {
                        console.log('son')
                    }
                }
            }
        })
    </script>

9. 父子组件传值

子改父的步骤
①使用Vue里面的$emit方法发送一个自定义事件,如childchange
②在父组件上使用@childchange="ctrl" 绑定自定义事件,触发父组件的ctrl方法
③在父组件中的ctrl方法内把父组件的对应的值给改了

    <!-- 父组件有个一个数组对象 传值给子组件展示    
    点击子组件里面的某一项 可以把父组件里面的数组的某一项给删除 -->
    <div id="box">
        <kid :list='list' @delkid='del' />
    </div>
    <template id="kid">
        <div>
            <ul>
                <li v-for="(item,index) in list" :key="index" @click="del(index)">{{item.content}}</li>
            </ul>
        </div>
    </template>
    <script src="./node_modules/vue/dist/vue.min.js"></script>
    <script>
        new Vue({
            el: '#box',
            data: {
                list: [{ content: 'apple' }, { content: 'pineapple' }, { content: 'applepie' }]
            },
            methods: {
                del(index) {
                    this.list.splice(index, 1)
                }
            },
            components: {
                kid: {
                    template: '#kid',
                    props: ['list'],
                    methods: {
                        del(index) {
                            this.$emit('delkid', index)
                        }
                    },
                }
            }
        })
    </script>

10. 插槽

(1)

    <div id="app">
        <child-a>
            <h1 slot="gy1">我爱美丽的小公园1</h1>
            <h1 slot="gy2">我爱美丽的小公园2</h1>
        </child-a>
    </div>
    <template id="childA">
        <div>
            <!-- 匿名插槽 <slot></slot> -->
            <!-- 具名插槽 -->
            <slot name="gy1"></slot>
            <h1>我是childA</h1>
            <slot name="gy2"></slot>
        </div>
    </template>
    <script src="./vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            /* 注册组件的命名是首字母大写 使用组件的时候中间要使用-分割 */
            components: {
                ChildA: {
                    template: "#childA"
                }
            }
        })
    </script>

(2) 作用域插槽

image.png

11. 数据变化且触发视图更新

(1)添加数据this.$set()

    <div id="app">
        <h1>{{list}}</h1>
        <button @click="fn">添加age</button>
        <hr>
        <h1>{{arr}}</h1>
        <button @click="fn2">添加car</button>
    </div>
    <script src="./vue.min.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                list: {
                    name: 'tom'
                },
                arr: [{
                    name: 'jack'
                }, {
                    name: 'taotao'
                }]
            },
            methods: {
                fn() {
                    /* Vue2的一个缺陷不可以给对象添加新属性 
                    解决这个bug的方案是 使用$set*/
                    /* this.$set(目标对象,具体的key要使用引号,具体的值) */
                    this.$set(this.list, 'age', 30)
                    console.log(this.list)
                },
                fn2() {
                    /* 这是更新对象数据的方式 */
                    // this.$set(this.arr[1],'car','bmw')
                    /* 这个更新数组的到视图的方式 */
                    /* 三个参数分别对应 目标数组 数组所在的索引 和需要修改的内容 */
                    this.$set(this.arr, 1, { name: 'taotao', car: 'benchi' })
                }
            }
        })
    </script>

(2)删除数据this.$delete()

(3)强制刷新this.$forceUpdate()

    <div id="app">
        <!-- <h1>{{obj}}</h1>
        <button @click="fn1">添加age</button>
        <button @click="fn2">删除age</button> -->
        <!-- <h1>{{list}}</h1>
        <button @click="add">添加benchi</button> -->
        <ul>
            <li v-for="(v,i) in arr" :key="i" @click="del(i)">{{v}}</li>
        </ul>
        <button @click="addAge">添加age</button>
    </div>
    <script src="./vue.js"></script>
    <script>
        new Vue({
            el: "#app",
            data: {
                obj: {
                    name: '张三'
                },
                list: [{
                    car: 'bmw',
                }],
                arr: [{ name: 'zhangsan' }, { name: 'lisi' }]
            },
            methods: {
                fn1() {
                    /* 第一种值和视图都改变的方式 */
                    // this.$set(this.obj,'age',30)
                    /* 第二个种更直接更暴力的渲染视图的方式 */
                    this.obj.age = 30;
                    this.$forceUpdate();
                },
                fn2() {
                    /* 第一种删除对象里面的属性更新视图的方式 */
                    // delete this.obj.age;
                    // this.$forceUpdate();
                    /* 第二种删除对象里面的属性并更新视图的方式 */
                    /*  $delete 第一个参数是 目标对象 第二个参数是 具体的key
                    要使用字符串*/
                    this.$delete(this.obj, 'age')
                    console.log(this.obj)
                },
                add() {
                    /* 第一种方式 */
                    // this.$set(this.list,0,{car:this.list[0].car,car2:'benchi'})
                    this.$set(this.list[0], 'car2', 'benchi')
                    /* 第二种方式 */
                    // this.list[0].car2 = 'benchi'
                    // this.$forceUpdate();
                    console.log(this.list)
                },
                addAge() {
                    // this.arr.forEach(r=>{
                    //     r.age = 30;
                    // })
                    // this.$forceUpdate();
                    this.arr.forEach(r => {
                        this.$set(r, 'age', 30)
                    })
                },
                del(i) {
                    /* 删除对象的某一个属性 */
                    // this.$delete(this.arr[i],'name')
                    // delete this.arr[i].name;
                    // this.$forceUpdate();
                    /* 删除数组的一个对象 */
                    this.arr.splice(i, 1);
                    this.$forceUpdate();
                    // this.$delete(this.arr,i)
                }
            }
        })
    </script>

12. $nextTick 获取最新数据

6{@@5U0I32KH8D0U$_5YV(5.png