Vue2总结
一、Vue核心
官网:https://cn.vuejs.org/
1. Vue简介
1.1 介绍和描述
Vue是一套用来动态构建用户界面的渐进式JavaScript
框架
1.2 vue特点
- 遵循MVVM模式
- 编码简介,体积小,运行效率高,适合移动/pc端开发
- 本身只关注UI,可以引入其他第三方库开发项目
- 采用组件化模式,提高代码复用率、且让代码更好维护
1.3 SPA页面
单页面应用程序,也就是整个应用只有一个html文件
优点
- 即时性
- 不需要加载整个页面就可以修改内容
- 页面之间的切换不会出现白屏的现象
- 服务器压力小
缺点
1 2 3 4
| - 首次加载耗时比较多(因为首次需要一次性加载完所有的资源) - 不利于 SEO - CSS 命名冲突 - 前进后退功能复杂度较高
|
2. Vue初识
2.1 模板语法
1. 插值语法
1 2 3 4 5 6 7 8 9
| 描述:在标签之间使用后 {{}} 进行插值,页面渲染后,{{}} 内的语法会替换为 data 中的数据。
应用:
- 直接渲染:<h1>{{msg}}</h1> - 简单的加减乘除:{{num + 1}} - 调用函数:{{new Date() }} - 三元运算符:{{条件?真:假 }}
|
注意:不能使用 if else 、switch、for
2. v-bind
描述:用于解析标签
应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <p v-bind:title="myTitle"></p> <p v-bind:id="'list' + myId">{{msg}}</p>
<p :title="myTitle"></p> <p :id="'list' + myId">{{msg}}</p>
<p :class="{active: isActive}"></p> <p :class="{active: isActive, text-danger: hasError}"></p> <p :class="[class1, class2]">数组 class</p> <p :class="[isActive ? 'danger' : 'success']">三元运算</p> <p :style="myStyle">style绑定对象</p> <p :style="[myStyle1, myStyle2]">style绑定对象</p>
|
3. v-once
描述:表示只执行一次插值,当数据改变后,插值处的内容不会更新
应用:<p v-once>{{msg}}</p>
4. v-text 和 v-html
描述:类似于 innerText
和 innerHTML
应用:<p v-text="myText"></p>
<p v-html="myText"></p>
5. template模板编译
vue中的模板template无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所有需要将template转化成一个JavaScript函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段:
- 解析阶段: 使用大量的正则表达式对template字符串进行解析,将标签、指令、属性等转化为抽象语法树AST。
- 优化阶段: 遍历AST,找到其中的一些静态节点并进行标记,方便在页面重渲染的时候进行diff比较时,直接跳过这一些静态节点,优化runtime的性能。
- 生成阶段: 将最终的AST转化为render函数字符串。
2.2 数据绑定
1. 单向绑定 v-bind
v-bind
数据只能从 data 流向页面
2. 双向绑定 v-model
v-model
数据不仅能从 data 流向页面,还可以从页面流向 data
<input v-model="text" />
相当于
<input :value="text" @input="text = $event.target.value"/>
组件绑定v-model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <Component v-model='data'></Component>
<template> <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > </template>
<script> export default { name:'Component', model:{ prop: 'checked', event: 'change' }, props:{ checked:Boolean }, data() { return { } } }
|
2.3 MVVM模型
1. 模型介绍
M:模型Model,data中的数据
V:视图View,模板代码
VM:视图模型ViewModel,Vue实例
总结:
- data中所有的属性,最后都会出现在 vm 身上
- vm 身上所有的属性及 Vue原型 身上所有的属性,在Vue 模板 中都可以直接使用
2. 响应式原理
Object.defineproperty
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var obj = { name: 'jack' } Object.defineProperty(obj, 'age', { configurable: true, enumerable: true, get: function () { return 20 }, set: function () {
} }) console.log(obj);
|
原理:
vue是采用数据劫持配合发布者-订阅者的模式的方式,通过Object.defineProperty()来劫持各个属性的getter和setter,在数据发生变动时发布消息给订阅者,触发相应的监听回调。具体为:对data中的属性进行递归遍历,并将之转化为getter和setter,同时watcher实例对象会在组件渲染时将属性添加到Dep中,负责管理数据的依赖列表,compile解析模板指令,生成指令对象,然后数据更新时就会触发setter,这时候相应的watcher就会重新计算,如果值确实发生变化了,就会通知相应的指令,调用指令的update方法,由对DOM做更新,这就实现了数据驱动DOM的变化。同时vue还会对DOM做事件监听,如果DOM发生变化,vue监听到,就会修改相应的data。
2.4 条件渲染
1. v-if
<button v-if="isShow">按钮</button>
2. v-else-if v-else
v-else-if 必须跟在 v-if 或者 v-else-if 的后面
同时控制多个节点的渲染
第一个节点
第二个节点
第三个节点
3. v-show
<p v-show="isShow"></p>
4. 区别
- v-if 是真正的渲染,会有插入节点和移除节点的过程
- v-show 任何情况下都会被渲染,只是切换 display 属性
- v-if 有比较高的切换开销,而 v-show 有更高的初始化渲染开销
2.5 列表渲染
1. v-for
用于展示列表数据,可用于遍历数组,对象
<li v-for="(item, index) in items" :key="index">
key 属性唯一值,从而重用和重新排序现有元素,建议使用唯一id
2.6 事件处理
1. v-on
使用 v-on:xxx 或者 @xxx 绑定事件,xxx为事件名称
事件的回调需要配置在methods对象中,最终挂载到 vm 组件实例对象上
<button v-on:click="sayHi"></button>
简写<button @click="sayHi"></button>
1 2 3
| methods: { sayHi() {} }
|
传递参数并注入事件对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <button v-on:click="getOther2(123, $event)">传递参数及事件对象</button>
{ data() {
}, methods:{ getOther2(num, e) { console.log(e, num) console.log(arguments) } } }
|
2. 修饰符
事件修饰符
- .stop 阻止事件冒泡(常用)
- .precent 阻止默认事件(常用)
- .once 事件只触发一次(常用)
- .self 事件只在元素本身触发,子元素不会触发
- .sync 字组件可修改父组件的传的数据,实现简单的双向绑定
1 2 3 4 5 6
| <zi :price.sync="price"/>
<div>子{{price}}</div> <button @click="$emit('update:price', price - 100)">花钱</button>
|
键盘修饰符
2.7 自定义指令
1. 局部指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { data() {}, directives: { 'big-number'(element,binding) { element.innerText = binding.value * 10 }, focus: { inserted: function (el) { el.focus() } } } }
<span v-big-number="n"></span>
|
2. 全局指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Vue.directive('fbind', { bind(element, binding) { element.value = binding.value }, inserted(element, binding) { element.focus() }, update(element, binding) { element.value = binding.value } })
|
3. Vue核心
3.1. 脚手架
1. 安装
npm install -g @vue/cli
2. 检查安装
vue --version
3. 搭建项目
vue create 项目名
根据需求进行项目配置
4. 启动项目
cd 项目名称
npm run serve
3.2 组件化编程
1. 概念
组件就是一个提供特定功能的一段代码,包含html,css,js,用来实现局部功能的代码和资源的集合。
2. 作用
解决代码高耦合、低内聚、无重用的问题。
3. 单文件组件
该组件是一个文件,以.vue结尾
1 2 3 4 5 6 7 8 9 10 11
| <template> 这里编写html代码,并且只能有一个根标签 <template/> <script> 这里编写js代码 <script/> <style> 这里编写css代码 <style/>
|
注:data 必须是一个函数,如果组件中 data 写为对象,那么每一个组件的data都会互相影响。
4. 使用组件
- 创建子组件
- 通过import from 引入子组件
- 注册子组件
- 在 template 中直接当做标签使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import MyModal from './MyModal.vue'
components: { MyModal}, <MyModal />
import MyModal from './MyModal.vue' Vue.component('MyModal', MyModal)
<MyModal />
|
5. 动态组件
概述:渲染一个元组件,根据is 的值来决定渲染那一个组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <el-button @click="changeLogin">切换</el-button> <component :is="whichWay"></component>
import QrUser from '../components/QrUser.vue' import UserAccount from '../components/UserAccount.vue'
export default { name: 'LoginKeep', data() { return { whichWay: 'UserAccount' } }, components: { UserAccount, QrUser }, methods: { changeLogin() { if(this.whichWay === 'UserAccount') { this.whichWay = 'QrUser' } else { this.whichWay = 'UserAccount' } } } }
|
6. 组件缓存
概述:通过 keep-alive 组件去包裹组件,那么会将当前组件缓存,切换组件时就不会销毁组件了。
语法:
1 2 3 4 5 6 7 8 9 10 11
| <keep-alive> 组件 </keep-alive>
<keep-alive include="UserAccount"> <component :is="whichWay"></component> </keep-alive>
|
3.3 组件间通信
1. props
概述:props 是父组件传递给子组件的数据,子组件只能使用,而不能修改。
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <TodoMvc title="代办列表"></TodoMvc> </template>
<template> <div class="todo"> <h2>{{title}}</h2> </div> </template>
<script> export default { name: 'TodoMvc', props: ['title'] } </script>
|
第二种接受数据类型
1 2 3 4 5 6 7
| props: { name: { type: String, required: true, default: 'cess' } }
|
2. $emit
概述:子组件向父组件传递数据。
实现方法:父组件向子组件传递方法,子组件通过$emit去触发这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| // 父组件 <template> <MyBrotherA @add="addText" /> </template>
<script> import MyBrotherA from './MyBrotherA.vue'
export default { name: 'MyFather', components: { MyBrotherA }, data() { }, methods: { addText(text) { console.log('接收到子组件传递的数据:',text);
this.list.push(text); } } } </script>
// 子组件 <template> <input type="text" v-model="text" placeholder="子组件A 输入框" @keydown.enter="addChild"> </template>
<script> export default { name: 'MyBrotherA', data() { return { text: '' } }, methods: { addChild() { // 父组件给子组件添加了一个自定义事件,事件名为 add,那么该事件可以在子组件中通过 $emit 去触发 this.$emit('add', this.text); } } } </script>
|
3. .sync
概述:字组件可修改父组件的传的数据,实现简单的双向绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // 父组件 <template> <div> <buttn @click="show">点我显示子组件</button> // 引入的子组件 <child :isShow="isShow" @closeShow="closeShow" v-show="!show"></child> // 相当于 <child :isShow.sync="isShow" @closeShow="closeShow" v-show="!show"></child> </div> </template>
// 子组件 <template> <div> <button @click="changeShow" v-show="show">点我隐藏自己</button> // 相当于 <button @click="$emit('update:isShow',isShow)" v-show="show">点我隐藏自己</button> <div> </template>
|
4. EventBus
概述:又称为中央事件总线,作为Vue 组件沟通的桥梁,所有的组件会共同使用一个事件中心,可以向该中心去注册事件或者发送事件。
1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
import Vue from 'vue' export default new Vue()
import Bus from './Bus.js'
exoprt default { created() { Bus.$on('send', function(arg) { }) }, beforeDestroy() { Bus.$off('text'); } }
import Bus from './Bus.js'
export default { methods: { sendMsg() { Bus.$emit('send', 参数) } } }
|
2. 优缺点
优点:
缺点:
- 时间必须成对出现(监听,触发)
- 事件只能单向传递
- 事件监听须在事件触发之前完成
- 一定要在组件销毁之前取消事件监听,否侧会出现多次监听的效果
5. Vuex
概述:集中式存储管理当前应用的所有组件状态,保证状态以一种可预测的方式进行变化。
应用场景:任何两个组件甚至是多个组件之间传递数据。
1. store
概述:每一个Vue实例只能包含一个store
实例,该store
相当于一个仓库,所有需要跨组件通信的组件都从store
中读取数据,修改数据的时候也通过store
提供的方法来修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { }, getters: { }, mutations: { }, actions: { }, modules: { } })
|
2. state
理解:作为store中的数据,所有跨组件的数据都可以放在这里
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| state: { num: 0, msg:'hi' },
this.$store.state.num
import {mapState} from 'vuex'
export default { computed: mapState({ num: state => state.num, numStr: 'num', }), computed: { ...mapState([ 'num' ]) } }
|
3. getters
理解:可以认为是Vuex
的计算属性,getters
的返回值会根据他的依赖被缓存
起来,只有他的以来发生了改变才会重新被计算。
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| getters: { doubleNum(state) { return state.num * 2 }, countNumN(state) { return (n) => state.num * n } },
this.$store.getters.doubleNum
export default { computed: { ...mapState([ 'num' ])} }
|
4. mutations
理解:修改store
中的数据必须通过mutations
修改
该函数内部只能是同步操作
,不能有其他与数据修改无关的操作,比如请求、计时器、本地存储等等。
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| mutations: { increment(state) { state.num += 1; }, incrementPayload(state, payload) { state.num += payload; } },
this.$store.commit('increment') this.$store.commit('incrementPayload', 5)
methods: { ...mapMutations([ 'increment',
'incrementPayload' ]), ...mapMutations({ add: 'increment' }) }
|
5. actions
理解:可以用于异步操作
、或者其他非纯函数操作
(本地存储),当异步完成后,可以去提交 mutations
,从而改变状态。
actions
不能修改数据,但是可以在actions中国提交mutation
。
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| actions: { incrementAsync(context) { setTimeout(() => { context.commit('increment') }, 1000) }, incrementAsyncP(context, payload) { setTimeout(() => { context.commit('incrementPayload', payload) }, 1000) } },
this.$store.dispatch('incrementAsync'); this.$store.dispatch('incrementAsyncP', 5)
methods: { ...mapActions(['incrementAsync']), } this.incrementAsync() { ... }
|
6. modules
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import Vue from 'vue' import Vuex from 'vuex' import serviceOrder from './create'
Vue.use(Vuex)
export default new Vuex.Store({ state: { }, getters: { }, mutations: { }, actions: { }, modules: { serviceOrder, banner: bannerStore, } })
export default { namespaced:true state:{} }
computed: { ...mapState( { tableData: state => state.serviceOrder.tableData, total: state => state.serviceOrder.total, count: state => state.serviceOrder.count }),
methods: { ...mapActions({ getData: 'serviceOrder/getDataAction', findOrder: 'serviceOrder/searchOrderAction', findService: 'serviceOrder/searchServiceAction', findCounselor: 'serviceOrder/searchCounselorAction', changeTab: 'serviceOrder/changeTabAction' }), }
|
6. $root
所有组件通过 this.$root 访问根组件。
7. $parent
子组件通过 this.$parent 可以获取父组件实例
8. $children
父组件通过 this.$children 获取子组件实例
9. $ref
获取 DOM 节点 或者 组件实例的
3.4 生命周期
概述:每一个Vue实例被创建时都要经历的一系列的初始化过程,在这个过程中自动运行的一系列的函数。
1. vue生命周期
- beforeCreate:实例初始化完成后,在数据观测和事件配置之前调用,在当前阶段 data、methods等还无法被访问
- created:实力已经创建,可以访问data,但是不能访问dom节点,可以用于在这里发送请求
- beforeMount:在节点挂载之前调用,此时页面呈现的都是未经Vue编译的Dom结构。在这里对所有的DOM的操作,都是不奏效的。
- mounted:节点挂载完成后调用,可以访问data,也可以访问节点,可以用于在这里发送请求。
- beforeUpdate:数据更新之前调用
- updated:发生在更新之后。注意:不能再此修改数据,因为修改数据后又会导致该函数调用,就会陷入死循环。
- beforeDestroy:实例销毁前,一般在这里进行一些计时器、事件的清除工作。
- destroyed:实例销毁后。
2. 组件生命周期
加载渲染过程:
⽗ beforeCreate -> ⽗ created -> ⽗ beforeMount -> ⼦ beforeCreate -> ⼦ created -> ⼦ beforeMount -> ⼦ mounted -> ⽗ mounted
子组件更新过程:
⽗ beforeUpdate -> ⼦ beforeUpdate -> ⼦ updated -> ⽗ updated
父组件更新过程:
⽗ beforeUpdate -> ⽗ updated
销毁过程:
⽗ beforeDestroy -> ⼦ beforeDestroy -> ⼦ destroyed -> ⽗ destroyed
3.5 计算属性和监听属性
1. 计算属性 computed
概述:主要用于对数据进行改造输出的,适用于复杂数据的转换,统计,格式编辑等等场景。
原理:底层借助了Object.defineproperty()
方法提供的get和set
优势:计算属性会在调用后对结果进行缓存,如果再次调用,依赖的数据没有发生改变,那么直接从缓存中取出之前的结果使用,如果依赖的数据发生了改变,再重新计算,然后再次缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| data: { firstName:'张', lastName:'三', x:'你好' }, computed: {
fullName() { console.log('get被调用了') return this.firstName + '-' + this.lastName } }
|
2. 监听属性 watch
概述:监控已有的属性,一单属性发生改变就会自动调用相应的方法。
应用场景:根据数据变化去请求。监控用户输入。分页页码发生改变需要请求新的数据。
配置项属性 **immediate:false**
,改为true,则初始化时调用一次。
1 2 3 4 5 6 7 8 9 10 11 12
| data: { isHot: true, }, watch:{ isHot:{ immediate:true, handler(newValue,oldValue){ console.log('isHot被修改 了',newValue,oldValue) } } }
|
深度监听:Vue中的watch默认不检测对象内部属性值的改变。为了提高效率。
在watch中配置deep:true可以监听对象内部属性值的改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| watch: {
numbers: { deep: true, handler() { console.log('numbers改变了') } } }
|
3. 区别
- computed能完成的功能,watch都能完成,但watch能完成的,但computed不一定能完成。
- computed一般应用于tabs切换列表数据
- computed只能执行同步操作,watch能执行异步操作。
- computed处理的数据有缓存,只有依赖的 data 或者 props 发生改变后才会重复执行,
3.5 插槽
概述:允许开发者进行组件的扩展,可以更好的复用组件以及定制化处理
1. 匿名插槽
1 2 3 4 5 6 7 8 9 10 11 12
| 父组件中: <Category> <div>html结构1</div> </Category>
子组件中: <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template>
|
2. 具名插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| : <Category> <template slot="center"> <div>html结构1</div> </template>
<template v-slot:footer> <div>html结构2</div> </template> </Category>
子组件中: <template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
|
3. 作用域插槽
概述:数据在子组件自身,但根据数据生产翁的结构需要组件的使用者(父组件)来决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 父组件中: <Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category>
<Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
|
应用场景:一般情况应用于编辑,删除等按钮来获取这一行的数据。
3.6 vue-router
概述:前端路由理解为单页开发中,负责页面内容的开发,根据不同的url地址去展示不同的内容。
1. 配置:
- main.js配置:
1 2 3 4 5 6 7
| import router from './router'
new Vue({ router, store, render: h => h(App) }).$mount('#app')
|
- router配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import Vue from 'vue' import VueRouter from 'vue-router' import LayoutView from '../views/LayoutView.vue'
Vue.use(VueRouter)
const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/about', name: 'about', component: () => import('../views/AboutView.vue') } ]
const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) router.beforeEach((to, from, next) => { const token = store.state.token
if (to.meta.auth) { if (token) { next() } else { next('/login') } }else { next() } }) export default router
|
2. 实现切换
1
| <router-link active-class="active" to="/about">About</router-link>
|
3. 指定展示位置
1
| <router-view></router-view>
|
4. 注意点
- 路由组件通常存放在
pages
文件夹,一般组件q 通常存放在components
文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息。 - 整个应用只有一个router,可以通过组件的
$router
属性获取到。
5. 多级路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, children:[ { path:'news', component:() => import('../views/NewView.vue') }, { path:'message', component:Message } ] } ]
|
跳转:
1
| <router-link to="/home/news">News</router-link>
|
6. 路由传参
1. query传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!-- 跳转并携带query参数,to的字符串写法 --> <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link> <!-- 跳转并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link>
// 接受参数 $route.query.id $route.query.title
|
2. params传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'/home/message/detail', params:{ id:666, title:'你好' } }" >跳转</router-link>
$route.query.id $route.query.title
|
3. 动态路由
在router目录下的index.js文件中,对path属性加上/:id。使用router对象的params.id获取动态参数
1 2 3 4 5 6 7 8 9 10 11
| { path: 'student/:id', name: 'ViewStudent', component: () => import('../views/ViewStudent.vue') }
this.$router.push('/student/' + id);
this.$route.params.id
|
4. query和params区别
params 传参类似于网络请求中的 post 请求,params 传过去的参数不会显示在地址栏中(但是不能刷新), params 传参后,刷新页面会失去拿到的参数。params 只能配合 name 使用,如果提供了 path,params 会失效。
query 传参类似于网络请求中的 get 请求,query 传过去的参数会拼接在地址栏中(?name=xx)。query 较为灵活既可以配合 path 使用,也能配合 name 使用(亲测可用)。
7. 编程式路由导航
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } })
this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } })
his.$router.push({ path:'/xiangqing', query:{ id:xxx, title:xxx } })
this.$router.replace({ path:'xiangqing', query:{ id:xxx, title:xxx } }) this.$router.forward() this.$router.back() this.$router.go()
this.$route.query
this.$route.params
|
router-link的replace属性:控制路由跳转时操作浏览器历史记录的模式,浏览器的历史记录有两种写入方式:分别为push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
如何开启replace
模式:```<router-link replace …….>News``
8. 路由组件钩子
概述:keep-alive
让步展示的路由组建保持挂载,不被销毁
1 2 3
| <keep-alive include="News"> <router-view></router-view> </keep-alive>
|
activated和deactivated
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| ```deactivated```路由组件失活时触发。
应用:可以将定时器写在被激活时里,销毁定时器写在失活里,组件跳转时会被触发
##### 9. 路由守卫
###### 1. 全局守卫
概述:对路由进行权限控制
```js //全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() //放行 } })
//全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
|
2. 独享守卫
概述:当前路由独享的守卫,可判断当前路由是否需要权限控制
1 2 3 4 5 6 7 8 9 10 11 12 13
| beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){ if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') } }else{ next() } }
|
3. 组件内守卫
1 2 3 4 5 6
| beforeRouteEnter (to, from, next) { },
beforeRouteLeave (to, from, next) { }
|
10. 路由模式
1. hasn模式
hash
模式是一种把前端路由的路径用井号 #
拼接在真实 url
后面的模式。当井号 #
后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange
事件。不会被包括在 http
请求中。- hash变化会触发网页跳转,即浏览器的前进和后退。
hash
通过 window.onhashchange
的方式,来监听 hash
的改变,借此实现无刷新跳转的功能hash
可以改变 url
,但是不会触发页面重新加载,不会出现在``http`中- 首先,
hash
本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。其次,``hash的传参是基于
url`的,如果要传递复杂的数据,会有体积的限制
2. history模式
- 利用H5的 history中新增的两个API
pushState()
和 replaceState()
和一个事件``onpopstate`监听URL变化 history
每次刷新会重新像后端请求整个网址,也就是重新请求服务器。如果后端没有及时响应,就会报错404!history
模式不仅可以在``url`里放参数,还可以将数据存放在一个特定的对象中。- 修改历史状态: 包括了 HTML5 History Interface 中新增的
pushState()
和 replaceState()
方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了 url,但浏览器不会立即向后端发送请求。如果要做到改变 url 但又不刷新页面的效果,就需要前端用上这两个 API。 - 切换历史状态: 包括
forward()
、back()
、go()
三个方法,对应浏览器的前进,后退,跳转操作。
3. 两种模式对比
- history 模式的 pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
- pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
- pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
- pushState() 可额外设置 title 属性供后续使用。
- hash 模式下,仅 hash 符号之前的 url 会被包含在请求中,后端如果没有做到对路由的全覆盖,也不会返回 404 错误;history 模式下,前端的 url 必须和实际向后端发起请求的 url一致,如果没有对用的路由处理,将返回 404 错误。
3.7 过滤器(v3已删)
概述:Vue 为开发者提供了一个方法,帮助我们进行数据的筛选处理,一般应用在模板上的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <div id="app"> <ul v-for="(item, index) in arr" :key="index"> <li>运输状态:{{ item.expressState | showState }}</li> </ul> </div> </template>
<script> export default { filters: { showState(state) { switch (state) { case "1": return "待发货"; break; case "2": return "已发货"; break; case "3": return "运输中"; break; case "4": return "派件中"; break; case "5": return "已收货"; break; default: return "快递信息丢失"; } }, }, }; </script>
|
删除原因:在v3中为了精简代码,删除了过滤器,因为在computed计算属性和methods方法中也可以对数据进行加工。
4. Vue 引入
4.1 图片引入
1. 相对路径
<img src="@/assets/images/g1.jpeg" alt="">
最终图片路径会发生改变
2. require
1 2 3 4 5 6 7 8 9
| <img :src="logo" alt=""> { ... data() { return { logo: require('../assets/pic.jpg') } } }
|
3. import
1 2 3 4
| <img :src="logo" alt="">
import logo from '../assets/logo.png'
|
4.2 动画引入
概述:Vue 中可以实现在 插入、更新、移除 DOM 的时候,添加过度效果。
条件:
- 条件渲染(v-if,v-show)
- 动态组件
- 列表渲染
使用
需要添加动画的标签或者组件,在外部添加 transition 组件包装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <transition> <p v-if=""></p> </transition>
v-enter: 表示开始进入过度
v-enter-active:正在进入过度
v-enter-to:进入过度结束
v-leave:准备离开的过度
v-leave-active:正在离开的过度
v-leave-to:已经离开
|
5. Vue属性
1. scoped 属性
概述: 因为 css 没有局部和全局的概念,所有的css都是全局css,所以默认情况下 <style>
就是全局,各个组件之间如果 class 名字相同,则会相互影响。
解决:给 style 添加 scoped
属性,则当前 style 中的 css 会变为局部 css。
作用:组件样式不相互污染。
原理:
- 给``HTML
的dom节点添加一个不重复的
data`属性(例如: data-v-5558831a)来唯一标识这个dom 元素 - 在每句
css
选择器的末尾(编译后生成的css语句)加一个当前组件的data
属性选择器(例如:[data-v-5558831a])来私有化样式
样式穿透: 当我们引入第三方组件库时,需要在局部组件中修改第三方组件库的样式,而又不想去除scoped属性造成组件之间的样式覆盖。
使用:
- stylus的样式穿透 使用>>>
外层 >>> 第三方组件
- sass和less的样式穿透 使用/deep/
外层 /deep/ 第三方组件
2. nextTick
概述:等待下一次 DOM 更新刷新的方法
Vue有个异步更新策略
,意思是如果数据变化,Vue不会立刻更新DOM,而是开启一个队列
,把组件更新函数保存在队列中
,在同一时间循环发生的所有数据变更会异步的批量更新
。这一策略导致我们对数据的修改不会立刻
体现在DOM
上,此时如果想要获取更新后的DOM状态
,就需要使用nextTick.
使用场景: created
中想要获取DOM时,响应式数据变化
后获取DOM
更新后的状态,比如希望获取列表更新后的高度
。
1 2 3 4 5 6
| created(){ let that=this; that.$nextTick(function(){ that.$refs.aa.innerHTML="created中更改了按钮内容"; });
|
二、Vue 周边
1. element-ui
1.1 环境安装
npm i element-ui -S
1.2 引入框架
1. 全局引入
main.js文件
1 2 3 4
| import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
|
2. 按需加载
安装模块:npm install babel-plugin-component -D
babel.config.js文件
1 2 3 4 5 6 7 8 9 10 11 12
| module.exports = { "presets": ['@vue/cli-plugin-babel/preset'], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
|
main.js中:
1 2 3 4 5
| import Vue from 'vue'; import { Button, Select } from 'element-ui';
Vue.use(Button) Vue.use(Select)
|
2. sass
概述:属于 css 的扩展语言、支持所有 css 的语法,然后新增语法。
1. 安装
npm i sass/node-sass/dart-sass
2. sass 语法
1. 变量
1 2 3 4 5 6 7 8 9 10
| $theme-color: #ff0; $b: border;
nav { background: $theme-color; } footer { border: 1px solid $theme-color;
|
2. 插值
1 2 3
| p .#{$b} { #{$b}-left: 1px solid $theme-color; }
|
3. 导入
@import ‘文件名’; // 文件后缀如果是 .scss 那么可以省略不写
4. 嵌套
1 2 3 4 5 6 7 8 9 10 11
| .body { width: 200px;
p { text-align: center; }
a { color: red; } }
|
5. 混合
概述:当出现大段重复的代码时,就可以使用混合趋势线打断样式的复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @mixin center { width: 800px; margin-left: auto; margin-right: auto; }
.content { @include center; background: red; }
@mixin center($w) { width: $w; margin-left: auto; margin-right: auto; } .content { @include center(1000px); }
@mixin center($w:800px) { width: $w; margin-left: auto; margin-right: auto; } .content { @include center(); }
|
6. 继承
概述:一个选择器继承另外一个选择器。
1 2 3 4 5 6 7 8 9
| .error { background: red; color: #fff; }
.seriousError { @extend .error; background: darkred; }
|
3. axios
概述:axios 是一个基于 Promise 的 HTTP 请求库,可以在浏览器和 nodejs 中使用。有非常多的特性:请求拦截和响应拦截、取消请求、客户端 XSRF 防御。
1. 安装
npm i axios
2. 使用axios
npm i axios vue-axios
main.js配置
1 2 3
| import http from 'axios'
Vue.prototype.$http = http
|
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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); })
axios({ method: 'get', url: '/user/12345', params: { firstName: 'Fred', lastName: 'Flintstone' } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function getUserAccount() { return axios.get('/user/12345'); }
function getUserPermissions() { return axios.get('/user/12345/permissions'); }
Promise.all([getUserAccount(), getUserPermissions()]) .then(function (results) { const acct = results[0]; const perm = results[1]; });
|
3. 封装
原因:
- 方便修改baseUrl
- loading 处理
- token 处理
- 错误处理
- 返回数据过虑
- 取消请求
- 过滤相同请求
具体封装见axios总结