vue项目基础用法教程(二)

创建项目

vue-cli脚手架生成项目

vue create 项目名称

配置参考

vue-router配置

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
我们可以访问其官方网站对其进行学习: https://router.vuejs.org/zh/

1. 安装vue-router(创建项目时若已勾选vue-router请忽略)

npm install vue-router --save

2. 创建路由实例

一般在src目录下创建router文件夹再创建index.js文件

import Vue from 'vue'
import VueRouter from 'vue-router'

// 注入插件
Vue.use(VueRouter)

// 定义公共路由
const routes = [
]

// 创建路由实例
const router = new VueRouter({
    // 配置单页应用的基路径
    base: '',
    // 路由模式 hash || history
    mode: 'hash',
    // 切换路由滚动条置顶
    scrollBehavior: () => ({
        y: 0,
    }),
    // 路由数组
    routes: routes
})

// 导出实例
export default router

3. 挂载到vue实例中

在main.js中引入router

import Vue from 'vue'
import App from './App.vue'
//导入router
import router from './router'
 
Vue.config.productionTip = false
 
new Vue({
  router, //挂载
  render: h => h(App)
}).$mount('#app')

4. 创建路由组件

在views目录下创建about.vue和home.vue两个组件
home.vue

<template>
  <div>
      <h2>我是首页标题</h2>
      <p>我是首页内容</p>
  </div>
</template>
 
<script>
export default {
    name: 'home'
}
</script>
 
<style>
 
</style>

about.vue

<template>
  <div>
      <h2>我是关于标题</h2>
      <p>我是关于内容</p>
  </div>
</template>
 
<script>
export default {
    name: 'about'
}
</script>
 
<style>
 
</style>

5. 配置组件和路径的映射关系

创建路由实例的index.js中创建了router实例,但是我们并没有配置路由间的映射关系

import Vue from 'vue'
import VueRouter from 'vue-router'
 
 
Vue.use(VueRouter)
 
const routes =  [
  {
    //默认首页
    path: '/',
    redirect: '/home'
  },
  {
    // 路由地址
    path: '/home',
    // 路由名称
    name:'Home', 
    // 采用异步引入方式
    component: () => import('@/views/home'),
    // 存储一些自定义参数
    meta:{
        title:'首页',
        hidden: false // menu是否显示此路由
    }
  },
  {
    path: '/about',
    name:'About',
    component: () => import('@/views/about'),
    meta:{
        title:'关于我们',
    }
  }
]
 
// 创建路由实例
const router = new VueRouter({
    // 配置单页应用的基路径
    base: '',
    // 路由模式 hash || history
    mode: 'hash',
    // 切换路由滚动条置顶
    scrollBehavior: () => ({
        y: 0,
    }),
    // 路由数组
    routes: routes
})
 
// 导出router实例
export default router

6. 使用路由

在App.vue中使用路由

<template>
  <div id="app">
    <!-- 
        1. :to="" 可以实现绑定动态的 路由 和 参数
        根据路由路径(/home)跳转 <router-link :to="{path: '/home', query:{id: 'abc'}}">点击查看子页面</router-link>
        根据路由名称(About)跳转 <router-link :to="{name: 'About', params:{id: 'abc'}}">点击查看子页面</router-link>
        另外还可以直接在path后面+?拼接key=value&key=value的方式直接传参,例<router-link to="/home?id=xxx&&name=xxxx">首页</router-link>
        备注:query和params为两种不同的传参方式,query会拼接在url地址后面刷新不会丢失,params在url上不会显示,刷新会丢失。
    --> 
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    
    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
  </div>
</template>
 
<script>
  export default {
    name: 'App',
    methods:{
        toHome(){
        }
    }
  }
</script>
 

7. 路由跳转方式

声明式

:to="" 可以实现绑定动态的 路由 和 参数
根据路由路径(/home)跳转 <router-link :to="{path: '/home', query:{id: 'abc'}}">点击查看子页面</router-link>
根据路由名称(detail)跳转 <router-link :to="{name: 'detail', params:{id: 'abc'}}">点击查看子页面</router-link>

编程式

this.$router.push({path: '/home',query:{id: 'abc'}})
this.$router.push({path: '/home',params:{id: 'abc'}})

params 和 query 传参的区别
1、params传参时,参数不会出现在url的路径上面,但是刷新页面时param里面的数据会消失
2、query传参时,参数出现在url的路径上面,刷新页面时query里面的数据不变

8. back && go 返回上一页

go(-1): 原页面表单中的内容会丢失

this.$router.go(-1):后退+刷新;
this.$router.go(0):刷新;
this.$router.go(1) :前进

back(): 原页表表单中的内容会保留

this.$router.back():后退 ;
this.$router.back(0) 刷新;
this.$router.back(1):前进

9.keep-alive的使用

keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现

  1. 利用meta标签
    首先在路由中的meta标签中记录keepAlive的属性为true
...
{
    path: '/about',
    name:'About',
    component: () => import('@/views/about'),
    meta:{
        title:'关于我们',
        keepAlive:true // 代表改页面需要缓存
    }
  }
...
  1. 在需要缓存的router-view组件上包裹keep-alive组件
<keep-alive>
   <router-view v-if='$route.meta.keepAlive'></router-view>
</keep-alive>
<router-view v-if='!$route.meta.keepAlive'></router-view>
  1. 使用include、exclude属性和beforeRouteEnter钩子函数

include是需要缓存的组件;
exclude是除了某些组件都缓存;
include 和 exclude
属性允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:
将需要缓存的组件加在include属性里
max 表示最大缓存路由个数

<keep-alive :include="['home','about']">
      <router-view></router-view>
</keep-alive>

备注:一般情况设置tab路由切换,访问一个路由push到访问历史访问数组中,过滤需要缓存的路由push到include

10. 路由钩子函数

  1. vue router.beforeEach(全局前置守卫)

to: (Route路由对象) 即将要进入的目标 路由对象 to对象下面的属性: path params query hash fullPath matched name meta
from: (Route路由对象) 当前导航正要离开的路由
next: (Function函数) 一定要调用该方法来 resolve 这个钩子。 调用方法:next(参数或者空) ***必须调用

应用场景:判断需要登录的页面进行拦截

router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) {
        //判断该路由是否需要登录权限
        if (cookies('token')) {
            //通过封装好的cookies读取token,如果存在,name接下一步如果不存在,那跳转回登录页
            next()//不要在next里面加"path:/",会陷入死循环
        }
        else {
            next({
                path: '/login',
                query:  { redirect:to.fullPath } //将跳转的路由path作为参数,登录成功后跳转到该路由
            })
        }
    }
    else {
        next()
    }
})
  1. vue router.afterEach(全局后置守卫)

router.beforeEach 是页面加载之前,相反router.afterEach是页面加载之后

  1. 组件内路由守卫

beforeRouteEnter(进入前)、beforeRouteUpdate(刷新)、beforeRouteLeave(离开时) 类似于组件内生命周期函数。

应用场景:清除当前组件中的定时器

beforeRouteLeave (to, from, next) {
  window.clearInterval(this.timer) //清楚定时器
  next()
}

应用场景:当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转

beforeRouteLeave (to, from, next) {
 //判断是否弹出框的状态和保存信息与否
 if (this.dialogVisibility === true) {
    this.dialogVisibility = false //关闭弹出框
    next(false) //回到当前页面, 阻止页面跳转
  }else if(this.saveMessage === false) {
    alert('请保存信息后退出!') //弹出警告
    next(false) //回到当前页面, 阻止页面跳转
  }else {
    next() //否则允许跳转
  }

应用场景:保存相关内容到Vuex中或Session中

beforeRouteLeave (to, from, next) {
    localStorage.setItem(name, content); //保存到localStorage中
    next()
}
  1. 实现缓存界面保存滚动位置
    简单方法
    router.js
// keepAlive是否需要保持页面,scrollTop记录页面的滚动位置
...
meta: {
  keepAlive: true,
  scrollTop: 0, // 用于保存滚动条位置
}
...

vue文件中

export default {
    data(){
      scrollTop: 0, // 储存滚动位置
    },
    activated() {
      // 进入该组件后读取数据变量设置滚动位置
      // 注意, 此处由页面是否具有 DTD (如: `<!DOCTYPE html>`), 
      // 决定具体选择, 详见参考资料
      document.documentElement.scrollTop = this.scrollTop;
      // document.body.scrollTop = this.scrollTop;
    },
    beforeRouteLeave(to, from, next) {
      // 离开组件时保存滚动位置
      // 注意, 此时需调用路由守卫`beforeRouterLeave`而非生命周期钩子`deactivated`
      // 因为, 此时利用`deactivated`获取的 DOM 信息已经是新页面得了
      this.scrollTop = document.documentElement.scrollTop;
      next();
    },
  }

router.js 全局保存方法

// keepAlive是否需要保持页面,scrollTop记录页面的滚动位置
...
meta: {
  keepAlive: true,
  scrollTop: 0, // 用于保存滚动条位置
}
...

router.beforeEach((to: Route, from: Route, next: () => void) => {  
  if (from.meta.keepAlive) {
  // 同意使用id = conetent外层包裹,且仅记录外层滚动条
  const $content = document.querySelector('#content');
  const scrollTop = $content ? $content.scrollTop : 0;
  from.meta.scrollTop = scrollTop;
 }
 next();
});

Vuex

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

1. 安装vuex

npm install vuex --save

2. 创建实例

为了项目格式便于维护和相对规范一点,目录下建立一个 store 文件夹,并且在下面建立一个 index.js 文件和 modules文件夹

index.js
用于导入modules文件夹所有文件

import Vue from 'vue'
import Vuex from 'vuex'
// 注入插件
Vue.use(Vuex)
const files = require.context('./modules', false, /\.js$/)
const modules = {}

files.keys().forEach((key) => {
  modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
Object.keys(modules).forEach((key) => {
  modules[key]['namespaced'] = true
})
const store = new Vuex.Store({
  modules,
})

导出实例
export default store

3. 挂载到vue实例中

在main.js中引入router

import Vue from 'vue'
import App from './App.vue'
//导入router
import router from './router'
//导入vuex
import store from './store'
 
Vue.config.productionTip = false
 
new Vue({
  router, //挂载
  store, //挂载
  render: h => h(App)
}).$mount('#app')

4.vuex store

在modules目录下创建user.js文件

  1. state 初始化全局可访问的数据
const state = () => ({
    username:''
})

vue页面中可使用 this.store.state.user.username 获取参数

  1. getters 可以实时监听state值的变化
const getters = {
     username: (state) => state.username,
}

vue页面中可使用 this.store.getters['user/username'] 获取参数
或者

import { mapGetters } from 'vuex'
 export default {
  computed: {
    ...mapGetters({
      username: 'user/username',
    }),
  },
}
  1. mutations 用于修改state值,一般都是action中调用
const mutations = {
    setUsername(state, username) {
        state.username = username
    },
}

vue页面中可使用 this.store.getters['user/username'] 获取参数

  1. Action 提交的是 mutation,而不是直接变更状态。
    Action 可以包含任意异步操作。
const actions = {
    async getUserInfo({ commit, state }) {
    //调取用户信息接口
    const { data } = await getUserInfo(state.accessToken)
    if (!data) {
     // 失败操作
      return false
    }
    let { username } = data
    // 修改用户名的值
    commit('setUsername', username);
  },
}

vue页面中调用 this.$store.dispatch('user/getUserInfo')

  1. 最后导出方法
export default { state, getters, mutations, actions }

user.js 完整代码

// 引入封装的接口
import { getUserInfo } from "@/api/user.js"
const state = () => ({
    username:''
})
const getters = {
     username: (state) => state.username,
}
const mutations = {
    setUsername(state, username) {
        state.username = username
    },
}
const actions = {
    async getUserInfo({ commit, state }) {
    //调取用户信息接口
    const { data } = await getUserInfo(state.accessToken)
    if (!data) {
     // 失败操作
      return false
    }
    let { username } = data
    // 修改用户名的值
    commit('setUsername', username);
  },
}
export default { state, getters, mutations, actions }

补充Vue用法

  1. vue页面中通过v-for进行数据渲染,如果层次太多,有时候数据发生改变但是页面上看到的效果是毫无变化。render函数没有自动更新,可以通过this.$forceUpdate()手动刷新.

原因:对象添加属性未使用this.set()赋值 解决:使用this.forceUpdate();

vm.$forceUpdate()

迫使Vue实例重新渲染,但仅影响实例本身和插入插槽内容的子组件。

vm.$set(target, propertyName/index, value)

官方推荐我们给对象中添加一个属性的时候用 this.$set(target, propertyName/index, value)

  1. 我们更改了某个dom元素内部的文本,而这时候我们想直接打印出这个被改变后的文本时需要dom更新之后才会实现的,也就好比我们将打印输出的代码放在setTimeout(fn, 0)中,此时可以使用this.$nextTick()

vm.$nextTick()

this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。

示例1:

<template>
  <section>
    <div ref="hello">
      <h1>Hello World ~</h1>
    </div>
  </section>
</template>
<script>
  export default {
    methods: {
      get() {
      }
    },
    // created生命周期进行时Dom节点还没有渲染完成。
    created() {
      console.log(this.$refs['hello']); // undefined
      this.$nextTick(() => {
        console.log(this.$refs['hello']);// <div>...</div>
      });
    },
    // mounted生命周期进行时Dom节点已经渲染完成。
    mounted() {
      console.log(this.$refs['hello']);// <div>...</div>
      this.$nextTick(() => {
        console.log(this.$refs['hello']);// <div>...</div>
      });
    }
  }
</script>

示例2:

<template>
  <section>
    <h1 ref="hello">{{ value }}</h1>
    <el-button type="danger" @click="get">点击</el-button>
  </section>
</template>
<script>
  export default {
    data() {
      return {
        value: 'Hello World ~'
      };
    },
    methods: {
      get() {
        this.value = '你好啊'; 
        console.log(this.$refs['hello'].innerText); // Hello World ~
        this.$nextTick(() => {
          console.log(this.$refs['hello'].innerText); // 你好啊
        });
      }
    },
    mounted() {
    },
    created() {
    }
  }
</script>

vm.$refs

当元素通过ref绑定后,可以通过 this.$refs.refName 获取这个dom节点。

示例:

<template>
    <ul>
        <li v-for="(item,index) in  list" :key="index"  ref="list">
            {{item}}
        </li>
    </ul>
</template>
<script>
export  default {
    data(){
        return{
            list:[1,2,4,5,6,7,8]
        }
    },
    mounted(){
        console.log(this.$refs.list)
        //添加随机颜色
        this.$refs.list.forEach((v)=>{
        v.style.color ='#' + Math.floor(Math.random()*1000000)
        })
    }
}
</script>

ref 加在子组件上,用 this.$refs.name,获取到的是组件实例,可以使用组件的所有方法。

示例:

<!-父组件--->
<template>
  <div>
    <child ref="child" ></child>
  </div>
</template>

<script>
import child from './child'
export default {
  components:{
    child
  }
  mounted(){
      this.$refs.child.getData() // 调用
  }
}

<!-子组件--->
<template>
  <div></div>
</template>

<script>
export default {
  methods:{
      getData(){
          console.log('父组件调用了我的方法')
      }
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容