4.1 脚手架的使用
4.1.1 关于不同版本Vue的区别
-
vue.js 与 vue.runtime.xxx.js的区别
-
vue.js是完整版的Vue,包含:核心功能 + 模板解析器
-
vue.runtime.xxx.js是运行版本的Vue,包含:核心功能,没有模板解析器
-
-
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项
-
需要使用render函数接收到的creatElement函数,去指定具体内容
4.1.2 脚手架文件结构
|----node_modules
|----public
| |----favicon.ico:页签图标
| |----index.html:主页面
|----src
| |----assets:存放静态文件
| | |----logo.png
| |----component:存放组件
| | |----HelloWorld.vue
| |----App.vue:汇总所有组件
| |----main.js:入口文件
|----.gitignorre:git版本控制忽略文件配置
|----babel.config.js:babel配置
|----package.json:应用包配置文件
|----package-lock.json:包版本控制文件
|----README.md:应用描述文件
4.1.3 vue.config.js配置文件
-
使用 vue inspect > out.putjs 可以查看脚手架默认配置
-
使用vue.config.js可以对Vue进行定制化配置,官网有实例教程
4.2 常用的属性及配置项
4.2.1 ref 属性
-
用来给元素或者子组件注册引用信息(id的替代者)
-
应用在html标签上获取真是的DOM元素,应用在组件标签上获取组件对象(vc)
-
使用方式:
-
打标识:<h1 ref="xxx" >...</h1> 或者 <HelloWorld ref="xxx"/>
-
获取:this.$refs.xxx
-
4.2.2 props 配置项
-
功能:让组件接收外部传入数据
-
传递数据:<demo name="xxx"/>
-
接收数据:
-
第一种:props:['name'] 只接收
-
第二种:props:{ name:String } 限制类型
-
第三种:props:{ name:{ type: String, required: true, default: 'cs' }} } 限制类型、必要性、默认值
-
-
备注:
-
props是只读的,vue底层会监视你对props的修改,如果进行了修改,会发出警告
-
如果需要修改,复制props中的数据到data中,修改data中的数据
-
4.2.3 mixins 配置项
混入基础概念
-
mixin 是一个js对象,将多个组件共同代码抽取出来,进行复用
-
是一种分发 Vue 组件中可复用功能的非常灵活的方式
-
它可以包含我们组件中的任意功能选项,如data、components、methods 、created、computed等
-
当组件使用 mixins 对象时,所有mixin对象的选项都将被混入该组件本身的选项中来
什么时候使用mixins?
-
当我们存在多个组件中的数据或者功能很相近时,我们就可以利用mixin将公共部分提取出来
-
通过 mixin 封装的函数,组件调用他们是不会改变函数作用域外部的
// myMixins.js
export default {
data () {
return {
num:1
}
},
mounted() {
this.speak();
},
methods: {
speak() {
console.log(this.num);
},
}
}
mixins的特点
-
方法和参数在各组件中不共享:
-
虽然组件调用了mixins并将其属性合并到自身组件中来了,但是其属性只会被当前组件所识别
-
不会被共享,也就是其他组件无法从当前组件中获取到mixins中的数据和方法。
-
-
引入mixins后组件会对其进行合并:
-
将mixins中的数据和方法拓展到当前组件中来,在合并的过程中会出现冲突【如下】
-
-
异步请求的情况:
-
混入包含异步请求函数,而我们又需要在组件中使用异步请求函数的返回值时,会取不到此返回值
-
解决方案:不要返回结果,而是直接返回异步函数
-
关于mixins合并冲突
-
值为对象(components、methods 、computed、data):
-
混入组件时选项会被合并,键冲突时优先组件,组件中的键会覆盖混入对象
-
-
值为函数(created、mounted):
-
混入组件时选项会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用
-
4.2.4 插件
-
功能:用于增强Vue
-
本质:包含一个install方法的对象,install方法的第一个参数是Vue,后续参数为插件使用者传递的数据
-
插件使用:
-
定义插件:myPlugin.install = function(Vue,options){ 添加的方法 }
-
使用插件:Vue.use( myPlugin )
-
4.2.5 scoped 样式
-
将样式作用域指定为当前
-
使用方法:<style scoped></style>
4.3 webStorage
-
存储大小一般为5M,不同浏览器可能不一样
-
浏览器通过
Windows.locaStorage
或者Windows.sessionStorage
属性来实现本地存储机制 -
相关API:
-
xxxxStorage.setItem( 'key', 'value' ):
将键值添加到存储中,存在则会更新(默认保存字符串) -
xxxxStorage.getItem( 'key' ):
获取一个键的存储值,不存在返回null -
xxxxStorage.removeItem( 'key' ):
移除一个键的存储值 -
xxxxStorage.clear():
移除所有存储数据
-
-
备注:
-
locaStorage:
需要手动清除才会消失 -
sessionStorage:
浏览器关闭即会消失 -
getItem( 'key' ):
获取不到值,将会返回null,JSON.parse( 'value' ) 返回也是null
-
4.4 组件间的通讯
4.4.1 组件自定义事件
-
一种组件间的通讯方式,适用于
子组件 ===> 父组件
-
使用场景:A父组件、B子组件,B想传数据给A,那么就要在A中给B绑定自定义事件(事件的回调在A中)
-
绑定自定义事件方式:
-
第一种方式:在父组件中
<Hello @myEvent="test" />
-
第二种方式:在父组件中mounted函数中绑定,
this.$refs.xxx.$on('myEvent', this.test)
-
若想让事件只执行一次,可以使用
once
修饰符或者使用$once
方法
-
-
触发自定义事件:子组件中使用
this.$emit('myEvent', '数据')
-
解绑自定义事件:
this.$off('myEvent')
-
组件上也可以使用原生的事件,使用
.native
修饰符 -
注意:
-
通过
this.$refs.xxx.$on('myEvent', 回调)
绑定自定义事件时 -
回调要么配置在methods中,要么使用箭头函数,否则this指向会出错
-
4.4.2 全局事件总线
-
一种组件间的通讯方式,适用于
任意组件间的通讯
---【Vue中推荐使用】 -
安装全局事件总线:
new Vue({ el:"app", render: (h) => h(App), beforeCreate(){ Vue.prototype.$bus = this, // 安装全局事件总线,$bus就是当前应用的vm } });
-
使用全局事件总线:
-
接收数据:A组件想接收数据,就在A组件中给
$bus绑定自定义事件
,事件的回调留在A组件自身
{ methods() { demo(){ ...... } }, mounted(){ this.$bus.$on('xxxx', 数据), } }
-
提供数据:
this.$bus.$emit('xxxx', 数据)
在任意组件中触发事件
-
-
注意:最好在
beforDestroy
钩子函数中,用$off('key')
解绑当前组件所有使用到的事件
4.4.3 消息发布与订阅
-
一种组件间通讯方式,适用于
任意组件间的通讯
-
使用步骤:
-
安装pubsub:
npm intasll pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的
回调留在A组件自身
{ methods(){ demo(data){ ...... } }, mounted(){ this.pubId = pubsub.subscribe('xxxx',this.demo); // 订阅消息 } }
-
提供数据:
pubsub.publish('xxxx', 数据)
-
最好在beforeDestroy钩子函数中,使用
pubsub.unsubscribe(pubId)
取消订阅
-
4.5 $nextTick原理及运用
4.5.1 nextTick是什么
-
Vue.nextTick( [callback, context] )
,在下次 DOM 更新循环结束之后执行延迟回调 -
在修改数据之后立即使用这个方法,获取更新后的 DOM
4.5.2 为什么需要它
原理
-
Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环
(event loop)
当中观察到数据变化的 watcher 推送进这个队列 -
如果这个watcher被触发多次,只会被推送到队列一次
-
这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOM操作
-
而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新
实例
-
假使你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新
-
如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题
-
为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback)
-
这样回调函数在 DOM 更新完成后就会调用
4.5.3 在什么地方用它
created()钩子函数进行的DOM操作
-
在Vue生命周期的
created()
钩子函数进行的DOM操作,一定要放在Vue.nextTick()的回调函数中 -
原因是在created()钩子函数执行的时候,DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中
-
与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题
数据变化后需要操作DOM
-
在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候
-
例如v-if/v-show根据字段变化显隐,这个操作都应该放进Vue.nextTick()的回调函数中
4.6 Vue封装的过渡与动画
4.6.1 基础概念
-
作用:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式和类名
-
图示:
4.6.2 使用方法
-
元素进入时样式:【v-xxxx 中的v是默认名字,如果name有值,v将使用name值】
-
v-enter:
进入的起点 -
v-enter-active:
进入的过程 -
v-enter-to:
进入的终点
-
-
元素离开时的样式:
-
v-leave:
离开的起点 -
v-leave-active:
离开的过程 -
v-leave-to:
离开的终点
-
-
使用<transition>包裹要过渡的元素,并配置name属性:
<Transition name="hello"> <div v-show="isShow">toggled content</div> </Transition>
-
备注:若有多个元素需要过渡,则需要使用<transitionGroup>包裹,每个
元素需要指定key值
4.7 devServer.proxy 代理配置
4.7.1 跨域问题
-
前端应用和后端 API 服务器没有运行在同一个主机上
-
前端应用和后端 API 服务器端口不同
4.7.2 配置代理
-
在
vue.config.js
中的devServer.proxy
选项来配置 -
changeOrigin: 默认为为 true
-
true:服务器收到请求头中的host为 localhost:5000
-
false:服务器收到请求头中的host为 localhost:8080
-
-
target:代理目标的基础路径
-
ws:是否启用webservice
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
devServer: {
proxy: {
'/api': { // 匹配所有以 /api 开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
pathRewrite:{'^/api':''}, //路径改写:将 /api 替换为空
ws: true,
changeOrigin: true
},
}
}
})
4.7.3 代理执行流程
-
使用
axios
发出请求 -
访问代理服务器:拦截请求
-
代理服务器帮我们访问目标服务器
-
返回结果
4.8 slot 插槽
4.8.1 概念
-
作用:让父组件可以向子组件指定位置插入html结构,是一种组件间通讯方式,适用于
父组件 ===> 子组件
-
分类:默认插槽、具名插槽、作用域插槽
4.8.2 使用方式
默认插槽
-
没有任何命名的插槽
// 父组件
<Hello>
<div>html结构</div>
</Hello>
// 子组件
<template>
<div>
<!-- 定义默认插槽 -->
<slot>插槽默认内容。。。。</slot>
</div>
</template>
具名插槽
-
有命名的插槽,可以有多个插槽
// 父组件
<Hello>
<template slot="mySlot1">
<div>html结构1</div>
</template>
<template slot="mySlot2">
<div>html结构2</div>
</template>
</Hello>
// 子组件
<template>
<div>
<!-- 定义具名插槽 -->
<slot name="mySlot1">插槽默认内容。。。。</slot>
<slot name="mySlot2">插槽默认内容。。。。</slot>
</div>
</template>
作用域插槽
-
理解:数据在组件自身,但是数据生成的结构需要组件的使用者决定
-
实例:games数据在Hello组件中,但使用数据遍历出来的结构由APP来决定
// 父组件
<Hello>
<template scope="{ games }">
<!-- 生成ul结构 -->
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</template>
</Hello>
// 子组件
<template>
<div>
<!-- 定义具名插槽 -->
<slot :games="games"></slot>
</div>
</template>
<script>
export default{
name: "Hello",
data() {
return {
games: ["cs", "lol", "csgo"],
}
}
}
</script>