高阳的技术专栏 gy

vue笔记

2018-07-15
gaoyang

阅读:

js

Vue 动态构建用户界面 渐进式JavaScript 框架

vue 特点

1.遵循MVVM模式

2.编码简洁, 体积小, 运行效率高, 适合移动/PC端开发

3.它本身只关注UI, 可以轻松引入vue插件或其它第三库开发项目

双向绑定原理

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。

我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。

https://www.jianshu.com/p/f194619f6f26详解

vue的原理分析

数据代理

1)数据代理: 通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)

2)vue数据代理: 通过vm对象来代理data对象中所有属性的操作

3)好处: 更方便的操作data中的数据

4)基本实现流程

a.通过Object.defineProperty()给vm添加与data对象的属性对应的属性描述符

b.所有添加的属性都包含getter/setter

c.getter/setter内部去操作data中对应的属性数据

模板解析

模板解析的基本流程

1)将el的所有子节点取出, 添加到一个新建的文档fragment对象中

2)对fragment中的所有层次子节点递归进行编译解析处理

* 对大括号表达式文本节点进行解析

* 对元素节点的指令属性进行解析

* 事件指令解析

* 一般指令解析

3)将解析后的fragment添加到el中显示

模板解析(1): 大括号表达式解析

1)根据正则对象得到匹配出的表达式字符串: 子匹配/RegExp.$1 name

2)从data中取出表达式对应的属性值

3)将属性值设置为文本节点的textContent

模板解析(2): 事件指令解析

1)从指令名中取出事件名

2)根据指令的值(表达式)从methods中得到对应的事件处理函数对象

3)给当前元素节点绑定指定事件名和回调函数的dom事件监听

4)指令解析完后, 移除此指令属性

模板解析(3): 一般指令解析

1)得到指令名和指令值(表达式) text/html/class msg/myClass

2)从data中根据表达式得到对应的值

3)根据指令名确定需要操作元素节点的什么属性

  • v-text—textContent属性

  • v-html—innerHTML属性

  • v-class–className属性

4)将得到的表达式的值设置到对应的属性上

5)移除元素的指令属性

数据绑定

一旦更新了data中的某个属性数据, 所有界面上直接使用或间接使用了此属性的节点都会更新

数据劫持

1)数据劫持是vue中用来实现数据绑定的一种技术

2)基本思想: 通过defineProperty()来监视data中所有属性(任意层次)数据的变化, 一旦变化就去更新界面

四个重要对象

1)Observer

a.用来对data所有属性数据进行劫持的构造函数

b.给data中所有属性重新定义属性描述(get/set)

c.为data中的每个属性创建对应的dep对象

2)Dep(Depend)

a.data中的每个属性(所有层次)都对应一个dep对象

b.创建的时机:

* 在初始化define data中各个属性时创建对应的dep对象 * 在data中的某个属性值被设置为新的对象时

c.对象的结构 js { id, // 每个dep都有一个唯一的id subs //包含n个对应watcher的数组(subscribes的简写) }

d.subs属性说明

 * 当watcher被创建时, 内部将当前watcher对象添加到对应的dep对象的subs中
* 当此data属性的值发生改变时, subs中所有的watcher都会收到更新的通知, 从而最终更新对应的界面

3)Compile

a.用来解析模板页面的对象的构造函数(一个实例)

b.利用compile对象解析模板页面

c.每解析一个表达式(非事件指令)都会创建一个对应的watcher对象, 并建立watcher与dep的关系

d.complie与watcher关系: 一对多的关系

4)Watcher

a.模板中每个非事件指令或表达式都对应一个watcher对象

b.监视当前表达式数据的变化

c.创建的时机: 在初始化编译模板时

d.对象的组成
js { vm, //vm对象 exp, //对应指令的表达式 cb, //当表达式所对应的数据发生改变的回调函数 value, //表达式当前的值 depIds //表达式中各级属性所对应的dep对象的集合对象 //属性名为dep的id, 属性值为dep }

5)总结: dep与watcher的关系: 多对多

 a.data中的一个属性对应一个dep, 一个dep中可能包含多个watcher(模板中有几个表达式使用到了同一个属性)

 b.模板中一个非事件表达式对应一个watcher, 一个watcher中可能包含多个dep(表达式是多层: a.b)

 c.数据绑定使用到2个核心技术
* defineProperty() * 消息订阅与发布

vue-cli 脚手架

   npm install -g vue-cli   //下载脚手架
   vue init webpack vue_demo   //创建脚手架 vue_demo
   npm run start             //开发环境
   npm run build             //生产环境 生成dist文件夹
   npm install -g serve      //运行 dist文件
   serve dist

vue生命周期

生命周期:Vue 实例从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期,各个阶段有相对应的事件钩子

生命周期钩子函数

avatar

avatar

注意:

created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态

mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick

详情请访问:

https://segmentfault.com/a/1190000014705819

http://web.jobbole.com/94470/

双大括号表达式

语法:

{ { exp } }

功能: 向页面输出数据

可以调用对象的方法

指令一: 强制数据绑定

功能: 指定变化的属性值

完整写法:

 v-bind:xxx='yyy'  //yyy会作为表达式解析执行

简洁写法:

:xxx='yyy'

指令二: 绑定事件监听

功能: 绑定指定事件名的回调函数

完整写法:

v-on:keyup='xxx'
v-on:keyup='xxx(参数)'
v-on:keyup.enter='xxx'

简洁写法:

@keyup='xxx'
@keyup.enter='xxx'

class 绑定

语法:

:class='xxx'

表达式是字符串:

'classA'

表达式是对象:

{classA:isA, classB: isB}

表达式是数组:

 ['classA', 'classB']

style 绑定

语法:

:style="{ color: activeColor, fontSize: fontSize + 'px' }"

其中activeColor/fontSize是data属性

条件渲染指令

语法:

<div v-if="bool">study vue</div>

示例:

  <div id="demo">
  <h2 v-if="ok">表白成功</h2>
  <h2 v-else>表白失败</h2>
  <h2 v-show="ok">求婚成功</h2>
  <h2 v-show="!ok">求婚失败</h2>
  <button @click="ok=!ok">切换</button>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  var vm = new Vue({
    el: '#demo',
    data: {
      ok: false
    }
  })
</script>

v-if是动态的向DOM树内添加或者删除DOM元素;

v-show是通过设置DOM元素的display样式属性控制显隐;

#v-for 列表渲染

遍历数组

 <li v-for="(p, index) in persons" :key="index">
      { { index } }--{ { p.name} } -- { { p.age } }
 </li>
  • 遍历对象
   < li v-for="(value, key) in persons">
      { { key } } : { { value } }
    < /li>

v-model

双向数据绑定 自动收集数据

v-on 绑定事件

语法:

v-on:xxx="fun"

简写:

@xxx="fun"

$nextTick

语法:

   this.$nextTick(() => {})

用法: 界面更新后执行回调函数 示例1:

component组件

   mounted () {
      this.$store.dispatch('getShopGoods', () => {
        this.$nextTick(() => {
          this._initScroll()
          this._intTops()
        })
      })
    }

   // 异步获取商品列表 action
   async getShopGoods ({commit},cb) {
      const result = await reqShopGoods()
    if (result.code === 0) {
      const goods = result.data
      commit(RECEIVE_GOODS,{goods})
      cb && cb()
    }
  }

通过dispach更新数据的时候可以传函数,在action中commit之后调用此函数,说明是在更新完数据之后才调用的,但是这是界面还没有更新完成,所以用$nextTick,这样界面更新后执行$nextTick里面的回调函数 此时在回调函数中执行的语句才会生效

示例2:

watch: {
      // 更新状态数据后, 界面会异步更新
      categorys (val) { // 状态数据改变了, 但界面还没有更新
        /* setTimeout(() => {
          new Swiper('.swiper-container', {
            pagination: { // 圆点指示器的容器div
              el: '.swiper-pagination'
            },
            loop: true, // 可以循环轮播
          })
        }, 100) */
        // 回调是在当前数据变化的DOM更新之后自动调用
        this.$nextTick(function () {
          new Swiper('.swiper-container', {
            pagination: { // 圆点指示器的容器div
              el: '.swiper-pagination'
            },
            loop: true, // 可以循环轮播
          })
        })
      }
    }

组件间通信

语法:

slot

  父组件:<slot name="left"></slot>

  子组件:<span class="header_search" slot="left">

示例:

子组件

  <template>
  <header class="header">
    <slot name="left"></slot>
    <span class="header_title">
      <span class="header_title_text ellipsis"></span>
    </span>
    <slot name="right"></slot>
  </header>
</template>

父组件

<HeaderTop :title="address.name">
      <span class="header_search" slot="left">
        <i class="iconfont icon-sousuo"></i>
      </span>
    <span class="header_login" slot="right">
        <span class="header_login_text">登录|注册</span>
      </span>
  </HeaderTop>

props

语法:

 <my-component :set-name='setName'>study vue</my-component>

方式一: 只指定名称

       props: ['name', 'age', 'setName']

方式二: 指定名称和类型

	   props: {
	     name: String,
	      age: Number,
	     setNmae: Function
	  }

方式三: 指定名称/类型/必要性/默认值

	 props: {
	   name: {type: String, required: true, default:xxx},
	 }

注意:

  1. 此方式用于父组件向子组件传递数据

  2. 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用

3.问题:

a.如果需要向非子后代传递数据必须多层逐层传递

b.兄弟组件间也不能直接props通信, 必须借助父组件才可以

vue自定义事件

语法: 方式一: 通过v-on绑定

     this.$emit('deleteTodo', data) //子组件中

     @deleteTodo="deleteTodo"   //父组件中  注意:必须写在子组件对应的组件标签上

方式二: 通过$on()

     this.$refs.xxx.$on('delete_todo', function (todo) {
     this.deleteTodo(todo)
         })

注意:

1.此方式只用于子组件向父组件发送消息(数据)

  1. 隔代组件或兄弟组件间通信此种方式不合适

消息订阅与发布(PubSubJS库)

npm install pubsub-js --save

订阅消息

    PubSub.subscribe('msg', function(msg, data){})

发布消息

    PubSub.publish('msg', data)

注意:优点: 此方式可实现任意关系组件间通信(数据)

vue-ajax

axios

执行 GET 请求

为给定 ID 的 user 创建请求

    axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

可选地,上面的请求可以这样做

   axios.get('/user', {
     params: {
       ID: 12345
      }
   })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

执行 POST 请求

    axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

执行多个并发请求

   function getUserAccount() {
    return axios.get('/user/12345');
   }

   function getUserPermissions() {
    return axios.get('/user/12345/permissions');
   }

   axios.all([getUserAccount(), getUserPermissions()])
    .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));

axios封装

ajax.js

import axios from 'axios'
const baseUrl = ''
// const baseUrl = 'http://localhost:4000'
export default function ajax(url, data={}, type='GET') {

  return new Promise(function (resolve, reject) {
    let promise
    // 执行异步ajax请求的代码
    url = baseUrl + url
    if(type==='GET') { // 发送GET请求
      // 拼请求参数串
      // data: {username: tom, password: 123}
      // paramStr: username=tom&password=123
      let paramStr = ''
      Object.keys(data).forEach(key => {
        paramStr += key + '=' + data[key] + '&'
      })
      if(paramStr) {
        paramStr =  '?' + paramStr.substring(0, paramStr.length-1)
      }
      // 使用axios发get请求
      promise = axios.get(url + paramStr)
    } else {// 发送POST请求
      // 使用axios发post请求
      promise = axios.post(url, data)
    }
    promise.then(response => {
      // 成功了, 调用 resolve
      resolve(response.data)
    }).catch(error => {
      // 失败了, 调用 reject
      reject(error)
    })
  })
}

index.js

定义接口请求函数//获取短信验证码

   export const reqSendCode = phone => ajax('/api/sendcode', {phone})

Mint UI 组件库

下载:npm install –save mint-ui

实现按需打包

  1. 下载
  npm install --save-dev babel-plugin-component

2.修改babel配置

   "plugins": ["transform-runtime",["component", [
    {
     "libraryName": "mint-ui",
     "style": true
    }
]]]

vue-router

下载: npm install vue-router --save

基本步骤:

1.分析是否需要 在哪需要 创建路由组件,然后创建路由组件

2.将我们创建的一般组件映射映射成路由组件

3.< router-link to=’/msite’/>

< router-view/>

示例:

 router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import MSite from '../pages/MSite/MSite.vue'
import Search from '../pages/Search/Search.vue'
import Order from '../pages/Order/Order.vue'
import Profile from '../pages/Profile/Profile.vue'
import Login from '../pages/Login/Login.vue'
import Shop from '../pages/Shop/Shop.vue'
import ShopGoods from '../pages/Shop/ShopGoods/ShopGoods.vue'
import ShopRatings from '../pages/Shop/ShopRatings/ShopRatings.vue'
import ShopInfo from '../pages/Shop/ShopInfo/ShopInfo.vue'

Vue.use(VueRouter)

export default new VueRouter({
  // 所有路由
  routes: [
    {
      path: '/msite',
      component: MSite,
      meta: {
        isShow: true
      }
    },
    {
      path: '/search',
      component: Search,
      meta: {
        isShow: true
      }
    },
    {
      path: '/Order',
      component: Order,
      meta: {
        isShow: true
      }
    },
    {
      path: '/profile',
      component: Profile,
      meta: {
        isShow: true
      }
    },
    {
      path: '/login',
      component: Login
    },

    {
      path: '/shop',
      component: Shop,
      children: [
        {
          path: '/shop/goods',
          component: ShopGoods
        },
        {
          path: '/shop/ratings',
          component: ShopRatings
        },
        {
          path: '/shop/info',
          component: ShopInfo
        },
        {
          path: '',
          redirect: '/shop/goods'
        },
      ]
    },

    {
      path: '/',
      redirect: '/msite'
    },

  ]
})

main.js

import Vue from 'vue'
import {Button} from 'mint-ui'
import App from './App.vue'
import router from './router' // <---
import store from './store'
import Split from './components/Split/Split.vue'
import './mock/mockServer' // 只需要引入即可

Vue.component(Button.name, Button)
Vue.component('Split', Split)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  render: h => h(App),
  router,      // <---
  store
})

router跳转

1.标签跳转

  <router-link to='/msite'/>

  <router-view/>

2.js动态跳转及传参

query方式传参和接收参数

传参:

   this.$router.push({
           path:'/xxx'
           query:{
             id:id
           }
         })

接收参数:

   this.$route.query.id

params方式传参和接收参数

传参:

this.$router.push({
       name: 'dispatch',
       params: {
       id:id
       }
})

接收参数:

this.$route.params.id

注意:params传参,push里面只能是 name:’xxx’,不能是path:’/xxx’,因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!

另外,二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示

vuex

概念:对vue应用中多个组件的共享状态进行集中式的管理(读/写)

avatar

4部分组成

action

mutations

getters

state

示例:

state.js

  export default {
  latitude: 40.10038, // 纬度
  longitude: 116.36867, // 经度
  address: {}, // 地址信息对象
  categorys: [], // 分类数组
  shops: [], //商家数组
  user: {}, // 用户信息对象
  goods: [], // 商品列表
  ratings: [], // 商家评价列表
  info: {}, // 商家信息
  cartFoods: [] // 购物项food数组
}

actions.js

 import {
  RECEIVE_ADDRESS,
  RECEIVE_CATEGORYS,
  RECEIVE_SHOPS

} from './mutation-types'

import {
  reqAddress,
  reqFoodTypes,
  reqShops
} from '../api'

export default {
  // 异步获取地址信息的异步action
  async getAddress ({commit, state}) {
    const {latitude, longitude} = state
    const geohash = `${latitude},${longitude}`
    // 发送异步请求, 得到响应数据
    const result = await reqAddress(geohash)  // {code: 0, data: address}
    if(result.code===0) {
      // 提交mutation
      const address = result.data
      commit(RECEIVE_ADDRESS, {address})
    }
  }

  // 同步更新指定food数量
  updateFoodCount ({commit}, {isAdd, food}) {
    if(isAdd) {
      commit(INCREMENT_FOOD_COUNT, {food})
    } else {
      commit(DECREMENT_FOOD_COUNT, {food})
    }
  },

  // 同步清空购物车
  clearCart ({commit}) {
    commit(CLEAR_CART)
  }
}

mutations.js

 import Vue from 'vue'
 import {
  RECEIVE_ADDRESS,
  RECEIVE_CATEGORYS
} from './mutation-types'

export default {
   [RECEIVE_ADDRESS] (state, {address}) {
    state.address = address
  },
 [RECEIVE_CATEGORYS] (state, {categorys}) {
    state.categorys = categorys
  }
}

mutation-types.js

export const RECEIVE_ADDRESS = 'receive_address' // 接收地址信息

export const RECEIVE_CATEGORYS = 'receive_categorys' // 接收分类数组

getters.js


export default {

    // 购物车中所有food的数量
  totalCount (state) {
    return state.cartFoods.reduce((preTotal, food)=> preTotal + food.count, 0)
  },
    // 购物车中所有food的总价格
  totalPrice (state) {
    return state.cartFoods.reduce((preTotal, food)=> preTotal + food.count*food.price, 0)
  }
}

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

main.js

import Vue from 'vue'
import {Button} from 'mint-ui'
import App from './App.vue'
import router from './router'
import store from './store'  //  <-----
import Split from './components/Split/Split.vue'
import './mock/mockServer' // 只需要引入即可


Vue.component(Button.name, Button)
Vue.component('Split', Split)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  render: h => h(App),
  router,
  store         //  <-----
})

获取状态 、 更新状态

方式一

import {mapState, mapGetters, mapActions} from 'vuex'
export default {
    computed: {
        ...mapState(['xxx'],['mmm']),
        ...mapGetters(['xxx'],['mmm']),
    }
    methods: {
       ...mapActions(['xxx'],['mmm'])
         }
}

方式二

 this.$store.state.xxx

 this.$store.getters.xxx

 this.$store.dispatch('action名称', data1)

dispatch用法

   this.$store.dispatch('allUnionInstitution')
               .then(() => {      })
               .catch() => {      })

注册全局组件

 import Split from './components/Split/Split.vue'

 Vue.component('Split', Split)

注册局部组件

export default{
    components: {
      AlertTip
    }
}

set

给状态添加属性值

语法:

Vue.set( food, 'count', 1 )
           说明: food  state里面的属性
                'count' 添加属性名
                 1  添加属性值

缓存路由组件对象

      <keep-alive>
        <router-view />
      </keep-alive>

好处: 复用路由组件对象, 复用路由组件获取的后台数据

路由组件懒加载

src/router/index.js

/*
import goods from 'pages/goods/goods.vue'
import ratings from 'pages/ratings/ratings.vue'
import seller from 'pages/seller/seller.vue'
*/
const goods = () => import('pages/goods/goods.vue')
const ratings = () => import('pages/ratings/ratings.vue')
const seller = () => import('pages/seller/seller.vue')

图片懒加载: vue-lazyload

2)下载包

npm install --save vue-lazyload

使用

//main.js
  import VueLazyload from 'vue-lazyload'
  import loading from './common/img/loading.gif'
  Vue.use(VueLazyload, {
    loading
  })
//component
  <img v-lazy="food.image">

better-scroll

中文地址:

http://ustbhuangyi.github.io/better-scroll/doc/zh-hans/#

下载

npm install better-scroll --save  //生产环境依赖

示例:

   <template>
       <div class="wrapper">
         <ul class="content">
           <li>111</li>
           <li>222</li>
           <li>333</li>
           <li>444</li>
         </ul>
       </div>
   </template>
   <script>
   import BScroll from 'better-scroll'
   export default {
     name: 'App',
     mounted () {
       new BScroll('.wrapper',{
         bounce: true,
         momentumLimitDistance: 1,
         scrollY: true,
         scrollbar: {
           fade: true,
           interactive: false
         },
         mouseWheel: true,
       })
     }
   }
   </script>
   <style scoped lang="less">
     li{
       height:400px;
       background: #ff0;
     }
     .wrapper{
       height:100%;
     }
   </style>

less

下载:

   npm install less less-loader --save-dev

安装成功后,打开 build/webpack.base.conf.js ,在 module.exports = 的对象的 module.rules 后面添加一段:

   module.exports = {
       //  此处省略无数行,已有的的其他的内容
       module: {
           rules: [
             //  此处省略无数行,已有的的其他的规则
             {
               test: /\.less$/,
               loader: "style-loader!css-loader!less-loader",
             }
           ]
       }
   }

最后,在代码中的 style 标签中 加上 lang=”less” 属性即可

  <style scoped lang="less">

  </style>

Monment

下载:

npm install moment --save   # npm

示例:

    watch:{
           flashSaleIndexVO(val){
             let time = val.remainTime
               const interval=setInterval(()=>{
                 time-=1000
                 const x= moment.duration(time); //得到的是对象
                 this.timer = x
                 if(time<=0){
                    clearInterval(interval)
                 }
               },1000)
           }
         },

示例:自定义事件过滤器

   import Vue from 'vue'
   import moment from 'moment'

   // 定义一个自定义过滤器
   Vue.filter('date-format', function (value, format='YYYY-MM-DD HH:mm:ss') {

     return moment(value).format(format)
   })

   < div>{ { flashSaleIndexVO.nextStartTime | date-format('HH:mm:ss') } } </div>

vue2.0 keep-alive

1.基本用法

vue2.0提供了一个keep-alive组件用来缓存组件,避免多次加载相应的组件,减少性能消耗

<keep-alive>
<component>
  <!-- 组件将被缓存 -->
</component>
</keep-alive

有时候 可能需要缓存整个站点的所有页面,而页面一般一进去都要触发请求的

在使用keep-alive的情况下

  <keep-alive><router-view></router-view></keep-alive>

将首次触发请求写在created钩子函数中,就能实现缓存,

比如列表页,去了详情页 回来,还是在原来的页面

2.缓存部分页面或者组件

(1)使用router.meta属性

// 这是目前用的比较多的方式
<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view

router设置


...
  routes: [
    { path: '/', redirect: '/index',  component: Index, mate: { keepAlive: true }},
    {
      path: '/common',
      component: TestParent,
      children: [
        { path: '/test2', component: Test2, mate: { keepAlive: true } }
      ]
    }
    ....
    // 表示index和test2都使用keep-alive

(2).使用新增属性inlcude/exclude

2.1.0后提供了include/exclude两个属性 可以针对性缓存相应的组件


<!-- comma-delimited string -->
<keep-alive include="a,b">
  <component :is="view"></component>
</keep-alive>
<!-- regex (use v-bind) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive

注意:这种方法都是预先知道组件的名称的

(2)动态判断

<keep-alive :include="includedComponents">
  <router-view></router-view>
</keep-alive

vue实现前进刷新,后退不刷新

  const router = new Router({
    routes: [
      {
        path: '/',
        redirect: '/login'
      },
      {
        path: '/login',
        component: Login,
        meta: {
          keepAlive: true
        }
      },
      {
        path: '/login/server',
        component: ServerList,
        meta: {
          keepAlive: true
        }
      },
      {
        path: '/login/server/main',
        component: Main,
        meta: {
          keepAlive: true
        }
      }
    ]
  })

由于这三个界面path的层级不同,我就能通过beforeEach这个钩子判断出什么时候是后退了。在后退时将from路由的keepAlive置为false,to路由的keepAlive置为ture。

  router.beforeEach((to, from, next) => {
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    if (toDepth < fromDepth) {
      console.log('后退。。。')
      from.meta.keepAlive = false
      to.meta.keepAlive = true
    }
    next()
  })

最后需要缓存的界面用keep-alive包起来,就能实现时前进刷新,后退时不刷新的效果了。

  <keep-alive>
            <router-view v-if="$route.meta.keepAlive">
              <!-- 这里是会被缓存的视图组件 -->
            </router-view>
          </keep-alive>

          <router-view v-if="!$route.meta.keepAlive">
            <!-- 这里是不被缓存的视图组件 -->
          </router-view>

vue+mockjs

下载

npm install mockjs

网址

http://mockjs.com/

mockServer.js

示例:

  /*
  使用mockjs提供mock数据接口
   */
  import Mock from 'mockjs'
  import data from './msite.json'


  Mock.mock('/headCateList', {code:0, data: data.headCateList})
  Mock.mock('/focusList', {code:0, data: data.focusList})
  Mock.mock('/tagList', {code:0, data: data.tagList})

在main.js入口文件里面引入mockServer.js就可以了(import ‘../src/mock/mockServer’)

vue解决跨域 代理

config/index.js

  proxyTable: {
       '/api': { // 匹配所有以 '/api'开头的请求路径
         target: 'http://localhost:4000', // 代理目标的基础路径
         changeOrigin: true, // 支持跨域
         pathRewrite: {// 重写路径: 去掉路径中开头的'/api'
           '^/api': ''
         }
       }
     }

api/index.js

  export const reqFoodTypes = () => ajax('/api/index_category')

Similar Posts

下一篇 缓存

Comments