VUE 2.0入门知识点

VUE 2.0

社区资料:https://www.vue-js.com/

vue官方手册:https://cn.vuejs.org/v2/api/

1.入门

1.1 简介

  • vue.js是一套构建用户界面的渐进式框架
  • vue只关注视图层,采用自底向上增量开发的设计。
  • Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
  • vue是基于MVVM设计模式,支持数据的双向绑定。

MVVM:model view viewmodel

MVC:model模型,数据层、view视图、controller控制器

1.2 vue安装

1 下载vue.js直接引入

<script src="vue.js"></script>

2 CDN加载

https://cdn.staticfile.org/vue/2.2.2/vue.min.js

3 npm 安装

npm install vue -g
npm install vue-cli -g

通过
npm run dev 启动项目(2.0)   npm run serve(3.0)
npm run build 打包项目

1.3 基本使用

<div id="app">
    <h2>{{msg}}</h2>
</div>
<script>
        const vm = new Vue({
            el:'#app',//el为vue的作用范围
            //data为vue的数据
            data:{
                msg:'hello vue.js'
            },
            //定义函数方法
            methods:{

            }
        })
</script>

2.指令(重点)

2.1 vue插入值

v-html

v-text

v-html和v-text区别?

  • v-html相当于innerHTML,可以识别标签和文本,表单提交的时候不能使用,容易导致XSS攻击
  • v-text相当于txtContent,只可以识别文本
<div id="app">
    <!-- 方式一:采用{{}} -->
    <p>{{msg}}</p>

    <!-- vue的指令 v-... -->
    <!-- 方式二:v-text -->
    <p v-text='msg'></p>

    <!-- 方式三:v-html -->
    <p v-html='msg'></p>
</div>

<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>    
    new Vue({
        //选项
        //el代表的是element 元素
        el:'#app',
        data:{
            msg:'学习VUE'
        }
    })
</script>

2.2 vue条件语句

v-if

v-else

v-else-if

v-if  //给出布尔值,true就显示,false就隐藏
v-else
v-else-if
<div id="app">
    <p v-if="active">明天下雨吗</p>
    <p>明天是大晴天</p>

    <button v-on:click="change">切换</button>
    <p v-if="active">珊瑚宫</p>
    <p v-else>心海</p>

    <p v-if="star>=6">五郎</p>
    <p v-else-if="star>=4">托马</p>
    <p v-else>凌人</p>
</div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>    
    new Vue({
    //选项
    //el代表的是element 元素
    el:'#app',
    data:{
        active:false,
        star:5
    },
    //方法
    methods:{
        change:function(){
            this.active=!this.active
        }
    }
})
</script>

效果:

2.3 vue显示隐藏

v-if

v-show

v-if 和 v-show的区别?

  • v-if直接操作DOM,隐藏之后直接把标签移除
  • v-show通过display:none来隐藏元素
<p v-if="true">珊瑚宫心海</p>
<p v-show='false'>反抗军五郎</p>

2.4 vue循环遍历

v-for

<div id="app">
  <div v-for="items in arr">
      <p>{{items}}</p>
  </div>
  <hr>
  <div v-for="items in obj">
      <p>{{items}}</p>
  </div>
</div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>    
    new Vue({
    //选项
    //el代表的是element 元素
    el:'#app',
    data:{
        arr:['胡桃','甘雨','行秋'],
        obj:{
            name:'七七',
            stars:5,
            attr:'冰属性'
        }
    }
})
</script>

效果:

image-20210922203850045

游览器中查看代码源码,是没有我们写的vue代码存在的

image-20210922203959607

2.5 vue样式绑定

v-bind

在vue中,我们采用v-bind来绑定属性和class样式

class属性的绑定,可简写为‘ :’

.bgColor{
    background-color: rgb(110, 209, 240);
    width: 200px;
    height: 50px;
    line-height: 50px;
    text-align: center;
}
.txt{
    color: white;
}
<div id="app">
    <!-- 方式一:传入对象,多个样式之间采用逗号连接 -->
    <p v-bind:class="{bgColor:isActive,txt:isActive}">珊瑚宫心海</p>

    <!-- 方式二:传入数组,多个样式之间采用逗号连接 -->
    <p v-bind:class=[bg,txt]>反抗军五郎</p>

    <!-- 方式三(不推荐):内联样式 -->
    <p v-bind:style="{color:'white',background:'lightblue',fontSize:'20px'}">雷电将军</p>
</div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>
    new Vue({
    //选项
    //el代表的是element 元素
    el:'#app',
    data:{
        isActive:true,
        bg:'bgColor',
        txt:'txt'
    }
})
</script>

效果:

image-20210922204405512

注意:在v-bind:绑定属性和class样式时,可以简写为’:'

2.6 vue事件处理

v-on

数量加减案例:

<p><button v-on:click="num--">-</button>{{num}}<button v-on:click="num++">+</button></p>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>    
    new Vue({
    //选项
    //el代表的是element 元素
    el:'#app',
    data:{
        num:1
    }
})
</script>

效果:

点击切换样式案例:

ul li{
    width: 200px;
    height: 50px;
    line-height: 50px;
    background: rgb(126, 196, 243);
    text-align: center;
    color: white;
}
.bgColor{
    background: rgb(245, 146, 17);
}
<div id="app">
    <!-- 事件绑定 -->
    <ul>
        <li :class="{bgColor:isActive}" @click="change()">
            {{num}}<button @click.stop="num++">+</button>
        </li>
    </ul>
</div>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script>    
    new Vue({
    //选项
    //el代表的是element 元素
    el:'#app',
    data:{
        num:1,
        isActive:false,
    },
    methods:{
        change(){
            this.isActive=!this.isActive
        }
    }
})
</script>

效果:

注意:v-on:可以简写为’@'

事件修饰符

.stop 阻止事件冒泡行为
.prevent 阻止事件默认行为
.capture 使用事件捕获模式
.self 阻止事件委派(只能当前元素触发事件而不是子元素)
.once 事件只触发一次
<!-- 阻止事件冒泡 -->
<div class="father" v-on:click="fatherSay">
    <div class="son" v-on:click.stop="sonSay"></div>
</div>

<!-- 阻止事件默认行为 -->
<a href="http://www.mi.com" v-on:click.prevent="sonSay">跳转小米</a>

<!-- once只触发一次 -->
<button v-on:click.once="num+=1">点击一次</button>

按键修饰符

.enter  回车键
.tab    tab键
.delete (捕获 "删除" 和 "退格" 键)
.esc    ESC键
.space  空格键
.up     上键
.down    下键
.left    左键
.right   右键
.ctrl    Ctrl键
.alt     alt键
.shift   shift键
.meta    meta键

2.7 vue双向绑定

v-model

vue中采用v-model进行数据双向绑定

<!-- 输入框 -->
<h2>输入框</h2>
输入框:<input type="text" v-model.lazy="msg">
<p>输入的内容为:{{msg}}</p>

<!-- 文本域 -->
<h2>文本域</h2>
<textarea name="" id="" cols="30" rows="10" v-model="msg2"></textarea>
<p>文本域输入内容为:{{msg2}}</p>

<!-- 单选框 -->
<h2>单选框</h2>
<input type="radio" name="sex" v-model="sex" value=""><input type="radio" name="sex" v-model="sex" value=""><input type="radio" name="sex" v-model="sex" value="保密">保密
<p>单选框选中的为:{{sex}}</p>

<!-- 复选框 -->
<h2>复选框</h2>
<p>你最想吃啥:</p>
<input type="checkbox" name="foods" value="重庆火锅" v-model="foods">重庆火锅
<input type="checkbox" name="foods" value="冰淇淋" v-model="foods">冰淇淋
<input type="checkbox" name="foods" value="麻辣小龙虾" v-model="foods">麻辣小龙虾
<input type="checkbox" name="foods" value="辣条" v-model="foods">辣条
<p>复选框选中的为:{{foods}}</p>

<!-- 下拉框 -->
<h2>下拉框</h2>
<select name="city" id="" v-model="city">
<option value="武汉">武汉</option>
<option value="成都">成都</option>
<option value="美国">美国</option>
<option value="意大利">意大利</option>
</select>
<p>下拉框选中的内容为:{{city}}</p>


const vm = new Vue({
    el:'#app',
    data:{
    	msg:'',
    	msg2:'',
    	sex:'男',
    	foods:['麻辣小龙虾','辣条'],//复选框需要传入一个数组
    	city:'',
    	age:'',
    	msg2:''
    }
})

表单修饰符

.lazy 输入框再change事件中同步
.number 将用户输入的内容自动转换为number类型
.trim 将去除用户输入的内容的首尾空格
关于getter/setter方法
1.当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项 (图中就是 a 对象中的 b 属性,即 a.b),Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter 方法。
2. 用户看不到 getter/setter 方法,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
3. 同时 Vue 会对模板进行编译,解析之后会生成指令对象,也例如 v-text, v-hide 等。当指令中 v-text='a.b’时,其实就是触发 getter 方法,获取对应的数据。

2.8 自义定组件

Vue.directive(指令的名字,{指令执行的方法})

Vue.directive('focus', {
    // 当绑定元素插入到 DOM 中。
    inserted: function (el) {
        // 聚焦元素
        el.focus()
    }
})

3.安装node环境和vue环境

node环境:

https://note.youdao.com/s/dXarCr8e

vue环境:

https://note.youdao.com/s/bR6pMdUO

4.计算属性

在vue内计算属性的关键词为:computed

<div id="app">
    <!-- 调用computed的方法时,不需要加小括号 -->
    <p>{{splitStr}}</p>
</div>

const vm = new Vue({
    el:'#app',
    data:{
    	str:'你们现在看过招聘信息吗'
    },
    computed:{
    	// 定义一个拆分字符串的方法  所有的计算操作都放到了vue的实例中
    	splitStr(){
    		return this.str.split('')
    	}
    }
})

methods和computed的区别

我们可以使用 methods 来替代 computed,效果上两个都是一样的。

但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。

而使用 methods ,在重新渲染的时候,函数总会重新调用执行。

5.实例方法/数据

vm.$watch 监听数据的变化
vue.$set 为数组和对象添加指定的值
vue.$delete 为数组和对象删除指定的值

5.1 vm.$watch

vue中watch属性可以去响应数据的变化

方式一:全局监听
监听的内容发生改变则触发函数
vm.$watch(监听的内容,function(){
    
})

方式二:局部监听   推荐使用
  watch:{
      //写法一:不能深度监听
      items(){
		  Storage.save('todoList',this.items)
	  }
      //写法二(重点)
	  // 当items发生改变则自动调用handler函数
	  items:{
		  // 必须为handler函数
		  handler(){
			  Storage.save('todoList',this.items)
		  },
              deep:true//深度监听,可检测到数组中某个对象属性的变化
	  }
  }

5.2 vue.$set

语法:vue.$set( target, propertyName/index, value )
 
 this.$set(obj,'isFinished',true);

5.3 vm.$delete

vue.$delete( target, propertyName/index )

this.$delete(this.items,i)

6.组件

组件(component)是vue中最强大的功能之一。可以拓展html标签,可以封装可重用性的代码。组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。

组件的划分:根据结构划分、根据功能划分

6.1组件的使用

1、定义组件,并将组件导出
(在src/components文件夹内新建一个.vue后缀的文件作为组件)
每一个.vue文件由templete、script、style构成
注意:子组件内style可以写上scoped属性,限制当前的css样式只对当前的组件生效
组件的名称一般首字母大写

2、引入组件
语法:import 自定义组件名 from '组件路径'
注意:
    1、若导入的文件名为vue后缀则可以省略(vue中可以自动识别.vue文件后缀)
    2、导入的路径可以写绝对路径(@默认代表项目中的src目录)

3、在局部components内注册组件(多个组件之间采用逗号连接)
  components: {
        Head:Head,
	Content:Content
  }
  简写为:
  components: {
    Head,Content
  }


4、使用组件(相当于使用标签)
方式一:
<自定义的组件名 />(建议使用)

方式二
<自定义的组件名><自定义的组件名 />

注意:写成双标签的形式,一般标签内不能写任何html代码

6.2.组件传值(重点)

1 父组件向子组件传值

父组件向子组件使用props传值

步骤:

1)、在父组件App.vue内使用v-bind:绑定一个属性

<First :send="msg"/>

2)、在子组件内定义props接收自定义属性(和父组件定义的属性同名)

export default {
    name:"First",
    data(){
        return{
        }
    },
    //方法一:用数组获取值
    //    , props:['send']

    //方法二:用对象获取值
    props:{
        send:String,
    }
}

2 子组件向父组件传值

子组件向父组件传值需要使用自定义事件

使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件

步骤:

1)、在子组件内通过emit触发一个自定义事件

export default {
    name:"First",
    data(){
        return{
            info:'123456' 
        }
    },
    created(){
        this.$emit('asinfo',this.info);// (事件名,参数)
    }
}

2)、在父组件内监听自定义的事件

<First @asinfo="info"/>
methods:{
    info(data){
        console.log(data)
    }
}

6.3.组件中插槽slot的使用

	在子组件内使用特殊的元素<slot></slot>就可以为子组件开启一个slot(插槽)。在父组件的模板内,插入在子组件标签内的所有内容将代替子组件的<slot>标签及其内部的内容
1 单个插槽的使用

父组件:

<template>
  <div id="app">

    <First :send="msg" @asinfo="info">
      <p>插槽的使用</p>    
    </First>

  </div>
</template>

子组件:

<template>
    <div id="First">
        
        <h1>这是一个组件</h1>
        <p>{{send}}</p>
        <slot></slot>
        
    </div>
</template>

image-20210927174828340

2 命名插槽的使用

父组件:

<template>
    <div id="app">

        <First :send="msg" @asinfo="info">
            <p slot="last">插槽的使用222222</p>  
            <p slot="first">插槽的使用11111</p>
        </First>

    </div>
</template>

子组件:

<template>
    <div id="First">
        
        <slot name="last"></slot>
        <h1>这是一个组件</h1>
        <slot name="first"></slot>
        <p>{{send}}</p>

    </div>
</template>

image-20210927180316846

7.全局配置

全局配置需要写在main.js中的new Vue之前,引入Vue之后

所有的全局配置的属性都在Vue.config

// 自定义按键修饰符
//main.js的写法
Vue.config.keyCodes.f2 = 113;

//在app.vue中使用
<input type="text" @keyup.f2 = "getMsg">

8.全局API

Vue.extend 
Vue.nextTick
Vue.set 
Vue.delete
Vue.directive 自定义指令
Vue.filter 过滤器
Vue.component 注册组件
Vue.use 注册插件
Vue.mixin
Vue.compile
Vue.observable
Vue.version

8.1过滤器

作用:将数据格式化成所需要的格式:比如时间、价格

1 局部过滤器

filters 只作用于当前组件,

<template>
  <div id="app">
	单价:<input type="text" v-model="price"><br>
	数量:<input type="number" v-model="num"><br>
	<h2>总计:{{price*num | formatMoney}}</h2>
	<h2>总计:{{price*num | money}}</h2>
  </div>
</template>

export default {
  name: 'App',
  data(){
	return{
		price:15,
		num:1
	}  
  },
  filters:{
	  // 局部过滤器
	  money(value){
		  return '$'+value.toFixed(2)+'美元';
	  }
  }
}
注意:'|'叫做管道符
管道符前面的为需要格式化的数据
管道符后为格式化数据的过滤器方法,该方法会自动传入需要格式化的数据
可以出现多个管道符,比如{{a | guan1 | guan2 | guan3}},依次把前面值传给|后面的管道函数

2 全局过滤

Vue.filter('全局过滤函数名',过滤方法) 任意组件都可以使用

main.js中定义:
Vue.filter('formatMoney',function(value){
	// value自动获取管道符之前的内容
	return '¥'+value.toFixed(2)+'元';
})

//组件中的使用
<template>
  <div id="app">
	单价:<input type="text" v-model="price"><br>
	数量:<input type="number" v-model="num"><br>
	<h2>总计:{{price*num | formatMoney}}</h2>
  </div>
</template>

8.2 格式化时间插件的使用

安装插件

npm install moment --save-dev

引入插件

import moment from 'moment';

定义过滤器

filters:{
    // dateStr不需要手动传入  由管道符自动传入
    formatDate(dateStr,pattern='YYYY/MM/DD HH:mm:ss'){
        return moment(dateStr).format(pattern)
    }
}

在组件内使用过滤器

<template>
  <div id="app">
	<h2>{{time | formatDate('YYYY年MM月DD日 HH时mm分ss秒')}}</h2>
	<h2>{{time | formatDate}}</h2>
  </div>
</template>

9.生命周期

生命周期函数也叫做钩子函数,可以自动绑定this到上下文,可以访问数据(data)、方法(methods)、计算(computed)等

注意:不能使用箭头函数定义生命周期的方法

常见的生命周期函数:

分为四大类:

1、创建前后 beforeCreate created

2、挂载前后 beforeMount mounted

3、更新前后 beforeUpdate updated

4、销毁前后 beforeDestroy destroyed

beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

created:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前尚不可用。

beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。

mounted:实例被挂载后调用,这时 el 被新创建的 vm. e l 替 换 了 。 如 果 根 实 例 挂 载 到 了 一 个 文 档 内 的 元 素 上 , 当 m o u n t e d 被 调 用 时 v m . el 替换了。 如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm. elmountedvm.el也在文档内。

注意: mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick

beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性watcher 取而代之。

beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

10.路由的使用

官方手册:https://router.vuejs.org/zh/

10.1 静态路由

手动配置一个静态路由:

安装路由

命令:vue create 项目名

注意:初始化时 Install vue-router需要选择yes

先创建一个空的没有配置路由的vue项目

vue create router3

安装路由

npm i vue-router --save

在src目录下创建两个空的文件夹

router:里面放路由的配置文件index.js

views:里面放需要显示的视图界面

image-20210928185731707

在index.js中引入路由文件并配置

import Vue from 'vue';
//1.下载并导入路由文件
import Router from 'vue-router';
//2.使用路由插件
Vue.use(Router);

//4.配置路由
const routes=[
    {

    },
]

// 3.实例化并导出路由
export default new Router({
    routes//这里写法为ES6的简写,原写法为‘routes:routes’
})

在views文件夹下写上两个视图文件

image-20210928191235558

<template>
<div class="home">
    <p>这是首页</p>
    </div>
</template>
<template>
    <div class="about">
    <p>这是关于页</p>
    </div>
</template>

再回到index.js配置路径和component的映射关系

import Vue from 'vue';
//1.下载并导入路由文件
import Router from 'vue-router';
//2.使用路由插件
Vue.use(Router);

//导入首页要显示的内容
import Home from '@/views/Home.vue'

//4.配置路由
const routes=[
    {
        path:'/',
        name:'Home',
        component:Home
    },
    {
        path:'/about',
        name:'About',
        component:()=> import ('@/views/About.vue')
    },
    
]

// 3.实例化并导出路由
export default new Router({
    routes//这里写法为ES6的简写,原写法为‘routes:routes’
})

在main.js里面导入和挂载路由

import Vue from 'vue'
import App from './App.vue'
//引入路由的配置文件index.js,这里是简写的,vue会自动帮我们寻找index.js文件
import router from './router'

Vue.config.productionTip = false

new Vue({
  //挂载路由
  router,
  render: h => h(App),
}).$mount('#app')

最后在App.vue中使用配置好的路由文件

<template>
  <div id="app">
   <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>

    <!--路由视图 -->
    <router-view/>
  </div>
  
</template>

<script>

export default {
  name: 'App',
}
</script>

<style lang="less">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>

运行结果:

image-20210928193150985

image-20210928193213758

对上述功能的总结:

image-20210928195813541

除了上述的手动创建之外,项目开发的时候应该在创建vue项目的时候配置自动安装好默认的路由文件。

<router-link to=""></router-link>路由的跳转(相当于超链接)
属性to代表跳转的地址(写上路由配置表内配置的路径)

<router-view></router-view>路由出口
将匹配的路由路径对应的组件替换掉router-view的内容

在router内index.js配置如下:
import Login from '@/components/login'
import Register from '@/components/register'
路由文件的配置
  routes: [
    {
		path: '/',//定义路由的路径   /为默认路径
		component: HelloWorld//定义当前路径匹配到的组件
    },
	{
		path:'/login',
		component:Login
	},
	{
		path:'/register',
		component:Register
	}
  ]

10.2 动态路由

1 小案例起步

先在首页编写好界面

<template>
    <div class="home">
        <p>这是首页</p>
        <ul>
            <li v-for="item in goods" :key="item.id">
                <p><img :src="item.path" ></p>
                <p>{{item.name}}</p>
                <p>{{item.price}}</p>
            </li>
        </ul>
    </div>
</template>
<script>
export default{
	name:'Home',
    data(){
    return {
        goods:[
            {
                path:require('@/assets/images/1.jpg'),
                name:'鹌鹑蛋',
                price:16
            },
            {
                path:require('@/assets/images/2.jpg'),
                name:'哇哦零食',
                price:16
            },
            {
                path:require('@/assets/images/3.jpg'),
                name:'奶糖',
                price:11.8
            },
            {
                path:require('@/assets/images/4.jpg'),
                name:'原切牛肉',
                price:53
            },
            {
                path:require('@/assets/images/4.jpg'),
                name:'原切牛肉',
                price:53
            },

        ]
    }
}
}
</script>
<style lang="less">
    ul{width: 1200px;margin: 0 auto; display: flex;flex-wrap: wrap;}
	li{width: 300px;height: 350px; border: 1px solid red; box-sizing: border-box;}
</style>

效果:

image-20210929114241591

编写简单的路由跳转

router目录下的index.js

image-20210929124147606

Home.vue

image-20210929124206972

效果:

把当前点击的商品参数传递到详细页

方法一:使用path和query传递

image-20210929125116586

详细页接收参数:

image-20210929125148570

效果:

image-20210929125224989

方式二:通过name和param传递参数(推荐)

image-20210929125550948

在路由配置里面配置动态参数,name也是路由配置里面标记的名字

image-20210929125641869

接收参数

image-20210929125722529

效果:

image-20210929125510173

当前点击的商品的信息传递到详细页之后,应该在data数据里面定义一个变量接收,然后再在界面上显示出来,所以代码做了如下的更改:

<template>
    <div class="detail">
        <p>这是详细页</p>
        <!-- <p>{{this.$route.query}}</p> -->
        <p><img :src="good.path" ></p>
        <p>{{good.name}}</p>
        <p>{{good.price}}</p>	
    </div>
</template>
<script>
    export default{
        name:'Detail',
        data(){
            return{
                good:this.$route.params.item
            }
        }
    }
</script>


所以最终效果为:

2 设置全屏显示

在原有views视图页的基础上增加一个视图来显示全屏

image-20210929184226886

<template>
    <div class="whole">  
        <p>这是全屏显示的界面</p>
    </div>
</template>
<script>
    export default{
    }
</script>
<style lang="less">

</style>

把App.vue里面只留一个视图出口:

<template>
    <div id="app">
        <!--路由视图 -->
        <router-view/>
    </div>
</template>

把控制路由跳转的代码复制到Home.vue中

<template>
    <div class="home">
        <div id="nav">
            <router-link to="/">Home</router-link> |
            <router-link to="/about">About</router-link> |
            <router-link to="/whole">全屏</router-link>
        </div>
        <p>这是首页</p>

        <ul>
            <li v-for="item in goods" :key="item.id">
                <!-- <router-link :to="{path:'/detail',query:{item}}"> -->
                <router-link :to="{name:'Detail',params:{item}}">
                    <p><img :src="item.path" ></p>
                    <p>{{item.name}}</p>
                    <p>{{item.price}}</p>
                </router-link>
            </li>
        </ul>
    </div>
</template>

这时候看效果已经可以实现全屏显示了

但是这是我们可以发现路由跳转这段代码比较多,而且在开发小程序时这段代码会在多个view视图中使用到,所以我们可以把这段代码写成一个组件。

image-20210929190035626

然后在首页和关于页导入组件即可

<template>
    <div class="about">
        <TabBar></TabBar>
        <p>这是关于页</p>
    </div>
</template>
<script type="text/javascript">
    import TabBar from '@/components/TabBar.vue'
    export default{
        name:'About',
        components:{
            TabBar
        }
    }	
</script>

3 捕获所有404路由

如果需要设置一个默认的404界面,只需要在路由配置的最后一个位置加上如下代码即可:

{
      // 匹配所有的路径  一般作为404页面  放在所有的路径之后
	  path:'*',
	  name:'Error',
	  component: () => import('../views/Error.vue')//需要手写一个404界面
}

4 嵌套路由

接下来给全屏显示的界面写一个可点击切换的路由选项

首先写好界面:

image-20210929192403551

image-20210929191713540

image-20210929191724718

现在配置子路由:

{
    	path:'/whole',
   	 	name:'Whole',
    	component:()=> import ('../views/Whole.vue'),
        children:[
            {
                path:'/beijing',
                name:'Beijing',
                component:()=>import ('../views/Beijing.vue')
            },
            {
                path:'/shanghai',
                name:'Shanghai',
                component:()=>import ('../views/Shanghai.vue')
            },
        ]
}

image-20210929192734133

效果:

子路由的格式跟父路由类似,但是写成上述方式在界面上就会出现上面的效果途中显示的问题:直接进入主界面时没有加载默认需要显示的内容

现在只需要修改要默认显示加载的界面的path的值为空。

image-20210929193918389

但是建议最好写成如下方式:

image-20210930102451342

效果:

以上操作出现了bug:点击’北京’选项没反应

还需用v-bind来绑定需要跳转的界面,在对象里面写上响应的name属性,不然点击’北京’选项不会有反应。

<template>
    <div class="whole">  
        <p>这是全屏显示的界面</p>

        <router-link :to="{name:'Beijing'}">北京</router-link>|
        <!-- <router-link to="/shanghai">上海</router-link> -->
        <router-link :to="{name:'Shanghai'}">上海</router-link>
        <router-view></router-view>
    </div>

</template>

最终效果:

写在最后:

子路由是可以层层嵌套的,比如:

{	
    path:'',
    name:'',
    component:'',
        chlidren:[
             	path:'',
   			 	name:'',
    			component:'',
                     chlidren:[
                        path:'',
                        name:'',
                        component:'',
                	]
        ]
}

5 编程式导航

回顾所有可以实现跳转的方法:

HTML:

<a href="#"></a>

JavaScript:

//location对象
window.location.href='url'//属性 有历史记录
window.location.assign('url')//方法 有历史记录
window.location.replace('url')//方法 无历史记录
//history对象
history.forword()//前进 不带参数
history.back()//后退 不带参数
history.go()//参数 1,2,3.. 前进1,2,3...条记录
			//参数 -1,-2,-3... 后退1,2,3...条记录

Vue:(相当于HTML中用a标签来实现跳转)

<router-link to="/login"></router-link>

<router-link :to="{path:'',query:{}}"></router-link>
//接收数据
this.$route.query.item

<router-link :to="{name:'',params:{}}"></router-link>
//接收数据
this.$route.params.item

Vue编程式导航:(相当于JavaScript来实现跳转)

router.push()

// 字符串
this.$router.push('home')

// 对象
this.$router.push({ path: 'home' })

// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
this.$router.push({ path: 'register', query: { plan: 'private' }})

router.replace()

router.replace()和router.push()的使用方式相同,但是router.replace不会向history内添加历史记录

router.go()

router.go()方法相当于window.history.go方法,可以访问到指定的某条历史记录信息

router.go(-1)回到上一条历史记录

登录注册跳转问题

image-20210929203845368

6 命名路由

通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在routes配置中给某个路由设置名称

const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'User',
      component: User
    }
  ]
})

router-link使用命名路由跳转

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

router.push()使用命名路由跳转

router.push({ name: 'user', params: { userId: 123 }})

7 命名视图

官方对命名视图的解释:

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

测试案例:

先写一个新的视图

<template>
    <div class="side">
        <p>side</p>
    </div>
</template>

<script>
</script>

<style lang="less">
    .side{
        position: relative;
        p{
            position: absolute;
            right: 0;
            top: 100px;
            width: 50px;
            height: 200px;
            background: pink;
        }
    }
</style>

配置命名视图:

image-20210930132412663

使用命名视图:

image-20210930132447112

效果:

image-20210930132519307

8 重定向和别名

重定向

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

甚至是一个方法,动态返回重定向目标:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

别名

/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

const router = new VueRouter({
    routes: [
        { path: '/a', component: A, alias: '/b' }
    ]
})

9 路由组件传参

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

布尔模式

商品展示界面传递id的动态参数

<template>
    <div class="home">
        <TabBar></TabBar>
        <p>这是首页</p>
        <ul>
            <li v-for="(item ,index) in goods" :key="item.id">
                <!-- <router-link :to="{path:'/detail',query:{item}}"> -->
                <router-link :to="{name:'Detail',params:{id:index}}">
                    <p><img :src="item.path" ></p>
                    <p>{{item.name}}</p>
                    <p>{{item.price}}</p>
                </router-link>
            </li>
        </ul>
    </div>
</template>

路由配置动态参数和开启props解耦

{
    path:'/detail/:id',
    name:'Detail',
    component:()=> import ('../views/Detail.vue'),
    props:true
}
<template>
	<div class="detail">
		<p>这是详细页</p>
		<p>{{id}}</p>
	</div>
</template>
<script>
	export default{
		name:'Detail',
		props:['id'],//注意props要和data(){}是同一级的
		data(){
			return{
			
			}	
		}
	}
</script>

效果:现在点击了每个商品之后,都能够将相应的商品id传递到详细页面。

注意:对于包含命名视图的路由,你必须分别为每个命名视图添加 props 选项

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

对象模式

{
    path:'/detail/:id',
    name:'Detail',
    component:()=> import ('../views/Detail.vue'),
    // props:route=>({params:route.params.id})
    props:{id:true}
}
<template>
	<div class="detail">
		<p>这是详细页</p>
		<p>{{id}}</p>		
	</div>
</template>
<script>
	export default{
		name:'Detail',
		props:['id'],
		data(){
			return{
					
			}	
		}
	}
</script>

image-20210930171239252

通过这个结果可以看出,对象模式只能传布尔值

如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。

const router = new VueRouter({
  routes: [
    {
      path: '/promotion/from-newsletter',
      component: Promotion,
      props: { newsletterPopup: false }
    }
  ]
})

函数模式

{
        path:'/detail/:id',
        name:'Detail',
        component:()=> import ('../views/Detail.vue'),
		props:route=>({params:route.params.id})//如果使用的是query传参则改为query
}
<template>
	<div class="detail">
		<p>这是详细页</p>
		<p>{{params}}</p>
		
	</div>
</template>

<script>
	export default{
		name:'Detail',
		props:['params'],
		data(){
			return{
				
			}	
		}
	}
</script>

效果:

image-20210930153651685

你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

const router = new VueRouter({
  routes: [
    {
      path: '/search',
      component: SearchUser,
      props: route => ({ query: route.query.q })
    }
  ]
})

URL /search?q=vue 会将 {query: 'vue'} 作为属性传递给 SearchUser 组件。

请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应。

10.历史记录模式

vue-router默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载

如果不想要很丑的 hash,我们可以用路由的 history 模式

const router = new VueRouter({
  mode: 'history',//历史记录模式
  routes: [...]
})

11.导航守卫

全局前置守卫

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})
  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

全局后置钩子

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

导航守卫例子:

https://blog.csdn.net/qq_26769677/article/details/101003337

12.更多进阶功能

路由元信息、过渡效果、数据获取、滚动行为等见官网:

https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

11.Vuex

1 入门

vuex是为vue.js专门提供的状态管理模式

简单解释:将所有组件公用的数据、方法提取到指定的地方,进行统一管理

2 安装

下载vuex

npm i vuex --save

image-20211008165446298

在src根目录中新建一个store文件夹并新建一个index.js文件,并在index.js中引入vue和vuex

import Vue from 'vue'
//导入vuex插件
import Vuex from 'vuex'

//使用vuex插件
Vue.use(Vuex)

export default new Vuex.Store({
	state:{//state相当于普通组件中的data数据域
		
	},
	getters:{//getter相当于computed对象
		
	},
	mutations:{//mutations相当于methods对象
		
	},
	actions:{
		
	},
	modules:{//分割模块
		
	}
})

在main.js中导入index.js文件并挂载

image-20211008165749159

3 核心概念的使用

3.1 state

state为vuex中的公共状态,我们可以看成state为所有组件的data,用于保存所有组件的公共数据·。

export default new Vuex.Store({
	state:{//state相当于普通组件中的data数据域
		names:['胡桃','甘雨']
	}
})

在任意组件内可以使用this.$store.state.names来获取到state里面的数据

 <p>{{this.$store.state.names}}</p>

image-20211008170817020

3.2 getters

vuex的getters属性可以理解为所有组件的computed属性,也就是计算属性.getters的返回值会根据其依赖缓存起来的,只有当依赖的值发生改变,getters才会重新计算

export default new Vuex.Store({
	state:{//state相当于普通组件中的data数据域
		names:['胡桃','甘雨'],
		num:5
	},
	getters:{//getter相当于computed对象
		add(state){//state的数据会自动传入add的方法
			return state.num+5
		}
	}
})

在任意的组件内使用this.$store.getters.add调用计算的方法

<p>{{this.$store.state.names}}</p>
<p>{{this.$store.getters.add}}</p>

3.3 mutations

vuex中的mutations可以理解为所有组件的methods方法。mutations对象中保存了更改数据的回调函数。第一个参数为state,第二个参数为payload,相当于自定义参数

export default new Vuex.Store({
	state:{//state相当于普通组件中的data数据域
		names:['胡桃','甘雨'],
		num:5
	},
	getters:{//getter相当于computed对象
		add(state){
			return state.num+5
		}
	},
	mutations:{//mutations相当于methods对象
		add(state,payload){//注意:必须传入state参数,payload为自定义参数
			state.num+=payload;
		}
	}
})

在任意组件内通过this.$store.commit()触发mutations内的方法

<template>
	<div id='Home'>
        <p>{{this.$store.state.names}}</p>
        <p>{{this.$store.getters.add}}</p>
        <p><input type="text"  v-model="this.$store.state.num"/> <span @click="add()">+</span></p>
    </div>
</template>

<script>
export default{
	name:'Home',
	methods:{
		add(){
            // this.$store.commit('evnetName',自定义的参数)
			this.$store.commit('add',5)
		}
	}
}
</script>

每一次点击都会给state里面的num数据加5

3.4 actions

actions和mutations类似,不同点在于:

1、actions提交的是mutations,而不是直接变更状态

2、actions中包含异步,mutations中绝对不允许出现异步

3、actions中回调函数的第一个参数为context,得到一个与store具有相同属性和方法的对象

export default new Vuex.Store({
	state:{//state相当于普通组件中的data数据域
		names:['胡桃','甘雨'],
		num:5
	},
	getters:{//getter相当于computed对象
		add(state){
			return state.num+5
		}
	},
	mutations:{//mutations相当于methods对象
		add(state,payload){//注意:必须传入state参数,payload为自定义参数
			state.num+=payload;
		}
	},
	actions:{
		addSync(context,payload){
			setTimeout(()=>{
                //add为mutations内定义的函数
                //通过commit调用mutations内的函数
				context.commit('add',payload)
			},1000)
		}
	},
})

组件内绑定事件

<p><input type="text"  v-model="this.$store.state.num"/> <span @click="addSync()">+</span></p>

通过 this.$store.dispatch调用actions内的异步方法

methods:{
		addSync(){
			this.$store.dispatch('addSync',2)
		}
	}

测试的效果就是每次点击之后,要过1s才会改变state里面的数值num

3.5 modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

在组件内分模块进行访问

<h2>{{this.$store.state.a.count}}</h2>
<h2>{{this.$store.state.b.msg}}</h2>

4 辅助函数的使用

mapState、mapGetters、mapMutations、mapActions

引入

import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'

在computed中使用扩展运算符进行定义

export default {
  name: 'list',
  data () {
    return {
    }
  },
  computed:{
    	  ...mapState({
		  //Index则代表state的num属性
		index:'num'
	  }),
	  ...mapGetters({
		  // 属性值add则代表getters内的add方法
		  add:'add'
	  }) 
  },
  methods:{
     ...mapMutation({
		  addNum:'addNum'
	  }),
	  ...mapActions({
		  
	  }) 
  }
}

5 vuex好文档推荐

参考资料:https://vuex.vuejs.org/zh/

https://www.jianshu.com/p/a804606ad8e9

组件间通讯八种方式:https://blog.csdn.net/zhoulu001/article/details/79548350

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值