Vue 超详细手记

Vue

Vue实例属性

名称 内容
$data 传入的data内容,修改可引发页面内容变化
$props 父组件传入子组件
$el 绑定的html的节点
$options 新建Vue实例对象时穿进去的内容,修改optins的内容不会引发页面变化,有render方法,在页面从新渲染时会调用
$root 整个Vue对象的根节点,app.$root === app
$children 组件内部的元素
$slots 插槽
$scopedSlots 区域化插槽
$refs 模板的引用
$isServer 服务端渲染时能够用来判断
$watch 监听内容,与写在实例中效果相同,但经过$watch写的监听要返回的unWatch方法结束监听
$on 监听事件是否被触发,能够获取emit传递的参数
$emit 触发事件,监听哪一个对象就要经过哪一个事件触发,能够传值
$once 与on类似,但只被触发一次
$forceUpdate 强制组件从新渲染
$set 给新值赋值而且页面会渲染
$delete 删除值
$nextTick 异步操做

Vue 生命周期

生命周期 介绍 可调用次数
beforeCreate 还未绑定Dom,不能进行dom操做**(不要放入ajax请求)**
created 还未绑定Dom,不能进行dom操做
beforeMount el只为html中写的节点,还未绑定,说明还未渲染(未调用render方法)
mounted el绑定为渲染后的节点,说明以前进行了渲染(已调用render方法),实例已经建立完成
beforeUpdate 更新的对象时模板,须要虚拟DOM的重洗渲染和补丁修改,可是若是更改的数据在模板中没有使用,就不会进行更新
updated 数据更改致使虚拟DOM从新渲染和打补丁,注意要避免在这个钩子函数中操做数据
activated
deactivated
beforeDestory 实例销毁以前调用。在这一步,实例仍然彻底可用,this仍能获取到实例。通常在这一步中进行:销毁定时器、解绑全局事件、销毁插件对象等操做
destoryed Vue 实例销毁后调用。调用后,Vue 实例指示的全部东西都会解绑定,全部的事件监听器会被移除,全部的子实例也会被销毁

生命周期图示

Computed 的特性

  • computed内置缓存,在页面从新渲染时若是computed的值没有发生变化便不会再从新渲染,提升性能
  • computed能够作设置操做,但不建议用computed作设置操做,有必定几率致使重复计算

Wacth 的特性

  • watch能够用来监听数据的变化
  • 第一次渲染watch并不会渲染出内容,若是要初次渲染加上handler方法
  • handler 只监听监听对象引用的变化,属性的变化并不会调用handler方法
  • immediate属性能够用来确认是否以当前的初始值执行handler的函数
  • 将deep属性设置为true就能够监听对象属性的变化
  • 建议不要再watch内修改监听的值和属性

Vue指令

指令名 指令做用
v-text 标签里面要显示的文本内容,内容较多时不建议使用
v-html 将html代码片断展现为html标签
v-show 根据内容的Boolean值来显示或不显示内容,原理是设置display:none;
v-if 与v-show不一样的是若是判断为false将不会放在文档流里,若是频繁切换会动态增删节点,会形成性能问题 搭配指令有 v-else v-else-if
v-for 循环,用法为 v-for="item in arr",item为数组内的值或对象,arr为储存值的数组,若是要拿到顺序则写为v-for="(item, index) in arr" 遍历对象可用 v-for="(val, key,index) in obj"的方式,val为值,key为对应的键,index为对应的索引值即顺序,使用v-for时须要:key=""来绑定一个惟一识别
v-on 监听事件,能够监听到$emit触发的事件
v-model 双向绑定数据,可进行实时更改,通常用于input,通常输入以后会变为字符串
若是想让值为数字,在v-model后加.number修饰符
若是想去掉首位的空格,在v-model后加.trim修饰符
若是不想实时更改,而是失焦后更改,在v-model后加.lazy修饰符
v-pre 不解析内部内容,即内部内容保留源格式
v-cloak 通常直接在页面引入vue.js代码时使用,在vue代码完成以前让内容隐藏,vue加载完成后再展现
v-once 数据绑定内容只执行一次,通常为静态内容使用,节省性能开销,不会作虚拟DOM对比

组件

定义组件

首先写一个组件内容javascript

const component = {
    templete: '<div>This is compoent</div>'
}
复制代码

而后咱们用Vue实例的方法引入组件html

import Vue from 'vue'
Vue.component('ComponentName',component); //定义了一个名为name的组件
//组件名用大写开头并用驼峰命名 (类的思想)
复制代码

使用时:前端

第一种状况-直接在tempelate内使用:vue

new Vue({
    el:'#root',
    tempelate:'<component-name></component-name>' //使用时使用小写,而且单词之间用-链接
})
复制代码

第二种状况-从新命名使用:java

new Vue({
    el:'#root',
    tempelate:'<new-name-component></new-name-component>',
    //使用时使用小写,而且单词之间用-链接
    components: {
        newNameComponent: component
    }
})
复制代码

在组件内部绑定数据-data

const component = {
    templete: '<div>{{text}}</div>',
    data(){  //必需要使用函数返回值的方式
        return{
            text:'hello'
        }
    }
}
复制代码

使用一个return新对象的方法能够防止多个组件动态绑定至一个相同的值,例如v-model的场景就会出现相关的问题webpack

组件内部传递数据-props

const component = {
    templete: ` <div> <span v-show="active">see me if active</span> <span>{{propOne}}</span> //prop与data类似都是绑定在this上,能够直接使用 </div> `
    data(){  //必需要使用函数返回值的方式
        return{
            text:'hello'
        }
    },
    props: {
        active: Boolean,//向组件传递一个布尔值,key为active
        propOne: String  //驼峰式命名
        
    }
}
复制代码

使用时web

new Vue({
    el:'#root',
    tempelate:` <new-component :active="true" prop-one="123"></new-component> //须要用v-bind解析传入的值 <new-component :active="false" prop-one="456"></new-component> //在prop中为驼峰式的命名在使用时使用小写而且用-链接的方式(非强制,只是规范) `,
    //使用时使用小写,而且单词之间用-链接
    components: {
        NewComponent: component
    }
})
复制代码

props数据的一些设置ajax

const component = {
    props: {
        active: {
            type: Boolean, //设置为布尔值
            required: true, //这个属性为必需属性
            default: true, //设置默认值为true,若是有了default就不须要required
            validator (value) {   //可用来检测输入的值是否符合要求,可用来自定义检测
            	return typeof value === 'Boolean' 
        	}
        },
        propOne: {
            type: Object,
            default() {   //若是默认值为一个对象,则应该用函数返回一个对象的形式
                return {
                    
                }
            }
        }
    },
    .....
}
复制代码

组件的extend

extend实例内的值

当咱们新建一个Vue实例时,它内部都是一些默认的属性,若是咱们想使用咱们已经定义过的组件的一些属性内容咱们能够用下列代码:vue-router

... 已建立组件component

const ComponentVue = Vue.extend(component);  //前提是component没有required为true的props

new ComponentVue({
    el: '#root',
})
复制代码

但若是咱们须要用到props呢?vuex

咱们能够用下面的方式:

... 已建立组件component

const ComponentVue = Vue.extend(component); 

new ComponentVue({
    el: '#root',
    propsData: {   //用propsdata来向原组件的props传值才能使用
    	propOne: 'xxx',
	}
})
复制代码

extend的mounted

会先执行原组件的mounted,再执行extend实例的mountend

组件内如何实现v-model双向绑定

const component = {
    props: ['value'],
    template: ` <div> <input type="text" @input="handleInput" v-model="value" > </div> `,
    methods: {
        handleInput(e) {
            this.$emit('input', e.target.value); //向父组件触发input事件
        }
    }
}

//在Vue实例中

new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    data () {
        return {
            value: '123'
        }
    }
    template: ` <div> <comp-one :value="value" @input="value = arguments[0]"></comp-one> //arguments数组即为经过$emit后面传递的参数 </div> `
})
复制代码

咱们还能够经过下面的功能实现

const component = {
    model: {   
        prop: 'value1', //为要传的值
        event: 'change' //为触发的事件
    },
    props: ['value1'],
    template: ` <div> <input type="text" @input="handleInput" v-model="value1"> </div> `,
    methods: {
        handleInput(e) {
            this.$emit('input', e.target.value); //向父组件触发input事件
        }
    }
}

//在Vue实例中

new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    data () {
        return {
            value: '123'
        }
    }
    template: ` <div> <comp-one v-model="value"></comp-one> //arguments数组即为经过$emit后面传递的参数 </div> `
})
复制代码

组件高级属性

插槽 slot

当咱们在Vue实例调用的组件内部直接写入html元素时,它是不会显示的,由于咱们没有告诉组件要放在哪里显示,这时就须要 插槽

//组件内
const component = {
    template: ` <div> <slot></slot> //为内容添加插槽位置,没有声明插槽位置的元素都会放在这个里面 </div> `
}

//实例中
new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    template: ` <div> <comp-one> <span>我会被放在slot中</soan> </comp-one> </div> `
})
复制代码

具名插槽

//组件内
const component = {
    template: ` <div> <div class="header"> <slot name="header"></slot> </div> <div class="body"> <slot name="body"></slot> </div> </div> `
}

//实例中
new Vue({
    components: {
        CompOne: component
    },
    el: '#root',
    template: ` <div> <comp-one> <span slot="header">我会被放在slot name为header的插槽中</soan> <span slot="body">我会被放在slot name为body的插槽中</soan> </comp-one> </div> `
})
复制代码

做用域插槽 让插槽内绑定的数据为组件的数据

使用方法为:

//组件内
template: ` <div> <slot value="456" anothervalue="content"></slot> </div> `

//实例中
template:` <div> <comp-one><span slot-scope="props">{{props.value}} {{props.anothervalue}}</span></comp-one> //slot-scope 的对象即为组件内slot传递值的对象 </div> `
复制代码

或者可使用组件内部data的值,它并不影响本地data的使用:

//组件内
template: ` <div> <slot :value="value" anothervalue="content"></slot> //注意要使用v-bind形式 </div> `,
data() {
    return{
        value="this is data value"
    }    
}

//实例中
template:` <div> <comp-one><span slot-scope="props">{{props.value}} {{props.anothervalue}}</span></comp-one> //slot-scope 的对象即为组件内slot传递值的对象 </div> `
复制代码

跨级数据交流

如何越级拿到vue组件实例呢?

使用 provide属性!

//子组件
const ChildComponent = {
    template: '<div>child component</div>',
    inject: ['grandfather']  //获取实例向外提供的对象
}

//组件
const component = {
    name: 'comp',
    component: {
        ChildComponent //声明子组件
    },
    template: ` <div> <child-component /> //若是为空组件能够这样书写 </div> `,
    
}

//实例
new Vue({
    components: {
        CompOne: component
    },
    provide (){
        return{
          grandfather: this  //将本身这个实例做为对象向外提供,仅为节点下的节点(子节点 & 子节点的子节点 $ ......)
        } //使用函数返回值的方式的缘由与data相同
    },
    ...
})
复制代码

但通常provide只提供一次值,不会实现model模式,若要实现须要给provide的值提供得到方法,方法以下:

provide (){
        return{
          const data = {}  //先设置一个空对象
    
    Object.defineProperty(data,'value',{
        get: () => this.value, //没次使用值时实际上是用了这个get方法
        enumerable: true //可读属性
    })
        } 
    }
复制代码

这种方法虽然是实现了跨级model,但属于一种hack方法,官方不建议使用。

Render方法

render方法是咱们渲染的一个方法,它内部会返回一个createElement方法,这个方法是Vue给咱们提供的建立节点的方法,使用render方法咱们就能够不使用template

//组件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
            //data数据 data: this.data形式
        },[   //注意要传递数组
            this.$slots.default  //无名为default,具名slot将default改成名字便可
            this.props1 //传递prop值
        ]) 
    }
}

//实例
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不必定为createElement,能够用其余字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp'     
            },[            //注意节点内在建立节点要传递一个数组
            createElement(
                'span',{
                    ref: 'span',
                    slot: 'header'  //能够指定要渲染的slot插槽位置
                },this.value)
        ])  //Vue提供的建立节点的函数
    }
})
复制代码

Render方法下的事件监听,on 和 nativeOn

on形式的监听,须要实例和组建中都使用on

//组件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
            on: {
                click: () => {this.$emit('click')} //监听click事件,向父级触发click事件
            }
        },[   //注意要传递数组
            this.$slots.default  //无名为default,具名slot将default改成名字便可
            this.props1 //传递prop值
        ]) 
    }
}

//实例
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不必定为createElement,能够用其余字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp',
                on: {
                    click: this.handleClick //点击组件会受到组件传递的click事件,并触发handleClick方法 
                }
            },[            //注意节点内在建立节点要传递一个数组
            createElement(
                'span',{
                    ref: 'span'
                },this.value)
        ])  //Vue提供的建立节点的函数
    },
    methods: {
        handleClick() {
            //do something....
        }
})
复制代码

nativeOn 有个很大的便捷,那就是咱们只须要在实例中声明而组件中能够不用声明

//组件
const component = {
    name: 'comp',
    props: ['props1'],
    render (createElement) {
        return createElement('div', {
        },[   //注意要传递数组
            this.$slots.default  //无名为default,具名slot将default改成名字便可
            this.props1 //传递prop值
        ]) 
    }
}

//实例
new Vue({
    components: {
        CompOne: component
    },
    render( createElement ) {  //不必定为createElement,能够用其余字母表示,但下面的方法名要保持一致
        return createElement(
            'comp-one',{
                ref: 'comp',
                nativeOn: {
                    click: this.handleClick //nativeOn会自动绑定到组件根节点的dom上面
                }
            },[            //注意节点内在建立节点要传递一个数组
            createElement(
                'span',{
                    ref: 'span'
                },this.value)
        ])  //Vue提供的建立节点的函数
    },
    methods: {
        handleClick() {
            //do something....
        }
})
复制代码

template实际上是render方法的一种编译方式

V-router

router,顾名思义即为路由,vue框架可让咱们在前端实现路由功能,这个功能对WebApp这种应用来讲是必不可少的。

使用路由以前须要先安装模块,npm install vue-router

通常咱们将路由分为两个文件,router.js 和 routes.js,一个用来存放路由信息,另外一个用来写路由逻辑功能

//routes.js
import Todo from '../views/todo/todo.vue'  //引入要展现的页面
import Login from '../views/login/login.vue'

export default [
    {
        path: '/', //网页的路径
        component: Todo //指定输入上方路径后显示什么页面
    },
    {
        path: '/login',
        compoent: Login
    }
]
复制代码
//router.js
import Router from 'vue-router'  //引入vue-router模块
import routes from './routes' //引入咱们写的路由信息


//每次引入路由文件时会返回一个路由对象
export default () => {
    return new Router({
        routes
    })
}
复制代码

为了实现跳转,咱们还须要用router-view来实现对路由跳转事后的展现

// app.vue
<template>
  <div id="app">
    <div id="cover"></div>
    <Header></Header>
    <router-view></router-view>  //这部分来进行跳转,页面内容展现在这部分
    <!-- <todo></todo> -->
    <Footer></Footer>
  </div>
</template>
复制代码

咱们也能够用路由来实现首页重定向,在routes.js写入这部份内容

//routes.js
import Todo from '../views/todo/todo.vue'  //引入要展现的页面
import Login from '../views/login/login.vue'

export default [
    {
      path: '/',  
      redirect: '/app' //当输入默认路径时重定向到app页面
    },
    {
        path: '/app', //网页的路径
        component: Todo //指定输入上方路径后显示什么页面
    },
    {
        path: '/login',
        compoent: Login
    }
]
复制代码

此时咱们会注意到网页上的url是 http://localhost:8080/#/app,他默认会传一个hash,咱们能够更改这个东西须要更改router.js的内容

import Router from 'vue-router'  //引入vue-router模块
import routes from './routes' //引入咱们写的路由信息


//每次引入路由文件时会返回一个路由对象
export default () => {
    return new Router({
        routes,
        mode:'history'  //mode改成 'hash'即为hash方式
    })
}
复制代码

此时在此运行我就能够发现那个#消失了

咱们还能够经过base属性设计基路径

import Router from 'vue-router'  //引入vue-router模块
import routes from './routes' //引入咱们写的路由信息


//每次引入路由文件时会返回一个路由对象
export default () => {
    return new Router({
        routes,
        mode:'history',
        base: '/base/'  //设置基路径为base,经过vue-router的跳转都会加入基路径
    })
}
复制代码

此时咱们再访问localhost:8080发现他的路径跳转为了

localhost:8080/base/app

但基路径并非强制的,咱们把base去掉访问依然能够访问到app

还有两个属性使咱们要配合router-link使用的

//app.vue
<template>
  <div id="app">
    <div id="cover"></div>
    <Header></Header>
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
    <router-view></router-view>
    <!-- <todo></todo> -->
    <Footer></Footer>
  </div>
</template>
复制代码

咱们在app.vue中加入两个router-link(与a标签相似,原理是经过a标签实现的),to的内容即为他们要跳转的路由,而后咱们能够全局设置连接的样式

export default () => {
    return new Router({
        routes,
        mode:'history',
        linkActuveCLass:'active-link', //只要有一部分被激活会加上
        linkExactActiveClass: 'exact-active-link' //当前路径彻底匹配才加上
    
    })
}
复制代码

可能会有点难理解,那咱们这样想,你当前有一个/app的页面,展现的是app.vue,而后app下面有个子页面是childApp.vue,路径是/app/childApp,当咱们在localhost:8080/app/childApp的路径下时,此时routerlink to 为childApp的连接的样式为'active-link' 和'exact-active-link',而app的routerlink的样式为'active-link' 而不会有'exact-active-link'

路径的映射

咱们把路径输入url时,按下回车,会想服务器发送一个请求,若是此时咱们没有在服务器中,添加路由映射时,浏览器会返回咱们一个错误信息

Cannot GET /xxx //xxx即为url的内容

//webpack.config.client.js
const devServer = {
    port: 8000,
    host: '0.0.0.0',
    overlay: {
      errors: true,
    },
    historyApiFallback: {
        index: 'index.html'      //加上这个咱们输入url后刷新页面仍是会显示出内容
    },
    hot: true
  }

复制代码

页面的跳转时的滚动保留

咱们有时在跳转页面时,不想回到上一级时已经返回到了顶部而不是跳转以前的位置,咱们能够在Router里设置一个scrollBehavior

export default () => {
    return new Router({
        routes,
        mode:'history',
        scrollBehavior (to, from, savedPosition){ //to -> 跳转的路由 from -> 当前的路由即跳转的起始点 savedPosition -> 保存当前滚动条滚动的位置 
            if(savedPosition) {  //若是是有滚动距离的,返回到以前的页面位置
                return savedPosition
            } else { //不然,返回顶部
                return {x:0 , y:0}
            }
        }
    
    })
}
复制代码

配置路由的参数

这部分主要讲路由方面的参数

name

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/',
    redirect: '/app' //当输入默认路径时重定向到app页面
  },
  {
    path: '/app',
    component: Todo,
    name: 'app'  //给当前的路由设置一个姓名,能够用来跳转,与路径和组件名无强制联系
  },
  {
    path: '/login',
    compoent: Login
  }
]

复制代码

而后在routerlink上能够进行经过name跳转

<router-link :to="{name:'app'}"> </router-link>  //传入对象,让Vue来解析它
复制代码

以后就能够进行跳转了,没必要每次都要写路径

meta

用来存放一些元信息

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/',
    redirect: '/app' //当输入默认路径时重定向到app页面
  },
  {
    path: '/app',
    component: Todo,
    meta: {
          title:'this is app',  //与html的meta一样的效果
          description: 'author Reaper Lee'
      }
  },
  {
    path: '/login',
    compoent: Login
  }
]
复制代码

children

用来写当前路径下的子组件

import Todo from '../views/todo/todo.vue'
import Login from '../views/login/login.vue'

export default [
  {
    path: '/app',
    component: Todo,
   children: [ //注意是数组
       {
           path: 'child', //路径信息,与父级相同
           component: Child 
       }
   ]
  }
]
复制代码

但注意这个只会在 父组件即(Todo)下的routerview展现,不会和Todo抢占同一个routerview

路由的Transition

transition顾名思义就是路由的过分,即为一次过渡动画,咱们使用时要配合routerview使用

//app.vue
<template>
  <div id="app">
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
      <transition name="fade"> //使用fade的动画做为过渡
          <router-view></router-view>
      </transition>
  </div>
</template>

<style> //定义这个fade动画 .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
复制代码

路由传参

咱们能够经过路由传递一些参数,这个会在咱们好比查看一个物品的详情页等场景使用会比较多。具体方法以下

// routes.js
export default [
  {
    path: '/app/:id', //这里咱们就声明了一个路由上的参数
    component: Todo,
    
  }
]
复制代码

此时咱们直接访问/app 是不会有内容的

但当咱们访问/app/xxxx (xxxx为要传递的参数),此时咱们就能够正常的显示页面,咱们获取这个参数能够在父组件中使用this.$route来获取参数

this.$router.params.id 就能够拿到路径中的参数了,id是咱们在上面的path中给的参数名为id,要与参数名保持一致
复制代码

还有一种更强大的方法,那就是把路径中的参数做为组件的props传递给组件

// routes.js
export default [
  {
    path: '/app/:id', //这里咱们就声明了一个路由上的参数
    props:true, //这里咱们设置为true便可让参数做为props传递给组件
    component: Todo
  }
]
复制代码

而后在组件中咱们须要把参数的props声明

//todo.vue
export default {
    props: ['id']  //声明一个名为 id 的props数据
}
复制代码

而后咱们就能够在组件中拿到这个参数了!并且经过props传递的参数咱们能够更好的使用,因此若是真的须要经过路径传参数咱们尽量使用props。

咱们有时会碰见在主页要使用多个router-view来展现组件的状况,此时咱们须要给一些router-view提供name属性

//app.vue
<template>
  <div id="app">
    <router-link to="/app">app</router-link>
    <router-link to="/login">login</router-link>
    <router-view />   //内部不放内容能够写成空标签形式
    <router-view name="a" />
  </div>
</template>
复制代码

与此同时,咱们也要修改路由信息的JavaScript文件的一些内容

//routes.js
export default [
  {
    path: '/app/:id', //这里咱们就声明了一个路由上的参数
      components:{ //注意由于要展现多个组件,因此咱们这里定义为components
          default: App   //在没有给name属性的router-view中展现的组件
          a: Another  //在name属性为a的router-view中展现
      } ,  
    
  }
]
复制代码

路由导航守卫

当咱们触发一个导航时,全局前置守卫按照穿件的顺序调用,守卫是异步执行的。咱们能够经过守卫来进行一些拦截,好比只有登陆以后才能进入的一些页面或者使用的一些功能。

全局:路由守卫有三个钩子函数
函数名 做用
beforeEach 通常在这个守卫方法中进行全局拦截,好比必须知足某种条件(用户登陆等)才能进入路由的状况
beforeResolve 和beforeEach相似,区别是在导航被确认以前,同时在全部组件内守卫和异步路由组件被解析以后,解析守卫就被调用
afterEach 在全部路由跳转结束的时候调用这些钩子不会接受 next 函数也不会改变导航自己
beforeEnter 可直接定义在路由配置上,和beforeEach方法参数、用法相同,调用位置在beforeEach 和 beforeResolve

大致的用法以下

//index.js
import createRouter from './config/router' //先导入咱们写的路由信息

const router = createRouter()

router.beforeEach((to, from, next) >= { //to -> 跳转的路由 from -> 当前的路由即跳转的起始点,next为这次跳转的函数,调用他才能执行跳转
    if(若是没有登陆){
        next('/login')  //跳转到登陆页面
    }else if(没有注册) {
        next({path:'/register',replace:true});  //咱们也能够传递一个对象,replace设置为true就不会放在history的栈堆内
	}
})

router.beforeResolve((to, from, next) >= { 
    //do something
    next();
})

router.beforeEach((to, from) >= { 
    //do something
    next();
})
复制代码

在路由上设置beforeEnter

//router.js
export default [
  {
    path: '/app',
    component: Todo,
    name: 'app', // 给当前的路由设置一个姓名,能够用来跳转,与路径和组件名无强制联系
    meta: {
      title: 'this is app', // 与html的meta一样的效果
      description: 'author Reaper Lee'
    },
      beforeEnter (to,from,next) { //只有在进入路由以前调用
          //dosomething
      }
  }
]
复制代码
组件内路由守卫
函数名 做用
beforeRouteEnter 在渲染该组件的对应路由前调用,用法与参数和beforeEach相似, next须要被主动调用 ,此时实例并未被建立,不能使用this
beforeRouteUpdate 在当前路由改变,而且组件被复用时调用,此时实例已经建立,this能够访问实例,next须要被主动调用,不能传回调
beforeRouteLeave 导航离开该组件的对应路由时调用。能够访问组件实例this,next须要被主动调用,不能传回调
//comp.vue
export default {
    beforeRouteEnter (to, from, next) {
        //在这里咱们由于没有实例,因此不能用this,但咱们要用获取的参数,就在next里使用回调函数
        next( vm => {
            //在这里能够拿到参数
        })
    },
    beforeRouteUpdate (to,from, next){
      	next()  
    },
    beforeRouteLeave (to, from, next){ //咱们能够经过下面的代码来实现退出时的提示确认
        if(flobal.confirm('are you sure to leave?')){ 
            next()
        }
    }
    data() {
        return {
            
        }
    }
}
复制代码

异步组件

咱们路由若是有很是多,一次性经过webpack把所有打包进去会致使js文件变得异常的大,而且初次加载的时间会变得很是长,显然是不可取的。咱们可让对应不一样的路由只加载那一部分的组件的代码。

使用这部分功能咱们须要安装一个babel的插件

npm i babel-plugin-syntax-dynamic-import -D

以后在 .babelrc文件中咱们写入

"plugins": [
    "syntax-dynamic-import"
  ]
复制代码
//routes.js
//注意咱们不在开头import组件

export default [
  {
    path: '/app',
    component:() => { import('组件的路径')},  //注意要用一个函数
  }
]
复制代码

VueX

VueX 是基于Vue框架的一个状态管理工具

官网的介绍是:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

加入VueX

npm install vuex -S

以后我么你在项目的文件夹里建一个新的文件夹为stroe,并在内部建立stroe.js,他将做为整个数据存储的入口

//store.js
import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex)

export default () => {
	return	new Vuex.store({
            state:{  //储存的对象
                count: 0
            },
            mutations:{  //对state进行操做的方法
                updateCount(state,num){ //第一个参数都为state,第二个参数为你能够传的参数
                    state.count = num;
                }
            }
    })
}


复制代码

咱们须要在index.js引入Vuex

//index.js
import Vuex from 'vuex'
import createStore from './store/store'

const store = createStore()

new Vue({
  router,
  store,
  render: (h) => h(App)
}).$mount(root)
复制代码

到这里以后咱们就已经把Vuex引入了

使用Vuex

//app.vue
<script>
    export default {
        components:{
        },
        mounted () {
            this.$router //咱们就能够经过this.$store拿到所需的数据
            this.$store.commit('updateCount',参数) //咱们使用store的mutaitions时须要使用 commit方法,第一个参数
        }
    }
</script>
复制代码

state

咱们能够把state当作vue组件的data数据来使用

//state.js
export default {
    
}
复制代码

咱们也要在store里引入

//store.js
import defaultState from './state/state'

export default () => {
    return new Vuex.Store({
        state: defaultState,
        mutations: {
            updateCount(state,num){
                state.count = num;
            }
        }
    })
}
复制代码

咱们也能够把mutations也单独写个文件

//mutations.js
export default {
    updateCount (state, num){
        state.count = num;
    }
}
//store.js
import Vuex from 'vuex'

import defaultState from './state/state'
import mutations from './mutations/mutations'

export default () => {
    return new Vuex.Store({
        state: defaultState,
        mutations: mutations
    })
}

复制代码

getters

咱们能够把一些要直接在页面用的数据放在getters,而后能够直接方便使用

好比咱们在state.js里写入了咱们的名字信息

// state.js
export default {
    count: 0,
    firstName: 'Reaper',
    lastName: 'Lee'
}
复制代码

咱们在getters里写入获取全名的方法

//getters.js
export default {
    fullName (state) {
        return `${state.firstName} ${state.lastName}`
    }
}
复制代码

而后咱们能够经过Vuex提供的方法快速使用getters

//app.vue
<template>
	<p>
        {{fullName}}
    </p>
</template>


<script>
    export default {
        computed: {
            fullName () {
                return this.$store.getters.fullName
            }
        }
    }
</script>
复制代码

mutations

咱们通常把修改state值的一些方法放在mutation中,而后在组件中经过调用修改state

注意,mutation 只能传递两个参数,第一个是state对象,第二个是传参的对象,单个参数能够单独放入,若是要传递多个参数,则要放在一个对象中

其实咱们在组件中能够直接使用this.$store.state来进行修改,但这样不够规范,若是想防止这种状况就在实例化时将strict属性设置为true

export default () => {
  return new Vuex.Store({
    strict:true
  })
}

复制代码

actions

mutations只能同步操做,不能写入异步的代码,若是要执行异步操做,必需要写入actions中

例如咱们要根据传入的参数延时修改数据

//actions.js
export default {
    updateCountAsync (store, data) {
        setTimeout(() => {
            store.commit('updateCount',data.num) //触发mutations的修改事件
        }, data.time)
    }
}
复制代码

咱们在vue文件中触发actions的方法与mutations有所不一样,触发mutation使用commit,触发action使用dispatch

//app.vue
export default {
	...
    mounted() {
    	this.$store.dispatch('updateCountAsync',{ num:5,time:2000})
	}
}
复制代码

模块

咱们有时调用store要存在多种场景,须要划分做用域,这时咱们就要使用到Vuex的模块

//store.js
export default () => {
  return new Vuex.Store({
    modules: {
      a: {
         namespaced: true, //使用这个在不一样的模块中可使用相同命名的mutations
        state: {
          text :1
        },
          mutations: {
              updateText(state,text){
                  state.text = text;
              }
          },
          getters: {
          textPlus(state,getters,rootState){ //第二个参数是getter方法,第三个参数是全局state
          return state.text + 1
      		}
      	  },
      actions:{
          add({state,commit,rootState}){
              commit('updateText',rootState.count,{root:true}) //使用{root:true} 就可让rootState为全局
          }
      }
      },
      b: {
        text: 2
      }
    }
  })
}

复制代码

在vue文件调用时

//app.vue
computed: {
    textA() {
        return this.$store.state.a.text //调用a模块的text值
    },
    textB() {
        return this.$store.state.b.text //调用b模块的text值
    }
}
复制代码

Vuex热更替

咱们在使用vuex时会发现,每当咱们修改vuex内的内容再保存时,vue不会热更替显示内容,而是会刷新一下,WTF,这么好用的功能难道Vuex用不了:horse:?

固然不是,咱们只须要在store.js加入部分代码便可

//store.js

import defaultState from './state/state'
import mutations from './mutations/mutations'
import getters from './getters/getters'
import actions from './actions/actions'

export default () => {
    const store = new Vuex.Store({
     	state: defaultState,
    	mutations: mutations,
    	getters: getters,
        actions: actions,
    })
        
        if (module.hot) {
        module.hot.accept([
        	'./state/state',
            './mutations/mutations',
            './actions/actions',
            './getters/getters'
        ],() => {
            const newState = require('./state/state').default,
            const newMutations = require('./mutations/mutations').default
            const newGetters = require('./getters/getters').default
            const newActions = require('./actions/actions').default
            
            store.hotUpdate({
                state:newState,
                mutations:newMutations,
                actions:newActions,
                getters:newGetters
            })
        })
    }
    
    return store
}
复制代码

一些更多的有关Vuex的API 能够查看

服务端渲染

服务端渲染的优点:

  • 更好的 SEO,因为搜索引擎爬虫抓取工具能够直接查看彻底渲染的页面。
  • 更快的内容到达时间(time-to-content),特别是对于缓慢的网络状况或运行缓慢的设备。无需等待全部的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,因此你的用户将会更快速地看到完整渲染的页面。一般能够产生更好的用户体验,而且对于那些「内容到达时间(time-to-content)与转化率直接相关」的应用程序而言,服务器端渲染(SSR)相当重要。

但同时也要注意一些权衡之处:

  • 开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数(lifecycle hook)中使用;一些外部扩展库(external library)可能须要特殊处理,才能在服务器渲染应用程序中运行。
  • 涉及构建设置和部署的更多要求。与能够部署在任何静态文件服务器上的彻底静态单页面应用程序(SPA)不一样,服务器渲染应用程序,须要处于 Node.js server 运行环境。
  • 更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源(CPU-intensive - CPU 密集),所以若是你预料在高流量环境(high traffic)下使用,请准备相应的服务器负载,并明智地采用缓存策略。
相关文章
相关标签/搜索