vue基础
开发文档
开发文档肯定要比本人写的好,放在最上边。
此文档链接以下的内容均为个人见解,如有错误敬请指正!!!
基础学习
在学习阶段,没有配置脚手架:
vue.js //开发版本包含完整的警告和调试模式
vue.min.js //生产版本没有警告
学习思想
我相信学到vue一般都会有一定的编程基础了,有mvc的思想,也就是对数据模型,视图,控制器有所了解。
在传统开发中,我们通过控制器来实现对数据的控制。但是这样有一个弊端,我们需要通过一系列比较麻烦的操作来控制数据在视图中的变化。而在vue中我们需要了解的是MVVM模式,M,V依旧是数据模型,但是控制方式发生了改变,由我们的vm来自动控制。通过这个可以实现数据与模型的实施变化。
说到这里就不得不说一下虚拟dom了,我们在js中通过操作dom树来对标签进行改变,但是这样的改变是费时费力的,我们每一个元素都要通过先获取后更改,也就是需要一步一步走。虚拟dom相当于内存中放了一个集合,把需要渲染的数据一次性拿出,按顺序渲染。
多少无益,我们看例子。
初步入门
<body>
<div id="app">
<!-- 插值表达式 内容 data中对象的属性 view -->
<h1>{{hello}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
//新规定: 1.结尾的;号可以省略 2.字符一般使用'单引号'
//1.实例化VUE对象 函数式编程 vm
const vm = new Vue({
//1.定义el元素 要在哪个位置使用vue进行渲染
el: "#app",
//2.定义数据对象 model
data: {
hello: 'VUE入门案例!!!!'
}
})
</script>
</body>
上述例子中,vm对象会对id为app的标签的innerHtml属性做遍历寻找{{dataname}}这样的东西,来进行一次性渲染。
数据渲染
<body>
<div id="app">
<h1>v-cloak未完成渲染不显示</h1>
<p v-cloak>{{hello}}</p>
<h1>以优化后的效果进行展现 内部兼容了v-cloak</h1>
<p v-text="msg"></p>
<h1>以html解析之后的效果进行展现</h1>
<p v-html="html"></p>
<h1>v-pre指令 跳过vue的解析的过程,直接展现数据</h1>
<p v-pre>{{name}}</p>
<h1>v-once指令 元素只被解析一次 once后续改变也不会再重新渲染</h1>
<h3 v-once>{{once}}</h3>
<h1>v-model 测试双向数据绑定 </h1>
<!-- 1.服务器将数据给用户展现 2.用户需要传给数据给服务器 -->
双向数据绑定: <input name="model" v-model="model"/>
<h1>简单的表达式应用</h1>
<p>{{isVip?"是":"不是"}}</p>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
hello: 'vue案例',
msg: '测试v-text指令',
html: '<h1>html效果展现</h1>',
once: '测试解析次数',
model: '测试双向数据绑定',
name: '跳过vue的解析的过程,直接展现数据',
className1: 'a',
className2: 'b',
isVip: true
}
})
</script>
</body>
条件渲染
v-if
<body>
<div id="app">
<h1>{{name}}</h1>
<h2 v-if="age>18">24小时上网</h2>
<h2 v-else-if="age>12">8小时上网</h2>
<h2 v-else>不能上网</h2>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
name: '张三',
age: 20
}
})
</script>
</body>
v-show
<body>
<div id="app">
<h1>{{name}}</h1>
<h2 v-show="age>18">24小时上网</h2>
<h2 v-show="age>12&&age>18">8小时上网</h2>
<h2 v-show="12>age">不能上网</h2>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
name: '张三',
age: 20
}
})
</script>
</body>
注意两点:
- v-if是第一次不符合条件就不渲染,后几次从dom中去除,v-show是不符合条件隐藏(如果需要频繁显示/消失用v-show)。
- 在条件判断的时候,目前仅仅支持大于>号
循环渲染
<body>
<div id="app">
<h1>循环数组 此处index为下标</h1>
<p v-for="hobby,index in hobbies">{{index}}.{{hobby}}</p>
<h1>循环对象 此处index为属性名</h1>
<p v-for="item,index in users[0]">{{index}}.{{item}}</p>
<h1>循环对象 此处index为属性名</h1>
<p v-if="item.age%2==0" v-for="item,index in users">{{index}}.{{item}}</p>
<h1>循环对象 此处:key为元素标签</h1>
<p v-for="item,index in users" :key="index">{{index}}.{{item}}</p>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
w: 'w',
hobbies: ['敲鼠标','敲键盘','敲电脑'],
users:[{
name: '张三',
age: 18,
sex: '男'
},{
name: '李四',
age: 17,
sex: '男'
},{
name: '王五',
age: 18,
sex: '男'
},{
name: '赵六',
age: 17,
sex: '男'
}]
}
});
</script>
</body>
在Vue2中,v-if的优先级小于v-for
key
具体说一下key吧,在循环中我们引入了:key,他的作用在于:为该元素加上一个唯一的标签。vue有时候会偷懒,就是如果一个地方的值不一样,他可能仅仅更改了值而不去渲染整个标签。我们通常可以把会引起冲突的地方加入key。同时注意:上文中之所以用:key是因为此时的key值是动态的,所谓的双向绑定。如果仅仅是一个固定的可以用固定的标签。
<input key="in"/>
计算属性
计算属性最大的特点在于,缓存计算结果,这样当我们多次调用的时候提高效率(只有参与计算的变量改变才会重新计算)
计算属性默认情况下仅有get,但是我们也可以加入set
<body>
<div id="app">
<p>{{fn}}</p>
<p>{{ln}}</p>
<h1>{{fullname}}</h1>
<p v-for="item,index in oddusers" :key="index">{{item}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
fn: '张',
ln: '三',
users:[{
name: '张三',
age: 18,
sex: '男'
},{
name: '李四',
age: 17,
sex: '男'
},{
name: '王五',
age: 18,
sex: '男'
},{
name: '赵六',
age: 17,
sex: '男'
}]
},
// 在计算后会对缓存,在fn,ln未改变的情况下就不会改变
computed: {
fullname:{
get:function(){
return this.fn+this.ln;
},
set:function(val){
var names = val.split('')
this.fn = names[0]
this.ln = val.substring(1,val.length);
}
} ,
oddusers: function(){
let res = this.users.filter(function(item,index,arr){
console.log(index)
return item.age%2==0
});
return res;
}
}
});
</script>
</body>
侦听属性
<body>
<div id="app">
{{msg}}<br>
{{gsm}}
</div>
<script src="./js/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
msg: 'hello',
gsm: ''
},
watch:{
msg:function(val){
this.gsm = val.split('').reverse().join('');
}
}
});
</script>
</body>
在侦听和计算属性中,个人感觉如果仅仅是做数字计算,字符拼凑用计算属性可能是一个更好的选择。
样式获取
v-bind:class等同于:class
<style>
.page{
width: 200px;
height: 200px;
}
.a{
background-color: red;
}
.b{
background-color: seagreen;
}
.c{
border: 2px black solid;
}
</style>
</head>
<body>
<div id="app">
<h1>变量方式</h1>
<div class="page" v-bind:class="{a:isTrue}"></div>
<h1>对象方式</h1>
<div class="page" v-bind:class="styObj"></div>
<h1>数组方式</h1>
<div class="page" v-bind:class="styArr"></div>
<h1>字符串方式</h1>
<div class="page" v-bind:class="styStr"></div>
</div>
<script src="./js/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
isTrue: true,
styObj: {a: true,c: true},
styArr: ['b','c'],
styStr: 'b c'
}
});
</script>
</body>
事件属性
常见的事件属性最常见的是鼠标事件,键盘事件,事件修饰符
点击事件及修饰符
<style>
.parent{
width: 100px;
height: 100px;
background-color: skyblue;
}
button{
margin-bottom: 20px;
}
</style>
</head>
<body>
<div id="app">
<h1>点击事件</h1>
<button v-on:click="count++">点击增加{{count}}</button>
<h1>调用无参方法</h1>
<button v-on:click="sayhi()">无参调用</button>
<h1>调用有参方法</h1>
<button v-on:click="saymsg(count)">说出输入信息</button>
<h1>传入dom事件 $event</h1>
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<h1>事件属性</h1>
<!-- 阻止单击事件向上冒泡 -->
<div class="parent" v-on:click="parent()">
<button v-on:click.stop="son()">
不冒泡
</button>
</div>
<!-- 提交事件去除默认事件 -->
<form action="" method="GET">
<input type="submit" v-on:click.prevent="onSubmit" value="提交表单"></input>
</form>
<!-- 仅触发一次修饰符 -->
<button v-on:click.once="one">我只触发一次</button>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div class="parent" v-on:click.capture="parent()">
<button v-on:click="son()">
父先动
</button>
</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div class="parent" v-on:click.self="parent()">
<button v-on:click="son()">
自己动
</button>
</div>
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data:{
count: 0
},
methods: {
sayhi(){
alert("hi");
},
saymsg(msg){
alert(msg);
},
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) {
event.preventDefault()
}
alert(message)
},
parent(){
console.log("parent");
},
son(){
console.log("son");
},
one(){
console.log("我只触发一次")
},
onsubmit(){
console.log("表单不能提交");
}
}
});
</script>
</body>
建盘鼠标事件
请看文档
表单属性
<body>
<div id="app">
<div>
<span>姓名:</span>
<input type="text" name="username" v-model="username"><br>
<span>单选框:</span>
<input type="radio" v-for="item,index in sex" name="username" v-model="usersex" :key="index" :value="item"><br>
<span>复选框:</span>
<input type="checkbox" v-for="item,index in hobbies" name="hobbies" v-model="userhobbies" :key="index" :value="item"><br>
<span>所在城市:</span>
<select name="cities" v-model="usercity">
<option value="">无</option>
<option v-for="item,index in cities" :key="index" :value="item">{{item}}</option>
</select><br>
<span>喜欢的城市:</span>
<select name="cities" multiple v-model="userlikecity">
<option value="">无</option>
<option v-for="item,index in cities" :key="index" :value="item">{{item}}</option>
</select><br>
<span>备注:</span>
<textarea name="text" v-model="text"></textarea><br>
<span>lazy</span>
<input type="text" v-model.lazy="lazy"><br>
<span>number</span>
<input type="text" v-model.number="number"><br>
<span>trim</span>
<input type="text" v-model.number="trim"><br>
</div>
<div>
<p>姓名:{{username}}</p>
<p>性别:{{usersex}}</p>
<p>爱好:{{userhobbies}}</p>
<p>所在城市:{{usercity}}</p>
<p>喜欢的城市:{{userlikecity}}</p>
<p>备注:{{text}}</p>
<p>lazy:{{lazy}}</p>
<p>number:{{number}}</p>
<p>trim:{{trim}}</p>
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
username: "张三",
sex: ['男','女'],
hobbies: ['游泳','读书','跳舞'],
cities: ['上海','北京','杭州'],
text: '',
userhobbies: [],
usersex: '',
usercity: '',
userlikecity: [],
lazy: ''
}
})
</script>
</body>
v-model
<body>
<div id="app">
<input-com :username="username" @childin="change"></input-com>
<input-com :username="username" @childin="username=$event"></input-com>
<!-- 此种方式,事件名称把必须是input -->
<input-com v-model="username"></input-com>
<h1>{{username}}</h1>
</div>
<script>
Vue.component('input-com',{
template: `<input type="text" @input="$emit('childin',$event.target.value)" :value="username"/>`,
//template: `<input type="text" @input="$emit('input',$event.target.value)" :value="username"/>`,
data:function(){
return {
}
},
props:['username']
})
let vm = new Vue({
el: "#app",
data: {
username: ''
},
methods:{
change:function(data){
this.username=data;
}
}
});
</script>
</body>
动画属性
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
通过
-
enter-class
-
enter-active-class
-
enter-to-class
(2.1.8+) -
leave-class
-
leave-active-class
-
leave-to-class
(2.1.8+)几条来自定义属性。
<style> .context{ width: 50px; height: 50px; background-color: springgreen; margin: 0 auto; margin-bottom: 50px; } .movie-enter-active, .movie-leave-active { transition: opacity .5s; width: 100px; height: 100px; } .movie-enter, .movie-leave-to { opacity: 0; width: 100px; height: 100px; } </style> </head> <body> <div id="app"> <button v-on:click="show = !show"> 展示 </button> <transition name="slider" enter-active-class="animated fadeInRight" leave-active-class="animated fadeOutRight"> <div v-if="show" class="context"></div> </transition> <transition name="movie"> <div v-if="show" class="context"></div> </transition> </div> <script> let vm = new Vue({ el: "#app", data:{ show: true } }); </script> </body>
组件
入门
<body>
<div id="app">
<simple-com></simple-com>
</div>
<script>
let SimpleCom = Vue.component('simple-com',{
data: function(){
return {
msg: 'SimpleCom1'
}
},
// 父元素对子元素传值
// 组件外边必须包有一层
template: "<div><h1>msg</h1><h1>msg</h1></div>"
});
let vm = new Vue({
el: "#app",
data:{
},
components:{
'simple-com':SimpleCom
}
});
</script>
</body>
父传子值
<body>
<div id="app">
<!-- 注意传值的参数保存在props中,组件自己的数据保持在data中,两者的命名不能重复 -->
<!-- 静态属性赋值 -->
<user-table user-name="张三"></user-table>
<!-- 动态属性赋值 -->
<!-- 注意如果user是驼峰命名,例如props是userInfo 则此处属性应为 user-info -->
<user-table :user-name="'李四'" :user-age="'18'"></user-table>
<!-- 父值子传 -->
<user-table :user-name="userName"></user-table>
<!-- 循环传递类 -->
<user-table :user-name="userName" v-for="item,index in users" :key="index" :user="item"></user-table>
</div>
<script>
let userTable =('user-table',{
props: ['userName','userAge','user'],
template: '<div><h1>{{msg}}{{userName}}{{userAge}}</h1><h1>我是类传过来的{{user.sex}}</h1></div>',
data: function(){
return {
msg: '姓名:',
}
}
});
let vm = new Vue({
el: "#app",
data: {
userName: '王五',
users:[{
name: '张三',
age: 18,
sex: '男'
},{
name: '李四',
age: 17,
sex: '男'
},{
name: '王五',
age: 18,
sex: '男'
},{
name: '赵六',
age: 17,
sex: '男'
}]
},
components: {
'user-table':userTable
}
});
</script>
</body>
子传父值
通过事件监听,子向父传递参数
<body>
<div id="app">
<!--父组件 监听事件并做出反应 -->
<user-table v-for="item,index in users" @suser='change()' :key="index" :user-name="item"></user-table>
{{userName}}
</div>
<script>
let userTable =('user-table',{
props: ['userName'],
template: `<div>
<h2>{{userName}}</h2>
<button @click="select(userName)">选择</button>
</div>`,
data: function(){
return {
msg: '姓名:',
}
},
methods: {
select:function(userName){
// 写入事件并传入值
this.$emit('suser',userName);
}
}
});
let vm = new Vue({
el: "#app",
data: {
userName: '王五',
users:[ '张三','李四', '王五','赵六']
},
components: {
'user-table':userTable
},
methods: {
// 对事件做出反应
change:function(data){
this.userName=data;
}
}
});
</script>
</body>
通过父向子传递方法,子再更改父值
<body>
<div id="app">
<!--父组件 监听事件并做出反应 -->
<user-table v-for="item,index in users" :action='change' :key="index" :user-name="item"></user-table>
{{userName}}
</div>
<script>
let userTable =('user-table',{
props: ['userName','action'],
template: `<div>
<h2>{{userName}}</h2>
<button @click="select(userName)">选择</button>
</div>`,
data: function(){
return {
msg: '姓名:',
}
},
methods: {
select:function(userName){
// 写入事件并传入值
this.action(userName);
},
}
});
let vm = new Vue({
el: "#app",
data: {
userName: '王五',
users:[ '张三','李四', '王五','赵六']
},
components: {
'user-table':userTable
},
methods: {
// 对事件做出反应
change:function(data){
this.userName=data;
}
}
});
</script>
</body>
$属性
注意:$children在vue2中有所使用但是vue3已经废弃。
<body>
<div id="app">
<!--父组件 监听事件并做出反应 -->
<user-table v-for="item,index in users" :key="index" :user-name="item"></user-table>
{{userName}}
</div>
<script>
let userTable =('user-table',{
props: ['userName'],
// $root是获取根组件
// $parent是获取父组件
template: `<div>
<h2>{{userName}}</h2>
<button @click="select(userName)">select选择</button>
<button @click="$parent.change(userName)">$parent选择</button>
<button @click="$root.change(userName)">$root选择</button>
</div>`,
data: function(){
return {
msg: '姓名:',
}
},
methods: {
select:function(userName){
// $parent指的是该组件的父组件
this.$parent.change(userName);
},
}
});
let vm = new Vue({
el: "#app",
data: {
userName: '王五',
users:[ '张三','李四', '王五','赵六']
},
components: {
'user-table':userTable
},
methods: {
// 对事件做出反应
change:function(data){
this.userName=data;
}
}
});
</script>
</body>
动态组件
<body>
<div id="app">
<!-- 实现动态组件 -->
<button @click="c(1)">1</button>
<button @click="c(2)">2</button>
<button @click="c(3)">3</button>
<button @click="c(4)">4</button>
<component :is="com"></component>
<c5></c5>
</div>
<!-- 此种方式写template会轻松一点 -->
<script type="text/x-template" id="SimpleCom">
<p>123</p>
</script>
<script>
let c1 = Vue.component('c1',{
template: "<div><p>1</p></div>"
});
let c2 = Vue.component('c2',{
template: "<div><p>2</p></div>"
});
let c3 = Vue.component('c3',{
template: "<div><p>3</p></div>"
});
let c4 = Vue.component('c4',{
template: "<div><p>4</p></div>"
});
let SimpleCom = Vue.component('c5',{
template: "#SimpleCom"
});
let vm = new Vue({
el: "#app",
data:{
com: c1
},
components:{
c1,c2,c3,c4
},
methods:{
c:function(i){
this.com=this.$options.components['c'+i];
}
}
});
</script>
</body>
生命周期
我把生命周期放到基础阶段的最后一章,是因为学习到此时看生命周期可能会有更多的了解。尤其是组件化学完后才可以进行看到销毁前和销毁后。
<body>
<div id="app">
<button @click="show=!show">点击</button>
<simple-com v-if="show"></simple-com>
<div v-bind:class="className">{{msg}}</div>
</div>
<script>
let SimpleCom = Vue.component('simple-com',{
data: function(){
return {
msg: 'SimpleCom1'
}
},
template: "<h1>{{msg}}</h1>",
beforeDestroy(){
console.log("销毁前");
},
destroyed(){
console.log("销毁后");
}
});
let vm = new Vue({
el: "#app",
data: {
msg: 'Vue你好',
className: "aaa",
show: true
},
components:{
'simple-com':SimpleCom
},
methods:{
w(){
console.log("w");
}
},
beforeCreate(){
// data和method尚未挂载
console.log("初始化前");
console.log(this.msg)
console.log(this.w)
},
created(){
// data和method已经挂载
// 还未进行渲染
console.log("初始化后");
console.log(this.msg)
console.log(this.w)
console.log(document.querySelector('.aaa'))
},
beforeMount(){
console.log("渲染前");
console.log(this.msg)
console.log(this.w)
console.log(document.querySelector('.aaa'))
},
mounted(){
console.log("渲染后");
console.log(this.msg)
console.log(this.w)
console.log(document.querySelector('.aaa'))
},
beforeUpdate(){
console.log("更新前");
console.log(this.msg)
},
updated(){
console.log("更新后");
console.log(this.msg)
}
});
</script>
</body>