Vue2
一、数据绑定
1、v-bind 可以简写为:
单向数据绑定
后面跟的是js表达式
2、v-model
双向数据绑定
普通表单直接用v-model
单选框用v-model value
复选框用v-model value 与model绑定的数据必须是数组格式
下拉框用v-model value
文本框直接用v-model
v-model也可以用修饰符,用来控制输入,如lazy,trim,number
二、el和data的两种写法
1、el有2种写法
new Vue时候配置el属性
先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
2、data的两种写法
对象式
函数式
三、Object.defineProperty
四、数据代理
基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter内部去操作(读/写)data中对应的属性
五、事件处理
1、v-on 可以简写为@
2、事件修饰符
prevent:阻止默认事件
stop:阻止事件冒泡
once:事件只触发一次
capture:使用事件的捕获模式
self:只有event.target是当前操作的元素时才触发事件
passive:事件的默认行为立即执行,无需等待事件回调执行完毕
scroll:滚动条一滚动就会触发的事件
wheel:鼠标滚轮事件
六、键盘事件
vue中键盘事件的绑定 一般用keyUp(keydown)
Vue中常用的按键别名:
回车 => enter
删除 => delete
退出 => esc
空格 => space
换行 => tab
上 => up/下 => down/左 => left/右 => right
系统修饰键(用法特殊):ctrl、alt、shift、meta
配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
配合keydown使用:正常触发事件
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
七、计算属性
定义:将定义好的属性拿来计算
原理:底层借助了Objcet.defineproperty方法提供的getter和setter
1 computed: { 2 fullName: { 3 get(){ 4 5 }, 6 set(value){ 7 8 } 9 } 10 }
get函数什么时候执行?
初次读取时会执行一次
当依赖的数据发生改变时会被再次调用
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
简写前提:计算属性只考虑读取不考虑修改
1 computed: { 2 }
八、监视属性
定义:将定义好的属性拿来计算
当被监视的属性变化时, 回调函数自动调用, 进行相关操作
监视的两种写法:
new Vue时传入watch配置
通过vm.$watch监视
深度监视:配置deep:true可以监测对象内部值改变(多层)
1 watch:{ 2 //简写的前提watch的属性不需要immediate和deep属性的时候 3 //immediate:true;当这个属性为true时,页面刚渲染就运行handler函数 4 //deep:true;深度监视 5 isHot(newValue, preValue){ 6 console.log(newValue,preValue); 7 } 8 }
computed和watch之间的区别:
computed能完成的功能,watch都可以完成
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要的小原则:
所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,
九、绑定样式
1、绑定class对象 :class
:class='xxx';xxx可以是字符串、对象、数组
字符串写法,适用于样式的类名不确定,需要动态指定
数组写法,适用于要绑定的样式个数不确定,名字也不确定,需要动态指定
对象写法,适用于要绑定的样式个数确定,名字也确定,需要动态决定用不用
2、绑定style对象 :style
:style='xxx';xxx是样式对象
对象写法
数组写法
十、条件渲染
1、v-show
真显示,假隐藏
2、v-if /v-else-if /v-else
为假不做渲染,如果切换频率较高,建议用v-show;在用v-if和v-else-if时,代码中间不能被打断
十一、列表渲染
1、v-for
在react中必须有:key;v-for可以遍历数组、对象、字符串、指定字数,例如v-for="(number, index) in 6" :key="index"
2、虚拟DOM对比算法
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后进行新虚拟DOM和旧虚拟DOM的差异比较,比较规则如下:
1、旧虚拟DOM中找到了与新虚拟DOM相同的key,若内容没变则直接使用之前的真实DOM;若内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
2、未找到与新虚拟DOM相同的key,则直接创建新的真实DOM,随后渲染到页面
用index作为key,如果破坏原有数据的结构顺序,则有可能造成数据错乱
3、列表过滤
filter不改变源数据
4、列表排序
sort改变源数据
十二、Vue监测数据的原理
1、vue.set
1 vue.set(vm.student,'sex','男') 2 3 vm.$set(vm.student,'sex','男')
只能给data里面的某个属性追加属性,不能给data追加属性
2、对数组进行监测
push:数组最后位置添加元素 pop:删除数组最后一个元素 shift:删除数组第一个元素 unshift:在数组第一个元素前添加元素 splice:指定位置添加、删除、修改元素 sort:对数组进行排序 reverse:对数组进行反转
3、总结
vue会监视data中的所有层次的数据
通过setter实现监视,只要对data中的数据进行修改,则会调用该属性的setter方法,并重新对模板进行解析;在Vue实例化时就要传入要监测的数据
对数组的索引操作不对进行模板重新解析,监测数组是通过包裹数组更新元素的实现方法
十三、过滤器
过滤器的本质就是函数,可以对属性进行加工,不改变源数据
1 在页面中用 2 {{time | timeFormater}}
1 在Vue对象中用 2 filters:{ 3 timeFormater(value) { 4 return xxx 5 } 6 }
多个过滤器可以串联,链式编程
这里用的过滤器都是局部过滤器,只能在本vue实例中使用,想要全局使用需注册
1 Vue.filter('timeFormater',function(value){ 2 return xxx 3 })
支持v-bind,不支持v-model
十四、内置指令
1、v-text
对其绑定的数据进行文本渲染,不常用,多用插值表达方式
2、v-html
3、v-cloak
配合样式使用,解决当网速过慢时页面展示出未经解析的模板的问题
4、v-once
只初始化渲染一次就视为静态资源
5、v-pre
可以跳过所在节点的编译过程,页面不会去解析渲染,可以利用它跳过那些没有指令语法、没有插值语法的节点,加快了编译
十五、自定义指令
用directives进行配置
指令在与元素成功绑定时会被调用,指令所在的模板被重新解析时会被调用
指令的两个参数:Dom元素element,绑定的数据binding
指令里的this都是window
自定义指令是局部指令,全局注册跟过滤器一样
指令定义时不加v-,但使用时要加
指令名如果是多个单词,要用-连接命名,不要用小驼峰
1、对象式
bind:指令与元素成功绑定时调用
inserted:指令所在元素被插入页面时调用
update:指令所在的模板被重新解析时调用
2、函数式
相当于只用了bind和update
十六、生命周期
声明周期中的this指向的是vm 或 组件实例对象
1、beforeCreate
此时还没有进行数据监测和数据代理,无法通过vm获得到data和methods
2、created
数据监测和数据代理完毕,可以通过vm获得到data中的数据和methods中的方法
3、beforeMount
挂载之前,页面呈现的是未经vue编译的dom结构,所有对Dom的操作,最终都不奏效
4、mounted
vue完成初始化模板解析,并把真实Dom元素放入页面后(挂载完毕)调用mounted
至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
5、beforeUpdate
此时数据是新的,但页面是旧的
6、updated
根据新数据,生成新的虚拟Dom,并与旧的虚拟Dom进行比较,最终完成页面更新
7、beforeDestory
销毁实例vm,清理它与其他实例的连接,解绑全部指令及自定义事件监听器,但真实Dom还存在,此时vm中的所有data、methods、指令等都处于可用状态
在此阶段进行的操作都不触发页面更新
8、destoryed
销毁完毕
十七、组件
1、定义
实现应用中局部功能代码和资源的集合
组件定义时,不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm进行分配
组件使用方式:
1、创建组件
2、注册组件
3、使用组件
组件的分类:
1、非单文件组件
2、单文件组件
2、非单文件组件
<div id="root"> <h1> {{ msg }} </h1> <hello></hello> <!--使用组件--> <school></school> <hr/> <student></student> <hr/> </div> <div id="root2"> <h2>root2容器</h2> <hello></hello> </div> <script type="text/javascript"> Vue.config.productionTip = false; //创建school组件 const school = Vue.extend({ template: ` <div> <h2>学校名称:{{ schoolName }}</h2> <h2>学校地址:{{ address }}</h2> <button @click="showName">点我提示学校名</button> </div> `, //组件定义不要写el配置项,因为最终所有的组件都要被vm所管理,由vm决定服务于哪个容器 //这里data必须写成函数形式 避免多次使用组件导致共用data对象导致一个问题 data() { //注意这里不要写箭头函数 return { schoolName: '武汉科技大学', address: '武汉', } }, methods:{ showName(){ alert(this.schoolName) } } }) //创建school组件 const student = Vue.extend({ template: ` <div> <h2>学生名字:{{ studentName }}</h2> <h2>学生年龄:{{ age }}</h2> </div> `, data() { return { studentName: 'Jone', age: 18 } } }); const hello = Vue.extend({ template:` <div> <h2>你好世界,{{ name }}</h2> </div> `, data(){ return { name: 'panyue' } } }); //hello组件 Vue.component('hello', hello); //全局注册hello 就代表所有的vm都可以用hello组件了 // 创建vm new Vue({ el: "#root", //配置组件(局部注册) data:{ msg: 'hello world' }, components: { school, student }, }) new Vue({ el: '#root2', }); </script>
需要vue脚手架的情况:
1、vue注册组件时组件命名用大驼峰命名规范
2、多个组件标签在使用时自闭合
3、VueComponent
组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
关于this指向:
组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【VueComponent实例对象】
new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【Vue实例对象】
vm与vc属性配置并不是一模一样,尽管vc底层复用了很多vm的逻辑;因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等,仅有的例外是像 el 这样根实例特有的选项,并且vc的data只能用函数式表达
4、一个重要的内置关系
VueComponent.prototype.__proto__ === Vue.prototype
可以理解为vc的原型对象的原型对象继承自vm的原型对象
为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法
5、单文件组件
1 <template> 2 <!-- 组件的交互--> 3 </template> 4 5 <script> 6 //组件交互的代码 7 //export default school分别暴露 8 export default { 9 }; 10 //统一暴露 11 // export { school }; 12 // export default school //默认暴露 13 </script> 14 15 <style> 16 /* 组件的样式 */ 17 </style>
十八、脚手架
1、目录结构
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
2、render函数
vue.js与vue.runtime.xxx.js的区别:
vue.js是完整版的Vue,包含:核心功能+模板解析器
vue.runtime.xxx.js是运行版的Vue,只包含核心功能;没有模板解析器
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
1 new Vue({ 2 // render: h => h(App), 3 // el:'#app', 4 render: h => h(App), 5 // vue 传递帮你调render函数并传递了一个名为createElement的函数,这里的第一个参数代表h1元素,第二个参数是h1的内容 6 // render:(createElement) => createElement('h1',"你好") 7 // template: '<h1>你好</h1>' 8 }).$mount('#app')
组件的template有专门的解析器解析,不需要render函数
3、vue.config.js
使用output.js可以查看到vue脚手架的默认配置
vue.config.js 是一个可选的配置文件,可以调节脚手架
1 module.exports = { 2 pages: { 3 index: { 4 // page 的入口 5 entry: 'src/index/main.js', 6 }, 7 lintOnSave: false //关闭语法检查 8 }
4、ref属性
被用来给元素或组件注册引用信息,应用在html标签上可以获取真实的Dom元素,相当于html中的id属性
1 id: 2 document.getElementById(xx) 3 4 ref: 5 this.$refs.xx
对于组件来说,ref拿的是组件实例对象vc,id拿的是组件根目录的id
5、props配置
1 props: ['name', 'sex', 'age'] //简单声明接收 2 3 //限制props中属性的类型 类型错误了会提示错误信息 4 props: { 5 name: String, 6 sex: String, 7 age: Number 8 } 9 10 //接收时不仅限制类型还加上默认值的指定并加上必要性的限制 11 props:{ 12 name:{ 13 type: String, //类型 14 required: true //必要的 15 }, 16 age:{ 17 type: Number, 18 default: 99 //默认值 19 }, 20 sex:{ 21 type: String, 22 required: true 23 } 24 }
props中的数据是只读的,如果进行了修改vue会发出警告;如果业务需求确实要改,那么会复制props的内容到data中一份,然后修改data中的数据
6、mixin混入
多个组件共用的东西都可以提取出一个混入对象
可以局部混入,也可以全局混入
1 import { mixin, shareData } from "@/mixin"; 2 3 //局部混入 4 mixins:[ mixin, shareData ] 5 6 //全局混入 7 Vue.mixin(mixin); 8 Vue.mixin(shareData);
7、vue插件
包含install方法的一个对象,install的第一个参数是vue,第二个参数是插件使用者传递的数据
1 //引入插件 2 import plugins from './plugins'; 3 4 //vue应用插件 5 Vue.use(plugins);
8、scoped样式
十九、组件的事件交互
1、组件化编码流程
流程:
实现静态组件,组件要按照功能点拆分
实现动态数据,考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:一个组件在用,放在组件自身;一些组件在用,放在他们共同的父组件上(状态提升)
实现交互:从绑定事件开始
props适用于:
父组件==>子组件:子组件直接用props接收
子组件==>父组件:要求父组件先给一个函数,子组件调用函数,传参给父组件
2、浏览器的本地存储localStorage/sessionStorage
1 //保存一个数据 2 localStorage.setItem('键', '值'); 3 4 //读取一个数据 5 localStorage.getItem('键'); 6 7 //删除一个数据 8 localStorage.removeItem('键'); 9 10 //清空所有数据 11 localStorage.clear();
sessionStorage在页面关闭后清空,localStorage需要手动清空
3、组件的自定义事件
是一种组件间通信的方式,适用于子组件给父组件传数据,要在父组件中给子组件绑定自定义事件
(1)绑定事件
1 //通过父组件给子组件绑定一个自定义事件实现子给父传递数据 2 3 //回调函数 4 getStudentName(name, ...params){ 5 console.log(`app收到了学生名, ${name}`); 6 console.log(`剩余参数,${params}`); 7 } 8 9 //第一种写法使用@或v-on 10 <Student @java="getStudentName"/> 11 12 子组件中用this.$emit.('自定义方法名',相关参数)触发自定义事件 13 14 //第二种写法使用ref绑定事件 15 <Student ref="student"/> 16 this.$refs.student.$on('java', this.getName); 17 18 //只使用一次 19 <Student @java.once="getStudentName"/> 20 this.$refs.student.$once('java', this.getName);
绑定自定义事件时,要么配置在methods中,要么用箭头函数
(2)解绑事件
1 //解绑多个自定义事件 2 this.$off(['自定义事件名']) 3 4 //解绑所有自定义事件 5 this.$off() 6 7 //销毁当前的组件实例,销毁后实例及子组件所有的自定义事件全部销毁 8 this.$destroy()
(3)原生Dom事件
1 <Student @click.native="getStudentName"/>
4、全局事件总线
可以实现任意组件间的通信
1 //首先vm中定义全局事件总线,在main.js中 2 new Vue({ 3 el:'#app', 4 render: h => h(App), 5 beforeCreate() { 6 //此时这个this就是vm,只不过这个时候还并没有去解析模版 7 Vue.prototype.$bus = this; //安装全局事件总线 8 } 9 }); 10 11 //接收数据方要用$on绑定组件 12 //绑定组件 13 mounted() { 14 this.$bus.$on('hello', data) 15 }, 16 //销毁解绑 17 beforeDestroy() { 18 this.$bus.$off('hello'); 19 } 20 21 //发送数据方要用$emit发送数据 22 sendStudentName(){ 23 this.$bus.$emit('hello', this.name); 24 }
5、消息订阅与发布
1 //引入第三方库pubsub-js 2 import pubsub from 'pubsub-js' 3 4 //接收数据方要用subscribe订阅消息 5 //绑定组件 6 mounted() { 7 this.pudId = pubsub.subscribe('hello', (msgName,data)=>{如果用function注意这里的this指向的是null}) 8 }, 9 //销毁前取消订阅 10 beforeDestroy() { 11 pubsub.unsubscribe(this.pudId); 12 } 13 14 //发送数据方要用publish广播数据 15 sendStudentName(){ 16 pubsub.publish('hello', this.name); 17 }
二十、$nextTick
1 //编辑 2 handleEdit(todo){ 3 // todo.isEdit = true; //注意这里添加的数据并不是响应式的 一定清楚 4 if(Object.prototype.hasOwnProperty.call(todo, 'isEdit')){ 5 todo.isEdit = true; 6 }else{ 7 this.$set(todo, 'isEdit', true); //保证初次加入的时候存在响应式的数据 8 } 9 10 //自动获取焦点 11 //this.$refs.inputTitle.focus(); //此时你这行代码执行了,但是注意vue并没有重新解析模版(input并没有出现在页面上,dom节点并没有被更新),它一定要等这个回调函数执行完之后才会去重新渲染模版 12 //使用nextTick来解决 13 this.$nextTick(() => { 14 //这里的回调函数注意是在dom节点被更新之后才会运行的 15 this.$refs.inputTitle.focus(); 16 }) 17 console.log(todo); 18 }
语法:this.$nextTick(回调函数)
作用:在下一次Dom更新结束后执行其指定的回调函数
什么时候用:当改变数据后,要基于更新后的新Dom进行某些操作时,要在nextTick所指定的回调函数中执行
二一、过度与动画
1、动画简单用例
1 <div> 2 <!--如果不用name属性,style命名要用v;appear表示刚进入页面时就呈现动画效果--> 3 <transition name="hello" appear> 4 <h1 v-show="isShow">你好啊!</h1> 5 </transition> 6 </div> 7 8 <script> 9 export default { 10 name: "Test", 11 data(){ 12 return { 13 isShow: true 14 } 15 } 16 } 17 </script> 18 19 <style scoped> 20 h1{ 21 background: orange; 22 } 23 .hello-enter-active{ 24 animation: anim linear 0.5s; 25 } 26 .hello-leave-active{ 27 animation: anim linear 0.5s reverse; 28 } 29 @keyframes anim { 30 from { 31 transform: translateX(-100%); 32 } 33 to{ 34 transform: translateX(0px); 35 } 36 } 37 </style>
2、过度简单用例
1 <style scoped> 2 h1{ 3 background: orange; 4 } 5 /*进入的起点,离开的终点*/ 6 .hello-enter, 7 .hello-leave-to{ 8 transform: translateX(-100%); 9 } 10 /*进入的过程,离开的过程*/ 11 .hello-enter-active, 12 .hello-leave-active{ 13 transition: linear .5s; 14 } 15 /*进入的终点,离开的起点*/ 16 .hello-enter-to, 17 .hello-leave{ 18 transform: translateX(0); 19 } 20 </style>
3、第三方库animate.css
1 安装: 2 npm install animate.css --save 3 yarn add animate.css 4 5 导入文件: 6 import 'animate.css'; 7 8 使用: 9 <template> 10 <div> 11 <transition-group 12 appear 13 name="animate__animated animate__bounce" 14 enter-active-class="animate__swing" 15 leave-active-class="animate__backOutUp" 16 > 17 <h1 v-show="isShow" key="1">你好</h1> 18 <h1 v-show="isShow" key="2">哈哈</h1> 19 </transition-group> 20 </div> 21 </template>
二二、Ajax跨域问题
使用nginx或vue-cli开启代理服务器
1 方式一: 2 module.exports = { 3 devServer: { 4 proxy: 'http://localhost:5000' 5 } 6 } 7 8 方式二: 9 module.exports = { 10 devServer: { 11 proxy: { 12 '/api': {//加上前缀走服务器,不加前缀走代理 13 target: '<url>',//代理目标的基础路径 14 ws: true,//用于支持websocket 15 changeOrigin: true//用于控制请求头中的host值;react默认为false 16 }, 17 '/foo': { 18 target: '<other_url>' 19 } 20 } 21 } 22 }
方式一的优点是配置简单,缺点是不能配置多个代理,不能灵活控制请求是否走代理;方式二相反
二三、插槽
让父组件可以向子组件指定位置插入html结构
1、默认插槽
1 <slot></slot>
2、具名插槽
1 <slot name="center"></slot> 2 3 <ul slot="center"></ul> 4 5 <template v-slot:center></template>
3、作用域插槽
1 子组件: 2 <!--插槽,等着组件的使用者进行填充--> 3 <slot :games="games"></slot> 4 5 <script> 6 export default { 7 data(){ 8 return { 9 games:['红色警戒','穿越火线','劲舞团','超级玛丽'], 10 } 11 } 12 } 13 </script> 14 15 16 父组件: 17 <template scope="{games}"> 18 <!--这里data为插槽给你传递的对象包含你所需要的数据--> 19 <ul> 20 <li v-for="(g , index) in games" :key="index">{{ g }}</li> 21 </ul> 22 </template>
二四、Vuex
是什么:在Vue中实现集中式数据管理的一个插件

1、使用Vuex
1 安装vuex: 2 vue2安装vuex3,vue3安装vuex4 3 npm install vuex@3 4 5 引入并使用: 6 import Vuex from 'vuex' 7 Vue.use(Vuex) 8 //创建并暴露Store对象 9 import store from './store' 10 new Vue({ 11 store, 12 })
2、常用API
1、组件:
this.$store.dispatch(a,2)
2、store:
actions = {
a(context,value){
context.commit(A,value)
}
}
3、store:
mutations = {
A(state,value){
state.sum += value
}
}
4、组件:
{{ this.$store.state.sum }}
也可以由步骤1直接运行到步骤3
3、store.getters
用于将state中的数据进行加工
4、mapState、mapGetters
1 computed:{ 2 //方式一 3 Sum(){ 4 return this.$store.state.sum 5 } 6 //方式二,mapState对象写法 7 ...mapState({Sum:'sum'}) 8 //方式三,mapState数组写法 9 ...mapState(['sum']) 10 }
mapState方法用于帮助我们映射state中的数据为计算属性;mapGetters方法用于帮助我们映射getters中的数据为计算属性
5、mapActions、mapMutations
1 methods:{ 2 //方式一 3 getSum(){ 4 this.$store.commit(getSum,1) 5 } 6 //用以下方式要将参数传进函数,即{{getSum(1)}},因为默认的构造方法是getSum(value) 7 //方式二,mapMutations对象写法 8 ...mapMutations({getSum:'getSum'}) 9 //方式三,mapMutations数组写法 10 ...mapState(['getSum']) 11 }
mapActions方法用于帮助我们生成与actions对话的方法,即包含$store.dispatch(xxx)的函数;mapMutations方法用于帮助我们生成与mutations对话的方法,即包含$store.commit(xxx)的函数
6、vue模块化+namespaced
目的:让代码更好维护,让多种数据分类更加明确
修改store.js
1 const countAbout = { 2 namespaced:true,//开启命名空间 3 state:{x:1}, 4 mutations: { ... }, 5 actions: { ... }, 6 getters: { 7 bigSum(state){ 8 return state.sum * 10 9 } 10 } 11 } 12 13 const personAbout = { 14 namespaced:true,//开启命名空间 15 state:{ ... }, 16 mutations: { ... }, 17 actions: { ... } 18 } 19 20 const store = new Vuex.Store({ 21 modules: { 22 countAbout, 23 personAbout 24 } 25 })
开启命名空间后,组件中读取state数据:
1 //方式一:自己直接读取 2 this.$store.state.personAbout.list 3 //方式二:借助mapState读取: 4 ...mapState('countAbout',['sum','school','subject']),
开启命名空间后,组件中读取getters数据:
1 //方式一:自己直接读取 2 this.$store.getters['personAbout/firstPersonName'] 3 //方式二:借助mapGetters读取: 4 ...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch:
1 //方式一:自己直接dispatch 2 this.$store.dispatch('personAbout/addPersonWang',person) 3 //方式二:借助mapActions: 4 ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
开启命名空间后,组件中调用commit:
1 //方式一:自己直接commit 2 this.$store.commit('personAbout/ADD_PERSON',person) 3 //方式二:借助mapMutations: 4 ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
二五、路由
理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
前端路由:key是路径,value是组件
后端路由:key是路径,value是函数
基本使用:
安装vue-router,命令:npm i vue-router
应用插件:Vue.use(VueRouter)
编写router配置项
1 //引入VueRouter 2 import VueRouter from 'vue-router' 3 //引入Luyou 组件 4 import About from '../components/About' 5 import Home from '../components/Home' 6 7 //创建router实例对象,去管理一组一组的路由规则 8 const router = new VueRouter({ 9 routes:[ 10 { 11 path:'/about', 12 component:About 13 }, 14 { 15 path:'/home', 16 component:Home 17 } 18 ] 19 }) 20 21 //暴露router 22 export default router
实现切换(active-class可配置高亮样式):
1 <router-link active-class="active" to="/about">About</router-link>
指定展示位置:
1 <router-view></router-view>
二六、嵌套路由
1 routes:[ 2 { 3 path:'/about', 4 component:About, 5 }, 6 { 7 path:'/home', 8 component:Home, 9 children:[ //通过children配置子级路由 10 { 11 path:'news', //此处一定不要写:/news 12 component:News 13 }, 14 { 15 path:'message',//此处一定不要写:/message 16 component:Message 17 } 18 ] 19 } 20 ]
二七、路由Query传参
传递参数:
1 <!-- 跳转并携带query参数,to的字符串写法 --> 2 <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link> 3 4 <!-- 跳转并携带query参数,to的对象写法 --> 5 <router-link 6 :to="{ 7 path:'/home/message/detail', 8 query:{ 9 id:m.id, 10 title:m.title 11 } 12 }" 13 >跳转</router-link>
接收参数:
1 $route.query.id 2 $route.query.title
二八、路由命名
作用:可以简化路由的跳转
使用:
给路由命名:
1 { 2 path:'/demo', 3 component:Demo, 4 children:[ 5 { 6 path:'test', 7 component:Test, 8 children:[ 9 { 10 name:'hello' //给路由命名 11 path:'welcome', 12 component:Hello, 13 } 14 ] 15 } 16 ] 17 }
简化跳转:
1 <!--简化前,需要写完整的路径 --> 2 <router-link to="/demo/test/welcome">跳转</router-link> 3 4 <!--简化后,直接通过名字跳转 --> 5 <router-link :to="{name:'hello'}">跳转</router-link> 6 7 <!--简化写法配合传递参数 --> 8 <router-link 9 :to="{ 10 name:'hello', 11 query:{12 } 13 }" 14 >跳转</router-link>
二九、路由Params传参
配置路由,声明接收params参数:
1 { 2 path:'/home', 3 component:Home, 4 children:[ 5 { 6 path:'news', 7 component:News 8 }, 9 { 10 component:Message, 11 children:[ 12 { 13 name:'xiangqing', 14 path:'detail/:id/:title', //使用占位符声明接收params参数 15 component:Detail 16 } 17 ] 18 } 19 ] 20 }
传递参数:
1 <!-- 跳转并携带params参数,to的字符串写法 --> 2 <router-link :to="/home/message/detail/${m.id}/${m.title}">跳转</router-link> 3 4 <!-- 跳转并携带params参数,to的对象写法 --> 5 <router-link 6 :to="{ 7 name:'xiangqing', 8 params:{ 9 id:m.id, 10 title:m.title 11 } 12 }" 13 >跳转</router-link>
特别注意:
路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
二九、路由的props配置
作用:让路由组件更方便的收到参数
1 { 2 name:'xiangqing', 3 path:'detail/:id', 4 component:Detail, 5 6 //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件 7 // props:{a:900} 8 9 //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件 10 // props:true 11 12 //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件 13 props(route){ 14 return { 15 id:route.query.id, 16 title:route.query.title 17 } 18 } 19 }
三十、路由导航(前进/后退)
1、replace属性
作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
如何开启replace模式:<router-link replace .......>News</router-link>
2、路由实现前进后退
1 //$router的两个API 2 this.$router.push({ 3 name:'xiangqing', 4 params:{ 5 id:xxx, 6 title:xxx 7 } 8 }) 9 10 this.$router.replace({ 11 name:'xiangqing', 12 params:{ 13 id:xxx, 14 title:xxx 15 } 16 }) 17 this.$router.forward() //前进 18 this.$router.back() //后退 19 this.$router.go() //可前进也可后退
3、缓存路由组件
作用:让不展示的路由组件保持挂载,不被销毁
1 <!--如果要缓存多个路由组件就改写为:include="['News', 'Message']"--> 2 <keep-alive include="组件名"> 3 <router-view></router-view> 4 </keep-alive>
三一、两个新的生命周期钩子
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
1、actived
路由组件被激活时触发
2、deactivated
路由组件失活时触发
三二、路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
1 //全局前置守卫:初始化时执行、每次路由切换前执行 2 router.beforeEach((to,from,next)=>{ 3 console.log('beforeEach',to,from) 4 if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 5 if(localStorage.getItem('name') === 'zhangsan'){ //权限控制的具体规则 6 next() //放行 7 }else{ 8 alert('暂无权限查看') 9 // next({name:'guanyu'}) 10 } 11 }else{ 12 next() //放行 13 } 14 }) 15 16 //全局后置守卫:初始化时执行、每次路由切换后执行 17 router.afterEach((to,from)=>{ 18 console.log('afterEach',to,from) 19 if(to.meta.title){ 20 document.title = to.meta.title //修改网页的title 21 }else{ 22 document.title = 'vue_test' 23 } 24 })
独享守卫:
1 beforeEnter(to,from,next){ 2 console.log('beforeEnter',to,from) 3 if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 4 if(localStorage.getItem('name') === 'zhangsan'){ 5 next() 6 }else{ 7 alert('暂无权限查看') 8 // next({name:'guanyu'}) 9 } 10 }else{ 11 next() 12 } 13 }
组件内守卫:
1 //进入守卫:通过路由规则,进入该组件时被调用 2 beforeRouteEnter (to, from, next) { 3 }, 4 //离开守卫:通过路由规则,离开该组件时被调用 5 beforeRouteLeave (to, from, next) { 6 }
三三、路由器的两种工作模式
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
hash模式:
地址中永远带着#号,不美观
若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
兼容性较好
history模式:
地址干净,美观
兼容性和hash模式相比略差
应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
1 const router = new VueRouter({ 2 //默认开启hash模式 3 mode: 'history', 4 });

浙公网安备 33010602011771号