简介
vue官网
vue.js是一套构建用户界面的渐进式框架。
vue全家桶:vue.js+vue-router+vuex+axios
特点
- 核心只关注视图层(view)
- 易学,轻量,灵活的特点
- 适用于移动端项目
- 渐进式框架
渐进式的理解
- 声明式渲染(无需关系如何实现)
- 组建系统
- 客户端路由(vue-router)
- 大规模状态管理(vuex)
- 构建工具(vue-cli)
核心
- 响应的数据变化。当数据发生改变->试图的自动更新
- 组合的视图组件。ui页面映射为组建树,划分组建可维护、可复用、可测试
安装vue
- 安装vue
npm install vue
- 安装bootstrap
npm install bootstrap
- 安装 axios
npm install axios
- 初始化npm
npm init -y
,初始化生成一个新的 package.json 文件,如果使用了 -f(代表force)、-y(代表yes),则跳过提问阶段,直接生成一个新的 package.json 文件。
指令
v-model
:用于表单中的input ,textarea中,会忽略value,checked,selected,将数据绑定在试图上,视图修改后会影响数据的变化。同angularJS中的ng-modelv-text
:双向绑定数据,同{{}}。<div v-text="msg"></div>
v-cloak
:解决{{}}闪烁问题(作用于块区域),要赋予样式才起作用,[v-cloak]{display:none;}v-once
:一次绑定数据。<div v-once>{{msg}}</div>
v-html
:绑定html,一定是安全数据。<div v-html="msg"></div>
v-for
:循环数组,同angularJS中的指令ng-repeat
例:
o in arr 或 (o,index) in arr
a in obj 或 (value,key,index) in obj
v-bind
:动态绑定属性。v-bind:src="" 或 :src=""
v-if
:同angularJSng-if
,if操作的是domv-show
:同angularJSng-show
,show操作的是样式。如果频繁的切换dom使用v-show
,如果一开始就确定下来使用v-if
更好,如果if不通过内部指令不会再执行。只用dom从显示到隐藏或隐藏到显示菜使用vue动画- 绑定事件
v-on:click="fn" 或 @click="fn"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件</title>
</head>
<body>
<div id="app">
<!-- 如果不传递参数,则不要写括号会自动传入事件源,如果写括号要手动传入$event -->
<div v-on:mousedown="fn()">点我</div><br>
<!-- 事件绑定可以用@代替-->
<div @mousedown="fn2(2)">点我</div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({//根实例
el:'#app',
methods:{//methods和data中的数据会全部放到vm上,而且名字不能冲突,冲突会报错,methods中的this指向的都是实例,不能使用箭头函数
fn(){alert(this.a)},
fn2(i){alert(i)}
},
data:{
a:[1,2,3,4,5],
}
});
</script>
</body>
</html>
数据响应的变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据响应的变化(对象)</title>
</head>
<body>
<div id="app">
{{msg}}{{a.school}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
//引入vue后会给一个Vue的构造函数
//vue会循环data中的数据(数据劫持),依次的增加getter和setter
let vm=new Vue({//vm===viewModel
el:'#app',//vue控制的div
data:{//data的中的数据会被vue代理
msg:'hello world!',//可以通过vm.msg取得相对应的数据
a:{school:''}//1.使用变量时先进行初始化,否则新添加的属性不会导致页面刷新(推荐)
}
});
//2.如果没有初始化数据,可以通过$set方法来给对象添加响应式数据
vm.$set(vm.a,'school',1);
//3.通过替换原对象初始化数据
vm.a={school:''};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据响应的变化(数组)</title>
</head>
<body>
<div id="app">
{{arr}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
arr:[1,2,3,4,5],
}
});
//去改变数组中的某一项时监控不到的,也不能使用改变数组长度的方法
//错误:vm.arr[0]=100;vm.arr.length=2;
//可以使用变异方法:pop push shift unshift sort reserve splice
vm.arr=vm.arr.map(item=>item*3);//filter map
</script>
</body>
</html>
checkbox,select,radio
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>checkbox,select,radio</title>
</head>
<body>
<div id="app">
<div>
<input type="checkbox" v-model="hobby" value="0">游泳
<input type="checkbox" v-model="hobby" value="1">跑步
<input type="checkbox" v-model="hobby" value="2">射箭<br>
爱好:{{hobby}}
</div><br>
<div>
<select v-model="address">
<option value="" disabled>请选择</option>
<option value="1">上海</option>
<option value="2">北京</option>
<option value="3">天津</option>
</select><br>
地址:{{address}}
</div><br>
<div>
<input type="radio" v-model="sex" value="1">男
<input type="radio" v-model="sex" value="2">女
性别:{{sex}}
</div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
hobby:[],
address:'',
sex:''
}
});
</script>
</body>
</html>
Promise
//回调函数:将后续的处理逻辑传入到当前要做的事,事情做好后调用此函数
function buy(callback) {
setTimeout(()=>{
let a='蘑菇';
callback(a);
},2000);
}
buy(function (val) {
console.log(val);
});
//可用promise处理回调问题,共三个状态:成功、失败、等待
//resolve对应data,reject对应err,resolve和reject均是函数
//promise的实例就是一个then方法,then方法中有两个参数
var p=new Promise((resolve,reject)=>{
setTimeout(()=>{
let a='青椒';
resolve(a);
},2000);
});
p.then((data)=>{
console.log(data);
},(err)=>{
});
//promise示例
function buyPack() {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(Math.random()>0.5){
resolve('买');
}else {
reject('不买');
}
},Math.random()*1000);
});
}
buyPack().then((data)=>{
console.log(data);
},(data)=>{
console.log(data);
});
Promise-ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>promise-ajax</title>
</head>
<body>
<div id="app">
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基于promise -->
<script src="assets/js/promise-ajax.js"></script>
<script>
let vm=new Vue({
el:'#app',
created(){
ajax({url:'carts.json'}).then((data)=>{
console.log(data);
})
},
data:{
products:[]
}
});
function ajax({url='',type='get',dataType='JSON'}) {
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open(type,url,true);
xhr.responseType=dataType;
xhr.onload=function () {//shr.readyState=4 shr.status=200
if(xhr.status==200){
resolve(xhr.response);//成功调用成功的方法
}else{
reject('not found');//失败调用失败的方法
}
};
xhr.onerror=function (err) {
reject(err);//失败调用失败的方法
};
xhr.send();
});
}
</script>
</body>
</html>
carts.json文件
[
{
"isSelected":true,
"name":"由浅入深学习vue",
"img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
"info":"小米(MI) 米家自动伞 男女通用晴雨两用 一键自动开合 黑色",
"price":99,
"count":3
},
{
"isSelected":true,
"name":"由浅入深学习vue",
"img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
"info":"小米(MI) 米家自动伞 男女通用晴雨两用 一键自动开合 黑色",
"price":99,
"count":3
}
]
axios
更多关于axios,点击这里
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
</head>
<body>
<div id="app">
<input type="number" v-model.number="num">
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基于promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
let vm=new Vue({
el:'#app',
//专门用来发送Ajax的方法
created(){//再数据被初始化后会调用(钩子函数),this指向是vm实例
let that=this;
axios.get('carts.json').then(function (res) {
that.products=res.data;
},function (err) {
console.log(err);
});
//也可以用箭头函数
axios.get('carts.json').then(res=> {
this.products=res;
},err=> {
console.log(err);
})
},
data:{
products:[],
num:0
}
});
</script>
</body>
</html>
filter,created,computed
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物车</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<div class="container">
<table class="table table-bordered table-hover">
<tr>
<th>全选<input type="checkbox" v-model="selectAll"></th>
<th>商品</th>
<th>描述</th>
<th>单价</th>
<th>数量</th>
<th>总价</th>
<th>操作</th>
</tr>
<tr v-for="(product,index) in products">
<td><input type="checkbox" v-model="product.isSelected"></td>
<td><img :src="product.img">{{product.name}}</td>
<td>{{product.info}}</td>
<td>{{product.price}}</td>
<td><input type="number" v-model.number="product.count" min="1"></td>
<td>{{product.price*product.count|toFixed(2)}}</td>
<td><input type="button" class="btn btn-danger" value="删除" @click="remove(product)"></td>
</tr>
<tr>
<td>总价</td>
<td>{{sum}}</td>
</tr>
</table>
</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基于promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{//用来计算
selectAll:{//放弃赋予复选框change方法后,可以通过“计算属性”实现全选与单选
//当products值变化时会重新计算
get(){//get和set中的this指向实例,默认v-model会获取selectAll的值,多以调用get方法
return this.products.every(p=>p.isSelected);
},set(val){//当给checkbox赋值时
this.products.forEach(p=>p.isSelected=val);
}
},
sum(){//如果计算属性写成函数,默认调用get方法,等价于下面的sum属性
return this.products.reduce((pre,next)=>{
if(!next.isSelected) return pre;
return pre+next.price*next.count;
},0)
},
// sum:{//总价,sum结果会被缓存,如果依赖的数据没有变化就不会重新执行
// get(){
// return this.products.reduce((pre,next)=>{
// if(!next.isSelected) return pre;
// return pre+next.price*next.count;
// },0)
// }
// }
},
filters:{//过滤器
toFixed(n,s){//这里的this指的是window
return '¥'+n.toFixed(s);
}
},
created(){//在数据被初始化后会调用(钩子函数),this指向是vm实例
this.getData();
},
methods:{
// checkOne(){//单选
// this.selectAll=this.products.every(item=>item.isSelected);
// },
// checkAll(){//全选/反选
// this.products.forEach(item=>item.isSelected=this.selectAll);
// },
// sum(){//总价,数据只要有变化就会调用此方法,不会缓存上一次的结果,computed会解决这个问题
// return this.products.reduce((pre,next)=>{
// return pre+next.price*next.count;
// },0)
// },
remove(p){//删除
this.products=this.products.filter(item=>item!=p);
},
getData(){
axios.get('carts.json').then(res=> {
this.products=res.data;
// this.checkAll();
},err=> {
console.log(err);
})
}
},
data:{
products:[]
// selectAll:false
}
});
</script>
</body>
</html>
全局filter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全局filter</title>
</head>
<body>
<div id="app1">{{info|select}}</div>
<div id="app2">{{info|select}}</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
Vue.filter('select',(data)=>data+'小红');//全局filter
new Vue({
el:'#app1',
// filters:{
// choose(data){
// return data+'小红';
// }
// },
data:{
info:'hello'
}
});
new Vue({
el:'#app2',
data:{
info:'hello'
}
});
</script>
</body>
</html>
vue动画
v-if
:操作dom,可以和v-else-if,v-else连写v-show
:操作的是样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue动画</title>
<link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
<style>
.one-enter{opacity: 1;}
.one-leave-active{opacity: 0;transition: 1s linear}
/*如果只有一个动画可这样写*/
/*.v-enter{opacity: 1;}*/
/*.v-leave-active{opacity: 0;transition: 1s linear}*/
</style>
</head>
<body>
<div id="app">
<input type="button" @click="flag=!flag" value="切换">
<transition name="one"><!-- 当存在多个动画时,可设置name区分 -->
<div style="width: 100px;height: 100px;background: red;" v-show="flag"></div>
</transition>
<input type="text" v-model="query">
<transition-group name="two" enter-active-class="animated bounceInUp" leave-active-class="animated bounceOutDown">
<div v-for="(a,index) in newArr" :key="Math.random()">{{a}}</div>
</transition-group>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{
newArr(){
return this.arr.filter(item=>item==this.query);
}
},
data:{
query:'',
arr:[1,2,3,4,5],
flag:true
}
});
</script>
</body>
</html>
v-if
和v-else
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-if,else</title>
</head>
<body>
<div id="app">
<div v-if="flag">红色</div>
<div v-else>蓝色</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
flag:false
}
});
</script>
</body>
</html>
修饰符
可进入官网 查看修饰符内容
表单输入绑定修饰符
.lazy
:在默认情况下,v-model
在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用change
事件进行同步:.number
:限制输入框只能填写数字 。<input type="number" v-model.number="num">
.trim
:自动过滤用户输入的首尾空白字符。<input v-model.trim="msg">
事件修饰符
.stop
:阻止继续冒泡,向上传播。阻止事件传播。同e.stopPropagation
和cancelBubble=true
效果相同.capture
:停止捕获,向下传播。阻止事件传播。类似:xxx.addEventListener('clicl',fn,true)
.prevent
:阻止默认行为。如a标签的跳转。类似:e.preventDefault
和returnValue=false
.once
:只触发一次.self
:只有触发自身事件源才会调用。类似:e.srcElement&&e.target
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
computed
computed
计算属性,它不是方法。- 方法不会缓存,
computed
会根据依赖的属性进行缓存(归vue管理的数据,可以响应式变化的)。 - 由
get
和set
两部分组成(不能只写set
,可以只写get
)。一般情况下,通过js赋值影响其他元素或者表单元素设置值的时候会调用set方法。 - computed默认调用get方法,必须要有return,而且不支持异步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>computed</title>
</head>
<body>
<div id="app">
<input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{//computed默认调用get方法,必须要有return,而且不支持异步
msg(){
if(this.a.length<3) return '少了';
if(this.a.length>6) return '多了';
return '';
}
},
data:{
a:''
}
});
</script>
</body>
</html>
watch
- 侦听器
- 只有值变化的时候才会触发,并且支持异步。其他情况我们更善于使用computed
- watch的属性名字要和观察的名字一样
- 当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>watch</title>
</head>
<body>
<div id="app">
<input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
watch:{//只有值变化的时候才会触发,并且支持异步。其他情况我们更善于使用computed
a(newVal,oldVal){//watch的属性名字要和观察的名字一样
if(newVal.length<3){
this.msg='太少';
}else if(newVal.length>6){
this.msg='太多';
}else{
this.msg='';
}
}
},
data:{
a:'',
msg:''
}
});
</script>
</body>
</html>
template
template
标签是vue提供给我们的没有任何意义,只是用来包裹元素用的。v-show
不支持template
。- 默认情况下切换dom时相同的结构会被复用,如果不需要复用,需要添加key属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-if,else</title>
</head>
<body>
<div id="app">
<!-- template标签是vue提供给我们的没有任何意义,只是用来包裹元素用的。v-show不支持template-->
<template v-if="flag">
<div>红色</div>
<div>红色</div>
<div>红色</div>
<div>红色</div>
<div>红色</div>
</template>
<div v-else>蓝色</div>
<br><br>
<!-- 默认情况下切换dom时相同的结构会被复用,如果不需要复用,需要添加key-->
<input type="button" value="切换" @click="cut=!cut">
<template v-if="cut">
<label>登录</label>
<input type="text" key="1">
</template>
<template v-else>
<label>注册</label>
<input type="text" key="2">
</template>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
flag:true,
cut:true
}
});
</script>
</body>
</html>
:class :style
class和style的绑定有两种形式:一、对象,二、数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind-class,v-bind-style</title>
<style>
.x {color: red;}
.y {font-size: 30px;}
.z {background: antiquewhite;}
</style>
</head>
<body>
<div id="app">
<p :class="{x:flag,y:true,z:false}">测试</p>
<p :class="[class1,class2,{z:true}]">测试</p>
<p :style="{color:'red',fontSize:'30px',background:'antiquewhite'}">测试</p>
<p :style="[style1,style2,{background:'antiquewhite'}]">测试</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
class1:'x',
class2:'y',
style1:{color:'red'},
style2:{fontSize:'30px'},
flag:true,
cut:true
}
});
</script>
</body>
</html>
实现单页开发的方式
- 通过hash(#)记录跳转的路径(可以产生历史管理)
- 浏览器自带的历史管理的方法history(history。pushState()),但可能会导致404错误
- 开发时使用hash的方法,上线的时候使用history的方法
自定义指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义指令</title>
<style>
.box {
width: 100px;
height: 100px;
background: red;
position: absolute;;
}
</style>
</head>
<body>
<div id="app">
<button v-color="flag">变色</button>
<div class="box" v-drag></div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
directives:{
color(el,bindings){//el指代的是元素本身,这里指的是button
el.style.background=bindings.value;
},
drag(el,bindings){
el.onmousedown=function(e){
var disX=e.pageX-el.offsetLeft;
var disY=e.pageY-el.offsetTop;
document.onmousemove=function (e) {
el.style.left=e.pageX-disX+'px';
el.style.top=e.pageY-disY+'px';
};
e.preventDefault();
}
}
},
data:{
flag:'red'
}
});
</script>
</body>
</html>
组件
- 分类是页面级的,1.一个页面是一个组件;2.将可复用的部分抽离出来形成的基础组件
- 全局组件:可以声明一次在任何地方使用
- 局部组件:必须告诉这个组件属于谁
- 一般写插件用的是全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全局组件</title>
</head>
<body>
<div id="app">
<my-remark></my-remark>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//1.组件名不要带有大写,多个单词用-连接
//2.只要组件名和定义名字相同就可以,但只有首字母可以大写
//3.html采用短横线隔开命名法,js中转驼峰也是可以的
Vue.component('my-remark',{
template:'<div>{{msg}}测试啦</div>',
data(){//组件中的数据必须是函数类型,返回一个实例作为组件的数据
return {msg:'啦啦啦,'}
}
});
let vm=new Vue({
el:'#app',
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>局部组件</title>
</head>
<body>
<div id="app">
<component1></component1>
<component2></component2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//局部组件使用三部曲:1.创建组件,2.注册组件,3.使用组件
//组件是相互独立的,不能之间跨作用域。实例也是一个组件,组件中拥有声明周期函数
let obj={school:'lalala'};//如果组件公用了数据,会导致同时更新(独立性)
//子组件不能直接使用父组件的数据(组件之间的数据交互)
//组件理论上可以无限嵌套
let component1={
template:'<div @click="fn">组件1{{school}}</div>',
data(){
return obj;
},
methods:{
fn(){
this.school='hello'
}
}
};
let component2={
template:'<div>组件2{{school}}</div>',
data(){
return obj;
}
};
let vm=new Vue({
el:'#app',
components:{
component1,component2
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>嵌套组件</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//如果要再一个组件使用另一个组件,1.先保证使用的组件是真实存在的,2.在需要引用这个组件的实例上通过components注册这个组件,3.组件需要在父级的模板中通过标签的形式引入
let grandson={
template:'<h3>grandson</h3>'
};
let son={
template:'<h2>son<grandson></grandson></grandson></h2>',
components:{
grandson
}
};
let parent={
template:'<h1>parent<son></son></h1>',
components:{
son
}
};
let vm=new Vue({
el:'#app',
components:{
parent
}
});
</script>
</body>
</html>
- 父传子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父传子</title>
</head>
<body>
<div id="app">
父亲:{{money}}
<!-- 当前组件的属性=父级的值。给儿子加了一个m属性,属性对应的数据是属于父亲的-->
<child :m="money"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父传递子
let vm=new Vue({//parent
el:'#app',
data:{money:200},
components:{
child:{
props:{//属性名和data中名字不能相同,校验时不能阻断代码的指向,只是警告而已
m:{//校验属性的类型,如果不带:得到的肯定时字符串类型:m='1' :m='true'
type:[String,Function,Object,Array,Number,Boolean],
// default:0,//可以给m赋予默认值,如果不传默认会调用default
required:true,//次属性是限制必须传递,但不能给default同用
validator(val){//第一个参数是当前传递的值,返回true标识通过,false不通过
return val>300;//自定义校验器(用的不是很多)
}
}
},//对象的形式可以通过校验
// props:['m'],//this.m=200;会在当前子组件上声明一个m属性值是父亲的
template:'<div>儿子:{{m}}</div>'
}
}
});
</script>
</body>
</html>
- 发布订阅
//发布emit 订阅on
function Girl() {
this._events={};
}
Girl.prototype.on=function (eventName,callback) {
if(this._events[eventName]){//不是第一次
this._events[eventName].push(callback);//{失恋:[cry,shopping,eat]}
}else{
this._events[eventName]=[callback]//{失恋:[cry]}
}
};
Girl.prototype.emit=function (eventName,...args) {
//[我,你,他]
//[].slice.call(arguments,1);
//Array.from(arguments).slice(1);
this._events[eventName].forEach(cb=>cb(args));
};
let girl=new Girl();
let cry=(who)=>{console.log(who+'哭')};
let shopping=(who)=>{console.log(who+'购物')};
let eat=(who)=>{console.log(who+'吃')};
girl.on('失恋',cry);//{失恋:[cry]}
girl.on('失恋',shopping);//{失恋:[cry,shopping]}
girl.on('失恋',eat);//{失恋:[cry,shopping,eat]}
girl.emit('失恋','我','你');
- sync修饰符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sync修饰符</title>
</head>
<body>
<div id="app">
<!-- 使用.sync修饰符同步数据 -->
父亲:{{money}}
<!--<child :m="money" @update:m="val=>this.money=val"></child>-->
<child :m.sync="money"></child>
<!-- 写的时候还是按原有的方法来写即可,即注释的方法 -->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父亲绑定一些事件,儿子触发这个事件将参数传递过去,单向数据流,父亲数据刷新,儿子就刷新
let vm=new Vue({//parent
el:'#app',
data:{money:200},
components:{
child:{
props:['m'],
template:'<div>儿子:{{m}}<button @click="getMoney">多要钱</button></div>',
methods:{
getMoney(){
this.$emit('update:m',400);//触发自定义事件
}
}
}
}
});
</script>
</body>
</html>
- 实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>弹窗</title>
<style>
.mask {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0,0,0,.3);}
.pop {position: fixed;top: 50%;left: 50%;width: 500px;height: 200px;background: #fff;transform: translate3d(-50%,-50%,0);}
</style>
</head>
<body>
<div id="app">
<button @click="open">点击</button>
<pop :shows="flag" @close="()=>flag=false"></pop>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let pop={
props:['shows'],
template:' <div class="mask" v-show="shows">' +
'<div class="pop">' +
'<button @click="down">关闭弹窗</button>' +
'</div>' +
'</div>',
methods:{
down(){
this.$emit('close');
}
}
};
let vm=new Vue({//parent
el:'#app',
data:{flag:false},
components:{
pop
},
methods:{
open(){
this.flag=true;
}
}
});
</script>
</body>
</html>
- slot插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot插槽</title>
</head>
<body>
<div id="app">
<container><h1 slot="title">标题</h1><p slot="content">内容</p><span>默认内容吗</span></container>
<!--<box>测试啦</box>-->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let container={
template:'<div><slot name="title"></slot><slot name="content"></slot><slot name="default"></slot></div>',
};
let vm=new Vue({//parent
el:'#app',
components:{
container
}
});
//最基本的方法
// let box={
// //slot中的内容为组件标签内容为空时的默认内容,可不填
// template:'<div><slot>默认内容</slot></div>',//默认slot拥有name为default的属性,若只有一个slot,可不填写name属性
// };
// let vm=new Vue({//parent
// el:'#app',
// components:{
// box
// }
// });
</script>
</body>
</html>
- ref 父调用子方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父调用子方法,ref获取真实dom</title>
</head>
<body>
<div id="app">
<loading ref="load"></loading>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父调用子方法
let loading={
data(){
return {flag:true};
},
template:'<div v-show="flag">疯狂加载中...</div>',
methods:{
hide(){
this.flag=false;
}
}
};
let vm=new Vue({
el:'#app',
mounted(){
console.log(this.$refs.load);
this.$refs.load.hide();
// this.$refs.load.$el.style.color='red';
},
components:{
loading
},
});
</script>
</body>
</html>
- keep-alive
<keep-alive>
包裹动态组件时,
会缓存不活动的组件实例,而不是销毁它们。和<transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。
当组件在<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>keep-alive,component标签</title>
</head>
<body>
<div id="app">
<input type="radio" v-model="flag" value="home" name="flag">home
<input type="radio" v-model="flag" value="list" name="flag">list
<!-- component是vue自带的标签,template,slot,transition同样也是-->
<!-- 这种方法在跳转的时候会销毁前一个组件-->
<component :is="flag"></component>
<!-- 可以添加keep-alive标签,就不会销毁组件-->
<!-- 一般用作缓存,为的是后面的路由做准备-->
<keep-alive>
<component :is="flag"></component>
</keep-alive>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父调用子方法
let home={
template:'<div>home</div>',
beforeDestroy(){
alert('销毁');
}
};
let list={
template:'<div>list</div>',
beforeDestroy(){
alert('销毁');
}
};
let vm=new Vue({
el:'#app',
data:{
flag:'home'
},
components:{
home,list
},
});
</script>
</body>
</html>
组件异步渲染问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件渲染挂载问题$nextTick</title>
</head>
<body>
<div id="app">
<child ref="child"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//子组件和父组件同时拥有mounted,先执行子组件,后执行父组件
let child={
data(){
return {arr:[1,2,3]}
},
template:'<div><li v-for="a in arr">{{a}}</li></div>',
mounted(){
this.arr=[4,5,6];//此处是异步渲染dom
alert('child');
},
};
let vm=new Vue({
el:'#app',
mounted(){
alert('parent');
console.log(this.$refs.child.$el.innerHTML);//结果是<li>1</li><li>2</li><li>3</li>
//页面显示是4,5,6,为什么父组件打印的是1,2,3。因为mounted是异步渲染的,所以此时需要$nextTick,使用mounted一定要使用$nextTick
this.$nextTick(()=>{
console.log(this.$refs.child.$el.innerHTML);
})
},
components:{
child
},
});
</script>
</body>
</html>
- slot实践
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<panel type="primary" @say-title="parent">
<div slot="title">怎样学习vue</div>
<div slot="body">
<p>js基础</p>
<p>vue基础</p>
<p>vue进阶</p>
</div>
<div slot="foot">作者:小明</div>
</panel>
</div>
<template id="panel">
<div class="panel" :class="[color]">
<div class="panel-heading" ref="head">
<slot name="title"></slot>
</div>
<div class="panel-body">
<slot name="body"></slot>
</div>
<div class="panel-footer">
<slot name="foot">无名</slot>
</div>
<button class="btn btn-default" @click="getTitle">点击获取标题</button>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let panel={
template:'#panel',
computed:{
color(){
return 'panel-'+this.type
}
},
methods:{
getTitle(){
this.$emit('say-title',this.$refs.head.innerText);
}
},
props:{
type:{//this.type='primary',子不能更改父组件传递的属性
type:[String],
default:'default'
}
}
};
let vm=new Vue({
el:'#app',
components:{
panel
},
methods:{
parent(val){
alert(val);
}
}
});
</script>
</body>
</html>
- 组件的循环
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件的循环</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<!-- 在2.2.0版本以上,当组件使用v-for时,key是必须的 -->
<panel :type="article.type" @say-title="parent" v-for="(article,index) in articles" :key="index">
<div slot="title" v-html="article.title"></div>
<div slot="body">{{article.content}}</div>
<div slot="foot">{{article.auth}}</div>
</panel>
</div>
<template id="panel">
<div class="panel" :class="[color]">
<div class="panel-heading" ref="head">
<slot name="title"></slot>
</div>
<div class="panel-body">
<slot name="body"></slot>
</div>
<div class="panel-footer">
<slot name="foot">无名</slot>
</div>
<button class="btn btn-default" @click="getTitle">点击获取标题</button>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let panel={
template:'#panel',
computed:{
color(){
return 'panel-'+this.type
}
},
methods:{
getTitle(){
this.$emit('say-title',this.$refs.head.innerText);
}
},
props:{
type:{//this.type='primary',子不能更改父组件传递的属性
type:[String],
default:'default'
}
}
};
let vm=new Vue({
el:'#app',
components:{
panel
},
methods:{
parent(val){
alert(val);
}
},
data:{
articles:[
{type:'primary',title:'<h1>vue</h1>',content:'这是vue文章',auth:'作者:小明'},
{type:'success',title:'<h1>react</h1>',content:'这是react文章',auth:''},
{type:'info',title:'<h1>angularJs</h1>',content:'这是angularJs文章',auth:'作者:小红'},
]
}
});
</script>
</body>
</html>
路由
- 声明式路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>声明式路由</title>
<style>
/*.router-link-active {color: red;}*/
.active {color: red;}
</style>
</head>
<body>
<!-- hash模式#,开发时使用,不会导致404,但不支持seo-->
<!-- h5的history.pushState,上线后采用h5的跳转方式-->
<div id="app">
<!-- 声明式导航 -->
<router-link to="/home" tag="button">首页</router-link>
<router-link to="/list" tag="button">列表页</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div>首页</div>'};
let list={template:'<div>列表页</div>'};
let routes=[//路由的映射表,配置路径和组件的关系
{path:'/home',component:home},//配置的关系就是页面级组件
{path:'/list',component:list}//路径必须加/
];
let router=new VueRouter({//引入vue-router自带VueRouter类
//vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
mode:'history',
routes,
linkActiveClass:'active'//将router-link按钮自带触发类名改为active
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 编程式路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编程式路由</title>
<style>
/*.router-link-active {color: red;}*/
.active {color: red;}
</style>
</head>
<body>
<div id="app">
<!-- 编程式导航 -->
<router-link :to="{path:'/home'}" tag="button">首页</router-link>
<router-link :to="{path:'/list'}" tag="button">列表页</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={
template:'<div>首页<button @click="goList">列表页</button></div>',
methods:{
goList(){
this.$router.push('/list');
// this.$router.replace('/list');不常用
}
}
};
let list={
template:'<div>列表页<button @click="back">返回</button></div>'
,methods:{
back(){
this.$router.go(-1);//返回上一级
}
}
};
let routes=[//路由的映射表,配置路径和组件的关系
{path:'',component:home},//默认的路由
{path:'/home',component:home},//配置的关系就是页面级组件
{path:'/list',component:list},//路径必须加/
// {path:'*',component:list}//当没有可匹配的页面时,展示这个页面。但这个地方路径不会变,只是切换了组件而已
{path:'*',redirect:'/home'}//路径变,组件也会变。常用于404
];
let router=new VueRouter({//引入vue-router自带VueRouter类
routes,
linkActiveClass:'active'//将router-link按钮自带触发类名改为active
});
let vm=new Vue({
el:'#app',
router//定义router后,每个组件都会拥有$router(有r放的都是方法)和$route(没有r放的都是属性)
});
</script>
</body>
</html>
- 路由嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由嵌套</title>
</head>
<body>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/detail">详情页</router-link>
<router-view></router-view>
</div>
<template id="detail">
<div>
<router-link to="/detail/profile">个人中心</router-link>
<router-link to="/detail/about">关于我们</router-link>
<router-view></router-view>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div>home</div>'};
let detail={template:'#detail'};
let profile={template:'<div>profile</div>'};
let about={template:'<div>about</div>'};
let routes=[
{path:'/home',component:home},
{
path:'/detail',
component:detail,
children:[//children中的路径永远不带/,如果带/表示1级路由
{path:'profile',component:profile},
{path:'about',component:about}
]
}
];
let router =new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 路由参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由参数</title>
</head>
<body>
<div id="app">
<!-- 如果用对象作为to的属性,并且使用了参数,必须给路由起一个名字,通过名字跳转-->
<router-link :to="{name:'pro',params:{num:1,id:'a'}}">商品1</router-link>
<router-link to="/article/2/b">商品2</router-link>
<router-link to="/article/3/c">商品3</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let article={
template:'<div>这是第{{$route.params.num}}篇文章</div>',
watch:{//路径参数发生变化,监控参数的变化来发送Ajax
$route(){
alert('ajax');
}
}
};
let routes=[
{path:'/article/:num/:id',component:article,name:'pro'},
];
let router =new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 路由动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由动画</title>
<link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
</head>
<body>
<div id="app">
<router-link to="/home" tag="button">首页</router-link>
<router-link to="/list" tag="button">列表页</router-link>
<!-- mode="out-in"动画的模式,默认是in-out-->
<!-- 要想呈现相对流畅的动画,可以设置视图为绝对位置-->
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
<router-view style="position: absolute;top: 50px;left: 0;width: 100%;height: 50px;"></router-view>
</transition>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div style="background: red">首页</div>'};
let list={template:'<div style="background: green">列表页</div>'};
let routes=[
{path:'/home',component:home},
{path:'/list',component:list}
];
let router=new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>