VUE快速入门与实践(上)

简介

vue官网
vue.js是一套构建用户界面的渐进式框架。
vue全家桶:vue.js+vue-router+vuex+axios

特点

  1. 核心只关注视图层(view)
  2. 易学,轻量,灵活的特点
  3. 适用于移动端项目
  4. 渐进式框架

渐进式的理解

  1. 声明式渲染(无需关系如何实现)
  2. 组建系统
  3. 客户端路由(vue-router)
  4. 大规模状态管理(vuex)
  5. 构建工具(vue-cli)

核心

  1. 响应的数据变化。当数据发生改变->试图的自动更新
  2. 组合的视图组件。ui页面映射为组建树,划分组建可维护、可复用、可测试

安装vue

  1. 安装vue npm install vue
  2. 安装bootstrap npm install bootstrap
  3. 安装 axiosnpm install axios
  4. 初始化npm npm init -y,初始化生成一个新的 package.json 文件,如果使用了 -f(代表force)、-y(代表yes),则跳过提问阶段,直接生成一个新的 package.json 文件。

指令

  1. v-model:用于表单中的input ,textarea中,会忽略value,checked,selected,将数据绑定在试图上,视图修改后会影响数据的变化。同angularJS中的ng-model
  2. v-text:双向绑定数据,同{{}}。<div v-text="msg"></div>
  3. v-cloak:解决{{}}闪烁问题(作用于块区域),要赋予样式才起作用,[v-cloak]{display:none;}
  4. v-once:一次绑定数据。<div v-once>{{msg}}</div>
  5. v-html:绑定html,一定是安全数据。<div v-html="msg"></div>
  6. v-for:循环数组,同angularJS中的指令ng-repeat
    例:
    o in arr 或 (o,index) in arr
    a in obj 或 (value,key,index) in obj
  7. v-bind:动态绑定属性。v-bind:src="" 或 :src=""
  8. v-if:同angularJS ng-if,if操作的是dom
  9. v-show:同angularJS ng-show,show操作的是样式。如果频繁的切换dom使用v-show,如果一开始就确定下来使用v-if更好,如果if不通过内部指令不会再执行。只用dom从显示到隐藏或隐藏到显示菜使用vue动画
  10. 绑定事件 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-ifv-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>

修饰符

可进入官网 查看修饰符内容

表单输入绑定修饰符

  1. .lazy:在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change事件进行同步:
  2. .number:限制输入框只能填写数字 。<input type="number" v-model.number="num">
  3. .trim:自动过滤用户输入的首尾空白字符。<input v-model.trim="msg">

事件修饰符

  1. .stop:阻止继续冒泡,向上传播。阻止事件传播。同e.stopPropagationcancelBubble=true 效果相同
  2. .capture:停止捕获,向下传播。阻止事件传播。类似:xxx.addEventListener('clicl',fn,true)
  3. .prevent:阻止默认行为。如a标签的跳转。类似:e.preventDefaultreturnValue=false
  4. .once:只触发一次
  5. .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管理的数据,可以响应式变化的)。
  • getset 两部分组成(不能只写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('失恋','我','你');

arguments
剩余参数

  • 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> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。
<!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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值