【Vue2】基础知识二
脚手架文件结构
|—— node_modules
|—— public
| |—— favicon.ico: 页签图标
| |—— index.html: 主页面
|—— src
| |—— assets: 存放静态资源
| | |—— logo.png
| |—— components: 存放组件
| | |—— HelloWorld.vue
| |—— App.vue: 汇总所有文件
| |—— main.js: 入口文件
|
|—— .gitignore:git版本管理忽略的配置
|—— babel.config.js:babel的配置文件
|—— package-lock.json:包版本控制文件
|—— package.json:应用包配置文件
|—— README.md:应用描述文件
|—— vue.config.js:自定义控制脚手架配置
关于不同版本的Vue:
1.vue.js与vue.runtime.xxx.js的区别
1.vue.js是完整版的vue,包含:核心功能+模板解析器
2.vue.runtime.xxx.js 是运行版的Vue,只包含:核心功能:没有模板解析器
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。
vue.config.js配置文件
> 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
> 使用vue.config.js可以对脚手架进行个性化定制,详情见vue官网 > vue 生态cli >配置参数
ref属性
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识:<h1 ref="xxx">...</h1> 或 <School ref="xxx"></School>
获取: this.$refs.xxx
配置项props
功能:让组件接收外部传过来的值
(1)传递数据:
<Demo name="xxx"/>
(2)接收数据 (接受数据三种写法)
第一种方法(只接收):
props:['name']
第二种方法(限制类型):
props:{
name:String
}
第三种方法(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String,//类型
required:true,//必要性
default:'老王'//默认值
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,
若业务需求确实需要修改,name请复制props的内容到data中一份,然后去修改data中的数据。(data里重新return ,储存一个新变量名储存值,然后后用methods或computed计算一下)
mixin(混入) 基础模块暴露,代码复用
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合,例如:
{
data()(...),
methods:(...)
......
}
第二步使用混入,例如:
(1)全局混入:Vue.mixin(xxx)
(2)局部混入:mixins:['xxx']
插件(使用方法类似express里的中间件)
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:对象.install = function(Vue,options){
// 全局过滤器
Vue.filter('mySlice',function (value) {})
// 定义全局指令
Vue.directive('fbind', {})
// 配置全局混入
Vue.mixin({})
// 给vue原型上添加方法(vm和vc都能用了)
Vue.prototype.$myMethod = () =>{alert('你好啊')}
Vue.prototype.$myProperty = xxx
}
使用插件:
Vue.use(plugins)
scope样式
作用:让样式在局部生效,防止冲突。
写法:<style scoped> <style lang="less">
总结TodoList案例
1.组件化编码流程:
(1). 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2). 实现动态组件:考虑好数据的存放位置,数据是一个组件再用,还是一些组件再用;
1)一个组件在用:放在组件自身即可。
2)一些组件在用:放在他们共同的父组件上(状态提升)。
(3). 实现交互,从绑定事件开始。
2.props适用于:
(1)父组件==> 子组件 通信
(2)子组件==> 父组件 通信(要求父先给一个函数)
3.使用v-model要切记:v-model绑定的值不能是props传过来的值,因为props是只读不可修改的。
4.props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
webSrorage
1.存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
2.浏览器通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
3.相关API:
1.local/sessionStorage.setItem('key', 'value')
2.local/sessionStorage.getItem('key', 'value')
3.local/sessionStorage.removeItem('key')
4.local/sessionStorage.clear()
4.备注:
1.sessionStorage 存储的内容会随着浏览器窗口关闭而消失
2.LocalStorage存储的内容,需要手动清除才会消失
3.local/sessionStorage.getItem(xxx)如果对应的value获取不到,那么对应的返回值是null
4.JSONP.parse(null)的结果依然是null
组件的自定义事件★
1.一种组件间通信的方式,适用于: 子组件==>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,name就要在A中给B绑定自定义事件(事件的回调在A中)
3.绑定自定义事件(自定义时间名称必须用小写或者用 x-x 连接):
1.第一种方式,在父组件中 <Demo @atguigu="test" /> 或 <Demo v-on:atguigu="test" />
2.第二种方式
<Demo ref="demo" />
.....
mounted(){
this.$refs.xxx.$on('atguigu', this.test)
}
3.若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
4.触发自定义事件:this.$emit('atguigu', data)
5.解绑自定义事件:this.$off('atguigu')
6.组件上也可以绑定原生DOM事件,需要使用native修饰符。★
7.注意:通过this.$refs.xxx.$on('atguigu', options)绑定自定时事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出现问题。
全局事件总线(GlobalEventBus)
1.一种组件间通信方式,适用于任意组件间通信。
2.安装全局事件总线:
new Vue({
......
beforeCreate(){
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
在vm create时给vue原型对象安装一个傀儡$bus(傀儡vm实例对象)
}
.....
})
3.使用事件总线:
1).接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){
demo(data){......}
}
.....
mounted(){
this.$bus.$on('事件名', this.demo) //或者直接把this.demo写成成回调函数形式
}
2).提供数据:this.$bus.$emit('事件名', data)
4.最好在beforeDestroy钩子中,用$off 去解绑当前组件所用到的事件。
消息订阅与发布(全局事件总线的封装库)(react常用)
1.一种组件间通信的方式,适用于任意组件间通信。
2.使用步骤:
1.安装pubsub: npm i pubsub-js
2.引入: import pubsub from 'pubsub-js'
3.订阅方接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
method(){
demo(data){.....}
}
.....
mounted(){
this.pid = pubsub.subscribe('xxx', this.demo)//订阅消息
}
4.发布方提供数据:pubsub.publish('xxx', data)
5.最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid) 去取消订阅
nextTick
1.语法 this.$nextTick(回调函数)
2.作用 在下一次DOM更新结束后执行其指定的回调
3.什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
插槽
1.作用:让父组件向子组件指定位置插入html结构,也是一种组件间通信的方式,使用于父组件 ===> 子组件(props反向配置)
2.分类:默认插槽、具名插槽、作用域插槽
3.使用方式
1.默认插槽
父组件
<Category>
<div>html结构</div>
</Category>
子组件
<template>
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容</slot>
</div>
</template>
2.具名插槽
父组件
<Category slot="center">
<div>html结构1</div>
</Category>
<Category v-slot:footer>
<div>html结构2</div>
</Category>
子组件
<template>
<div>
<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>
</div>
</template>
3.作用域插槽
1.理解:数据在组件自身,但根据数据生成的结构需要的组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
2.具体编码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Category> <template scope="scopeData"> <!-- 生成ul列表 --> <ul> <li v-for="(g, index) in scopeData.games" :key="index">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成h4标题 --> <h4 v-for="(g, index) in scopeData.games" :key="index">{{g}}</h4> </template> </Category> <!-- 子组件 --> <template> <div> <slot :games="games">我是默认的</slot> </div> </template> <script> export default { name:"Category", props:['title'], data() { return { games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"], }; }, } </script>
Vuex
1.概念:在vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且使用以任意组件间通信。
2.何时使用:多个组件需要共享数据时
3.搭建vuex环境
1.创建文件 src/store/index.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !该文件用于创建Vuex中最为核心的store --> <!-- !引入vue --> import Vue from 'vue' <!-- !引入Vuex --> import Vuex from 'vuex' <!-- !使用vuex插件 --> Vue.use(Vuex) <!-- !准备actions,用于响应组件中的动作 --> const actions ={} <!-- !准备mutations,用于操作数据(state) --> const mutations ={} <!-- !准备state,用于存储数据 --> const state ={} <!-- !创建并暴露store --> export default new Vuex.Store({ actions, mutations, state })
2.main.js中创建vm时传入store配置项
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import Vue from "vue"; import App from './App.vue'; Vue.config.productionTip = false; <!-- !引入store --> import store from './store/index' new Vue({ el:"#app", render:(h)=> h(App), store, beforeCreate() { Vue.prototype.$bus = this }, })
3.基本使用,配置actions、配置mutations、操作文件store.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. <!-- !该文件用于创建Vuex中最为核心的store --> <!-- !引入vue --> import Vue from 'vue' <!-- !引入Vuex --> import Vuex from 'vuex' <!-- !使用vuex插件 --> Vue.use(Vuex) <!-- !准备actions,用于响应组件中的动作 --> const actions ={ jia(context, value) { console.log('actions中的jia被调用'); context.commit('JIA', value) } } <!-- !准备mutations,用于操作数据(state) --> const mutations ={ JIA(state, value){ console.log('mutations中的JIA被调用'); state.sum += value } } <!-- !准备state,用于存储数据 --> const state ={ sum:0 //当前的和 } <!-- !创建并暴露store --> export default new Vuex.Store({ actions, mutations, state }) <!-- !模板里不用写this,js脚本里写应该加this --> 2.组件中读取vuex中的数据:$store.state.sum 3.组件中修改vuex中的数据:$store.dispatch('actions中的方法名', 数据) 或直接 $store.commite('mutations中的方法名', 数据) 若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
4.getters的使用
1.概念:当state中的数据需要经过加工再使用时,可以使用getters加工
2.在store.js中追加getters配置
3.组件中读取数据,$store.getters.bigSum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !准备getters,用于将state中的数据进行加工 --> const getters ={ bigSum(state){ return state.sum*10 } } <!-- !创建并暴露store --> export default new Vuex.Store({ actions, mutations, state, getters })
6.四个map方法的使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1.mapState:用于帮助我们映射state中的数据为计算属性 <!-- !借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法) --> computed:{ ...mapMutations(['JIA', 'JIAN']), } <!-- !借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) --> computed:{ ...mapMutations({increase:'JIA', decrease:'JIAN'}), } 2.mapGetters方法:用于帮助我们映射getters中的数据为计算属性 <!-- !借助mapStae生成计算属性,从state中读取数据。(对象写法) --> computed:{ ...mapGetters({bigSum:'bigsum'}) } <!-- !借助mapState生成计算属性,从state中读取数据。(数组写法) --> computed:{ ...mapGetters(['bigSum']) } 3.mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数 <!-- !借助mapMutations生成对应的方法,方法中会调用diapatch去联系actions(数组写法) --> methods:{ ...mapActions(['jiaOdd', 'jiaWait']), } <!-- !借助mapMutations生成对应的方法,方法中会调用diapatch去联系actions(对象写法) --> methods:{ ...mapActions({increaseOdd:'jiaOdd', increaseWait:'jiaWait'}) }
7.模块化+命名空间
1.目的:让代码更好维护,让多种数据分类更加明确。
2.修改store.js(inde.js)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !求和相关的配置 --> const countAbout = { <!-- *开启命名空间,以区分不同组件 --> namespaced:true, state: { x:1 }, actions: { ... }, mutations: { ... }, getters: { bigSum(state) { return state.sum * 10 } } } <!-- !人员管理相关的配置 --> const personAbout = { <!-- *开启命名空间,以区分不同组件 --> namespaced:true, state: { x:1 }, actions: { ... }, mutations: { ... }, } <!-- !创建并(暴露)store --> const store = new Vuex.Store({ modules:{ countAbout:countOptions, personAbout:personOptions } })
3.开启命名空间后,组件中读取state数据(这里直接去案例里看具体写法)Count.vue里用了map方式,Person.vue用了纯自己写的
方式一、自己直接读取
return this.$store.state.personAbout.personList
方式二、借助mapState读取
...mapState('countAbout', ['sum', 'school', 'subject']),
4.开启命名空间后,组件中读取getters数据
方式一、组件直接读取
return this.$store.getters['personAbout/firstPersonName']
方式二、借助mapGetters读取
...mapGetters('countAbout', ['bigSum'])
5.开启命名空间后,组件中调用dispatch数据
方式一、组件直接读取
this.$store.dispatch('personAbout/addPersonWang', personObj)
方式二、借助mapGetters读取
...mapActions('countAbout', {increaseOdd:'jiaOdd', increaseWait:'jiaWait'})
6.开启命名空间后,组件中调用commit
方式一、组件直接读取
this.$store.commit('personAbout/ADD_PERSON', personObj)
方式二、借助mapGetters读取
...mapMutations('countAbout', {increase:'JIA', decrease:'JIAN'}),
路由
1.理解:一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router进行管理)
2.前端路由:key是路径,value是组件。
一、基本使用
1.安装vue-router,命令:npm i vue-router
2.应用插件:Vue.use(VueRouter)
3.编写router配置项
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !该文件专门用于创建整个应用的路由器 --> import VueRouter from "vue-router"; <!-- !引入路由组件 --> import About from '../components/About' import Home from '../components/Home' <!-- !创建并暴露一个路由器,去管理一组一组的路由规则 --> export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home }, ] })
4.实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
5.指定展示位置
<router-view></router-view>
二、几个注意点
1.路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
2.通过切换,‘隐藏’了的路由组件,默认是被销毁掉的,需要的时候再去挂载
3.每个组件都有组件的$route睡醒,里面存储着组件的路由消息。
4.整个应用只有一个router,可以通过组件的$router属性获取到
三、多级路由(嵌套路由)
1.配置路由规则,使用children配置项
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !创建并暴露一个路由器 --> export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ //通过childrend配置子级路由 { path:'news', //二级路由不能加路径/ component:News, }, { path:'message', //二级路由不能加路径/ component:Message, }, ] }, ] })
2.跳转(要写完整路径)
<router-link to="/home/message">Message</router-link>
四、路由的query参数
1.传递参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !跳转路由并携带query参数,to的字符串写法 --> <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link> <!-- !跳转路由并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:m.id, title:m.title } }"> {{m.title}}</router-link>
2.接收参数
$route.query.id
$route.query.title
五、命名路由
1.作用:可以简化路由的跳转
2.如何使用
1.给路由命名:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ path:'/demo', component:Demo, children:[ { path:'test', //二级路由不能加路径/ component:Test, children:[ { name:'hello', path:'welcome', //二级路由不能加路径/ component:Hello, } ] }, ] },
2.简化跳转
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !简化前,需要写完整路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!-- !简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!-- !简化写法配合传递参数 --> <router-link :to="{ name:'hello', <!-- //path:'/home/message/detail', --> query:{ id:m.id, title:m.title } }"> 跳转</router-link>
六、路由的params参数
1.配置路由,声明并接收params参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ path:'/demo', component:Demo, children:[ { path:'test', //二级路由不能加路径/ component:Test, children:[ { name:'hello', path:'/demo/:id/:title', //使用占位符声明并接收params参数 component:Hello, } ] }, ] },
2.传递参数
路由使用params参数时,若使用to的对象写法,则不能使用path配置项(/aa/bb),必须使用name配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !跳转并携带params参数,to的字符串写法 --> <router-link to="/demo/test/welcome/666/你好">跳转</router-link> <!-- !跳转并携带params参数,to 的对象写法 --> <router-link :to="{ name:'hello', //路由使用params参数时,若使用to的对象写法,则不能使用path配置项(/aa/bb),必须使用name配置 query:{ id:m.id, title:m.title } }"> 跳转</router-link>
3.接收参数
$route.params.id
$route.params.title
七、路由的props配置
作用:让路由组件更方便的收到参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ path:'message', component:Message, children:[ { name:'xiangqing', path:'detail', //二级路由不能加路径 component:Detail, <!-- !props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件 --> props:{a:1,b:'hello'} <!-- !props的第二种写法,值为对象,若布尔值为真,就会把路由组件收到的所有params参数,以props的形式传给Detail组件 --> props:true <!-- !props的第三种写法,值为函数,此写法最强大,此处可以解构赋值并连续解构赋值 --> props({$route}){ return { id:$route.query.id, title:$route.query.title } } } ] },
八、<router-link>的replace属性
1.作用:控制路由器跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前的记录,路由跳转时候默认为push
3.如何开启replace模式<router-link replace ...>News</router-link>
九、编程时路由导航
1.作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活
2.具体编码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- !$router的两个API --> this.$router.push({ name:'xiangqing', //使用params参数不允许使用path配置项 query:{ id:m.id, title:m.title } }) this.$router.replace({ name:'xiangqing', //使用params参数不允许使用path配置项 query:{ id:m.id, title:m.title } }) <!-- !$router的另外三个API --> 前进 back(){ this.$router.back() }, 后退 forward(){ this.$router.forward() }, 可前进也可后退 test(){ this.$router.go(+3) }
洗尽铅华始见金,褪去浮华归本真