起手式
完整版同时包括编译器(compiler) 和 运行时(runtime)将模板字符串编译为 JavaScript 渲染函数(render函数)的代码 运行时的功能包括创建 Vue 实例、渲染并处理虚拟 DOM 等,它包括除了编译器的其他所有功能\
两个版本的区别
| Vue完整版 | Vue只包含运行时版 | |
|---|---|---|
| 特点 | 有compiler | 没有compiler |
| 视图 | 写在HTML里,或者写在template选项里 | 写在render函数里,用h创建标签 |
| cdn引入 | vue.js | vue.runtime.js |
| webpack引入 | 需要配置alias | 默认使用 |
| vue@cli引入 | 需要额外配置 | 默认使用 |
那究竟应该使用哪一个版本呢?
1、 对于用户来说,非完整版 (即runtime版)体积小,用户体验好,但只支持h函数
2、 对于程序员来说,只能写h函数的话,开发体验不好,如果有compiler, 开发者就能写更直观更语义化的HTML标签和template, 所以我们需要一个compiler
3、 vue-loader就可以引入compiler, 把vue文件里的HTML标签和template 会在构建时预编译成 h函数,这样用户和开发者都高兴
template 和 render 的用法
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
template标签和JS里的template
//vue文件中的template标签
<template>
<div id="app">
{{n}}
<button @click="add">+1</button>
</div>
</template>
//js中的template
template : `
<div id="app">
{{n}}
<button @click="add">+1</button>
</div>
`
render函数:
//不完整版在js中构建视图
render(h){
return h('div', [this.n,h('{on:{click:this.add}’,'+1'])
}
//不完整版使用vue-loader
//先创建一个demo.vue文件,在里面构建视图
import demo from "./demo.vue"
new Vue({
el: "#app",
render(h) {
return h(demo)
}
})
options选项
new Vue() 这就是构造一个Vue的实例。
这个实例会根据你给的选项得出一个对象(vm),vm封装了这个DOM对象以及对应的所有操作,不管是事件绑定还是数据的读写、DOM更新,全部都由vm这个对象负责。你只需要去调用它的API就好了。
原型:对象.__proto__===其构造函数.prototype 推出vm.__proto__===Vue.prototype
函数也是对象,所以Vue函数对象的内存图如上。
函数.__proto__===Function.prototype推出Vue.__proto__===Function.prototype
问题一: 初始化时可以写些什么对象进去(options)?
问题二: vm自己有哪些属性?
问题三: Vue函数本身有哪些属性?
问题四: 每个函数都有个属性叫prototype,同时每个对象都有个属性叫__proto__。假设Vue.prototype对应的对象的地址是#419,那请问这个#419里面有哪些属性呢?
问题五: Vue.prototype.__proto__= ?
options的五类属性
- DON: el,template,render,rebderError
- 生命周期钩子函数:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,activated,deactivated,beforeDestroy,destroyed,erroCaptured。
- 资源:directives,filters,components
- 组合:parent,mxins,extends,provide,inject
第1类属性 数据(Data):
data 数据
props 属性
computed 计算属性 //被计算出来的
methods 方法,用来定义方法的
watch 观察 //当data变化时做某些事情就用watch
propsData //很少用,单元测试会用
方法和函数的区别?
1.概念:方法是属于面向对象概念,函数属于数学概念。
在面向对象里叫方法,有对象才有方法,方法依附于对象即对象.方法,比如说obj.sayhi()sayhi就叫方法也是函数,一般叫方法。 如果sayhi()这样写就叫函数
在数学里叫函数。
2.都指同一个东西
function(p1,p2){
return
}
第2类属性 DOM:
el 挂载点 //你要用你的模版替换页面上的哪一块,你的挂载点
template //你的HTML内容。着重讲语法v-if、v-for
render 渲染 //⚠️注意template和render只能二选一!
//template是给完整版用的,render是给非完整版用的。一起用必然有一个会失效!
renderError //很少用
第3类属性 生命周期钩子:
生命周期:Vue组件在页面中插入一个<div>监听它的事件,然后用户点击按钮时变化。可以切入的点叫做钩子。
beforeCreate 创建之前
created 创建之后
beforeMount
mounted 挂在之后
beforeUpdate
updated 更新之后
activated
deactivated
beforeDestroy
destroyed 失败之后
errorCaptured //很少用
第4类属性 资源
directives 指令
filters 过滤器 //尽量不去用,用methods代替
components 组件
//如果要在一个文件引用另一个Vue的文件就用组建。Demo.vue就叫组件,英文名叫components。
第5类属性 组合:
parent //很少用
mixins 混入
extends 扩展
provide 提供
inject 注入
入门属性
1.el 挂载点
可以用$mount代替
你要用你的模版替换页面上的哪一块,可以用$mount代替。组件或者实例的挂载点。
index.html
<div id="app"> {{n}} </div>
main.js
new Vue({
el: '#app',
render(h) {
return h(Demo)
}
})
/*可以用$mount替代:*/
new Vue({
render: h => h(Demo)
}).$mount('#app')
//const vm = new Vue({
// render: h => h(Demo)
//})
//vm.$mount('#app')
总结
特点1.名字要保持一致
特点2.如果在<div>内加内容,那么多半用户是看不见的。js加载完后会把hello干掉。
特点3.可以用$mount替代。挂载基本等同于replace或append
2.data 内部数据
组件的定义只接受函数。
用vue完整版来示例
main.js
console.log(window.Vue)
// import Vue from 'vue' 删掉,这次不从npm引入,直接使用全局的Vue
const Vue = window.Vue
Vue.config.productionTip = false
new Vue({ //实例
data: { //内部数据,只支持函数
n: 0
},
template: `
<div class="red">
{{n}}
<button @click="add">+1</button>
</div>
`,
methods: {
add() { //add要先定义好,不然会报错
this.n += 1
}
}
}).$mount('#app')
Bug: Vue的data有bug,后面讲"数据响应式"时会说。
为什么data必须是函数?
如果你是个组件,比如Demo组件,引入Demo组件import Demo from './Demo.vue'
//Demo.vue
export default {
data(){ //vue-loader写文件时,data必须是函数
return {
n:0
}
},
}
Demo实际上是对象,Vue会自动把Demo传给new Vue(Demo)
假设如果有两个组件共用内部数据data,当其中一个改变时另一个也会变,因为它们引用的是同一个data。函数会阻止两个组件共用data的问题。
main.js
render:h=>h(x,[h(Demo),h(Demo)])
3.methods 方法
事件处理函数或者是普通函数
add必须写在methods里面,如果写到外面会报错。
main.js
//new Vue({ 错误
// add() { this.n += 1 },
// methods: {
// }
//})
事件处理函数: 写到一个@click或keypress或者任何事件作为它的处理函数
普通函数method代替filter。
main.js
new Vue({ //实例
data: {
n: 0,
array:[1,2,3,4,5,6,7,8,9]
},
template: `
<div class="red">
{{n}}
<button @click="add">+1</button>
<hr>
{{filter(array)}} //2'filter()
</div>
`,
methods: {
add() {
this.n += 1
},
filter(array) {
return array.filter(i => i % 2 === 0)
}
//filter() {
//return this.array.filter(i => i % 2 === 0)
//}
}).$mount('#app')
bug: methods第2种用法,用来主动在模版里调用。这种调用特点是每次渲染都会重新调用。就算毫无意义跟之前是相同的结果它也会执行。
4.components 组件
使用Vue组件,注意大小写
如果要在一个文件引用另一个Vue的文件就用组件。Demo.vue就叫组件(components)。
const vm=new Vue({...})vm是Vue实例或Vue对象
这个不能叫做组件,它使用"其它的Vue实例"的时候,"其它的Vue实例"才是组件。
如何使用组件?
首先要创建组件,组件的3种引入形式
1' 创建一个.vue文件(推荐)。
这个文件就是Vue组件,比如Demo.vue,然后引入该文件。
使用组件
说明要用的组件是frank,然后就可以在template里面写frank。
main.js
import Demo from './Demo.vue' //引入Demo文件
new Vue({ //实例
components: { //说明要用的组件是frank
frank: Demo //名字:值,Demo组件
//Demo: Demo //es6语法可以简写为Demo
},
template: `
<div class="red">
<frank/>
</div>
`,
}).$mount('#app')
Demo.vue
<template>
<div class="red">
fuck
</div>
</template>
优先使用第1种,其它2种不够模块化。
2' 用js的方式
不要components,直接声明全局的Demo2。
main.js
Vue.component('Demo2', {
template: `
<div> demo2 </div>
`
})
new Vue({
template: `
<div class="red">
<Demo2/>
</div>
`,
}).$mount('#app')
你是入口就是实例,被别人用的就是组件。
3' 前2种的结合
保留js对象,又保留components
main.js
Vue.component('Demo2', {
template: `
<div> demo2 </div>
`
})
new Vue({
components: {
fuck: {
template: `
<div> demo3 </div>
`
}
},
template: `
<div class="red">
<fuck/>
</div>
`,
fuck也可以有data
fuck: {
data() { //组件data必须用函数
return { n: 0 }
},
template: `
<div> fuck's n:{{n}} </div>
`
}
fuck对象里面的写法,跟外面的options是完全一样的。
什么是组件?
组件:可以组合的物件就叫组件。比如手臂、腿就是人的组件
组件可以认为是实例中的实例。
注意大小写
1.文件名最好全小写,因为有些古老的操作系统,比如window10可能不能识别大小写,防止2个大小写文件重名。
2.组件首字母最好大写。
5.四个钩子
1.created 实例出现在内存中
2.mounted 实例出现在页面中
3.updated 实例更新了
4.destroyed 实例消亡了
1.2.3.created、mounted、updated
new Vue({ //实例
created() {
//debugger
console.log("这玩意出现在内存中")
},
mounted() {
//debugger
console.log("这玩意出现在页面中")
},
updated() {
console.log("更新了") //点击+1按钮后显示更新了
console.log(this.n) //每次拿到的n都是最新的
},
}).$mount('#app')
可以通过debugger验证实例是否出现在页面:n和button没加载出来说明出现在内存,加载出来证明出现在页面。
4.destroyed 实例消亡了
步骤
逻辑:让一个组件出现又消失
1.src新建文件demo2.vue。
把目前的实例变组件: 把main.js中new Vue({ //实例 })的实例剪切到demo2.vue的<script>里。别忘了把template内容也移到<template>里。
2.创建实例
//main.js
import Demo from './demo2.vue'
new Vue({ //实例
components: { Demo },
data: { //自己new Vue就不是组件,所以data可以是对象
visible: true
},
template: `
<div>
<button @click="toggle">toggle</button>
<hr>
<Demo v-if="visible===true"/>
</div>
`,
methods: {
toggle() {
this.visible = !this.visible //把visible变为反值,实现按钮的切换
}
}
}).$mount('#app')
3.监听destroyed
destroyed(){
console.log("已经消亡了")
}
每次toggle后n将重新初始化为0。
知识点
1.渲染页面: render函数
render: h => h(Demo) //更简单
//等价于
components: { Demo },
template: `
<Demo/>
`,
2.v-if什么时候出现
new Vue({
components: { Demo },
data: { //自己new Vue就不是组件,所有data可以是对象
visible: true
},
template: `
<Demo v-if="visible===true"/>
`
}).$mount('#app')
3.实例 VS 组件
实例就是main.js,代码特征new Vue({ }),data可以是对象、函数。 实例需要导入组件demo.vue实例包含组件,如果实例是爸爸,那组件就是流落在外的儿子。
main.js
import Demo from './demo.vue' //导入组件`demo.vue`
new Vue({
data: { //data可以是对象
visible: true
},
}).$mount('#app')
复制代码
组件就是新建的demo.vue,代码特征3个标签<template>、<script>、<style scoped>,data必须是函数。 可以认为是实例中的实例。
demo.vue
//组件
<template> //html
</template>
<script> //js
export default {
data(){ //vue-loader写文件时,data必须是函数 }
}
</script>
<style scoped> //css
</style>
4.函数和方法的区别?
函数(function) 是可以执行的javascript代码块,由javascript程序定义或javascript实现预定义。函数可以带有实际参数或者形式参数,用于指定这个函数执行计算要使用的一个或多个值,而且还可以返回值,以表示计算的结果。
方法(method) 是通过对象调用的javascript函数。也就是说,方法也是函数,只是比较特殊的函数。假设有一个函数是fn,一个对象是obj,那么就可以定义一个method。方法和对象相关,函数和对象无关。
方法和函数大致上是相同的,但有两个主要的不同之处:
(1)方法中的数据是隐式传递的。
(2)方法可以操作类内部的数据(请记住,对象是类的实例化–类定义了一个数据类型,而对象是该数据类型的一个实例化)
6.props 外部数据
外部数据是由外部来传值的(值是字符串),也叫外部属性
1' 传字符串
message="n"2' 传变量
:message="n"传入this.n数据3' 传函数
:fn="add"传入this.add函数
1' 传字符串 message="n"
步骤
(1)新建文件demo3.vue
props从外部接收message,这个message会自动绑到this上。Vue允许省掉this。
//demo3.vue
<template>
<div class="red">
这里是demo3的内部
{{message}} //{{this.message}}this可省
</div>
</template>
<script>
export default {
//声明:props:[属性名]
props:['message'] //从外部接收message,这个message会自动绑到this上
}
</script>
<style scoped>
.red{ color: red; }
</style>
(2)使用props外部数据
main.js
import Demo from './demo3.vue'
new Vue({ //实例
components: { Demo },
template: `
<div>
<Demo message="你好 props"/> //传值:在组件后加key value
</div>
`,
}).$mount('#app')
message="字符串"
2' 传变量 :message="n" 传入this.n数据
加空格和冒号" :", 注意Demo后有空格!
// main.js
import Demo from './demo3.vue'
new Vue({ //实例
components: { Demo },
data: {
n:0
},
template: `
<div>
<Demo :message="n"/> <!--传变量(数据) -->
</div>
`,
}).$mount('#app')
空格:message="JS(变量)"
3' 传方法(函数):fn="add" 传入this.add函数
1.添加第2个参数fn
demo3.vue
<template>
<div class="red">
这里是demo3的内部
{{message}}
<button @click="fn">call fn</button>
</div>
</template>
<script>
export default {
props:['message','fn']
//从外部接收message、fn,会自动绑到this上
}
</script>
<style scoped>
.red{ color: red; }
</style>
2.接收方法
main.js
import Demo from './demo3.vue'
new Vue({ //实例
components: { Demo },
data: { //实例的data可以是对象
visible: true,
n: 0
},
template: `
<div>
{{n}}
<Demo :fn="add"/> <!--传JS变量(数据) -->
</div>
`,
methods: {
add() {
this.n += 1
},
}
}).$mount('#app')
空格:message="JS(方法)"
把n回传给里面的儿子,得到的是最新的n。
main.js
template: `
<div>
{{n}}
<Demo :message="n" :fn="add"/>
</div>
`,
对Vue 数据响应式的理解
getter、setter
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter
总结一下:
- 任何一个 Vue Component 都有一个与之对应的 Watcher 实例。
- Vue 的
data上的属性会被添加 getter 和 setter 属性。 - 当 Vue Component
render函数被执行的时候,data上会被触碰(touch), 即被读,getter方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有data。(这一过程被称为依赖收集) data被改动时(主要是用户操作), 即被写,setter方法会被调用, 此时 Vue 会去通知所有依赖于此data的组件去调用他们的 render 函数进行更新
let obj0 = {
姓: "高", 名: "圆圆", age: 18
};
// 需求一,得到姓名
let obj1 = {
姓: "高", 名: "圆圆", 姓名() {
return this.姓 + this.名;
}, age: 18
};
console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?
// 需求二,姓名不要括号也能得出值
let obj2 = {
姓: "高", 名: "圆圆", get 姓名() {
return this.姓 + this.名;
}, age: 18
};
console.log("需求二:" + obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数,仅此而已。
// 需求三:姓名可以被写
let obj3 = {
姓: "高", 名: "圆圆", get 姓名() {
return this.姓 + this.名;
}, set 姓名(xxx) {
this.姓 = xxx[0]
this.名 = xxx.slice(1)
}, age: 18
};
obj3.姓名 = '高媛媛'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
Object.defineProperty()
Vue是通过 JS 标准内置对象方法 Object.defineProperty 来设定将data中普通的属性n转化为getter、setter方法的属性n的。
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
语法: Object.defineProperty(obj, prop, descriptor)
参数:
obj:要在其上定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:将被定义或修改的属性描述符。
返回值: 被传递给函数的对象。
Object.defineProperty() - JavaScript | MDN (mozilla.org)
let data0 = {
n: 0
}
// 需求一:用 Object.defineProperty 定义 n
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
console.log(`需求一:${data1.n}`)
// 总结:这煞笔语法把事情搞复杂了?非也,继续看。
// 需求二:n 不能小于 0
// 即 data2.n = -1 应该无效,但 data2.n = 1 有效
let data2 = {}
data2._n = 0 // _n 用来偷偷存储 n 的值
Object.defineProperty(data2, 'n', {
get() {
return this._n
},
set(value) {
if (value < 0) return
this._n = value
}
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)
// 抬杠:那如果对方直接使用 data2._n 呢?
// 算你狠
proxy() 代理
// 需求三:使用代理
let data3 = proxy({data: {n: 0}}) // 括号里是匿名对象,无法访问
function proxy({data}/* 解构赋值,别TM老问 */) {
const obj = {}
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
// 因为我怕你们看不懂
Object.defineProperty(obj, 'n', {
get() {
return data.n
},
set(value) {
if (value < 0) return
data.n = value
}
})
return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`需求三:${data3.n},设置为 1 成功`)
// 杠精你还有话说吗?
// 杠精说有!你看下面代码
// 需求四
let myData = {n: 0}
let data4 = proxy({data: myData}) // 括号里是匿名对象,无法访问
// data3 就是 obj
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n},设置为 -1 失败了吗!?`)
// 我现在改 myData,是不是还能改?!你奈我何
// 艹,算你狠
// 需求五:就算用户擅自修改 myData,也要拦截他
let myData5 = {n: 0}
let data5 = proxy2({data: myData5}) // 括号里是匿名对象,无法访问
function proxy2({data}/* 解构赋值,别TM老问 */) {
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
// 因为我怕你们看不懂
let value = data.n
Object.defineProperty(data, 'n', {
get() {
return value
},
set(newValue) {
if (newValue < 0) return
value = newValue
}
})
// 就加了上面几句,这几句话会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get() {
return data.n
},
set(value) {
if (value < 0) return//这句话多余了
data.n = value
}
})
return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)
// 这代码看着眼熟吗?
// let data5 = proxy2({ data:myData5 })
// let vm = new Vue({data: myData})
// 现在我们可以说说 new Vue 做了什么了
结论:Vue会遍历传入的data对象所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter,这样就生成一个新的对象全权负责数据——就是实例化的Vue对象vm。这样vm会成为data 的代理,对 data 的所有属性进行监控,当数值发生改变的时候,vue就调用render函数重新渲染视图。
数据响应式
当你创建一个实例时
const vm = new Vue({data:{n: 0}})
- vue 会让 vm 成为 myData 的代理。
- vue 会对 myData 的所有属性进行监控。
- 示例
1、在 data 中添加属性
对于一般的对象来说,可以在 data 中预先把所有可能用到的属性全部写出来,这样并不需要新增属性,只需要改它。 也可以通过其他方法来添加属性。 在了解以上原理后,我们来了解 Vue 提供的一个 API:
Vue.set(object, key, value)
或
this.$set(object, key, value)
2、对数组的方法
vue对数组进行了改变,给数组加了一层原型,在其中Vue修改了7个方法覆盖了之前数组原型的7个方法。调用这些Vue新定义的方法时,在这些新方法里Vue会加上对新添的元素的监听(相当于进行了set操作),把新数据也进行代理,这样vue就能重新监测到数组的变化了更新UI操作 具体的七个变更方法:
push()(在数组结尾处)向数组添加一个新的元素pop()方法从数组中删除最后一个元素shift()会删除首个数组元素,并把所有其他元素“位移”到更低的索引unshift()(在开头)向数组添加新元素,并“反向位移”旧元素splice()拼接,可用于向数组添加新项sort()reverse()
computed 和 watch的区别
computed
对比
这个我有过总结 computed 和 watch的区别
模板、指令与修饰符
进阶构造属性
Vue 进阶属性
directives、mixins、extends、provide、inject
directives 指令
内置指令
- v-if、v-for、v-show、v-html
自定义指令
一、 声明一个全局指令
Vue.directive('x', directiveOptions)
二、 声明一个局部指令
new Vue({
...,
directives: {
"x": directiveOptions
}
})
directiveOptions
五个函数属性
- bind(el, info, vnode, oldVnode) - 类似 created
- inserted(参数同上) - 类似 mounted
- update(参数同上) - 类似 updated
- componentUpdated(参数同上) - 用的不多
- unbind(参数同上) - 类似 destroyed
缩写
- directiveOptions 在某些条件下可以缩写为函数,自定义指令 — Vue.js (vuejs.org)
指令的作用
主要用于 DOM 操作
- Vue 实例/组件用于数据绑定、事件监听、DOM 更新
- Vue 指令主要目的就是原生 DOM 操作
减少重复
- 如果某个 DOM 操作你经常使用,就可以封装为指令
- 如果某个 DOM 操作比较复杂,也可以封装为指令
mixin 混入
类比
- directives 的作用是减少 DOM 操作的重复
- mixins 的作用是减少 data、methods、钩子的重复
技巧
- 选项智能合并 混入
- Vue.mixin 全局混入
extends 继承、扩展
减少重复
- 遇到与 mixins 同样的需求
- Vue.extend 或 options.extends
const MyVue = Vue.extend({
data(){return {name:'', time:undefined}}
created(){
if(!this.name){console.error(no name!)}
this.time = new Date()
},
beforeDestroy(){
const duration = (new Date()) - this.time
console.log(`${this.name}存活时间${duration}`)
}
})
- 然后就可以使用 new MyVue(options)
provide 和 inject
使用
- provide:
Object | () => Object - inject:
Array<string> | { [key: string]: string | Symbol | Object } - provide 是祖先组件向子孙后代注入一个依赖
- inject 是让后代注入祖先提供的依赖
示例
总结
- 作用:大范围的 data 和 method 等共用
- 注意:不能只传值不传方法,因为值是被复制给 provide的
- 可以传引用但不推荐,容易失控
表单与v-model
基本用法
input / textarea / checkbox / radio / select / form
- input-文本:
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
- textarea-多行文本:
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
- checkbox-复选框:
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
- radio-单选按钮:
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
-
select-选择框:
- 单选时:
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '...',
data: {
selected: ''
}
})
多选时:
<div id="example-6">
<select v-model="selected" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '#example-6',
data: {
selected: []
}
})
- form:
<form @submit.prevent="onSubmit">
<label>
<span>用户名</span>
<input type="text" v-model="user.username" />
</label>
<label>
<span>密码</span>
<input type="text" v-model="user.password" />
</label>
<button type="submit">登录</button>
</form>
new Vue({
name: "App",
data(){
return {
user:{
username: "",
password: ""
},
}
},
methods:{
onSubmit()
},
components: {},
})
修饰符
.lazy / .number / .trim
- v-model.lazy,焦点失去时生效
- v-model.number,只接收数字
- v-model.trim,两头空格去掉
v-model
-
默认利用名为
value的 prop 和名为input的事件 -
等价于
<input type="text" :value="user.username" @input="user.username = $event.target.value"> -
双向绑定:v-model会绑定一个变量,在变量变化的时候 UI 会变化,用户改变 UI 的时候,数据也会改变
-
v-model 是 v-bind:value 和 v-on:input 的语法糖
-
自定义:
v-on:input="$event",原生:v-on:input="$event.target.value" -
监听的事件
- input 事件,键盘、鼠标、任何输入设备的输入
- change 事件,只在 input 是去焦点时触发 (v-model.lazy)
使用 Ant Design of Vue
使用组件
$ npm i --save ant-design-vue
/* 完整引入 Antd 组件 */
import Vue from 'vue'
import App from './App.vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false;
Vue.use(Antd)
new Vue({
render: h => h(App),
}).$mount('#app')
/* 局部导入组件 */
import { Button, message } from 'ant-design-vue'
Vue.use(Button)
- 具体看文档
VueRouter
Vue 动画原理
#文档 过渡 & 动画
轮播组件slides
轮播难点在于最末位到首位的切换方式,在讲轮播之前需要讲下动画。 Vue动画支持很多种不同的方式。
Vue动画方式1 - CSS transition
Vue提供了transition组件
HTML
//先引入Vue(bootCDN)
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
//1.写`<transition>`
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
CSS
//2.写类
.fade-enter-active, .fade-leave-active {
transition: all 2s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
width:100px
}
//3.设置初始值
p{
border:1px solid red;
width:300px
}
JS
new Vue({
el: '#demo',
data: { show: true }
})
步骤
第1步.在html里写<transition>
第2步.在css里写.fade开头的一系列类
最后给需要的属性添加初始值
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 ,则v-是这些类名的默认前缀。如果你使用了 <transition name="fade">,那么v-enter会替换为fade-enter。
在进入/离开的过渡中,会有6个class切换:
v-enter-active、v-leave-active表示过渡时间有多长,一般会合并写这2个类,因为动的都是一样的。 v-enter、v-leave-to表示进入和退出状态,这2个类也会合写。剩下2个一般不用。
p经历的过程:
一开始p就是这个类,但是由于目前是隐藏的show: false,所以需要enter进入DOM,进入DOM的过程可以详细的去控制。
v-enter控制进入时的开始的状态,v-enter-to控制进入时的结束的状态,fade-enter-active控制它如何去添加补间动画,一般不需要加v-enter-to因为结束状态应该就是它原本状态p,没必要加。 等动画结束,就会把这3个类v-enter、fade-enter-active、v-enter-to都删掉,恢复到原始状态p
p由于目前是隐藏的所以需要enter进入DOM,进入DOM的过程可以详细的进行控制。开始是红色,然后变成黑色,过程持续3s。动画结束后enter被删掉,恢复到原始的白色。
CSS 过渡
html
<div id="example-1">
<button @click="show = !show"> Toggle render </button>
<transition name="slide-fade"> //滑出
<p v-if="show">hello</p>
</transition>
</div>
CSS
/* 可以设置不同的进入和离开动画,设置持续时间和动画函数 */
.slide-fade-enter-active {
transition: all 3s ease;//滑出淡入不是线性3s
}
.slide-fade-leave-active {
transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter, .slide-fade-leave-to {
//fade-leave-to"淡出的结束状态"就是"淡入开始的状态",fade-enter对应fade-leave-to
transform: translateX(10px);//淡入那一瞬间的位置
opacity: 0;
}
JS
new Vue({
el: '#example-1',
data: { show: true }
})
Vue动画方式2 - CSS animation
html
<div id="example-2">
<button @click="show = !show">Toggle show</button>
<transition name="bounce"> //bounce类的前缀
<p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</transition>
</div>
CSS
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse; //bounce-in
}
@keyframes bounce-in { //bounce-in
0% {transform: scale(0);}
50% {transform: scale(1.5);}
100% {transform: scale(1);} //结束时恢复正常状态
}
JS
new Vue({
el: '#example-2',
data: { show: true }
})
keyframes语法
@keyframes spin {
0% {opacity: 0;}
100% {opacity: 1;}
}
使用
.fade-enter-active{
animation:spin 1s reverse;
}
类名
可以通过以下 attribute 来自定义过渡类名:
enter-class、enter-active-class、enter-to-class、
leave-class、leave-active-class、leave-to-class
也可以结合第三方CSS动画库 Animate.css 提供了很多动画效果。
Animate.css
bootCDN选择animate.min.css 示例
html
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/3.5.2/animate.min.css" rel="stylesheet">
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
enter-active-class="animated tada"
//animated后接的就是动画效果,格式animated xxx
leave-active-class="animated bounce"
>
<p v-if="show">hello</p>
</transition>
</div>
JS
new Vue({
el: '#example-3',
data: {show: true}
})
CSS 不需要写CSS
在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation很快的被触发并完成了,而transition效果还没结束。在这种情况中,你就需要使用type属性并设置animation或transition来明确声明你需要Vue监听的类型。
有的时候需要手动设置淡入淡出时间
<transition :duration="1000">...</transition> //:duration="1000"
JavaScript 钩子
文档 JS钩子
可以在attribute中声明JS钩子,你可以用这些钩子知道当前动画处在哪个阶段。
Vue动画方式3 - JS 操作动画
velocity是一个非常著名的用JS操作动画的库,推荐用这个做动画。
bootCDN选择velocity.min.js,最好用版本1.2.3的。
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="example-4">
<button @click="show = !show">
Toggle
</button>
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
v-bind:css="false"
>
<p v-if="show">
Demo
</p>
</transition>
</div>
JS
new Vue({
el: '#example-4',
data: {
show: false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
el.style.transformOrigin = 'left'
},
enter: function (el, done) {
Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
Velocity(el, { fontSize: '1em' }, { complete: done })
},
leave: function (el, done) {
Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
Velocity(el, {
rotateZ: '45deg',
translateY: '30px',
translateX: '30px',
opacity: 0
}, { complete: done })
}
}
})
Vue动画方式4 - 多元素动画
一.多个元素的过渡 文档
示例1:淡出淡入
<div id="example-4">
<transition name="fade" mode="out-in">
<button key="on" v-if="status==='off'" @click="status='on'">on</button>
<button key="off" v-else @click="status='off'">off</button>
</transition>
</div>
JS
new Vue({
el: '#example-4',
data: { status: 'on'},
})
CSS
.fade-enter-active,.fade-leave-active {
transition: all 1s;
}
.fade-enter {
opacity: 0;
}
.fade-leave-to {
opacity: 0;
}
示例2:先进再出
<transition name="fade" mode="in-out">
CSS
.fade-enter-active,.fade-leave-active {
transition: all 1s;
}
.fade-enter {
opacity: 0;
transform:translateX(100px)
}
.fade-leave-to {
opacity: 0;
transform:translateX(-100px)
}
#example-4{
position:relative;
}
button{
position:absolute;
}
JS同上略
示例3:实现轮播效果
<transition name="fade">
CSS
#example-4{
position:relative;
padding:100px;
}
JS同上略
必须要加key才有动画,不然它不知道你要变什么,它会以为两个button是一个。当两个标签一样的时候一定要加key。
同时生效的进入和离开的过渡不能满足要求,所以Vue提供了过渡模式:
out-in:当前元素先进行过渡,完成之后新元素过渡进入。
in-out(用的少):淡出的时候往左移
mode="out-in/in-out"是为了解决2个元素占用位置冲突的问题。
二.多个组件的过渡
就是用<component>绑定is属性
只需要使用动态组件
//实际上就是tab切换
<div id="transition-components-demo">
<transition name="component-fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
</div>
JS
new Vue({
el: '#transition-components-demo',
data: {
view: 'v-a' //view是啥,就是哪个组件的名字
},
components: {
'v-a': {
template: '<div>Component A</div>'
},
'v-b': {
template: '<div>Component B</div>'
}
}
})
CSS
.component-fade-enter-active, .component-fade-leave-active {
transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to {
opacity: 0;
}
适用场景:如果你有10个组件在一个地方显示,想在切的时候有动画。就用<component :is="view">组件,如果你想让第一个组件出现就让view: 'v-a',view等于第几个组件,就默认显示它。不需要加key。
Vue动画5 - 列表动画(常用)
文档列表过渡
<div id="list-demo" class="demo">
<button v-on:click="add">Add</button>
<button v-on:click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
CSS
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to
/* .list-leave-active for below version 2.1.8 */ {
opacity: 0;
transform: translateY(30px);
}
JS
new Vue({
el: '#list-demo',
data: {
items: [1,2,3,4,5,6,7,8,9],
nextNum: 10
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.items.length)
},
add: function () {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
},
}
})
知识点
1.name="list"list就是css的前缀
2.<transition-group> 组件
使用场景:要想用v-for实现同时渲染整个列表,这时候就可以使用<transition-group>组件。
该组件特点:
(1)默认为一个<span>,也可以通过 tag attribute更换为其他元素。
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
tag='p'tag取值是p,<transition-group>就会将自身替换为p标签。tag取值是什么,<span>就会被什么标签包围。不写tag就默认是<span>。
(2)过渡模式不可用,因为我们不再相互切换特有的元素。 mode=“out-in"不能用,,<transition-group>不支持mode
(3)内部元素总是需要提供唯一的 key attribute 值。 内部元素<span>必须写keyv-bind:key="item"
(4)CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
for循环如何做动画?
<transition-group> + tag="div" + v-for循环的模版(span/div)
注意:不能加其它的标签,for循环模版外只能是<transition-group>
总结
Previous模板、指令与修饰符