Vue学习

Vue创建

Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

var app = new Vue({
            el: '#app',   //Vue实例负责管理的区域 接管处理dom的范围
            data: { message: '你好' }
 })
import Vue from 'vue';
import App from './App.vue'
...
Vue.config.devtools = true;

new Vue({
    router, // react-router
    store,  // vuex
    el: '#app', // 需要渲染的DOM节点
    render: h => h(App) // //h是createElement 的别名,创建/加载组件
});
或者
new Vue({
  render: h => h(App),
  router,
  store
}).$mount('#app')

如果你需要在客户端编译模板 (比如传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 内部的 HTML 作为模板),就将需要加上编译器,即完整版:

// 需要编译器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不需要编译器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

vue中数据变更页面会自动变化(案例)

比如延时两秒后修改数据,数据变了则页面会自动变化

    <div id="app">{{message}}</div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                message: 'hello'
            }
        })
        
        setTimeout(function () {
            app.$data.message = 'hi'
        }, 2000)

可以在外面改vue中data中数据

  • 循环渲染
    vue根据数据循环展示内容很常用,遍历数组

很方便,如果修改了,之后根据底层数据的不同会重新渲染页面

v-model数据的双向绑定

放在input中好用,通过this.inputValue=’ '可以把input输入框清空。
它能轻松实现表单输入和应用状态之间的双向绑定。

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

todolist(案例)

    <div id="app">
        <input type="text" v-model="inputValue">
        <button @click="btnClick()">提交</button>
        <ul>
            <li v-for="item in list">{{item}}</li>
        </ul>
    </div>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                inputValue: '',
                list: []
            },
            methods: {
                btnClick() {
                    this.list.push(this.inputValue)
                    this.inputValue = ""
                }
            }
        })
    </script>

vue中this 箭头函数

生命周期钩子的 this 上下文指向调用它的 Vue 实例。

 created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }

不要在选项 property 或回调上使用箭头函数,比如
created: () => console.log(this.a) 或
vm.$watch(‘a’, newValue => this.myMethod())。

因为箭头函数并没有 this,this会作为变量一直向上级词法作用域查找,直至找到为止,经常导致
Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。

全局及局部组件

全局子组件(案例)

创建全局组件的方法Vue.component(不加s)(组件名加引号 模板)

Vue.component("TodoItem",{template:"<li>todo</li>"}) 
    <div id="app">
        <input type="text" v-model="inputValue">
        <button @click="btnClick()">提交</button>
        <ul>
            <!-- <todo-item v-for="item in list"></todo-item> -->
            <todo-item v-for="item in list" v-bind:content="item"></todo-item>
            //数据传给子,由子决定如何展示数据
            //list发生变化时,每增加一个list(数组),多一次循环,多显示一个<todo-item>
            //内容传递给子组件显示
            //v-bind 可以向子组件传递绑定值
            //v-bind 父向子传值
            //把item通过v-bind的形式传给todo-item,通过content这个变量来传
        </ul>
    </div>
    <script>
        //写在Vue外面,先写
        Vue.component('TodoItem', {
            // props指从父组件接收什么样的内容  别忘加s
            props: ["content"],
            // template: "<li>1</li>"
            template: "<li>{{content}}</li>"
           // 这个是根据props里面有什么用什么content,不根据父
        })
        var app = new Vue({
            el: "#app",
            data: {
                inputValue: '',
                list: []
            },
            methods: {
                btnClick() {
                    this.list.push(this.inputValue)
                    this.inputValue = ""
                }
            }
        })
    </script>
    <div id="app">
        <div @click="btnClick()">{{message}}</div>
        <item></item>
    </div>
    <script>
        Vue.component('item', {
            template: '<div>hi</div>'
        })
        var vm = new Vue({
            el: '#app',
            data: {
                message: '你好'
            },
            methods: {
                btnClick() {
                    alert(1)
                }
            }
        })

局部子组件(案例)

component 加s
父组件决定子组件显示多少个,由父的数据决定

    <div id="app">
        <input type="text" v-model="inputValue">
        <button @click="btnClick()">提交</button>
        <ul>
            <!-- <todo-item v-for="item in list"></todo-item> -->
            <todo-item v-for="item in list" v-bind:content="item"></todo-item>
        </ul>
    </div>
    <script>
        //写在Vue外面
        var TodoItem = {
            props: ['content'],
            template: '<li>{{content}}</li>'
            //子组件标签最终会被子组件中template中的模板替换掉
        }
        var app = new Vue({
            el: "#app",
            components: { TodoItem: TodoItem },
            data: {
                inputValue: '',
                list: []
            },
            methods: {
                btnClick() {
                    this.list.push(this.inputValue)
                    this.inputValue = ""
                }
            }
        })

生命周期 及$el el template 区别

vue中$data $el

$data

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.a=2
vm.$data.a

vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

实例创建之后,可以通过 vm.$data 访问原始数据对象。Vue 实例也代理了 data 对象上所有的 property,因此访问 vm.a 等价于访问 vm.$data.a。

this.message=app.message=this.$data.message=app.message

                    console.log(this);
                    this指向vue实例
                    console.log(app.$data.message);
                    console.log(this.$data.message);
                    console.log(this.message);
                    console.log(app.message);
  • vm.$destory() 会销毁掉vue实例

$el

Vue 实例使用的根 DOM 元素。

el属性:该属性决定了这个Vue对象挂载到哪一个元素上
https://www.php.cn/js-tutorial-420260.html

            console.log(app.$el);
            // <div id="app">
            //     <div>1</div>
            //     <button></button>
            // </div >
            console.log(document.getElementById('app'));//同上
           vm.$el === document.getElementById('app')

el template 区别

$el 指的是

<div id="app">
        <div>{{message}}</div>
</div>

template指

template: "<div>{{divValue}}</div>"

根据官方文档生命周期函数那一节,有el和template的同时,用template

    <div id="app">
        <div>{{message}}</div>
    </div>
    <script>
        Vue.component('item', {
            template: '<div>hi</div>'
        })
        var vm = new Vue({
            el: '#app',
            template: "<div>{{divValue}}</div>",
            data: {
                message: '你',
                divValue: '好'
            }
        })
    </script>
    //结果是好,而不是你

生命周期函数

生命周期函数就是vue实例在某一个时间点会自动执行的函数

生命周期直接放在vue实例里,而不放在methods中

  1. 创建期间的生命周期函数:
  • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods
  • created:实例已经在内存中创建OK,此时data和methods已经创建OK,此时还没有开始编译模板
  • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中 。(模板编译到render)
  • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示。 (此时可以异步请求,操作dom、定时器等)(好像子组件不一定确保)
  1. 运行期间的生命周期的数:
  • beforeUpdate:状态更新之前执行此数,此时data中的状态值是新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点(依赖数据改变或$forceUpdate强制刷新)
  • updated:实例更新完毕之后调用此函数,此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新渲染好了!
  1. 销毁期间的生命周期给数:
  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
  • destroyed:Vue实例销毁后调用。调用后Vu实例指示的所有东西都会解绑定,所有的事件监听员会披移除,所有的子实例也会被销毁

生命周期函数

  • beforeMount mounted

有了template或者el 和数据后,也并不是立马就渲染的

模板和数据结合即将挂载到页面上的一瞬间,有个生命周期函数beforeMount
beforeMount执行的时候,hello world并没有显示在页面上(页面还没有被渲染)

模板结合数据生成的dom元素被挂载到页面上的时候,即hello world显示在页面上之后,生命周期函数mounted会自动执行
mounted执行的时候,hello world已经渲染在页面上(页面已经被渲染完毕了)

    <div id="app">
        <div>{{message}}</div>
    </div>
    <script>
        Vue.component('item', {
            template: '<div>hi</div>'
        })
        var vm = new Vue({
            el: '#app',
            template: "<div>{{divValue}}</div>",
            data: {
                message: '你',
                divValue: '好'
            },
            beforeMount() {
                console.log(this.$el);
                // <div id="app"></div> 即没内容
                // console.log(this.el); undefined
                // console.log(vm.$el); 报错
            },
            mounted() {
                console.log(this.$el);
                // <div>好</div>
                // console.log(vm.$el); 报错
            }
        })
        console.log(vm.$el);
    </script>
  • beforeDestory destoryed

这里并不会执行这两个函数,vm.$destroy()被调用后才会执行这两个函数

beforeDestory() {
    console.log('beforeDestory');
},
 destoryed() {
    console.log('destoryed');
}
  • beforeUpdate updated

数据data改变才会执行这俩函数

数据发生改变,页面还没有重新渲染之前,执行beforeUpdate
数据发生改变,页面重新渲染之后,updated才会被执行

模板语法

  • 插值表达式
<div id="app">
   {{message}}
</div>
  • v-text v-html
<div v-text="message"></div>
data: {
     message: '你好'
}
div>{{message}}</div>             <h1>hello</h1>
<div v-text="message"></div>      <h1>hello</h1>
<div v-html="message"></div>      hello

data: {
     message: '<h1>hello</h1>'
}
div>{{message + 'li'}}</div>             <h1>hello</h1>
<div v-text="message + 'li'"></div>      <h1>hello</h1>
<div v-html="message + 'li'"></div>      hello
  • 运算

var c = ‘aa’ + ‘bb’
‘aabb’

var c = ‘aa’‘bb’
报错

div>{{full}}</div>   //3
data: {
      full: 1 + 2
},
  • v-cloak
    可以使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        [v-cloak] {
            display: none;
        }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <div v-cloak>hello {{message}}</div>

    </div>
    <script>
        setTimeout(() => {
            let app = new Vue({
                el: '#app',
                data: {
                    message: 'h'
                }

            })
        }, 1000)


    </script>
</body>

vue指令后面js表达式

当我们看到一个vue指令后面跟着一个值的时候,里面的值就不再是字符串了,他指的一定是个js的表达式(里面可以用变量,里面还能运算操作呢)

传入一个数字

<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>

<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>

传入一个布尔值

<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post is-published></blog-post>

<!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:is-published="false"></blog-post>

传入一个对象

<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post
  v-bind:author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
></blog-post>

<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:author="post.author"></blog-post>

传入一个对象的所有 property

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:

post: {
  id: 1,
  title: 'My Journey with Vue'
}
下面的模板:

<blog-post v-bind="post"></blog-post>
等价于:

<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

computed methods watch

  1. computed

计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为Date.now() 不是响应式依赖:

computed: {
  now: function () {
    return Date.now()
  }
}

computed return必须

computed是内置缓存的

计算属性fullName依赖了两个变量,一个first ,一个last,当他依赖的变量没有发生任何改变时,这个属性fullName就不会重新计算了,会一直用上一次计算的结果.

<div>{{fullName}}</div>
<div>{{fullName}}</div>
<div>{{fullName}}</div>
data: {
    first: 'li',
    last: 'qiu',
}
computed:{
   fullName() {
       console.log('计算一次');
       return this.first + ' ' + this.last
  }
}
//只会输出一次计算一次 methods输出三次计算一次
<div>{{fullName}} {{age}}</div>
data: {
     first: 'li',
     last: 'qiu',
     age: 22
}
computed: {
      fullName() {
      console.log('计算一次');
      return this.first + ' ' + this.last
}
控制台
app.age=33  无关紧要的变量,改变的不是计算属性中存着的变量计算一次  methods不同 
33
app.first='li'   并没有改变,还是那个  methods不同 
'li'
app.first='buding'  
1.html:30 计算一次
'buding'
app.first='buding'
'buding'
  1. methods

当触发重新渲染时,调用方法将会再次执行函数。

methods方法也能实现,<div>{{fullName()}}</div>要加括号,而且没有缓存,methods跟上面案例完全不同

  1. watch

当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。(比如再调一个函数,是个防抖函数,防抖函数中的函数是个axios请求的函数)

watch方法也能实现,和computed很类似,会有一个缓存,如果你依赖的变量不发生变化,这个缓存值一直都会缓存在那里,性能好

三种使用方式:https://blog.csdn.net/stars0306/article/details/121489753
https://www.cnblogs.com/shiningly/p/9471067.html

vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})
watch:{
        message(newName,oldName){
          console.log(newName);
          console.log(oldName);
       }  
 }
    <div id="app">
        <div>{{fullName}} {{age}}</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                first: 'li',
                last: 'qiu',
                fullName: "li qiu",
                age: 22
            },
            watch: {
                first: function () {
                    console.log('计算了一次');
                    this.fullName = this.first + this.last
                },
                last: function () {
                    console.log('计算了一次');
                    this.fullName = this.first + this.last
                }
            }
        })
        // 第一次的时候控制台不会输出计算一次,要想知道为什么可以看看上面链接
        // 控制台app.age=24时,也不会输出计算一次
        // 控制台app.first='buding',会输出计算了一次
    </script>

watch和computed都有缓存的机制,但是书写方式上面computed复杂了

computed即简洁,性能又高

  1. computed的get set

get:通过其他的值,算出一个新值
set:通过设置一个值,来改变它相关联的值。改变它相关联的值之后,又会引起get的重新计算,页面也会跟着变化成新的内容。

    <div id="app">
        <div>{{fullName}}</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                first: 'li',
                last: 'qiu'
            },
            computed: {
                // fullName: function () {
                //     return this.first + this.last
                // }
                // 不写函数,写成对象,上下两种写法一样
                // fullName: {
                //     get: function () {
                //         return this.first + this.last
                //     }
                // }
                fullName: {
                    // 取fullName时,会执行get
                    get: function () {
                        return this.first + " " + this.last
                    },
                    // 设置fullname时,会执行set。设置的值会被set接收到
                    set: function (value) {
                        // console.log(value);
                        // app.fullName = 'qiuqiu'
                        // 1.html: 40 qiuqiu
                        // 'qiuqiu'
                        var arr = value.split(" ");
                        this.first = arr[0];
                        this.last = arr[1];
                        // 这样写了之后,设置fullName后,会间接的改变fist和last的值
                        // 再想想计算属性的特性,什么时候会重新计算呢:依赖的值发生变化时,就会重新计算。
                        // 所以get那会重新计算,所以页面会改变
                        // app.fullName="bu ding"
                    }
                }
            }
        })
    </script>

vue中的样式绑定

div被点击一次 div变成红色
实现数据和样式进行绑定。数据改变,样式就会改变

  1. class和对象绑定

做个取反,能点击一下,切换一下his.isActivated = !this.isActivated

下面语法表示 active 这个 class 存在与否将取决于数据 property isActive 的 truthiness。

    <style>
        .activated {
            color: red;
        }
    </style>
</head>

<body>
    <div id="app">
        <div @click="btnClick" :class="{activated:isActivated}">hello</div>
        <!-- :class="{}" 只要有:,后面字符串里面就是个js表达式,这个里面就是个js对象 -->
        <!-- div元素有个classclass的名字是什么,是activated,到底有没有取决于变量isActivated的truefalse -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isActivated: false
            },
            methods: {
                btnClick() {
                    // this.isActivated = true
                    // 做个取反,还能点击一下,切换一下
                    this.isActivated = !this.isActivated
                }
            }

        })
    </script>
</body>
  1. class和数组绑定

if-else 简单写法 三元表达式 ,三元表达式前面常搞个等号接收一下

    <script src="./vue.js"></script>
    <style>
        .activated {
            color: red;
        }

        .activated-one {
            font-size: 44px;
        }
    </style>
</head>

<body>
    <div id="app">
        <!-- <div @click="btnClick" :class="[activated]">hello</div> -->
        <div @click="btnClick" :class="[activated,activatedOne]">hello</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                activated: "",
                activatedOne: "activated-one"
            },
            methods: {
                btnClick() {
                    // this.activated === "activated"

                    // if (this.activated === "activated") {
                    //     this.activated = ""
                    // } else {
                    //     this.activated = "activated"
                    // }

                    // if-else 简单写法 三元表达式 ,三元表达式前面常搞个等号接收一下
                    this.activated = this.activated === "activated" ? "" : "activated"
                }
            }

        })
    </script>
</body>

3.style改变样式 (内联样式:1对象方式定义,2数组形式定义)

  • 0.在html上写样式
<p style="font-size: 30px;color: red;">1</p>
  1. vue中直接在元素上通过 :style 的形式,书写样式对象
		<div id="app">
			<div style="width: 100px; height: 100px; background-color: red; color: green; font-size: 100px;">1</div>
			  <!-- 内联样式书写为对象形式  其中font-size 必须加引号。注意:凡是有横线的都必须加引号 -->
			<div :style="{width: '100px', height: '100px', 'background-color': 'red' }">1</div>
		</div>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data: {
					todos: [1, 2, 3, 4]
				}
			})
  1. 将样式对象,定义到 data 中,并直接引用到 :style
<h1 :style="h1StyleObj">1</h1>

data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
  1. :style 中通过数组,引用多个 data 上的样式对象
<h1 :style="[h1StyleObj, h1StyleObj2]">1</h1>
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
数组
    <div id="app">
        <!-- <div :style="[styleObj]" @click="divClick">hello</div> -->
        <div :style="[styleObj,{fontSize:'50px'}]" @click="divClick">hello</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                styleObj: {
                    color: 'black'
                }
            },
            methods: {
                divClick() {
                    this.styleObj.color = this.styleObj.color === 'black' ? 'red' : 'black'
                }
            }
        })
    </script>

v-for对象 v-for 数组

对象
    <div id="app">
        <div v-for="(value,name,index) in obj">{{index}}--{{name}}--{{value}}</div>
        <!-- 第一个是属性值 第二个是属性名 第三个是索引 跟名字压根没关系 你想索:名:值 可以前面正常后面改顺序 -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                obj: {
                    title: 'How to do lists in Vue',
                    author: 'Jane Doe',
                    publishedAt: '2016-04-10'
                }
            }
        })
    </script>
数组
    <div id="app">
        <div v-for="(item,index) in obj">{{item}}--{{index}}</div>
        <!-- 第一个是数组元素 第二个是索引 跟名字压根没关系 -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                obj: [5, 6, 7]
            }
        })
    </script>

条件渲染v-if v-show

v-if Truthy(真值)

在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除 false、0、-0、0n、“”、null、undefined 和 NaN 以外皆为真值)。

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

<div v-if=“Math.random() > 0.5”>
<h1 v-if=“awesome”>

v-if后面跟的也是个js表达式,这个表达式的返回值即true或false,决定了标签是否真实的挂载到页面之上(决定标签是否在页面上存在)

v-if和v-else必须紧挨着使用,中间有标签不行

v-if v-else-if v-else

v-if里面可以是个运算表达式 v-if=“code===‘a’”

    <div id="app">
        <!-- <div v-show="show">{{message}}</div> -->
        <div v-if="show">{{message}}</div>
        <!-- <span>这样插入不行</span> -->
        <div v-else>bye</div>

        <div v-if="code==='a'">this is a</div>
        <div v-else-if="code==='b'">this is b</div>
        <div v-else>others</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                message: 'hello',
                show: false,
                // 控制台app.show=true 则显示 app.show=false 则不显示
                code: 'a'
            }

        })
    </script>

v-if v-show

v-if v-show 用法一样,都能控制标签是否在页面上显示,都能控制元素的切换显示与否https://segmentfault.com/q/1010000010254060

  • v-if的变量如果为false,标签就压根就不存在dom上了
    v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
    v-if来切换子组件,会直接删除和重新创建

  • 但v-show 结果会在页面上渲染,存在,只是不显示,标签对应的dom在页面上依然存在,但是是<div style=“display:none”>hello</div>(v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display。)

  • 经常改变一个元素的隐藏和显示时,用v-show性能高, 不会频繁的把dom从页面上删除添加

  • 而v-if频繁删除dom,增加dom时用

  • v-if可以用在 <template>上,搞多个元素,如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

  • v-show 不支持 <template> 元素,也不支持 v-else。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-if 与 v-for不一起用

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

问:那你用filter也要循环100次啊,这难道就不浪费性能吗?
答:v-for的优先级大于v-if,什么意思呢,就是我会把所有的代码先渲染出来在进行条件判断,这样就造成了性能的浪费,但是我在渲染之前进行数据筛选的话,就减少了不必要的数据渲染

当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

上面的代码将只渲染未完成的 todo。

而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 ) 上。如:

<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>

一般我们在两种常见的情况下会倾向于这样做:放一起 但是这样很不对

为了过滤一个列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。

为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“shouldShowUsers”)。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol)。

			<ul>
				    <li v-for="todo in todos" v-if="todo<3">
					<!-- 为了过滤一个列表中的项目。别这样,可以把todos换成一个计算属性,里面放过滤后的值 -->
					<!-- <li v-for="todo in activeUsers"> -->
					<!-- 对于不相关的一些值,可以这样 -->
					<!-- <ul v-if="shouldShowUsers">
						<li v-for="user in users" :key="user.id">
							{{ todo }}
						</li> -->
		 </ul>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data: {
					todos: [1, 2, 3, 4]
				},
				computed: {
                  activeUsers: function () {
                    return this.todos.filter(function (todo ) {
                     return todo.isActive
                  }
                }
          })
		</script>

显示过滤/排序后的结果

有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。

例如:

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

key

两个input框,所以会尝试复用dom,内容没有自动清空,导致input框内容没清空。为了解决这个问题,加key值

因为vue渲染页面时会尝试复用页面上存在的dom

元素标签加个key值,vue会知道它是页面上唯一的一个元素,如果两个key值不一样,vue就不会尝试复用以前的input标签了

虚拟dom用到的算法

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

data中数组和对象响应式更新

data中数据要初始化

只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。也就是说如果你添加一个新的 property,比如:

vm.b = ‘hi’

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。

data响应式实现

  • 数组循环

可以用v-for=“item in list” 也可以用v-for=“item of list”,结果一样,都是数组中元素

为了提升循环显示的性能,给每一个循环项加唯一的key,key必须唯一
不建议用index做key,在你频繁操作dom元素数据时,费性能,让vue没法充分复用dom节点,一般用id

app.list.push('d') 页面上并把你加的数据显示出来,响应式
4
app.list[4]='d' 页面上并没有把你加的数据显示出来,不是响应式
'd'
app.list 数据已经加入进去了,但最后加的页面不显示,不是响应式
(5) ['a', 'b', 'c', 'd', 'd', __ob__: Observer]



app.list[1]='f'  修改 数据变了,但是页面没变,不是响应式
'f'
app.list
(5) ['a', 'f', 'c', 'd', 'd', __ob__: Observer] 



app.list.splice(1,1,'f')   实现了修改数据,页面也变,响应式
['f']
app.list
(5) ['a', 'f', 'c', 'd', 'd', __ob__: Observer] 



app.list=['c','b','a']  改变引用,响应式
(3) ['c', 'b', 'a', __ob__: Observer]



Vue.set(app.list,1,'f')
'f'
app.$set(app.list,1,'g')
'g'
  1. 我们修改数组内容的时候,不能通过下标的形式来改变数组. app.list[1]=‘f’

  2. 我们只能通过vue提供的几个数组遍历方法来操作数组,才能够实现数据发生变化,页面跟着发生变化这种响应式效果 app.list.splice(1,1,‘f’)

vue中提供了七个数组遍历方法:pop push shift unshift splice sort reverse

  1. 还有一种方法能够实现数据发生变化,页面跟着发生变------- 改变引用。比如让数组等于一个新的引用 app.list=[‘c’,‘b’,‘a’]
  2. 还可以set方法,向数组里注入值,数据发生变化,修改了数据,页面也发生变化 Vue.set(app.list,1,“f”)

set即是vue上面的全局的方法,同时也是vue的实例方法

set实例方法用法:app是vue的实例,所以用实例方法app.$set(app.list,1,“f”)

    <div id="app">
        <div v-for="(item,index) in list">{{item}}---{{index}}</div>
        <div v-for="(item,index)  of list">{{item}}---{{index}}</div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                list: ['a', 'b', 'c']
            }
        })
    </script>
  • 对象循环
  1. app.userInfo.name=“qiu qiu” 修改对象可以的

遍历对象的时候,如果动态的往对象里直接去添加加值,不响应 ,app.userInfo.address=“hangzhou”

  1. 但可以改对象的引用,换成一个全新的对象 app.userInfo ={…address:“hangzhou”}

  2. 还可以set方法,向对象里注入值,数据发生变化,页面也发生变化 Vue.set(app.userInfo,“address”,“hangzhou”)

set即是vue上面的全局的方法,同时也是vue的实例方法

set实例方法用法:app是vue的实例,所以用实例方法app.$set(app.userInfo,“tele”,“110”)

app.userInfo.name="qiu qiu" 修改对象可以的,响应式
'qiu qiu'


app.userInfo.address="hangzhou" 给对象增加一条数据,数据增加了,但页面上没显示,不是响应式
'hangzhou'
app.userInfo 
{address: 'hangzhou', …}
address: "hangzhou"
age: 28
gender: "male"
name: "qiu qiu"
salary: "secret"


app.userInfo ={   响应式
                    name: 'lili',
                    age: 28,
                    gender: 'male',
                    salary: 'secret',
                    address:"hangzhou"
                }
{…}
address: (...)
age: (...)
gender: (...)
name: (...)
salary: (...)


Vue.set(app.userInfo,"address","hangzhou")  响应式
'hangzhou'


app.$set(app.userInfo,"tele","110")  响应式
'110'

数组方法是否返回原数组

  1. 会变更调用了这些方法的原始数组
    push()
    pop()
    shift()
    unshift()
    splice()
    sort()
    reverse()

数组尝试调用变更方法。比如 example1.items.push({ message: ‘Baz’ })

  1. 不会变更原始数组,而总是返回一个新数组
    filter()、concat() 和 slice()

用非变更方法时,可以用新数组替换旧数组:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

Vue 不能检测以下数组的变动:

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
举个例子:

var vm = new Vue({
data: {
items: [‘a’, ‘b’, ‘c’]
}
})
vm.items[1] = ‘x’ // 不是响应性的
vm.items.length = 2 // 不是响应性的

<template> template占位符

    <div id="app">
        <div v-for="(item,index) in list" :key="index">{{item}}</div>
        <span v-for="(item,index) in list" :key="index">{{item}}</span>
        <!-- <div>a</div>
        <div>b</div>
        <div>c</div>
        <span>a</span>
        <span>b</span>
        <span>c</span> -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                list: ['a', 'b', 'c']
            }
        })
    </script>

不想让循环执行两次

    <div id="app">
        <div v-for="(item,index) in list" :key="index">
            <div>{{item}}</div>
            <span>{{item}}</span>
        </div>
        <!-- 
        <div>
        <div>a</div>
        <span>a</span>
        </div>
        <div>
        <div>a</div>
        <span>a</span>
        </div>
        <div>
        <div>a</div>
        <span>a</span>
        </div>
    
        -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                list: ['a', 'b', 'c']
            }
        })
    </script>

template占位符,最外层的div就消失了
template模板占位符,可以帮助我们去包裹一些元素,循环的过程中并不会真正的渲染到页面上

    <div id="app">
        <template v-for="(item,index) in list" :key="index">
            <div>{{item}}</div>
            <span>{{item}}</span>
        </template>
        <!-- 
        <div>a</div>
        <span>a</span>
        <div>a</div>
        <span>a</span>
        <div>a</div>
        <span>a</span>
        -->
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                list: ['a', 'b', 'c']
            }
        })
    </script>

组件使用中细节点

1.is

is属性解决模板标签出现bug问题

table使用,tbody标签里面只能使用tr

    <div id="app">
        <table>
            <tbody>
                <!-- <tr>
                    <td>1</td>
                </tr>
                <tr>
                    <td>1</td>
                </tr>
                <tr>
                    <td>1</td>
                </tr> -->
                <!-- 这样不行,虽然也显示,会被放到table外面去,因为tbody中只能放tr ,浏览器解析row出问题-->
                <!-- <row></row>
                <row></row>
                <row></row> -->
                tbody中用组件,但不能直接写,虽然写的是tr,但实际上是把row组件的内容放到tr那
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>
    </div>
    <script>
        Vue.component('row', {
            template: "<tr><td>row</td></tr>"
        })
        var app = new Vue({
            el: '#app',//这个写了之后才能解析出来子组件
        })
    </script>

ul ol标签里面直接写row可能也有问题,所以写成li is=“row”
select标签里面必须写option

2.子组件data是函数

子组件里面props可以直接在子里面用。

子组件中定义data为对象形式报错。data需要是函数形式才行,函数返回一个对象。

根组件(最外层vue实例,即new Vue({}))中data对象形式没错。

根组件只会被调用一次,而子组件可能会被调用多次,每个子组件的数据不该是冲突的,该有自己独立的数据。

根实例的data是对象不是函数,子组件是函数

    <div id="app">
        <table>
            <tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>
    </div>
    <script>
        Vue.component('row', {
            // data: { content: 'row' },
            // The "data" option should be a function that returns a per-instance value in component definitions.
            data: function () {
                return {
                    content: 'hello'
                }
            },
            template: "<tr><td>{{content}}</td></tr>"
        })
        var app = new Vue({
            el: '#app',
        })

3.ref的两种使用 组件上 元素上

vue不建议操作dom,但有时又必须操作dom,通过ref引用获取dom,操作dom

this.$refs.hello 获取加ref的元素标签 获取标签对应的dom元素

        var app = new Vue({
            el: '#app',
            methods: {
                handleClick() {
                    // 元素标签上加ref 获取dom元素
                    // this.$refs.hello
                    // this.$refs整个vue实例里面所有的引用
                    // 指向这个div对应的dom节点
                    console.log(this.$refs.hello);//<div>Hello World</div>
                    console.log(this.$refs.hello.innerHTML);// Hello World
                }
            }
        })

组件上加ref 获取的是子组件的引用

       <div id="app">
			<counter @change="handleChange" ref="one"></counter>
			<counter @change="handleChange" ref="two"></counter>
			<div>{{total}}</div>
		</div>
        Vue.component('counter', {
            template: '<div @click="handleClick">{{number}}</div>',
            data() {
                return {
                    number: 0
                }
            },
            methods: {
                handleClick() {
                    this.number++
                    this.$emit('change')
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                total: 0
            },
            methods: {
                changeClick() {
                    // 组件上加ref  获取的是组件的引用
                    // this.total++
                    // 返回组件的引用
                    console.log(this.$refs.one);
                    //VueComponent {_uid: 2, _isVue: true, $options:...}
                    console.log(this.$refs.two);
                    console.log(this.$refs.one.number);
                    console.log(this.$refs.two.number);
                    this.total = this.$refs.one.number + this.$refs.two.number
                }
            }
        })

父子组件及非父子组件的数据传递方式

父传子

父组件通过属性形式向子组件传递数据

<child content="hello world"></child >

加了:传给子组件的0,会变成数字。因为加了:双引号里的内容,是js表达式了,所以是数字

不加:传给子组件的0,实际上是个字符串

    <div id="app">
        <!-- <counter :content="message"></counter> -->
        <counter content="0"></counter>
        <counter :content="0"></counter>
        <!-- 加了:传给子组件的0,会变成数字。不加:传给子组件的0,实际上是个字符串-->
        <!-- 因为加了:双引号里的内容,是js表达式了,所以是数字 -->
        <!-- 父组件通过属性形式向子组件传递数据 -->
        typeOf判断 看看到底穿过去的数字还是字符串
    </div>
    <script>
        var counter = {
            // template: '<div>1</div>',
            template: '<div>{{typeof(content)}}</div>',
            props: ['content']
        }
        var app = new Vue({
            el: '#app',
            components: { counter },
            data: {
                message: '你好'
            },
            methods: {
            }
        })
    </script>
  • vue中单向数据流概念 :父向子 可以通过属性传递参数,传递的参数父可以随意的修改。子组件不能反过来修改父组件传来的参数(对于父传来的,子只能用,不能改)

  • 原因:你想万一父传的是个对象形式,这时,你子组件改了,这个数据很可能也被其他子组件使用。这样子组件修改之后,多个子组件会相互影响。

  • 父传子 把父拷贝出副本一份 改父副本 不影响
    注意这里如果是对象的话,这样根本没拷贝,这样修改还是会影响父组件的数据的

    <div id="app">
        <counter :content="0"></counter>
    </div>
    <script>
        var counter = {
            // template: '<div @click="handleClick">{{content}}</div>',
            template: '<div @click="handleClick">{{number}}</div>',
            props: ['content'],
            data: function () {
                return {
                    number: this.content
                    // 即从父组件接收到content,把content数据复制一份放到子组件自己的data中。这样你就能随便改,不用担心是父传来的
                    // 之后点击的时候改的也不该是counter,而是number,这是自己的
                }
            },
            methods: {
                handleClick() {
                    // this.content++
                    // 能实现通过更改content,改父组件传递过来数字0,但会报错说 不要直接修改父组件传来的值
                    // 既然不能随便改父传来的值,那怎么实现我的功能呢。答 在子data函数中定义变量,值是父传来的
                    this.number++
                }
            }
        }
        var app = new Vue({
            el: '#app',
            components: { counter },
            data: {
            },
            methods: {
            }
        })
    </script>

单向数据流

注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。

		<div id="app">
			<my :post="post"></my>
		</div>
		<script type="text/javascript">
			Vue.component('my', {
				props: ['post'],
				template: '<div @click="btnClickk">{{posta}}</div>' ,//{ "id": 1, "title": "hello" },
				
				data(){
					return {
						posta:this.post
					}
				},
				methods:{
					btnClickk(){
						this.posta.id=2
					}
				}
			})
			var app = new Vue({
				el: '#app',
				data: {
					post: {
						id: 1,
						title: 'hello'
					},
				},
				methods:{
					btnClick(){
						this.post.id=5
					}					
				}
			})
		</script>

子传父

子组件通过事件触发的形式向父组件传值
案例:上下两个子组件相加

    <div id="app">
        <counter :content="1" @change="handleChange"></counter>
        <counter :content="3" @change="handleChange"></counter>
        <div>{{total}}</div>
    </div>
    <script>
        var counter = {
            template: '<div @click="handleClick">{{number}}</div>',
            props: ['content'],
            data: function () {
                return {
                    number: this.content
                }
            },
            methods: {
                handleClick() {
                    this.number++
                    this.$emit('change', 1)
                }
            }
        }
        var app = new Vue({
            el: '#app',
            components: { counter },
            data: {
                total: 4
            },
            methods: {
                // 写在根组件的方法中
                handleChange(value) {
                    this.total = this.total + value
                }
            }
        })

子组件向父组件传值(案例)

写vue代码时,不要直接操作dom,而是通过数据的改变,让页面自动变化

子向父传值时通过this.$emit(“delete”) 向外触发事件
父通过@delete监听到

决定删除哪个怎么实现,肯定是改变父中的数据实现的

以字符串数组形式列出的 props

       <div id="app">
        <input type="text" v-model="inputValue">
        <button @click="btnClick()">提交</button>
        <ul>
            <todo-item v-for="(item,index) in list" :content="item" :index="index" @delete="deleteItem"></todo-item>
        </ul>
    </div>

    <script>
        var TodoItem = {
            props: ['content', "index"],
            template: "<li @click='deleteLi'>{{content}}</li>",
            methods: {
                deleteLi() {
                    //console.log(this.index)
                    //绝了,都没传参数,就这就能得到index
                    this.$emit('delete', this.index)
                }
            }
        }
        var app = new Vue({
            el: '#app',
            components: { TodoItem: TodoItem },
            data: {
                inputValue: '',
                list: []
            },
            methods: {
                btnClick() {
                    this.list.push(this.inputValue)
                    this.inputValue = ''
                },
                deleteItem(index) {
                   //注意 这个函数上面是必须要加参数的
                    this.list.splice(index, 1)
                }
            }
        })
    </script>
  • props v-bind 位置
    <div id="app">
        <div :title="title" :contents="0">{{message}}</div> //这个传不到子,这个title是个属性。有效果
        <zi :title="title" :contents="0"></zi> //只有这样才能传到子,并不会有title属性效果
    </div>
<script>
    Vue.component('zi', {
        template: '<div title="哈哈">{{title}}</div>', //这个title有用
        props: ['title', 'contents']
    })
    var app = new Vue({
        el: '#app',
        data: {
            message: 'hello',
            title: '这是div元素'
        }
    })
</script>

非父子通信-- 总线

子父$emit 父子props

非父子通信

  • vuex
  • 总线

案例:两个子通信

    <div id="app">
        <child content="DELL"></child>
        <child content="Lee"></child>
    </div>
    <script>
        Vue.prototype.bus = new Vue()
        // Vue.prototype上挂载一个bus属性,这个属性指向  vue实例, 
        // 只要之后调用new Vue()或者创建组件,每个组件上都会有bus这个属性。  Vue.prototype.bus=1

        // 为啥:因为每个组件或者每个vue的实例,都是通过Vue这个类创建的,而Vue这个类上挂了个bus这个属性,而且挂载在Vue类的prototype上,
        // 每个通过这个类创建的对象,即vue的实例上都会有bus属性,都指向同一个vue的实例 = new Vue()
        var child = {
            props: {
                content: String
            },
            data() {
                return {
                    selfContent: this.content
                }
            },
            template: '<div @click="handleClick">{{selfContent}}</div>',
            methods: {
                // 向外触发事件
                handleClick() {
                    // alert(this.content) //各自的content
                    // 怎么把我的内容传递给另一个组件
                    this.bus.$emit('change', this.selfContent)
                    // this.bus指就是这个实例上挂载的bus,每个实例上都会有bus这个属性,所以bus存在
                    // bus又是vue的实例= new Vue(),所以上面有$emit()方法,就可以通过这个方法向外触发事件,可以携带内容
                }
            },

            // 事件的监听
            mounted() {
                var this_ = this
                this.bus.$on('change', function (msg) {
                    // alert(msg)
                    // 一个组件触发,其他组件都监听,两个child组件都弹出msg
                    // 两个结果  在一个child组件触发事件时,两个child组件都进行了同一事件的监听,所以两个组件都弹msg
                    // this.content = msg
                    //所以这里两个子组件都修改selfContent值
                    this_.selfContent = msg

                })
                // 让这个组件监听bus的改变,这个组件一定会有bus这个属性,因为Vue.prototype有bus,这个组件实际上通过vue创建出来的,vue的实例当然就要bus这个属性
                // bus是vue实例,所以bus上有$on方法,$on方法能监听到bus上面触发的事件
                // 监听到change事件,执行后面内容
            }
        }
        var app = new Vue({
            el: '#app',
            components: { child }
        })
    </script>

组件参数校验与非props特性

<div id="app">
        <!-- <counter content="hello"></counter> -->
        <!-- <counter :content="12"></counter> -->
        <!-- <counter :content="'12'"></counter> -->
        <!-- 这个还是字符串形式 -->
        <!-- <counter :content="{a:1}"></counter> -->
        <!-- <counter></counter> -->
        <counter content="123456"></counter>
    </div>
    <script>
        var counter = {
            template: '<div>{{content}}</div>',
            // props: ['content']
            props: {
                // content: String
                // content: Number
                // content: [String, Number]
                content: {
                    type: String,
                    // required: true
                    // 必传属性
                    // required: false,
                    // // 如果不传content,则content的内容就是这个默认值'default value'
                    // default: 'default value'
                    // 更复杂的校验,比如content长度5-25
                    // 有个函数 有个形参 value是传入的内容 满足才能显示
                    validator: function (value) {
                        return value.length > 5
                    }
                }
            }
            // 子组件接收到的属性必须是一个string类型
            // 子组件接受的属性可以是string也可以是number
        }
        var app = new Vue({
            el: '#app',
            components: { counter },
        })
    </script>

props特性 -----------------父组件传给子组件数据时,传递参数,子组件props有个接收
props特性特点:传递参数了,你接收了,使用了,不会显示这个属性

  <div id="app">
       <div>hello</div>
   </div>

非props特性 -----------------子组件没props,即父组件传递属性了,但是子没接收
非props特性特点:父传递参数了,子没接收,但使用了,会报错
非props特性特点:如果父传递了,没接收,没使用,不报错,但是会在子组件html中显示这个属性值

	<div id="app">
		<counter content="hell" class="foo bar">hello</counter>
	</div>
	<script>
		var counter = {
			template: '<div  class="baz boo">{{content}}</div>',
			props: ['content'],
			//使用了父传来的变量 <div  class="baz boo foo bar">hello</div>
			// template: '<div  class="baz boo">hello</div>',
			//没使用父传来的变量 <div content="hell"  class="baz boo foo bar">hello</div>
		}
		var app = new Vue({
			el: '#app',
			components: {
				counter
			},
		})
	</script>

子组件其实是把子组件文件里面的根标签等带过来了,比如添加类添加在子组件上,就是添加在子组件的根标签上

<keep-alive>

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。

当组件在<keep-alive>内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive>要求同时只有一个子元素被渲染。

动态组件和v-once指令

  • 动态组件
    <div id="app">
        <!-- <child-one v-if="type==='child-one'"></child-one>
        <child-two v-else></child-two> -->
        <component :is="type"></component>
        <!-- is里面数据的变化,自动加载不同的组件。is的值是child-one,就会找child-one这个组件,is变成child-two,就会清除child-one,加载child-two -->
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        Vue.component('child-one', {
            template: '<div>child-one</div>'
        })
        Vue.component('child-two', {
            template: '<div>child-two</div>'
        })
        var app = new Vue({
            el: '#app',
            data: {
                type: 'child-one'
            },
            methods: {
                btnClick() {
                    this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
                }
            }
        })
    </script>
  • v-once指令提高静态内容的展示性能
    <div id="app">
        <child-one v-if="type==='child-one'"></child-one>
        <child-two v-else></child-two>
        <button @click="btnClick">按钮</button>
        <!-- 我们每次切换时,vue的底层会判断这个组件不用了,取而代之用另一个组件,会把第一个组件销毁掉,创建第二个组件,之后再把第二个组件销毁,再创建第一个组件 -->
        <!-- 每次切换,底层都需要销毁一个组件再创建一个组件,耗费性能。如果每次组件都一样,那么可以加个v-once。 -->
        <!-- child-one组件第一次渲染时,有v-once所以直接就放到内存里了,之后再次点击点击会从内存中拿出来组件,不是再创建了,性能好-->
    </div>
    <script>
        Vue.component('child-one', {
            template: '<div v-once>child-one</div>'
        })
        Vue.component('child-two', {
            template: '<div v-once>child-two</div>'
        })
        var app = new Vue({
            el: '#app',
            data: {
                type: 'child-one'
            },
            methods: {
                btnClick() {
                    this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
                }
            }
        })
    </script>

vue中过渡和动画

原生js的过渡和动画

看一下原生的transiton和animate使用
https://www.runoob.com/try/try.php?filename=trycss3_transition(定义在一个元素上,但是这个元素等下会变化)

div
{
    width:100px;
    height:100px;
    background:red;
    transition:width 2s;
    -webkit-transition:width 2s; /* Safari */
}
div:hover
{
    width:300px;
}
​
<div></div>
<p>鼠标移动到 div 元素上,查看过渡效果。</p>

https://www.runoob.com/try/try.php?filename=trycss3_animation(直接定义在一个元素上,元素并没有变化)

div
{
	width:100px;
	height:100px;
	background:red;
	position:relative;
	animation:mymove 5s infinite;
	-webkit-animation:mymove 5s infinite; /*Safari and Chrome*/
}
@keyframes mymove
{
	from {left:0px;}
	to {left:200px;}
}
@-webkit-keyframes mymove /*Safari and Chrome*/
{
	from {left:0px;}
	to {left:200px;}
}


<div></div>

vue中过渡动画transition

想要有动画效果,标签外部包裹一层transition标签

样式中transition属性作用,检测opacity的变化,检测到这个变化,会让变化在3s内完成

opacity的原始值是1,如果你设置opacity值为0.name这个元素就会隐藏

通过在某一时刻,自动的向div元素上增加一些class,vue帮我们实现了css的过度效果

v-if、v-show、动态组件写到这里都会有动画效果

div{
		            width: 300px;
		            height: 300px;
		            background-color: red;
		            opacity: 0.3;/*0-1*/
		            0的时候没了,1的时候是完整的
}

隐藏---->显示动画
vue会构建显示动画流程

    <style>
        .fade-enter {
            opacity: 0;
            /* 隐藏 ,刚进来就是0是隐藏状态。动画运行到第二针,这个样式就移除了,变成默认的opacity 1了*/
        }

        .fade-enter-active {
            transition: opacity 10s;
            /* 监听opacity的变化,慢慢的从0变成opacity的值1,这个变化在10s内完成 */
        }

        /* 只隐藏到哦显示有过渡效果 */
    </style>

</head>

<body>
    <div id="app">
        <transition name="fade">
            <!-- 你这样transition包裹之后,他自动往div标签上添加几个样式  -->
            <!-- 这个name不写就没用了 -->
            <!-- 也可以不写,上面两个属性默认改成v-enter和v-enter-active 也可以 -->
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>
</body>

显示—>隐藏动画
vue会构建隐藏动画流程

    <style>
        .fade-enter {
            opacity: 0;
        }

        .fade-enter-active {
            transition: opacity 10s;
        }

        /* 显示--->隐藏动画 */
        .fade-leave-to {
            opacity: 0;
            /* 离开动画执行的第一个瞬间,第二个瞬间才有这个fade-leave-to */
            /* 离开的第一个瞬间,从显示状态到隐藏,第一瞬间opacity是1,之后fade-leave-to上来opacity变为0 */
        }

        .fade-leave-active {
            transition: opacity 10s;
            /* 从动画执行到结束,一直存在,动画运行过程中,时刻监听div的opacity属性,一旦opacity发生变化,就10s内慢慢变化 */
        }
    </style>

</head>

<body>
    <div id="app">
        <transition name="fade">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

vue中animate动画


    <style>
        /* 定义css3动画 */
        @keyframes bounce-in {
            0% {
                transform: scale(0);
            }

            50% {
                transform: scale(1.5);
            }

            100% {
                transform: scale(1);
            }
        }

        .fade-enter-active {
            /* class生效:div标签显示的过程中一直存在 */
            /* 保证放大缩小没问题 */
            transform-origin: left center;
            /* 使用animation,1s内运行结束 */
            animation: bounce-in 1s;

        }

        .fade-leave-active {
            /* 保证放大缩小没问题 */
            transform-origin: left center;
            /* 离开时,动画反向执行 */
            animation: bounce-in 1s reverse;
        }
    </style>

</head>

<body>
    <div id="app">
        <transition name="fade">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

可自定义添加的class的名字

    <style>
        /* 定义css3动画 */
        @keyframes bounce-in {
            0% {
                transform: scale(0);
            }

            50% {
                transform: scale(1.5);
            }

            100% {
                transform: scale(1);
            }
        }

        .active {
            /* class生效:div标签显示的过程中一直存在 */
            /* 保证放大缩小没问题 */
            transform-origin: left center;
            /* 使用animation,1s内运行结束 */
            animation: bounce-in 1s;

        }

        .leave {
            /* 保证放大缩小没问题 */
            transform-origin: left center;
            /* 离开时,动画反向执行 */
            animation: bounce-in 1s reverse;
        }
    </style>

</head>

<body>
    <div id="app">
        <transition leave-active-class="leave" enter-active-class="active">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

vue中animate库

<link href="https://unpkg.com/animate.css@3.5.2/animate.min.css" type="text/css" rel="stylesheet" />

https://animate.style/
不需要自己去写那种动画 @keyframes bounce-in{} .active{} .leave{}的代码,直接用.修改一下这个就可以了 <transition leave-active-class=“leave” enter-active-class=“active”><div v-if=“isShow”>123</div></transition>

下载animate.css,导入,最后 就在你需要的地方 加上 class=“animated shake”。

而在vue中这样写

抖动的入场出场<transition leave-active-class=“animated swing” enter-active-class=“animated shake”><div v-if=“isShow”>123</div></transition>

必须使用自定义class名字的形式enter-active-class来使用animate库,class类里面必须包含animated类

其实这个库就跟自己写个@keyframes 没区别,只是封装了一下

    <div id="app">
        <transition leave-active-class="animated swing" enter-active-class="animated shake">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

vue中同时使用过渡和动画

  1. 第一次显示页面时,没没有动画效果

实现页面的初次动画: 增加个自定义class,appear-active-class=“…”,想用自定义class名,还需要写个属性appear,让appear第一次显示时也有个动画效果。

<transition appear leave-active-class="animated swing" enter-active-class="animated shake"
            appear-active-class="animated swing">
            <div v-if="isShow">123</div>
        </transition>
  1. animated 这里仅仅替代了之前的@keyframes 动画,但不能替换过渡的动画效果(就是动画是你会晃动着隐藏,加了过渡之后就会晃动着慢慢慢慢变淡直至隐藏)
    <link href="https://unpkg.com/animate.css@3.5.2/animate.min.css" type="text/css" rel="stylesheet" />
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 3s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition appear name="fade" leave-active-class="animated swing fade-enter-active"
            enter-active-class="animated shake fade-leave-active" appear-active-class="animated swing">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: true
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>
  1. 问题:animated 即@keyframes 动画执行1s,transition动画执行3s,vue会以哪个为结束时长呢

3.1 可以手动设置type=“transition”,以transition的动画时长为准

<transition type="transition" appear name="fade" leave-active-class="animated swing fade-enter-active"
            enter-active-class="animated shake fade-leave-active" appear-active-class="animated swing">
            <div v-if="isShow">123</div>
</transition>

3.2 还可以自己定义动画总时长,不以那俩个为准:duration=“10000”

 <transition :duration="10000" appear name="fade" leave-active-class="animated swing fade-enter-active"
            enter-active-class="animated shake fade-leave-active" appear-active-class="animated swing">
            <div v-if="isShow">123</div>
        </transition>

这里其实3s就隐藏掉了,但是整体设置动画时长10s没完,一直到10s之后才会清除掉

3.3 可以设置复杂一点,设置入场动画时长,出场动画时长

    <div id="app">
        <transition :duration="{enter:5000,leave:10000}" appear name="fade"
            leave-active-class="animated swing fade-enter-active" enter-active-class="animated shake fade-leave-active"
            appear-active-class="animated swing">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>

可以自己看一下,刚开始进来不算,当你点击按钮,等10s之后,div上面的所有类都没了。再点击,等5s之后,div上面的类又没了。

全部代码

    <link href="https://unpkg.com/animate.css@3.5.2/animate.min.css" type="text/css" rel="stylesheet" />
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 3s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition :duration="{enter:5000,leave:10000}" appear name="fade"
            leave-active-class="animated swing fade-enter-active" enter-active-class="animated shake fade-leave-active"
            appear-active-class="animated swing">
            <div v-if="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: true
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

vue中js动画与js常用的动画库velocity.js

除了css实现动画,js实现也能实现动画效果

js里面有许多钩子函数

入场动画对应的钩子

@before-enter:(还没入场)隐藏到显示的一瞬间会触发before-enter事件。函数还能接收个参数el,el指动画包裹的div标签

@enter:(动画开始执行)before-enter触发结束之后,下一步真正运行动画,真正开始运行动画效果时,动画都写在哪里呢,写在enter这个钩子对应的回调函数中。函数能接受两个参数,一个el,一个done回调函数。
done也重要,当动画执行完之后,要手动调done(),相当于告诉vue,我的动画执行完了

@after-enter: done回调之后,会触发事件.

<body>
    <div id="app">
        <transition name="fade" @before-enter="beforeEnterHandle" @enter="handleEnter" @after-enter="handleAfterEnter">
            <div v-show="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: true
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                },
                beforeEnterHandle(el) {
                    el.style.color = 'red';
                },
                handleEnter(el, done) {
                    setTimeout(() => {
                        el.style.color = 'yellow'
                        // 动画还没执行之前,样式变红色。紧接着动画开始执行,动画是什么,动画就是2s之后后div颜色变成绿色
                    }, 2000)
                    setTimeout(() => {
                        done()
                        // 不写done这个动画一直不结束
                    }, 4000)
                },
                handleAfterEnter(el) {
                    el.style.color = 'black'
                }
            }
        })
    </script>
</body>

出场动画对应的钩子
@before-leave
@leave
@after-leave

<script src="http://shouce.jb51.net/velocity/velocity.min.js"></script>
    <script src="./vue.js"></script>
    <script src="http://shouce.jb51.net/velocity/velocity.min.js"></script>
</head>

<body>
    <div id="app">
        <transition name="fade" @before-enter="beforeEnterHandle" @enter="handleEnter" @after-enter="handleAfterEnter">
            <div v-show="isShow">123</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: true
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                },
                beforeEnterHandle(el) {
                    el.style.opacity = 0
                },
                handleEnter(el, done) {
                    // 把opacity从0慢慢变成1
                    // 参数:对el元素实现这个动画,让el元素的opacity从0慢慢变成1,opacity从0到1动画耗时,complete告诉vue动画执行完成
                    Velocity(el, { opacity: 1 }, { duration: 1000, complete: done })
                },
                handleAfterEnter(el) {
                    alert('动画结束')
                }
            }
        })
    </script>

vue中多个元素或组件的过渡动画

  1. 元素

isshow是true时hello显示在页面上,bye不显示
isshow是false时bye显示在页面上,hello不显示

transition包裹 的是两个元素
v-if 切换显示时有动画效果

css过渡动画 ,会自动加几个class名字。如果transition写名字fade,加名字比如fade-enter、fade-leave-to、否则加v-enter、v-leave-to

vue在两个元素切换时会复用dom,复用dom导致动画效果不出现。不同key,就不复用了

mode="in-out"需要显示的先进入,需要隐藏的后隐藏
mode="out-in"先隐藏后进入

    <script src="./vue.js"></script>
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition name="fade" mode="out-in">
            <div key="hello" v-if="isShow">hello</div>
            <div key="bye" v-else>bye</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>
  1. 组件
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition name="fade" mode="in-out">
            <!-- <div key="hello" v-if="isShow">hello</div>
            <div key="bye" v-else>bye</div> -->
            <child v-if="isShow"></child>
            <child-one v-else></child-one>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        Vue.component('child', {
            template: '<div>child</div>'
        })
        Vue.component('child-one', {
            template: '<div>child-one</div>'
        })
        var app = new Vue({
            el: '#app',
            data: {
                isShow: false
            },
            methods: {
                btnClick() {
                    this.isShow = !this.isShow
                }
            }
        })
    </script>

借助动态组件也可实现

    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition name="fade" mode="in-out">
            <component :is="type">
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>
        Vue.component('child', {
            template: '<div>child</div>'
        })
        Vue.component('child-one', {
            template: '<div>child-one</div>'
        })
        var app = new Vue({
            el: '#app',
            data: {
                type: 'child'
            },
            methods: {
                btnClick() {
                    this.type = this.type === 'child' ? 'child-one' : 'child'
                }
            }
        })
    </script>

Vue的列表过渡

循环展示数组v-for,点击一下按钮,往数组添加个元素,显示一个hello

i=10
temp=i++ 是先把i赋值给temp,然后执行i=i+1,此时temp是10
temp=++i 是先执行i=i+1,然后把i赋值给temp,此时temp是11
count=0
id: this.count++ 则第一次id的值为0

列表过渡–新标签transition-group

    <style>
        .v-enter,
        .v-leave-to {
            opacity: 0;
        }

        .v-enter-active,
        .v-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition-group>
            <div v-for="item in list" :key="item.id">{{item.title}}{{item.id}}</div>
        </transition-group>
        <!-- 相当于 单个列表过渡,当元素div显示或者隐藏的时候,会增加相应的class,所以每个列表项都有自己的动画
        <transition>
            <div>hello</div>
        </transition>
        <transition>
            <div>hello</div>
        </transition>
        <transition>
            <div>hello</div>
        </transition> -->
        <button @click="btnClick">按钮</button>
    </div>
    <script>

        var app = new Vue({
            el: '#app',
            data: {
                list: [],
                count: 0
            },
            methods: {
                btnClick() {
                    this.list.push({
                        id: this.count++,
                        title: 'hello'
                    })
                }
            }
        })
    </script>

Vue动画的封装

之前写的渐隐渐现的效果用的多,封装一下

建个组件,封装,模板里面有<transition>。得用v-if

经常用的

   <style>
        .v-enter,
        .v-leave-to {
            opacity: 0;
        }

        .v-enter-active,
        .v-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <transition>
            <div v-if="show">hello</div>
        </transition>
        <button @click="btnClick">按钮</button>
    </div>
    <script>

        var app = new Vue({
            el: '#app',
            data: {
                show: false
            },
            methods: {
                btnClick() {
                    this.show = !this.show
                }
            }
        })
    </script>

根据外部传来的变量决定是否显示外部传来的dom
template: <transition> <slot v-if="show"></slot> </transition>


        .v-enter,
        .v-leave-to {
            opacity: 0;
        }

        .v-enter-active,
        .v-leave-active {
            transition: opacity 1s
        }
    </style>
</head>

<body>
    <div id="app">
        <!-- <transition>
            <div v-if="show">hello</div>
        </transition> -->
        <fade :show="show">
            <div>hello</div>
        </fade>

        <fade :show="show">
            <h1>hello</h1>
        </fade>

        <button @click="btnClick">按钮</button>
    </div>
    <script>
        Vue.component('fade', {
            // 根据外部传来的变量决定是否显示外部传来的dom,
            props: ['show'],
            template: `<transition>
              <slot v-if="show"></slot>
            </transition>`
        })

        var app = new Vue({
            el: '#app',
            data: {
                show: false
            },
            methods: {
                btnClick() {
                    this.show = !this.show
                }
            }
        })
    </script>

样式封装到组件里,如何封装,不用js动画,转用css动画

    <div id="app">
        <!-- <transition>
            <div v-if="show">hello</div>
        </transition> -->
        <fade :show="show">
            <div>hello</div>
        </fade>

        <fade :show="show">
            <h1>hello</h1>
        </fade>

        <button @click="btnClick">按钮</button>
    </div>
    <script>
        Vue.component('fade', {
            // 根据外部传来的变量决定是否显示外部传来的dom,
            props: ['show'],
            template: `<transition @before-enter="handleBeforeEnter" @enter="handleEnter">
              <slot v-if="show"></slot>
            </transition>`,
            methods: {
                handleBeforeEnter(el) {
                    el.style.color = 'red'
                },
                handleEnter(el, done) {
                    setTimeout(() => {
                        el.style.color = 'green',
                            done()
                    }, 2000);
                }

            }
        })

        var app = new Vue({
            el: '#app',
            data: {
                show: false
            },
            methods: {
                btnClick() {
                    this.show = !this.show
                }
            }
        })
    </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值