vue2.x学习记录

引包、留坑、实例化、插值表达式

  1. 引包

    • npm install vue—安装vue

    • 从下载的vue包中找到vue.js, 并在页面引入

      <script src="vue.js"></script>
      
  2. 留坑 即留一个vue模板插入的地方或是vue代码对其生效的地方

  3. 实例化 即启动vue

    启动: new Vue({el:目的地, template:模板内容}); 实例化传入的是一个对象options

    • options
      • 目的地 el 对应上面留坑的坑位, 可通过id名, 类名, 标签名来查找.
      • 内容 template
      • 数据 data 值为函数形式也可是对象, 但都是用函数, 因为用的函数最后也是return一个对象
  4. 插值表达式

    • 插值表达式内填入data里面的变量即可在页面取到变量值
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="vue.js"></script>
<script>
    new Vue({
        // el是vue的目的地,即vue将被应用到何处,#app即id选择器
        el:'#app',
        // 运行后,<div id="app"></div>将被下面的模板替换        template:`
            <div>
                <div>我这里是模板内容  </div>
                <div>111</div>
            </div>
        `,
        // 值--可通过插值的方式绑定到html
        data:function(){
            return {
                msg:'Hello Vue!'
            }
        }
    })
</script>
</html>

常用指令

指令

常用指令

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        template:`
            <div>
                <div v-text='mytext'></div>
                </hr>

                <div v-html='myhtml'></div>

                <button v-if='num==1'>测试v-if</button>
                <div v-show='checkshow'></div>

                <ul>
                    <li v-for='item in arrayfor'>
                        
                    </li>
                    <li v-for='(item,index) in arrayfor'>
                        --
                    </li>
                </ul>

                <ul>
                    <li v-for='(oj,key,value) in ojfor'>
                        :--
                    </li>
                </ul>
            </div>
        `,
        data:function(){
            return {
                mytext:'<h1>这是v-text</h1>',
                myhtml:'<h1>这是v-html</h1>',
                num: 1,
                checkshow:false,
                // v-for可用于数组和对象, 写法基本相似
                // 对数组而言, item是某一项, index是对象的下标
                arrayfor:['篮球','足球','乒乓球'],
                // 对对象而言, item是某一项的值,key是值对应的键, vlaue是索引(下标)
                ojfor:{play:'篮球',people:'阿三',age:'19'}
            }
        }
    })
</script>
</html>

vue单双向数据流及事件绑定

vue单向数据流绑定属性值v-bind:(属性) 简写 : (属性)

例子: <input v-bind:value="name" v-bind:class="name">

vue双向数据流 v-model 只作用于有value属性的元素

例子: <input v-model="name" v-bind:class="name">

**事件绑定v-on:事件名=”表达式   函数名” 简写 @事件名=”表达式   函数名”**
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        // 在模板中获取data中的变量是不需要this的
        template:`
            <div>
                单向数据绑定v-bind
                <input type='text' v-bind:value="name" :class="name"><br>
                双向数据绑定v-model
                <input type='text' v-model="name"></br>
                
                <button v-on:click="name='这是新值'">点击改变值1</button>
                <button @click="change">点击改变值2</button>
            </div>
        `,
        data:function(){
            return {
                name:'hello'
            }
        },
        methods:{
            change:function(){
                this.name="通过方法改变的新值"
            }
        }
    })
</script>
</html>

**注意: **

vue双向数据绑定

过滤器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        我输入的: <input type="text" name="" v-model='instring'></br>
        我输出的:  </br>
        翻转输出:  </br>
        
    </div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    // 全局的过滤器--reversal必须用''包起来, 否则会被认为是个变量而不是方法名
    Vue.filter('reversal',function(val,arg2 = 0){
        var str='';
        if(arg2!==0)
            str=arg2+val.split('').reverse().join('')
        else 
            str=val.split('').reverse().join('')
        return str 
    })
    new Vue({
        el:'#app',
        data(){
            return {
                instring:''
            }
        },
        // 组件内的过滤器
        filters:{
            // 不同函数名实现方式
            reversal1(val){
                //   字符串转数组  翻转  数组转字符串
                return val.split('').reverse().join('')
            },
            reversal2(val,arg2){
                return arg2+val.split('').reverse().join('')
            },
            //同一函数名实现方式, 类似重载
            // js中的重写函数意味着覆盖, 所以不能用其他语言的重写函数方式来实现重载
            reversal(val,arg2 = 0){
                var str='';
                if(arg2!==0)
                    str=arg2+val.split('').reverse().join('')
                else 
                    str=val.split('').reverse().join('')
                return str    
            }
        }
    })
</script>
</html>

vue中的this是vue封装好给我们使用的, 跟平常方法里面的this是不同的

image-20211003175841885image-20211003175902650

数据监听watch计算属性computed

watch监听单个, computed监听多个

思考业务场景:

  1. 类似淘宝, 当我输入某个人名时, 我想触发某个效果
  2. 利用vue做一个简单的计算器

当watch监听的是复杂数据类型时, 需要做深度监听 (写法如下)

watch: {
	msg:{
		handler(val){
			if(val.text=='love'){
				alert(val.text)
			}
		},
		deep: true //开启深度监听
	}
}

computed 监听对象, 写在了函数内部, 凡是函数内部有this.相关属性, 改变都会触发当前函数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <div>watch监听数据</div>
      <input type="text" name="" v-model="msg.text" />
      <div>computed监听数据</div>
      (<input type="text" name="" v-model="n1">+
      <input type="text" name="" v-model="n2">)*
      <input type="text" name="" v-model="n3">=
    </div>
  </body>
  <script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
  <script>
    new Vue({
      el: "#app",
      data() {
        return {
          msg: { text: "" },
          n1:'',
          n2:'',
          n3:'1'
        };
      },
    //   computed里面监听的任何一个 this.变量 发生变化都会触发监听函数
      computed: {
          result() {
              return (Number(this.n1)+Number(this.n2))*Number(this.n3);
          },
      },
      watch: {
        msg: {
          handler(newval, oldval) {
            if (newval.text == "love") {
              alert(newval.text);
            }
          },
          deep: true,
        },
      },
    });
  </script>
</html>

组件化开发

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">

    </div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    // 1.组件局部声明
    var MyHeader= {
        template: `
            <div>头部</div>
        `,
    }
    var MyBody= {
        template: `
            <div>身体</div>
        `
    }
    //曾经的组件局部声明写法: 
    // var myBody= Vue.extend({
    //     template: `
    //         <div>身体</div>
    //     `
    // })

    //1. 2.组件全局注册(声明+注册)
    Vue.component('MyFooter',{
        template:`
            <div>尾部</div>
        `
    });
    new Vue({
        el: '#app',
        // 2.组件局部注册
        components: { 
            MyHeader,
            MyBody
         },
         //3.组件使用
         template: `
            <div>
                <my-header></my-header>
                <my-body></my-body>
                <my-footer></my-footer>
            </div>    
         `,
        data() {
            return { }
        }
    })
</script>
</html>

slot插槽和ref、$parent

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    // 子父组件都可以获取彼此的实例,变量,方法,DOM--实现互相通信
    var Child = {
        template: `
            <div>子组件</div>
        `,
        data() {
            return {
                msg: 'hello'
            }
        },
        //子组件通过$parent获取父组件
        created() { //数据加载完毕生命周期钩子函数
            //获得父组件实例
            console.log(this.$parent)
        }
    }
    var Parent = {
        //模板中, 父组件通过ref获取子组件
        template: `
            <div>
                父组件
                <slot name='hello'></slot>

                <child ref='childs'></child>
            </div>    
        `,
        components: {
            Child
        },
        data() {
            return {
                parents: '我父组件'
            }
        },
        //父组件中通过refs获取子组件
        mounted() {//dom挂载完毕时的生命周期钩子函数
            //this.$refs.childs即可获取到子组件实例,接着可进一步获取其内容,
            //比如.$el就是获取其DOM   子组件中的msg同样可以通过其他方法获取到
            console.log(this.$refs.childs.$el)
            //输出:  <div>子组件</div>
        },
        
    }

    new Vue({
        el:'#app',
        components:{
            Parent
        },
        // 插槽内容1不会显示,因为父组件中的插槽指定了name
        template:`
            <div>
                <parent>
                    <div>插槽内容1</div>
                    <div slot='hello'>插槽内容2</div>
                </parent>
            </div>        
        `,
        data(){
            return {}
        }
    })
</script>
</html>

父子组件通信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    //父传子: 子组件定义好 props, 直接使用即可(但子组件定义要在父组件前面)
    var Child = {
        template: ` 
            <div>子组件
                <button @click='sendparent'>我要为父组件反馈东西</button>    
            </div>
        `,
        props: ['sendchild'],
        methods: {
            sendparent() {
                //子传父: baba  就是子组件向父组件反馈信息的事件名
                this.$emit('baba', '子组件传来的数据','数据2');
            },
        },
    }
    //父传子: 父组件通过属性sendchild(子组件定义好的)传递数据给子组件
    var Parent = {
        //子传父: 父组件通过监听子组件的'baba'事件来接收值
        template: `
            <div>父组件 
                <child sendchild='父组件传来的' @baba='reserve'></child>
            </div>
        `,
        components: {
            Child
        },
        data(){
            return {
                //子传父: 用于接收子组件数据的变量
                msg1:'',
                msg2:''
            }
        },
        methods: {
            //子传父: 父组件中用于接收子组件数据的事件
            reserve(val1, val2) {
                this.msg1 = val1
                this.msg2 = val2
            },
        },
    }
    new Vue({
        el:'#app',
        components: { 
            Parent
         },
         template: `
            <div>
                <parent></parent>
            </div>    
         `,
         data(){
            return {}
         }
    })
</script>
</html>

非父子组件之间的通信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    Vue.prototype.$bus = new Vue()
    var MyHeader = {
        template: `
            <div>
                头部
                
            </div>
        `,
        data(){
            return {
                headermsg: '头部信息'
            }
        },
        created() {
            //将执行vue的this存起来, 因为函数的中this是指向函数内的
            // var self = this
            // self.$bus.$on('sending', function(val){
            //     self.headermsg = val
            // })

            //如果使用箭头函数, 就不必存储this了, 因为箭头函数
            //会将函数内的this指向到函数外部
            this.$bus.$on('sending',val=>{
                this.headermsg = val
            })
        },
    }
    var MyBody = {
        template: `
            <div>身体</div>
        `,
    }
    var MyFooter = {
        template: `
            <div>底部<button @click='sendhead'>向头部传送数据</button></div>
        `,
        methods: {
            sendhead() {
                this.$bus.$emit('sending','底部的数据')
            },
        },
    }
    new Vue({
        el:'#app',
        components:{
            MyHeader,
            MyBody,
            MyFooter
        },
        template:` 
            <div>
                <my-header></my-header>
                <my-body></my-body>
                <my-footer></my-footer>
            </div>
        `,
    })
</script>
</html>

Vue的生命周期

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script>
    var Test = {
        template: `
            <div>Test组件
                <button @click="msg+='1'">msg+1</button>    
            </div>
        `,
        data() {
            return {
                msg: 'Hello Vue'
            }
        },
        //组件创建前--数据(data)未初始化
        beforeCreate() {
            console.log('组件创建前')
            console.log(this.msg)
        },
        //组件创建后--数据加载完毕
        created() {
            console.log('组件创建后')
            console.log(this.msg)
        },
        //DOM挂载前--模板还未渲染到dom中
        beforeMount() {
            console.log('Dom挂载前')
            console.log(document.body.innerHTML)
        },
        //DOM挂载后--模板渲染完毕
        mounted() {
            console.log('Dom挂载后')
            console.log(document.body.innerHTML)
        },
        //数据(data)更新前
        beforeUpdate() {
            console.log('数据更新前')
            console.log(document.body.innerHTML)
        },
        //数据(data)更新后
        updated() {
            console.log('数据更新后')
            console.log(document.body.innerHTML)
        },
        //组件销毁前
        beforeDestroy() {
            console.log('组件销毁前')
        },
        //组件销毁后
        destroyed() {
            console.log('组件销毁后')
        },
        //如果用<keep-alive></keep-alive>包裹组        //那么在v-if值变化时, 不再是销毁而是停用
        //组件停用
        deactivated() {
            console.log('组件停用')
        },
        //组件激活
        activated() {
            console.log('组件激活')
        },
        //对于需要动态隐藏/显示的组件可以使用keep-alive提高性能
    }
    new Vue({
        el: '#app',
        components: { 
            Test
         },
        template: `
            <div>
                <keep-alive><test v-if='testshow'></test></keep-alive></br>
                <button @click='clickbtn'>销毁组件</button>    
            </div>
        `,
        data() {
            return {
                testshow: true
            }
        },
        methods: {
            clickbtn() {
                //如果是true则变为false, 如果是false则变为true
                this.testshow = !this.testshow
            },
        },
    })
</script>
</html>

路由的跳转原理 ( 哈希模式 )

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 路由跳转本质是锚点,就是# -->
    <a href="#/login">登录</a>
    |
    <a href="#/register">注册</a>
    <div id="app"></div>
</body>
<script>
    var appdiv=document.getElementById('app')

    window.addEventListener('hashchange', function(e){
        console.log(location.hash)
        switch(location.hash){
            case '#/login': 
                appdiv.innerHTML = '这是登录页面';
                break;
            case '#/register':
                appdiv.innerHTML = '这是注册页面';
                break;
        }
    })
</script>
</html>

安装和使用路由

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script src="./vue-router.js"></script>
<script>
    var Login = {
        template: `
            <div>登录页面</div>
        `,
    }
    //安装路由插件
    Vue.use(VueRouter);
    //创建路由对象
    var router = new VueRouter({
        //配置路由对象
        routes: [
            {path: '/login', name: 'login', component: Login}
        ]
        //将浏览器上的路由改为....#/login即可访问到
    })
    new Vue({
        el: '#app',
        router,
        template: `
            <div>
                <router-view></router-view>    
            </div>
        `,
        data() {
            return {
                
            };
        },
    })
</script>
</html>

路由的跳转

**路由的跳转方式有: **

  1. **通过标签: ** <router-link to='/login'></router-link>
  2. **通过js控制跳转: ** this.$router.push({path:'/login'})

**区别: **

this.$router.push()跳转到指定的url, 会向history插入新记录

this.$router.replace() 同样是跳转到指定的url, 但是这个方法不会向history里面添加新的记录, 点击返回, 会跳转到上上一个页面, 上一个记录是不存在的.

this.$router.go(-1) 常用来做返回, 读history里面的记录后退一个

**vue-router中的对象: **

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script src="../1-13路由vue-router使用/vue-router.js"></script>
<script>
    var Login = {
        template: `
            <div>登录页面</div>
        `,
    }
    var Register = {
        template: `
            <div>注册页面</div>
        `,
    }
    //安装路由插件
    Vue.use(VueRouter);
    //创建路由对象
    var router = new VueRouter({
        //配置路由对象
        routes: [
            {path: '/login', name: 'login', component: Login},
            {path: '/register', name: 'register', component: Register}
        ]
        //将浏览器上的路由改为....#/login即可访问到
    })
    new Vue({
        el: '#app',
        router,
        template: `
            <div>
                <router-link to='/login'>去登录</router-link>
                |
                <router-link to='/register'>去注册</router-link>
                <div>
                    <button @click='goregister'>去注册</button>
                    <button @click='back'>返回上一页</button>
                </div>
                <router-view></router-view>    
            </div>
        `,
        data() {
            return {
                
            };
        },
        methods: {
            goregister() {
                //push跟replace是达到同样效果, 但是replace是不会向history插入记录
                // this.$router.replace({path:'/register'})
                this.$router.push({path:'/register'})
            },
            //返回上一页,读history里面记录的后退一个
            back(){
                this.$router.go(-1)
            }
        },
    })
    
</script>
</html>

路由的传参和取参

  1. 查询参

    • 配置 ( 传参 ) ** **:to=”{ name : ‘login’, query : {id : loginid} }”

    • 获取 ( 取参 ) ** **this.$route.query.id

  2. 路由参数

    • 配置 ( 传参 ) :to=”{ name : ‘register’ , params : {id : registerid} }”

    • 配置路由的规则 { name : ‘detail’ , path : ‘/detail/:id’ }

    • 获取 this.$route.params.id

**总结: **

  1. :to 传参的属性里 params是和name配对的 query和name或path都可以
  2. 使用路由参数必须要在配置路由规则里面配置好参数名, 否则刷新页面参数会丢失

补充:

==如果希望路由相同而参数不同仍能够动态显示不同内容(即刷新),那就需要为router-view添加:<router-view :key="this.$route.fullPath"></router-view>==

建议都使用name, 因为无论是params还是query都可以与name配对

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
  <script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
  <script src="../1-13路由vue-router使用/vue-router.js"></script>
  <script>
    var Login = {
      template: `
            <div>登录页面</div>
        `,
      data() {
        return {
          msg: ''
        }
      },
      //获取查询参(类似 :id=123 )
      created() {
        this.msg = this.$route.query.id
      },
      
    };
    //获取路径参有两种方法: 法一
    var Register = {
      template: `
            <div>注册页面</div>
        `,
      data() {
        return {
          registerfoo: ''
        }
      },
      created() {
        this.registerfoo = this.$route.params.foo
      }
    };
    //法二 --(需要在配置路由对象时添加: props:true)
    var Buy = {
      template: `
            <div>购买页面</div>
        `,
      props:['fzz']
    };


    //安装路由插件
    Vue.use(VueRouter);
    //创建路由对象
    var router = new VueRouter({
      //配置路由对象
      routes: [
        { path: "/login", name: "login", component: Login },
        { path: "/register/:foo", name: "register", component: Register },
        { path: "/buy/:foo", name: "buy", props:true, component: Buy },
      ],
      //将浏览器上的路由改为....#/login即可访问到
    });
    new Vue({
      el: "#app",
      router,
      // 传参: 查询参和路由参(路由参需要在配置路由对象时加以配置)
      template: `
            <div>
                <router-link 
                    :to="{name:'login',query:{id:'123'}}">去登录</router-link>
                |
                <router-link 
                    :to="{name:'register',params:{foo:'bar'}}">去注册</router-link>
                |
                <router-link 
                    :to="{name:'buy',params:{fzz:'haha'}}">去购物</router-link>
			   |
			   <button @click='jslink'>js去登录</button>
                <router-view :key="this.$route.fullPath"></router-view>    
            </div>
        `,
      data() {
        return {};
      },
      methods: {
        goregister() {
          //push跟replace是达到同样效果, 但是replace是不会向history插入记录
          // this.$router.replace({path:'/register'})
          this.$router.push({ path: "/register" });
        },
        //返回上一页,读history里面记录的后退一个
        back() {
          this.$router.go(-1);
        },
        //js跳转传参也是一样的
        jslink() {
          this.$router.push({name:'login', query:{id: '456'}})
          //希望通过传入的参数不同显示不同内容时, 一定要为router-view添加一个key,并传入路径的完整值(包含参数),这样即便只是参数发送变化,也能够识别到锚点变化:  <router-view :key="this.$route.fullPath"></router-view>    
        }  
      },
    });
  </script>
</html>

嵌套路由

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script src="../1-2引包,留坑,实例化,插值/vue.js"></script>
<script src="../1-13路由vue-router使用/vue-router.js"></script>
<script>
    var Nav = {
        //<router-view></router-view>是路由入口, 因为子路由也需要入口,所以这里也要加<router-view></router-view>
        template: `
            <div>
                <router-view></router-view>
                <router-link :to= "{name: 'nav.index'}">首页</router-link>
                |
                <router-link :to= "{name: 'nav.personal'}">个人中心</router-link>
                |
                <router-link :to= "{name: 'nav.message'}">资讯</router-link>
                |
                <router-link :to= "{name: 'nav.mine'}">我的</router-link>
            </div>
        `,
    }
    var Index = {
        template: `
            <div>首页</div>
        `,
    }
    var Personal = {
        template: `
            <div>个人中心</div>
        `,
    }
    var Message = {
        template: `
            <div>资讯</div>
        `,
    }
    var Mine = {
        template: `
            <div>我的</div>
        `,
    }
    //安装路由插件
    Vue.use(VueRouter);
    //创建路由对象
    var router = new VueRouter({
        //配置路由对象
        routes: [
            {
                path: '/nav',
                name: 'nav',
                component: Nav,
                //嵌套路由增加这个属性
                children: [
                    //配置嵌套路由
                    //重定向, 如果到了/nav,则自动重定向到/nav/index
                    {path: '', redirect: '/nav/index'},
                    {path: 'index', name: 'nav.index', component: Index},
                    {path: 'personal', name: 'nav.personal', component: Personal},
                    {path: 'message', name: 'nav.message', component: Message},
                    {path: 'mine', name: 'nav.mine', component: Mine},
                ]
            }
        ]
    })
    new Vue({
        el: '#app',
        router,
        template: `
            <div>
                <router-link to='/nav'>去标签页</router-link>
                <router-view></router-view>
            </div>
        `,
        data() {
            return {

            }
        }
    })
</script>
</html>

路由守卫

const router = new VueRouter({ ... })
//前置的钩子函数  最后要执行next()才会跳转
router.beforeEach((to, from, next) => {
	// ...
})
//后置的钩子函数  已经跳转了不需要next
router.afterEach((to, from) => {
	// ...
})

主要了解: **路由守卫主要用于检测是否登录, 没登录就跳转到登录页面不让用户在其他页面停留, 但现在这种处理主要都用请求的全局拦截来做了. **

//沿用上一节的代码,仅仅在 new Vue中增加以下几行: 
methods: {
        },
        mounted() {
//to指的是到哪里去, from是从哪里来, next是放行
            router.beforeEach((to, form, next)=>{
                console.log(to)
                //如果目的路径是/nav/index才可以跳转
                if(to.path == '/nav/index'){
                    next()
                }
            })
        },

小实战—补充知识

  1. return this.chatarr.filter(v => v.active).length;

    filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。

  2. 本地存储

    ​ window.localStorage.setItem(‘chat’, JSON.stringify(this.chatarr))

    ​ this.chatarr = JSON.parse(window.localStorage.getItem(‘chat’))

  3. 提示框

    window.confirm(是否删除${this.chatarr[index].text}?)

  4. const result = this.chatarr.find((v) => v.text == goods.text);

    find方法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined。

利用插槽封装组件—更详细的插槽解释

  1. 留坑

    • 要封装的组件: navigation-link

      <a
        v-bind:href="url"
        class="nav-link"
      >
        <slot></slot>
      </a>
      
    • 使用封装好的组件:

      <navigation-link url="/profile">
        <!-- 添加一个 Font Awesome 图标 -->
        <span class="fa fa-user"></span>
        Your Profile
      </navigation-link>
      
    • 组件中 <slot></slot>在渲染时将被替换为新填入的内容:

      <a
        v-bind:href="url"
        class="nav-link"
      >
        <!-- 添加一个 Font Awesome 图标 -->
        <span class="fa fa-user"></span>
        Your Profile
      </a>
      
    • 可以填入任何内容, 包括其他的组件

  2. 编译作用域

    • ==父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。==

    • 可以这样写:

      <navigation-link url="/profile">
        Logged in as 
      </navigation-link>
      
    • 但不能这样写:

      <navigation-link url="/profile">
        Clicking here will send you to: 
        <!--
        这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
        _传递给_ <navigation-link> 的而不是
        在 <navigation-link> 组件*内部*定义的。
        -->
      </navigation-link>
      
  3. 为插槽准备后备内容(即默认内容)

    • submit-button组件

      <button type="submit">
        <slot>Submit</slot>
      </button>
      
    • 如果直接 <submit-button></submit-button> , 那么插槽内就会渲染Submit, 如果使用时填入了其他内容, 插槽内会渲染填入的内容, 不会渲染后备内容

  4. 具名插槽(为插槽指定name)

    • 带有多个插槽的 base-layout 组件

      <div class="container">
        <header>
          <!-- 我们希望把页头放这里 -->
            <slot name="header"></slot>
        </header>
        <main>
          <!-- 我们希望把主要内容放这里 -->
            <slot></slot>
        </main>
        <footer>
          <!-- 我们希望把页脚放这里 -->
            <slot name="footer"></slot>
        </footer>
      </div>
      

      **不带name的<slot> 会带有隐含的name ->default **

    • 现在我们就可以在 <template>元素上使用v-slot指令并提供参数来指定一个插槽

      <base-layout>
        <template v-slot:header>
          <h1>Here might be a page title</h1>
        </template>
           
        <p>A paragraph for the main content.</p>
        <p>And another one.</p>
           
        <template v-slot:footer>
          <p>Here's some contact info</p>
        </template>
      </base-layout>
      

      其中没有被包裹在带有v-slot<template>中的内容都会被视为默认插槽的内容, 或者也可以将这些内容包裹在<template v-slot:default></template>中, 因为solt有隐含的name

  5. 作用域插槽

    • 如果我们想让插槽内容访问子组件中data, 那我们就需要将子组件的数据绑定到slot上, vurrent-user组件:

      <span>
        <slot v-bind:user="user">
               
        </slot>
      </span>
      
    • 在父级作用域中, 使用带值的v-slot来定义插槽prop(user)的名字

      <current-user>
        <template v-slot:default="mySlotProps">
               
        </template>
      </current-user>
      

      上面我们将包含所有插槽prop的对象命名为mySlotProps, 当然也可以命名为其他名字

  6. 独占默认插槽的缩写语法和具名插槽缩写语法

    • 就像上例的组件, 它只有一个默认插槽, 那我们在父级作用域中就可以这样书写(更简洁):

      <current-user v-slot:default="mySlotProps">
             
      </current-user>
      

      **当然, 默认插槽的name也可以省略: **

      <current-user v-slot="mySlotProps">
             
      </current-user>
      

      ==注意: 默认插槽的缩写语法不能和具名插槽混用,只要出现多个插槽, 请始终使用完整的<template>语法==

      v-slot也有缩写形式, 就是: #

      <current-user #default="mySlotProps">
             
      </current-user>
      

      ==但请注意, 如果你这样写是会触发警告的: <current-user #=”{ mySlotProps}”>, 也就是说, #缩写使用于使用具名插槽name的情况.==

  7. 结构插槽Prop

    • 上例中我们需要写 ``, 这个属性访问也太长了, 当然有简写:

      <current-user #default="{ user }">
             
      </current-user>
      
    • 为什么可以这样写? 其实作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:

      function (mySlotProps) {
        // 插槽内容
      }
      

      这就表明, v-slot的值可以是任何能够作为函数参数的JS表达式, 那当然可以使用ES2015解构表达式了

    • 解构表达式远不止提供缩写那么简单, 它也开启了prop重命名功能

      <current-user #default="{ user: person }">
             
      </current-user>
      

      甚至可以定义后备内容(当插槽propundefined时, 非常有用)

      <current-user #default="{ user = { firstName: 'Guest' } }">
             
      </current-user>
      
  8. 动态插槽名

    动态指令参数 也可以用在 v-slot上, 这就实现了动态插槽名

    <base-layout>
      <template v-slot:[dynamicSlotName]>
        ...
      </template>
    </base-layout>
    <!-- 最终dynamicSlotName(变量)会被替换为它的值 -->
    
  9. 一个<todo-list>组件例子, 它是一个列表且包含布局和过滤逻辑

    <ul>
      <li
        v-for="todo in filteredTodos"
        v-bind:key="todo.id"
      >
        <!--
        我们为每个 todo 准备了一个插槽,
        将 `todo` 对象作为一个插槽的 prop 传入。
        -->
        <slot name="todo" v-bind:todo="todo">
          <!-- 后备内容 -->
             
        </slot>
      </li>
    </ul>
    

    使用: 可以选择为 todo 定义一个不一样的 <template> 作为替代方案,并且可以从子组件获取数据:

    <todo-list v-bind:todos="todos">
      <template v-slot:todo="{ todo }">
        <span v-if="todo.isComplete"></span>
           
      </template>
    </todo-list>