Vue初识
- 配置vscode模板(File -> Preferences -> User Snippets -> 'html’大括号里面加入)
后期只需输入prefix
中的值即可匹配对应模板内容
"html": {
"prefix": "html",
"body": [
"<!DOCTYPE html>",
"<html lang=\"$1zh-CN\">",
"\t<head>",
"\t\t<title>$2</title>",
"\t\t<meta charset=\"UTF-8\">",
"\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
"\t\t<link href=\"$3css/style.css\" rel=\"stylesheet\">",
"\t</head>",
"\t<body>",
"\t$4",
"\t</body>",
"</html>"
],
"description": "HTML - Defines a template for a html5 document",
"scope": "text.html"
},
"Vue": {
"prefix": "vue",
"body": [
"<!DOCTYPE html>",
"<html lang=\"zh-CN\">",
"\t<head>",
"\t\t<title>$1</title>",
"\t\t<meta charset=\"UTF-8\">",
"\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
"\t\t<script src=\"https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js\"></script>",
"\t</head>",
"\t<body>",
"\t\t<div id=\"app\"></div>",
"\t\t<script>",
"\t\t\tvar vm = new Vue({",
"\t\t\t\tel: \"#app\"",
"\t\t\t\t$2",
"\t\t\t})",
"\t\t</script>",
"\t</body>",
"</html>"
],
"description": "vue - Defines a template for a vue & html5 document"
}
-
基础语法学习
v-for
:循环
v-for=‘item in content_list’v-on
:绑定事件
饭粒-点击事件绑定函数:v-on:click=‘handleBtnClick’ 可简写成–> @click=‘handleBtnClick’v-model
:绑定数据(模板),实现表单输入和应用状态之间的双向绑定
v-model=‘inputValue’v-if
:条件判断
v-if=“seen”v-text
:接字符串(如果是HTML标签不做转义),嵌在标签里面,可以在里面使用js语法,如v-text=’“name:” + name’v-html
:接字符串(如果是HTML标签会做转义),嵌在标签里面,可以在里面使用js语法,如v-html=’“name:” + name’{{属性值}}
:插值表达式,可以在里面使用js语法,如{{“name:” + name}}v-once
:一次性
地插值,当数据改变时,插值处的内容不会更新- 说明:只要是
v-xxx
接上的值是js表达式
<div id="app"> <input type="text" v-model='inputValue'> <button v-on:click='handleBtnClick'>提交</button> <ul> <li>{{"name:" + example}}</li> <li v-for='item in content_list'>{{item}}</li> </ul> </div> <script> var app = new Vue({ el: '#app', data: { example: 'hhh', content_list: ['No.1', 'No.2', 'No.3'], inputValue: '' }, methods: { handleBtnClick: function() { this.content_list.push(this.inputValue), this.inputValue = '' } } }) </script>
Vue.component
:创建全局组件(不用注册)
Vue.component(“组件名”, {template: “…”})- 定义局部组件需要注册
v-bind:绑定名="绑定值"
:子组件向父组件传入绑定值(v-bind可省略不写,即:绑定名=“绑定值”)this.$emit()
:向上层触发事件
<div id="app"> <input type="text" v-model='inputValue'> <button v-on:click='handleBtnClick'>提交</button> <ul> <todo-item v-bind:content='item' v-bind:index='index' v-for='(item, index) in content_list' @delete="handleItemDelete"> </todo-item> </ul> </div> <script> // 定义全局组件 // Vue.component("TodoItem", { // props: ['content'], // template: "<li>{{content}}</li>" // }) // 定义局部组件 var TodoItem = { props: ['content', 'index'], template: "<li @click='handleItemClick'>{{content}}</li>", methods: { handleItemClick: function() { this.$emit("delete", this.index); } } } var app = new Vue({ el: '#app', // 局部组件注册 components: { TodoItem: TodoItem }, data: { content_list: [], inputValue: '' }, methods: { handleBtnClick: function() { this.content_list.push(this.inputValue), this.inputValue = '' }, handleItemDelete: function(index) { this.content_list.splice(index, 1) } } }) </script>
- Vue实例属性 & 方法(接上栗)
app.$data
:Vue组件数据
app.$el
:Vue接管的组件
app.$destroy()
:销毁Vue实例
-
Vue生命周期函数:Vue实例在某一个时间点会自动执行的函数(直接定义在Vue实例底下不用定义在methods中)
beforeCreate
:在实例初始化之后调用created
:在实例创建完成后被立即调用beforeMount
:在挂载开始之前被调用mounted
:挂载之后被调用beforeUpdate
:数据更新之前调用updated
:数据更新之后调用beforeDestroy
:实例销毁之前调用destroyed
:实例销毁之后调用
-
计算属性、方法、侦听器(在Vue实例底下声明)
- 计算属性:computed(调用不用带括弧) 有缓存功能,计算属性内的数据修改才会重新执行方法
<li>{{getFullName}}</li> ... computed: { getFullName: function() { console.log('Getting fullName...') return this.firstName + ' ' + this.lastName } }
- 方法:methods (调用要带括弧) 没有缓存功能,任何数据修改都会重新执行方法
<li>{{getFullName()}}</li> ... getFullName: function() { console.log('Getting fullName...') return this.firstName + ' ' + this.lastName }
- 监听器:watch 没有缓存功能
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<li>{{fullName}}</li> ... data: { firstName: 'Z', lastName: 'fone', fullName: 'Z fone', }, watch: { firstName: function(val) { console.log('firstName change...'); this.fullName = val + ' ' + this.lastName; }, lastName: function(val) { console.log('lastName change...'); this.fullName = this.firstName + ' ' + val; } }
-
样式绑定
- class对象绑定
<div id="app"> <!-- <div @click='handleDivClick' :class='{ activated: isActivated }'> --> <div @click='handleDivClick' :class='[activated]'> Hello World! </div> </div> ... data: { // isActivated: false, activated: "" }, methods: { handleDivClick: function() { // this.isActivated = !this.isActivated; // if (this.activated === 'activated') { // this.activated = '' // }else { // this.activated = 'activated' // } this.activated = this.activated === 'activated' ? '': 'activated' } }
- style对象绑定
<div id="app"> <div @click='handleDivClick' :style='[vueStyle, {background: "yellow"}]'> Hello World! </div> </div> ... data: { vueStyle: { color: 'red' } }, methods: { handleDivClick: function() { this.vueStyle.color = this.vueStyle.color === 'red' ? 'black': 'red' } }
-
条件渲染
- v-if … v-else-if … v-else
- 必须挨在一起,中间有其他标签无法识别
- 如果包裹的是input标签,需要加入
key
属性来加以区分
- v-show:根据条件展示元素的选项,用法同v-if
- 不管判定值是否为真都会渲染,当判定值为false时,display: none;
- v-if与v-show区别:
- v-if 是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
- v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
- v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
<div id="app"> <div v-if='show === "a"'>name:<input k='a'/></div> <div v-else-if='show === "b"'>mobile:<input k/></div> <div v-else>email:<input></div> <div v-show='isshow'>{{message}}</div> </div> <script> var vm = new Vue({ el: '#app', data: { show: 'a', isshow: true, message: 'get it', } }) </script>
- v-if … v-else-if … v-else
-
列表渲染
v-for
- 列表在页面渲染后修改特定元素无法同步到页面中;
- 字典在渲染以后修改特定属性可以同步到页面中,但是增加key/value无法同步到页面中;
- 重新赋值列表/字典可以同步到页面;
- 使用列表的
变异方法
修改元素可以同步到页面,如下例使用vm.nameList.splice(1,1,'haha')
- push() 往列表中加入元素
- pop() 删除最后一个元素
- shift() 删除第一个元素
- unshift() 返回元素个数
- splice(n, m, ‘xxx’) 指定从第n个元素开始删除m个元素,替换值xxx
- sort() 对列表进行排序
- reverse() 将列表倒叙排列
set
修改列表元素并同步到页面中- Vue.set(object, key, value)
- object. s e t ( o b j e c t , k e y , v a l u e ) 举 栗 : V u e . s e t ( v m . n a m e L i s t , 0 , ′ f o n e 93 3 ′ ) v m . set(object, key, value) 举栗: Vue.set(vm.nameList, 0, 'fone933') vm. set(object,key,value)举栗:Vue.set(vm.nameList,0,′fone933′)vm.set(vm.infoDict, ‘age’, 23)
<div id="app">
<template v-for='item, index in nameList'>
<li>{{index}}. {{item}}</li>
</template>
<ol>
<li v-for='(value, key, index) in infoDict'>{{key}}=={{value}} ({{index}})</li>
</ol>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
nameList: ['fone', 'alex', 'rain', 'jack'],
infoDict: {
'name': 'fone',
'age': 123,
'email': 'fone@z.com'
}
}
})
</script>
- 高阶 & 拓展
-
is
指示模板,对于h5标准嵌套标签,若不符合嵌套规则会出现渲染错乱的情况,如ul > li是标准。
若下栗中tbody标签中直接使用row子组件标签会出现渲染错乱。 -
子组件中定义data必须以函数的形式
- 1 2知识点汇总饭粒
<div id="table"> <table> <tbody> <tr is='row'></tr> <tr is='row'></tr> </tbody> </table> </div> <script> Vue.component('row', { data: function() { return { contend: 'This is one row.' } }, template: '<tr><td>{{contend}}</td></tr>' }) var app = new Vue({ el: '#table' }) </script>
ref
使用
(在Vue实例中使用this.$refs.引用名.引用属性
)
- 饭粒:累加求和
<div id="app"> <counter ref='one' @change='handleChange'></counter> <counter ref='two' @change='handleChange'></counter> <div>{{sum}}</div> </div> <script> Vue.component('counter', { template: '<div @click="handleClick">{{number}}</div>', data: function() { return { number: 0 } }, methods: { handleClick: function() { this.number ++, this.$emit('change') } } }) var vm = new Vue({ el: '#app', data: { sum: 0 }, methods: { handleChange: function() { this.sum = this.$refs.one.number + this.$refs.two.number } } }) </script>
-
组件传值
- 父组件通过
属性
的形式向子组件传递,子组件中用props
接收(属性不会渲染到页面中) - 单向数据流:子组件不能修改父组件传递过来的数据,若要改就重新定义一个变量来接收传递值,修改变量即可
- 父传子的数据为非字符就要用v-bind的方式绑定,如下栗 :count=“1” 传递的是数字
- 子组件通过
事件触发
的形式向父组件传递,如$emit
- 给子组件绑定Vue实例的事件使用
.native
<div id="app"> <counter :count="1" @change='handleChange'></counter> <counter :count="2" @change='handleChange'></counter> <div @click.native='onClick'>{{sum}}</div> </div> <script> var counter = { props: ['count'], data: function() { return { number: this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function() { this.number += 3, this.$emit('change', 3) } } } var vm = new Vue({ el: '#app', components: { counter: counter, }, data: { sum: 3 }, methods: { handleChange: function(step) { this.sum += step }, onClick: function() { alert('click...') } } }) </script>
- 父组件通过
-
props传值验证
- 类型:
type
,String、Number等,传入多个类型表示或关系(下栗注释部分) - 默认值:
default
- 是否必传:
required
- 验证函数:
validator
,传值以参数传入
<div id="app"> <child msg='ffffff'></child> </div> <script> Vue.component('child', { props: { // msg: [String, Number], msg: { type: String, default: '666999', required: true, validator: function(value) { return (value.length > 5) } } }, template: '<div>{{msg}}</div>' }) var vm = new Vue({ el: "#app", }) </script>
- 类型:
-
非父子组件传值: 定义
Vue.prototype.bus
,用钩子函数mounted
接收子组件触发的事件<div id="app"> <baba msg='woshiniba'></baba> <baba msg='woshimama'></baba> </div> <script> Vue.prototype.bus = new Vue() Vue.component('baba', { props: { msg: String }, data: function() { return { myData: this.msg } }, template: '<div @click="handleClick">{{myData}}</div>', methods: { handleClick: function() { this.bus.$emit('change', this.myData) } }, mounted: function() { var _this = this this.bus.$on('change', function(val) { _this.myData = val }) } }) var vm = new Vue({ el: '#app', }) </script>
-
slot
插槽(自定义组件中内容即为插槽)
· 饭粒1<div id="app"> <haha> <h1 slot='header'>header</h1> <h6 slot='footer'>footer</h6> </haha> </div> <script> Vue.component('haha', { template: `<div> <slot name='header'></slot> <p>content</p> <slot name='footer'></slot> </div>` }) var vm = new Vue({ el: '#app', }) </script>
· 饭粒2:子组件做循环或DOM结构需要从外部传入(插槽必须使用
template
,并使用slot-scope
属性接收传递值)<div id="app"> <tag> <template slot-scope='props'> <h3>{{props.item}}</h3> </template> </tag> </div> <script> Vue.component('tag', { data: function() { return { list: [1, 2, 3, 4] } }, template: `<div><slot v-for="item in list" :item=item></slot></div>` }) var vm = new Vue({ el: '#app' }) </script>
-
拓展
- 动态组件:
component
,下栗给动态组件绑定数据,数据值即为显示的组件 v-once
:将组件放入内存,降低渲染页面消耗的性能- 饭粒:点击change按钮改变组件
<div id="app"> <component :is='current'></component> <!-- <tag-one v-if='current === "tag-one"'></tag-one> <tag-two v-if='current === "tag-two"'></tag-two> --> <button @click='handleClick'>change</button> </div> <script> Vue.component('tag-one', { template: '<div v-once>Child One</div>' }) Vue.component('tag-two', { template: '<div v-once>I am you father!</div>' }) var vm = new Vue({ el: '#app', data: { current: 'tag-one' }, methods: { handleClick: function() { this.current = this.current === 'tag-one' ? 'tag-two' : 'tag-one' } } }) </script>
- 动态组件:
-
- Vue动画
-
CSS动画
· 纯transition
动画<!DOCTYPE html> <html lang="zh-CN"> <head> <title>CSS动画</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script> <style> .fone-enter, .fone-leave-to { opacity: 0; } .fone-enter-active, .fone-leave-active { transition: opacity 3s; } </style> </head> <body> <div id="app"> <button @click='handleClick'>{{btm}}</button> <transition name='fone'> <div v-if='isShow'>{{msg}}</div> </transition> </div> <script> var vm = new Vue({ el: "#app", data: { btm: '消失', msg: 'Hello World!!!', isShow: true }, methods: { handleClick: function() { this.isShow = ! this.isShow, this.btm = this.btm === '出现' ? '消失' : '出现' } } }) </script> </body> </html>
-
animate组件
keyframes
animate -
keyframes与transition
- 入场动画 appear appear-active-class=’’
- keyframes与transition同时加入 v-enter v-leave-to v-enter-active v-leave-active
- 动画优先级 type=’’
- 动画时长 :duration="{enter: 5000, leave: 6000} 单位ms"
<!DOCTYPE html> <html lang="zh-CN"> <head> <title>CSS动画</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css"> <style> #app { text-align: center } .fone-enter, .fone-leave-to { opacity: 0; } .fone-enter-active, .fone-leave-active { transition: opacity 3s; } /* @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(2); } 100% { transform: scale(1); } } .enter { transform-origin: center; animation: bounce-in 2s; } .leave { transform-origin: center; animation: bounce-in 2s reverse; } */ </style> </head> <body> <div id="app"> <button @click='handleClick'>{{btm}}</button> <transition name='fone' appear type='keyframes' enter-active-class='animated swing fone-enter-active' leave-active-class='animated tada fone-leave-active' appear-active-class='animated bounceInUp'> <div v-if='isShow'>{{msg}}</div> </transition> </div> <script> var vm = new Vue({ el: "#app", data: { btm: '消失', msg: 'Hello World!!!', isShow: true }, methods: { handleClick: function() { this.isShow = ! this.isShow, this.btm = this.btm === '出现' ? '消失' : '出现' } } }) </script> </body> </html>
-
-
js动画
- 钩子:@before-enter=’’
@enter=’’
@after-enter=’’ 要在enter函数中执行done才会到这个钩子
@before-leave=’’
@leave=’’
@after-leave=’’
<div id="app"> <button @click='handleClick'>biu</button> <transition @before-enter='handleBeforeEnter' @enter='handleEnter' @after-enter='handleAfterEnter' @before-leave='handleBeforeLeave' @leave='handleLeave' @after-leave='handleAfterLeave' > <div v-show='show'>我来也!</div> </transition> </div> <script> var vm = new Vue({ el: '#app', data: { show: true }, methods: { handleClick: function() { this.show = ! this.show }, handleBeforeEnter: function(el) { el.style.color = 'red' }, handleEnter: function(el, done) { setTimeout(() => { el.style.color = 'green' }, 2000), setTimeout(() => { done() }, 3000) }, handleAfterEnter: function(el) { el.style.color = 'yellow' }, handleBeforeLeave: function(el) { el.style.color = 'blue' }, handleLeave: function(el, done) { setTimeout(() => { el.style.color = 'pink' }, 2000), setTimeout(() => { done() }, 3000) }, handleAfterLeave: function(el) { el.style.color = 'origon' } } }) </script>
- verocity.js动画 verocity
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
handleEnter: function(el, done) { Velocity(el, {opacity: 1}, {duration: 2000, complete: done}) }
- 多个元素的动态切换transition
<style> .v-enter, .v-leave-to { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 2s; } </style> ... <div id="app"> <button @click='handleClick'>biu</button> <transition mode='out-in'> <div v-if='show' key='enter'>我来也!</div> <div v-else key='leave'>你走吧!</div> </transition> </div> <script> var vm = new Vue({ el: '#app', data: { show: true }, methods: { handleClick: function() { this.show = ! this.show }, } }) </script>
- 多个组件的动态切换 component
<!-- 与上栗style相同 --> <div id="app"> <button @click='handleClick'>biu</button> <transition mode='out-in'> <component :is='type'></component> </transition> </div> <script> Vue.component('baba', { template: '<div>我是你爸!</div>' }) Vue.component('mama', { template: '<div>我是麻麻!</div>' }) var vm = new Vue({ el: '#app', data: { type: 'baba' }, methods: { handleClick: function() { this.type = this.type === 'baba' ? 'mama' : 'baba' }, } }) </script>
- 列表过渡
transition-group
列表展开后相当于多个transition标签
<!-- 与上栗style相同 --> <div id="app"> <button @click='handleClick'>biu</button> <transition-group mode='out-in'> <div v-for='item in item_list' :key='item.id'>{{item.msg}}</div> </transition-group> </div> <script> var vm = new Vue({ el: '#app', data: { count: 0, item_list: [] }, methods: { handleClick: function() { this.item_list.push({ id: this.count++, msg: 'what???' }); }, } }) </script>
- 将动画封装到组件中
transition
slot
@before-enter
<div id="app"> <button @click='handleClick'>biu</button> <fade :show='show'> <div>你大爷!</div> </fade> </div> <script> Vue.component("fade", { props: ['show'], template: `<transition @before-enter='handleBeforeEnter' @enter='handleEnter' @after-enter='handleAfterEnter'> <slot v-if='show'></slot> </transition>`, methods: { handleBeforeEnter: function(el) { el.style.background = 'aqua' }, handleEnter: function(el, done) { setTimeout(() => { el.style.color = 'blue', done() }, 2000) }, handleAfterEnter: function(el) { el.style.background = 'yellow' } } }) var vm = new Vue({ el: '#app', data: { show: true }, methods: { handleClick: function() { this.show = ! this.show } } }) </script>
- 钩子:@before-enter=’’
-