Vue基础笔记

基础

  • Vue官网https://cn.vuejs.org/

数据绑定

  • 使用{{}}j进行数据绑定

    <p>{{变量}}</p>
    
  • 使用v-ones 可以指定元素只改变一次

    <p v-ones>{{变量}}</p>
    
  • 如果返回的数据是一个带有html格式的字符串,需要按照格式显示则需要v-html="变量"

    <p v-html="code"></p>
    
  • 如过想要将返回的值放在属性上则需要v-bind:属性="变量",其中v-bind可以省略,直接写:

    <img v-bind:scr="logo" alt="">
    <img :src="logo" alt="">
    

Class和style的绑定

  • 通过b-bind来绑定
<p :class="{title:true,'main-title':false}">我爱你:世界</p>
  • 通过对象的形式
<p :class="{title:true,'main-title':false}">我爱你:XXX</p>
  • 直接写style的方式
<p :style="[pStyle,fontW]">我爱你:oooo</p>
<script>
    new Vue({
        el: '#app',
        data: {
            username: "张锐",
            pclass1: "title",
            pclass2: "main-title",
            strong: false,
            pStyle:{
                'color': "red",
                "fontSize":"30px"
            },
            fontW:{
                "fontWeight":"800"
            }

        }
    })
</script>
  • 在使用花括号或者属性绑定时可以直接使用js的表达式和函数
    • 常见表达式有:变量读取、变量运算、三目运算符、函数调用、取反等。

条件判断

  • 条件判断,使用方式和其他语言一样,只是需要在前面加v-,如果需要判断的代码有很多则可以使用使用template标签来包裹,默认情况会重用相同的代码,如果想要不使用重用则可以在标签上加入key属性,实例代码如下
<div id="app">
    <p v-if="weather=='sun'">去公园</p>
    <p v-else-if="weather=='rain'">去看电影</p>
    <p v-else>呆在家</p>
    <tamplate v-if="age<18">
        <p>数学考了多少分</p>
    </tamplate>
    <tamplate v-else-if="age>18&&age<25">
        <p>有没有女朋友</p>
    </tamplate>
    <tamplate v-else>
        <p>工资多少啊</p>
    </tamplate>

    <template v-if="loginType=='username'" key='username'>
        <label>用户名</label>
        <input type="text" name="username" placeholder="用户名">
    </template>

    <template v-else="loginType=='email'">
        <label>邮箱</label>
        <input type="text" name="email" placeholder="email">
    </template>
    <button @click="changeLoginType">更换登陆类型</button>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            weather: "sun",
            age:25,
            loginType: "username"
        },
        methods:{
            say(){
                return "早上好"
            },
            changeLoginType(){
                this.loginType = this.loginType=='username'?"email":"username"
            }
        }
    })
</script>
  • v-ifv-show的区别
    • v-if只有在第一次为真时才会渲染,调整的标签的渲染
    • v-show在加载的时候就会全部加载上,只是display属性为none而已,在修改时只是会调整这个属性
    • 一般来说v-if有更高的切换开销,而v-show有更好的渲染开销,因此如果一个元素需要频繁的切换则一般使用v-show较好,如果在运行时条件很少改变则使用v-if比较好

循环语句

  • 循环遍历列表,使用v-for语句,可以只取元素,也可以将元素和索引都取出来,第一个参数为列表中的每一个元素,第二个参数为索引,使用 inof的方式遍历 。
  • 循环遍历对象,使用v-for的语句,可以只取元素,也可以将元素和键都取出来,

第一个参数为元素第二个参数为键,使用 inof的方式遍历 。

  • 一般使用循环的时候通常会绑定一个属性作为key,以Vue默认使用代码重用引发意想不到的bug
  • 如果在自定义组件中使用v-forkey是必须的
<div id="app">
    <table>
        <thead>
            <tr>
                <th>序号</th>
                <th>标题</th>
                <th>作者</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="(book,index) in books" :key="book.title">
                <td>{{ index }}</td>
                <td>{{ book.title }}</td>
                <td>{{ book.author }}</td>
            </tr>
        </tbody>
    </table>
    <div>
        <p v-for="(value,key) in user">{{key}}->{{value}}</p>
    </div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            books:[
                {
                    "title": "三国演义",
                    "author": "罗贯中"
                },
                {
                    "title": "西游记",
                    "author": "吴承恩"
                },
                {
                    "title": "水浒传",
                    "author": "施耐庵"
                },
                {
                    "title": "红楼梦",
                    "author": "曹雪芹"
                },
            ],
            user:{
                "name":"zr",
                "age":"19"
            }
        }
    })
</script>

触发视图更新

更新类型

  • 直接赋值更新。this.heros=[],模板会立即更新

  • 通过函数更新。this.heros.push("xxx")

    • push、pop、shift、unshift、splice、sort这些函数会直接触发更新
    • 其他函数可能不会触发更新,可以将修改后的数据进行赋值来更新
    <div id="app">
        <ul>
            <li v-for="hero in heros" :key="hero">{{hero}}</li>
        </ul>
        <button @click="update">直接更新</button>
        <button @click="fun_show_update">函数显示更新</button>
        <button @click="fun_update">函数不显示更新</button>
        <button @click="fun_concat">拼接</button>
    </div>
    <script>
        new Vue({
            el: '#app',
            data: {
                heros:["蜘蛛侠","美国队长"]
            },
            methods:{
                update(){
                   this.heros=["绿巨人"]
                },
                fun_show_update(){
                    // 追加一个
                    // this.heros.push("绿巨人")
                    // 删除最后一个
                    // this.heros.pop()
                    // 删除第一个
                    // this.heros.shift()
                    // this.heros.unshift("绿巨人")
                    // 删除两个
                    // this.heros.splice(0,2)
                    // 在第一个位置插入一个元素
                    // this.heros.splice(0,0,"绿巨人")
                    // 将从0开始的两个元素替换为绿巨人
                    // this.heros.splice(0,2,"绿巨人")
                },
                fun_update(){
                    this.heros.slice(0,1)
                    // 不能触发更新的还有 filter 、 concat 、 slice
                    // 可以采用直接赋值来取消这种结果
                },
                fun_concat(){
                    this.heros = this.heros.concat(["蝙蝠侠","超人"])
                }
            }
        })
    </script>
    

视图更新

1、如果直接修改数组中的某个值不会触发视图更新,着用情况需要使用splice或者Vue.set方法来实现。

Vue.set(this.books,0,"钢铁是怎么练成的")

2、如果同台给对象添加属性,也不会触发视图更新,只能通过Vue.set来进行添加。

Vue.set(this.user,"age",18)

事件绑定

  • 使用v-on来绑定事件,可以直接使用变量或者函数。
  • 也可以使用语法糖@符号来完成。
  • 在调用函数的时候可以传递参数
<div id="app">
    <p v-for="book in books" :key="book" @click="fun_click(book)">{{ book }}</p>
</div>
<script>
    new Vue({
        el:'#app',
        data: {
            books: ["水浒传","三国演义","红楼梦","西游记"]
        },
        methods:{
            fun_click(value){
                alert(value)
            }
        }
    })
</script>
  • 传入js的事件event,使用固定写法$event

    <div id="app">
        <a href="http:www.baidu.com" @click="fun($event)">点击不跳转</a>
    </div>
    <script>
        new Vue({
            el:app,
            data: {
            },
            methods:{
                fun(event){
                    event.preventDefault()
                    alert("不跳转")
                }
            }
        })
    </script>
    
  • 阻止标签的默认行为,prevent

    <a href="http:www.baidu.com" @click.prevent="">点击不跳转</a>
    
  • 常见修饰符,用于事件后的修饰

    • .stop:event.stopPropagation阻止事件冒泡
    • .capture:事件捕获
    • .once:这个事件只执行一次
    • .self:代表当前这个被点击的元素自己
    • .passive:在页面滚动的时候告诉浏览器不会阻止默认行为,从而让滚动更流畅
  • js中的冒泡机制

    冒泡机制就是在嵌套的元素中内部元素触发的事件会冒泡到外面元素,也就是说外面元素会监听到内部元素的事件。阻止这种冒泡的方法可以在需要阻断的位置加入.stop修饰符。

  • js中的事件捕获机制

    捕获的方式就是会先执行捕获,可以使用.capture来进行捕获

计算属性和监听器

计算属性

  • 计算属性都写在computed

  • 计算属性默认情况中只有get,没有set,如果想要实现set,那么就必须实现get,放到计算属性的名字中

  • 扩展:取到输入框中的值可以使用v-model:value或者简写v-model

    <div id="app">
        <div>
            <label>宽:</label>
            <input type="text" v-model="width">
        </div>
        <div>
            <label>高:</label>
            <input type="text" v-model="height">
        </div>
        <div>
            面积:{{ area }}
        </div>
    
    
        <div>
            <label>省:</label>
            <input type="text" v-model:value="province">
        </div>
        <div>
            <label>市:</label>
            <input type="text" v-model:value="city">
        </div>
        <div>
            <label>区:</label>
            <input type="text" v-model:value="district">
        </div>
        <div>
            <label>详细地址:</label>
            <input type="text" v-model="address">
        </div>
    
    </div>
    
    
    <script>
        new Vue({
            el:'#app',
            data: {
                width:0,
                height:0,
                province:"",
                city:"",
                district:""
            },
            computed:{
                // 默认只有get
                area(){
                    return this.width*this.height
                },
                address:{
                    // 取值的时候会调用这个方法
                    get(){
                        let result = ""
                        if(this.province){
                            result += this.province + "省"
                        }
                        if(this.city){
                            result += this.city + "市"
                        }
                        if(this.district){
                            result += this.district + "区"
                        }
                        return result
                    },
                    set(value){
                        // xx省xx市xx区
                        let result = value.split(/省|市|区/)
                        if(result && result.length > 0){
                            this.province = result[0]
                        }
                        if(result && result.length > 1){
                            this.city = result[1]
                        }
                        if(result && result.length > 2){
                            this.district = result[1]
                        }
                    }
                }
            }
        })
    </script>
    

监听属性

  • 监听的属性需要写在watch中,默认会传递两个参数,一个新的值,一个是旧的值
<div id="app">
    <div>
        <input type="text" v-model="kw">
    </div>
    <div>
        <span>推荐的关键字</span>
        {{result}}
    </div>
</div>
<script>
    new Vue({
        el:'#app',
        data: {
            kw:"",
            result:""
        },
        watch:{
            kw(newValue,oldVaule){
                // 模拟加载
                this.result = "正在加载中...."
                setTimeout(()=>{
                    this.result = "推荐结果:" + newValue
                },1000)
            }
        }
    })
</script>

表单输入绑定

输入绑定

  • 普通文本输入框采用v-model来绑定属性获取输入值
  • 多选框checkbox需要注意v-model的值必须是同一个,然后返回值为一个数组,数组中存放的是每一个选择框的value
  • 单选框radio同一组单选按钮的v-model也必须绑定同一个值,返回的是选中的按钮的value的值
  • 下拉框select需要将v-model绑定到select标签上,如果option中没有value属性,默认会区其中显示的值,如果指定了value属性,则选中的value

修饰符

  • .lazy 会在输入框输入完成后才更新
  • .number会将输入的值转换为数值类型,如果字符串不能被转换为数字则不会修改值,而且会清掉输入框中的字符串
  • .trim会将输入的字符串首尾的空白字符串清理掉

自定义组件

基本使用

  • 注册方式使用Vue.component("组件名",{data,template})

    • data: 必须为一个函数,这个函数返回一个对象,用法和Vue中的data用法相同
    • template:用来表示这个组件的渲染后的具体代码
    <div>
        <button-count></button-count>
    </div>
    <script>
        
        Vue.component("button-count",{
            // template 为渲染内容
            template:"<button @click='count+=1'>点击了{{count}}次</button>",
            // data 为数据,必须是函数,然后返回一个对象
            data: function () {
                return {count: 0}
            }
        })
        
    </script>
    
  • 注意: 组件模板中必须只包含同一个 根标签

为组件添加属性

  • 使用props来表示属性,props可以采用数组的方式,或者是对象的方式,采用对象的方式可以提供一些限制。
    • 限制包含type类型、required是否必须、default默认值
<div id="app">
    <article-list :articles="articles"></article-list>
</div>
<script>

    Vue.component("article-list",{
        // props:["articles"],
        props:{
            articles:{
                type:Array,
                required:true
            }
        },
        // ``符号用于可以换行的字符
        template: `
            <table>
        <thead>
        <tr>
            <th>序号</th>
            <th>标题</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(article,index) in articles" :key="article.title">
            <td>{{index+1}}</td>
            <td>{{article.title}}</td>
        </tr>
        </tbody>
    </table>
`

    })

    new Vue({
        el:'#app',
        data: {
           articles:[
               {
                   title:"《三国演义》"
               },
               {
                   title:"《水浒传》"
               },
               {
                   title:"《红楼梦》"
               },
           ]
        },
    })
</script>

自定义组件添加事件

  1. 需要在组件模板中添加事件
  2. 在主键的methods中定义这个事件应该执行的函数
  3. 在这个函数中执行this.$emit('xxx',传参)来执行函数(外部传过来的函数 比如叫xxx),这里需要注意这个xxx不要使用驼峰命名法,可以使用-
  4. 在外部绑定函数,使用@XXX='外部定义的函数'
<div id="app">
    <blog-item v-for="blog in blogs" :blog="blog" @check-change="outChange"></blog-item>
    <h1>选中的博客:</h1>
    <div v-for="blog in select_blogs">
        {{blog.title}}
    </div>
</div>
<script>

    Vue.component("blog-item",{
        props: ["blog"],
        template: `
            <div>
                <span>{{blog.title}}</span>
                <input type="checkbox" @click="onClick">
            </div>
        `,
        methods: {
            onClick(){
                this.$emit("check-change",this.blog)
            }
        }
    })

    new Vue({
        el:app,
        data: {
           blogs:[
               {
                   "title": "这是一个测试",
                   "id": 1
               },
               {
                   "title": "这也是一个测试",
                   "id": 2
               }
           ],
            select_blogs:[]
        },
        methods: {
            outChange(blog){
                // 判断元素在数组中的位置,如果值为非负数,则是下标
                let index = this.select_blogs.indexOf(blog)
                if(index>=0){
                    this.select_blogs.splice(index,1)
                }else{
                    this.select_blogs.push(blog)
                }

            }
        }
    })
</script>

自定义组件的V-model

  • 在模板中配置model
    • event表示什么情况下触发v-model的行为
    • prop表示传给v-model的变量绑定到模板中的哪个变量上
  • 在合适的时机调用this.$emit(model.event,计算结果)就可以了,注意这里面只需要返回计算结果而不需要改变值,Vue会自动改变这个值同时改变模板外面的值
<div id="app">
    <Stepper v-model="goods_count"></Stepper>
    <!--外部的值也会同步更改-->
    <span>{{goods_count}}</span>
</div>
<script>

    Vue.component("Stepper",{
        props: ["count"],
        model:{
            // model 的事件
            event: "xxx",
            // 绑定的值
            prop: "count"
        },
        template: `
        <div>
        <button @click="sub">-</button>
        <span>{{ count }}</span>
        <button @click="add">+</button>
        </div>
        `,
        methods:{
            // 这里面不需要修改this.count的值,只要把结果传出去就可以
            sub(){
                this.$emit("xxx",this.count-1)
            },
            add(){
                this.$emit("xxx",this.count+1)
            }
        }
    })

    new Vue({
        el:"#app",
        data: {
            goods_count: 0
        }
    })
</script>

自定义组件的插槽

  • 在模板中使用<slot></slot>来占位,以后在标签内写入的内容(包括标签)就会在<slot></slot> 的位置进行替换
<div id="app">
    <navigation-link url="www.baidu.com">百度</navigation-link>
</div>
<script>
    Vue.component("navigation-link",{
        props: ["url"],
        template: `
        <a :href="url">
        <slot></slot>
        </a>
        `
    })
</script>
  • 可以在<slot>默认值</slot>中添加默认值,也就是在使用模板但模板标签中无任何值的时候显示
命名插槽
  • 在定义插槽的时候指定<slot name="xxxx"></slot>属性表示名字
  • 未指定名字的插槽名字为default
  • 在使用组件时需要在<template v-slot="xxxx"></template> 指定名字,来加载不同的数据
<div id="app">
    <main>
        <template v-slot="header">
            <p>这是头部</p>
        </template>
        <template v-slot="body">
            <p>这是主体</p>
        </template>
        <template v-slot="footer">
            <p>这是尾部</p>
        </template>
    </main>
</div>
<script>
    Vue.component("mian",{
        props: ["url"],
        template: `
       <div class="main">
        <slot name="header"></slot>
        <slot name="body"></slot>
        <slot name="footer"></slot>
        </div>
        `
    })

</script>
插槽作用于
  • 在模板中定义的变量插槽是无法访问到的,也就说作用域不同,不可见。’
  • 可以在插槽中通过v-bind:传参的方式传入
  • 在使用的时候可以以v-slot:header="headerProps"讲插槽里面的值得到存入headerProps
  • 当组件中只有一个插槽并且这个插槽没有名字的时候可以使用v-slot="props"来简写
<div id="app">
    <container>
        <template v-slot:header="headerProps">
            <p>{{ headerProps.navs }}</p>
        </template>
        <template v-slot:body>
            <p>这是主体</p>
        </template>
        <template v-slot:footer="footerProps">
            <p>{{footerProps.address}}</p>
            <p>{{footerProps.aboutus}}</p>
        </template>
    </container>
</div>
<script>
    Vue.component("container",{
        template: `
       <div class="main">
            <slot name="header" :navs="navs"></slot>
            <slot name="body"></slot>
            <slot name="footer" :address="address" :aboutus="aboutus"></slot>
        </div>
        `,
        data: function () {
            return {
                address: "公司地址",
                aboutus: "关于我们",
                navs: ["首页","新闻","课程"]
            }
        }
    })


    new Vue({
        el:"#app",
        data: {
            username: "张锐"
        }
    })
</script>

生命周期函数

创建阶段

  • beforeCreateVue已经创建了,但是datamethods还没有创建好
  • creadteddatamethods已经创建好了
  • beforeMount:模板经过编译,还没有挂在到网页中
  • mounted:模板经过编译,并且已经挂在到网页中,创建阶段的事情都做好了

运行期间

  • beforeUpdate:数据data已经更新了,但是模板还未更新
  • updated:在data中更新了,在模板中也更新了

销毁期间

  • beforeDestory:Vue执行实例或者组件销毁之前执行的函数,在这个函数中Vue或者组件中的所有属性都是可以使用的。
  • destoryedVue实例或者组件被销毁以后执行的。此时Vue实例上所有东西都会解绑,所有事件都会被移除,所有元素都会被销毁。

过滤器

  • 使用:{{username|strip}}的方式可以为属性username添加过滤器,也可以为<a :href="url|strip></a>"这样的标签属性添加过滤器

  • 定义:都是定义一个函数,这个函数的第一个参数永远都是被过滤的变量

    • 局部定义:在组件中添加一个属性filters,然后在filters中添加过滤器。
    • 全局定义:通过Vue.filter("过滤器名",函数)的方式定义
  • 传参:如果使用过滤器的时候还需要传递额外的参数,那么可以在定义过滤器的时候,提供其他参数,在使用的时候与普通函数相同。

    <body>
    <div id="app">
        {{username|strip("-")}}
    </div>
    <script>
        Vue.filter("strip",function (value, str) {
            // 将空格替换为 str
            return value.replace(" ",str)
        })
        new Vue({
            el:"#app",
            data: {
                username: "张   锐"
            }
        })
    </script>
    

VueRouter

基本使用

  1. 创建一个VueRouter对象:new VueRouter()

  2. VueRouter中,需要传递一个routes参数。这个参数是一个数组类型,数组中存储的是对象,对象中最少有两个属性,一个是path,代表url,第二个是component,代表数据更新的组件,示例代码如下:

let index = Vue.extend({template:"<h1>首页</h1>>"})
let find = Vue.extend({template:"<h1>发现音乐</h1>>"})
let friend = Vue.extend({template:"<h1>我的朋友</h1>>"})
let router = new VueRouter({
        routes: [
            {path:"/",component:index},
            {path:"/find",component:find},
            {path:"/friend",component:friend},
        ]
    })
  1. router传给Vue

    new Vue({
            el:"#app",
            router: router
        })
    
  2. 将网页中之前的a标签,替换成router-link。然后指定参数to表示需要跳转的路径。

    <li><router-link to="friend">我的朋友</router-link></li>
    
  3. 使用router-view指定网页中哪个地方需要被更新

  • extendcomponent之间的区别
    • extend只是创建了一个组件,但是没有加载的Vue
    • component是创建了一个组件,并且起了一个名字,然后加载到Vue

动态路由

  • $route$router的区别
    • $route是表示当前访问的路由,其中参数有
      • fullpath:完整路径
      • name:路由名字
      • params:参数
      • query:查询字符串参数,或者叫get参数
    • $router表示全局的VueRouter对象
  • url中定义一个参数,那么以后url中就可以动态的传递这个参数。语法是:/about/:参数名
  • 在组件中可以通过this.$oute.params.参数名拿到这个参数,或者在组件的模板中,可以直接使用$route.params.参数名拿到。
  • this.$routethis.$router

组件复用

  • 使用动态路由的时候,如果只有请求参数的不同,则Vue默认使用组件的复用,这虽然回加快渲染效率,但是由于组件一直存在,所以不会在调用生命周期函数,可能出现问题。

    • 解决这个问题可以采用监听属性

          let index = Vue.extend({template:"<h1>首页</h1>"})
          let about = Vue.extend({
              template:"<h1>个人中心:{{$route.params.userid}}</h1>",
              mounted(){
                  console.log(this.$route.params.userid);
              },
              watch: {
                  "$route": function (to, from_) {
                      console.log(to);
                      console.log(from_);
                  }
              }
          })
          let friend = Vue.extend({
                  template:"<h1>我的朋友</h1>",
              })
          let router = new VueRouter({
              routes: [
                  {path:"/",component:index},
                  {path:"/about/:userid",component:about},
                  {path:"/friend",component:friend},
              ]
          })
      
          new Vue({
              el:"#app",
              router: router
          })
      
    • 使用路由守卫

       beforeRouteUpdate: function (to, from, next) {
            console.log(to);
            console.log(from);
            next()
      }
      
      • to表示下一个路由
      • from表示当前路由
      • next是一个函数,也就是跳转函数

匹配404错误

  • 使用*来匹配未知的路由,可以将将这个路由映射到一个404的组件中
  • 数据不存在的处理,这种情况,前端是无法判断的,只能通过访问服务器来判断存不存在,如果服务器返回不存在,那么我们可以通过this.$router.replace来跳转到404的页面
<div id="app">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">网易云音乐</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><router-link to="/">首页</router-link></li>
                    <li><router-link to="find">发现音乐</router-link></li>
                    <li><router-link to="friend">我的朋友</router-link></li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    <div class="container">
        <router-view></router-view>
    </div>
</div>
<script>
    let index = Vue.extend({template:"<h1>首页</h1>"})
    let find = Vue.extend({template:"<h1>发现音乐</h1>"})
    let friend = Vue.extend({
        template:"<h1>我的朋友{{$route.params.user_id}}</h1>",
        mounted(){
            if(this.$route.params.user_id !== '123'){
                this.$router.replace("/404")
            }
        },
        watch:{
            "$route": function (to, from) {
                // 监听user_id改变
                // 想服务端发送请求,验证是否存在
                // 如果返回不存在需要跳转到404页面
            }
        }
    })
    let notfound = Vue.extend({
        template:"<h1>404页面没有找到</h1>",
    })
    let router = new VueRouter({
        routes: [
            {path:"/",component:index},
            {path:"/find",component:find},
            {path:"/friend/:user_id",component:friend},
            {path:"/404",component:notfound},
            {path:"*",component:notfound},
        ]
    })

    new Vue({
        el:"#app",
        router: router
    })
</script>

嵌套路由

  • 在大的路由下面有时候可能使用一些子路由来切换数据,那么这时候可以使用路由嵌套。

  • 首先在定义路由的时候不需要放在routes中,而是应该放在父路由的children中。

  • 在父路由的组件中,要记得添加路由出口<router-view>,示例代码如下。

    <div id="app">
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">网易云音乐</a>
                </div>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li class="active"><router-link to="/">首页</router-link></li>
                        <li><router-link to="find">发现音乐</router-link></li>
                    </ul>
                </div><!-- /.navbar-collapse -->
            </div><!-- /.container-fluid -->
        </nav>
        <div class="container">
            <router-view></router-view>
        </div>
    </div>
    <script>
        let index = Vue.extend({template:"<h1>首页</h1>"})
        let find = Vue.extend({
            template:`
            <div>
                <h1>发现音乐</h1>
                <ul class="nav nav-pills">
                  <li role="presentation" class="active"><router-link to="/find">刘德华</router-link to=></li>
                  <li role="presentation"><router-link to="/find/lm">黎明</router-link to=></li>
                  <li role="presentation"><router-link to="/find/gfc">郭富城</router-link to=></li>
                </ul>
                <div class="container"><router-view></router-view></div>
            </div>
        `
        })
    
        let user_ldh = Vue.extend({
            template:`<h1>刘德华</h1>`
        })
        let user_lm = Vue.extend({
            template:`<h1>黎明</h1>`
        })
        let user_gfc = Vue.extend({
            template:`<h1>郭富城</h1>`
        })
    
        let router = new VueRouter({
            routes: [
                {path:"/",component:index},
                {
                    path:"/find",
                    component:find,
                    children: [
                        {path:"",component:user_ldh},
                        {path:"lm",component:user_lm},
                        {path:"gfc",component:user_gfc},
                    ]
                },
            ]
        })
    
        new Vue({
            el:"#app",
            router: router
        })
    </script>
    

编程式导航

  • this.$router.push:跳转到下一个url,会把新转入的url添加到浏览器的history中。参数
    • 字符串:直接路径
    • 对象:pathname都可以,但是如果使用了path,那么参数必须放到path中,放到params中无用
  • this.$router.replace:用法和push一样,只是会替换当前页面,不会存在历史记录,也就是当点击前进或者后退的时候都没有这个页面。
  • this.$router.go:前进或者后退。
    • 正数表示前进this.$router.go(1),前进一步
    • 负数表示后退this.$router.go(-2),后退两步
    • 如果无法前进或者后退了,那么执行将没有任何结果
<div id="app">
    <button @click="gotoPost">帖子列表</button>
    <button @click="gotoProfile">个人中心</button>
    <button @click="gotoLogin">登陆页面</button>
    <router-view></router-view>
</div>
<script>

    let post = Vue.extend({
        template: `<h1>帖子列表</h1>`
    })
    let profile = Vue.extend({
        template: `<h1>个人中心{{$route.params.userid}}</h1>`
    })
    let login = Vue.extend({
        template: `<h1>登陆页面{{$route.query}}</h1>`
    })
    let router = new VueRouter({
        routes:[
            {
                path:"/post",
                component:post
            },
            {
                path:"/profile/:userid",
                component:profile,
                name:"myprofile"
            },
            {
                path:"/login",
                component:login,
                name:"login"
            },
        ]
    })

    new Vue({
        el: "#app",
        router:router,
        methods:{
            gotoPost(){
                this.$router.push("/post")
            },
            gotoProfile(){
                // 传url路径
                // this.$router.push("/profile/123")
                // 传对象
                // this.$router.push({path:"/profile/123"})
                // 传命名
                this.$router.push({name:"myprofile",params:{userid:"123"}})
            },
            gotoLogin(){
                let currentPath = this.$route.fullPath
                this.$router.push({name:"login",query:{from:currentPath}})
            }
        }
    })
</script>

命名路由和命名视图

命名路由
  • 在定义路由的时候指定name属性作为路由的名字,然后在使用路由的时候传递一个对象,这个对象中包含name属性,使用方法和this.$router.push中参数一样。

  • 需要注意使用router-link的时候要在to前使用:因为使用了变量。

    <router-link :to="{ name: 'myprofile',params:{userid:123}}">个人中心</router-link>
    

命名视图(多组件)

  • 在一个路由下面展示多个组件,需要使用命名的视图。
  • 在定义路由的时候需要传递components参数,然后把所有需要展示的组件都放到这个里面components是一个对象,{name:组件}映射。
  • 在模板中,通过<router-view name="组件名"></router-view>来实现。
  • 默认没有名字的出口是default,可以在components中指定这个出口对应的组件。
<body>
    <div id="app">
        <div class="header">
            <router-view name="header"></router-view>
        </div>
        <div class="body">
            <div class="sidebar">
                <router-view name="sidebar"></router-view>
            </div>
            <div class="main">
                <router-view name="main"></router-view>
            </div>
        </div>
        <div class="footer">
            <router-view name="footer"></router-view>
        </div>
    </div>
</body>
<script>
    let headerComponent = Vue.extend({
        template: `<div>header部分</div>`
    })
    let sidebarComponent = Vue.extend({
        template: `<div>sidebar部分</div>`
    })
    let mainComponent = Vue.extend({
        template: `<div>main部分</div>`
    })
    let footerComponent = Vue.extend({
        template: `<div>footer部分</div>`
    })

    let router = new VueRouter({
        routes:[
            {
                path:"/",
                components:{
                    header: headerComponent,
                    sidebar:sidebarComponent,
                    main:mainComponent,
                    footer:footerComponent
                }
            }
        ]
    })

    new Vue({
        el: "#app",
        router:router
    })
</script>

重定向和别名

  • 重定向可以在定义路由的时候指定参数redirect来指定重定向的位置。
  • 起别名可以在定义路由的时候指定参数alias来指定url的别名。起别名之后,也可以通过别名来访问这个路由。
<div id="app">
    <router-view></router-view>
</div>
<script>
    let index = Vue.extend({template: "<h1>首页</h1>"})
    let article = Vue.extend({template:"<h1>文章列表页</h1>"})
    let router = new VueRouter({
        routes: [
            {path: "/", redirect:"/article"},
            // {path: "/", redirect:{name:"article"}},
            {path: "/article", component: article,name:"article",alias:"/list"},
        ]
    })

    new Vue({
        el: "#app",
        router: router
    })
</script>

全局导航守卫

beforeEach
  • 全局导航守卫就是在VueRouter上实现的。

  • 这个是在路由发生变化了,但是页面还没有跳转时候执行的函数。

  • beforeEach(to,from,next)

    • to:是上一个路由对象
    • from: 是下一个路由对象
    • next:表示下一步该怎么走
      • next():按照正常走
      • next('/'):中断之前的路由,重新走到 / 路由上,也可以传递{name:'/'}这种指定名字的路由
      • next(false):中断路由,什么也不做,不会导向任何一个路由。
afterEach
  • 这是在路由完成后的回调函数
  • afterEach(to,from)
    • to:表示上一个路由
    • from:表示下一个路由
<div id="app">
    <router-link to="/">首页</router-link>
    <router-link to="/account">我的账户</router-link>
    <router-link to="/order">我的订单</router-link>
    <router-link to="/login">登陆</router-link>
    <router-view></router-view>
</div>
<script>

    const logined = false

    let index = {template: "<h1>首页</h1>"}
    let account = {template:"<h1>我的账户</h1>"}
    let order = {template:"<h1>我的订单</h1>"}
    let login = {template:"<h1>登陆</h1>"}
    let router = new VueRouter({
        routes: [
            {path: "/", component:index,name:'index'},
            {path: "/account", component:account,name:'account'},
            {path: "/order", component:order,name:'order'},
            {path: "/login", component:login,name:'login'},
        ]
    })
    router.beforeEach(function (to, from, next) {
        // next(): 按照正常的跳转
        // next():传递一个路由,就会跳转到指定路由
        // next(false)或者没有调用next()则不会做任何跳转
        next()
        const authRoutes = ['account','order']
        if(authRoutes.indexOf(to.name)>=0){
            if(!logined){
                console.log(from.name)
                next({path:'/login'})
                console.log(to.name)
            }else{
                next()
            }
        }else if(to.name === 'login'){
            if(logined){
                next('/')
            }
        }else{
            next()
        }
    })
    new Vue({
        el: "#app",
        router: router
    })
</script>

路由导航守卫

  • 是绑定到路由上的,在定义路由的时候需要指定beforeEnter属性

    <div id="app">
        <router-link to="/">首页</router-link>
        <router-link to="/account">我的账户</router-link>
        <router-link to="/order">我的订单</router-link>
        <router-link to="/login">登陆</router-link>
        <router-view></router-view>
    </div>
    <script>
    
        const logined = true
    
        let index = {template: "<h1>首页</h1>"}
        let account = {template:"<h1>我的账户</h1>"}
        let order = {template:"<h1>我的订单</h1>"}
        let login = {template:"<h1>登陆</h1>"}
        let router = new VueRouter({
            routes: [
                {path: "/", component:index,name:'index'},
                {path: "/account", component:account,name:'account'},
                {path: "/order", component:order,name:'order'},
                {
                    path: "/login",
                    component:login,
                    name:'login',
                    beforeEnter: function (to, from, next) {
                        if(logined){
                            next('/')
                        }else{
                            next()
                        }
                    }
    
                },
            ]
        })
        new Vue({
            el: "#app",
            router: router
        })
    </script>
    

组件导航守卫

  • 主键导航守卫是绑定到组件上的,有三个方法:

  • veforeRouteEnter:是在组件之前调用

    • 在渲染组件的对应路由呗comfirm之前调用
    • 注意:这这个函数中不能使用this来代表组件,因为组件还没创建
    • 可以在next中使用vm来代表组件next(vm=>{ console.log(vm.username) })
  • beforeRouteYpdate:这个是在进入组件之前调用,也就是当前路由改变,页面还没渲染的时候调用

    • 这个函数主要用于监听路由发生变化,当页面组件被复用的时候也会调用
    • 在这个函数中可以访问组件实例this
  • beforeRouteLeave:这个是在即将离开组件路由的时候执行的函数

    • 这个函数主要用于一些未保存的确认按钮
    • 这里可以访问this

导航解析流程

  1. 导航被触发
  2. 在失活的组件里调用离开守卫
  3. 调用全局的beforeEach守卫
  4. 在重用的组件里调用beforeRouteUpdate守卫
  5. 在路由配置里调用beforeEnter
  6. 解析异步路由组件
  7. 在激活的组件里调用beforeRouteEnter
  8. 调用全局的beforeResolve守卫(2.5+)
  9. 导航被确认
  10. 调用全局的afterEach
  11. 触发DOM更新元素
  12. 用创建好的实例调用beforeRouteEnter守卫传递next的回调函数

Vue-cli

组件定义和导入

  • 定义:.Vue中分成三个模块,templatescriptstyle
    • template:模板
    • script:js相关代码
    • style:样式相关代码
  • 导入:使用import XXX from XXX

style

  • 默认情况下.vue中的style中的样式是全局可用的
  • style标签中加入scoped属性后,使样式只能在当前标签可用<style scoped>
  • 因为App.vue作为主入口文件,因此一般全局的样式会写在这个中,比如清空浏览器默认样式等
  • 指定属性lang='scss'可以用来写sass语法
适配移动端
  • 使用包postcss-pxtoremlib-flexible

    npm install postcss-pxtorem --save-dev
    npm install lib-flex --save-dev
    
  • 需要配置package.json文件

  "postcss": {
    "plugins": {
      "autoprefixer": {},
      "postcss-pxtorem": {
        "rootValue": 37.5,// 基数,也就是1rem = 多少px,一般为设计师给的数除以10
        "propList": [ // 什么属性支持转换
          "*"
        ],
        "selectorBlackList": [ // 什么不用转换
          "van-*"
        ]
      }
    }
  },
  • 引用组件需要安装包babel-plugin-import,用于解析import

    cnpm install babel-plugin-import --save
    
    • 需要配置babel.config.js文件

      • 如果这个配置文件写错可能出现找不到babel-plugin-vant错误
      module.exports = {
        presets: [
          '@vue/app'
        ],
          plugins : [
              ['import',{
                libraryName: 'vant',
                  libraryDirectory: 'es',
                  style : true,
              },'vant']
          ]
      }
      
    • 导入语法{ botton } from 'vant'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值