Vue 脚手架
1. 初识脚手架
1.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:包版本控制文件
1.2 不同 Vue 版本的区别
Vue.js 与 Vue.runtime.xxx.js 的区别:
- Vue.js 是完整版的 Vue,包含:核心功能 + 模板解析器。
- Vue.runtime.xxx.js 是运行版 Vue,只包含核心功能,没有模板解析器。
由于 Vue.runtime.xxx.js 没有模板解析器,因此在 new Vue()
中不能使用 template 配置项,需要使用 render 函数接收的 createElement 函数去指定具体内容。
// Vue 工程的 main.js
import App from './App.vue'
new Vue({
render(createElement) {
return createElement(App)
},
// 简写
render: h => h(App)
})
1.3 Vue.config.js 配置文件
终端使用 vue inspect > output.js
可以将 Vue 脚手架默认配置输出到 output.js。
工程里通过 vue.config.js 可以对脚手架进行个性化定制。
2. ref 属性和 props 配置项
2.1 ref 属性
- 用来给元素或子组件注册引用信息(id 的替代者)。
- 应用在 html 标签上获取的是真实 DOM 元素,应用在组件标签上获取的是组件实例对象(vc)。
- 使用方式:
<template>
<h2 ref="xxx"></h2>
</template>
<script>
new Vue({
methods: {
showDom() {
alert(this.&refs.xxx)
}
}
})
</script>
2.2 配置项 props
功能:让组件接收外部传过来的数据。
传递数据:
<!-- 向demo组件传递name和age -->
<Demo name="xxx" :age="18"></Demo>
接收数据:
- 简单接收。
export default {
props: ['name', 'age']
}
- 限制类型。
export default {
props: {
name: String,
age: Number
}
}
- 限制类型、必要性、指定默认值。
export default {
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
default: 18
}
}
}
备注:props 是只读的,Vue 底层会进行监控,如果进行更改,会发出警报,如果需要修改,可以复制 props 中的数据到 data 中,在 data 中进行修改。
3. mixin 混入
功能:可以把多个组件共用的配置项提取成一个混入对象。
使用方式:
- 在另外的 js 文件中定义混入(暴露的方式)。
export const mixin = {
methods: {
showMsg() {
alert('hello')
}
}
}
- 使用混入。
import {mixin} from '../mixin'
// 全局混入
Vue.mixin(mixin)
// 局部混入,在单个组件内
new Vue({
mixins: ['mixin']
})
4. 插件
功能:用于增强 Vue。
本质:包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的是插件使用者传递的数据。
定义插件:
const obj = {
install(Vue) {
// 1. 添加全局过滤器
Vue.filter('mySlice', function(value){
return value.slice(0, 4)
})
// 2. 添加全局指令
Vue.directive('...')
// 3. 配置全局混入
Vue.mixin('...')
// 4. 添加实例方法
Vue.prototype.$myMethod = function() {...}
}
}
使用插件:在 main.js 文件中通过 Vue.use(插件名)
使用。
5. scoped 样式
作用:让样式在组件局部生效,防止冲突。
写法:<style scoped>
。
6. webStorage
存储内容一般支持 5M 左右。
浏览器通过 Window.sessionStorage
和 Window.localStorage
来实现本地存储机制。
相关 API:
xxxStorage.setItem('key', 'value')
:该方法接收一个键和值作为参数,将键值对添加存储,如果键存在,则更新其对应值。xxxStorage.getItem('key')
:该方法接受一个键名作为参数,返回键名对应的值。xxxStorage.removeItem('key')
:该方法接收一个键名作为参数,并把该键名从存储中删除。xxxStorage.clear()
:该方法会清空存储中所有数据。
备注:
- SessionStorage 存储的内容会随着浏览器窗口的关闭而消失。
- LocalStorage 存储的内容需要手动清除才会消失。
xxxStorage.getItem(xxx)
如果 xxx 对应的 value 获取不到,getItem 的返回值为 null。JSON.parse(null)
结果仍为 null。
7. 组件自定义事件
一种组件间通信方式,适用于。
使用场景:A 是父组件,B 是子组件,B 想要给 A 传数据,那么就要在 A 中给 B 绑定自定义事件()。
绑定自定义事件:
- 第一种方式,在父组件中。
<template>
<Demo @myClick="handle"/>
<!-- 或 -->
<Demo v-on:myClick="handle"/>
</template>
- 第二种方式,在父组件中。
<template>
<Demo ref="demo"/>
</template>
<script>
new Vue({
mounted() {
this.$refs.demo.$on('myClick', this.handle)
}
})
</script>
若想让自定义事件只能触发一次,可以使用 once
修饰符或 $once()
方法。
触发自定义事件:在子组件中 this.$emit('myClick', 数据)
。
解绑自定义事件:在子组件中 this.$off('myClick')
,如果不传参数,则将该组件所有自定义事件解绑。
组件上可以绑定原生 DOM 事件,但需要使用 native
修饰符。
注意:通过 this.$refs.xxx.$on(事件名, 回调)
方式绑定自定义事件,回调要么配置在 methods 中,要么用箭头函数,否则 this 指向会出问题。
8. 全局事件总线
一种组件间通信的方式,适用于。
安装全局事件总线:
new Vue({
...
beforeCreate() {
Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前的vm
}
...
})
使用事件总线:
- 接收数据:A 组件想要接收数据,则在 A 组件中给
$bus
绑定自定义事件,事件的回调留在 A 组件自身。
methods: {
demo(data) {......}
},
mounted() {
this.$bus.$on('xxx', this.demo)
}
- 提供数据:
this.$bus.$emit('xxx', 数据)
。
注意:最好在 beforeDestroy 钩子中主动解绑当前组件用到的自定义事件。
9. 消息订阅与发布
一种组件间通信的技术,适用于。
使用步骤:
- 安装 pubsub:
npm i pubsub-js
。 - 引入:
import pubsub from 'pubsub-js'
。 - 接收数据:A 组件想要接收数据,则在 A 组件中订阅消息,订阅的回调留在 A 组件自身。
methods: {
demo(data) {......}
},
mounted() {
this.pid = pubsub.subscribe('xxx', this.demo) // 订阅消息
}
- 提供数据:
pubsub.publish('xxx', 数据)
。 - 最好在 beforeDestroy 钩子中用
pubsub.unsubscribe(this.pid)
来。
10. nextTick()
语法:this.$nextTick(回调)
。
作用:在下一次 DOM 更新结束后执行其指定的回调。
使用场景:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick
指定的回调函数中执行。
11. Vue 封装的过渡与动画
作用:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名。
写法:
- 准备好样式。
元素进入的样式:
- v-enter:进入的起点。
- v-enter-active:进入的过程中。
- v-enter-to:进入的终点。
元素离开的样式:
- v-leave:离开的起点。
- v-leave-active:离开的过程中。
- v-leave-to:离开的终点。
- 使用
<transition>
包裹要过度的元素,并配置name
属性。
<transition name="v">
<h1 v-show="isShow">
你好啊
</h1>
</transition>
- 如果有多个元素需要过度,则需要使用
<transition-group>
,且每个元素需要指定 key 值。
12. Vue 脚手架配置代理
方式一:在 vue.config.js 中添加如下配置。
devServer: {
proxy:"http://localhost:5000"
}
优点:配置简单,请求资源时直接发给前端。
缺点:不能配置多个代理,不能灵活的配置请求是否走代理。
工作方式:优先匹配前端资源,资源不存在才将请求转发给服务器。
方式二:编写 vue.config.js 配置具体代理规则。
module.exports = {
devServer:{
proxy: {
'/api1':{ // 匹配所有以api1开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
changeOrigin: true, // 为true时,服务器收到的请求头中host为localhost:5000,否则为前端服务器端口,默认为true
pathRewrite: {'^/api1': ''} // 将路径中的/api1替换成''
},
'/api2': { // 匹配所有以api2开头的请求路径
target: 'http://localhost:5001', // 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
优点:可以配置多个代理,且可以灵活控制是否走代理。
缺点:配置略微繁琐,请求资源时必须加前缀。
13. 插槽
作用:让父组件可以向子组件指定位置插入 html 结构,也是一种组件间通信的方式,适用于。
分类:默认插槽、具名插槽、作用域插槽。
默认插槽
<!-- 父组件中: -->
<category-vue>
<div>HTML结构</div>
</category-vue>
<!-- 子组件中: -->
<template>
<div>
<!-- 定义插槽 -->
<slot>默认显示内容</slot>
</div>
</template>
具名插槽
<!-- 父组件中: -->
<category-vue>
<template slot="center">
<div>HTML结构1</div>
</template>
<template v-slot:footer>
<div>HTML结构2</div>
</template>
</category-vue>
<!-- 子组件中: -->
<template>
<div>
<!-- 定义插槽 -->
<slot name="center">默认显示内容</slot>
<slot name="footer">默认显示内容</slot>
</div>
</template>
作用域插槽
(games 数据在 CategoryVue 组件中,但使用数据遍历出来的结构需要 App 组件决定)。<!-- 父组件中: -->
<category-vue>
<template slot-scope="scopeData">
<!-- 生成ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</category-vue>
<category-vue>
<template slot-scope="scopeData">
<!-- 生成ol列表 -->
<ol>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ol>
</template>
</category-vue>
<!-- 子组件中: -->
<template>
<div>
<!-- 定义插槽 -->
<slot :games="games">默认显示内容</slot>
</div>
</template>
<script>
export default {
name: 'CategoryVue',
data() {
return {
games: ['魂斗罗', '超级玛丽']
}
}
}
</script>