template(必须) 影响组件渲染的结构 html
只能有一个根元素
vue通过data提供数据
data必须是一个函数,并且返回一个对象
export default {
data () {
return {
money: 100,
msg: 'hello'
}
}
}
插值表达式显示数据 {{}}
{{ msg }}
{{ obj.name }}
{{ msg.toUpperCase() }}
{{ obj.age > 18 ? '成年' : '未成年' }}
v-bind与v-on指令
v-bind:动态的设置html元素的属性
<!-- 缩写 -->
<a :href="url"></a>
v-bind对于style 的增强
<div class="box" :class="isRed ? 'red': ''">123</div>
v-on:绑定事件
<button v-on:click="addMoney(1)">搬砖</button>
v-on事件修饰符
- .prevent 阻止默认行为
- .stop 阻止冒泡
<a @click.prevent="fn" href="http://www.baidu.com">去百度</a>
v-on获取事件对象
- 没有传参, 通过形参接收 e
@click="fn2"
methods: {
fn2(e) {
e.preventDefault()
}
}
- 传参了, 通过$event指代事件对象 e
@click="fn2(100, $event)"
methods: {
fn2(num, e) {
e.preventDefault()
}
}
v-if 和 v-show
v-show:实质是在控制元素的 css 样式, display: none;
v-if:实质是在动态的创建 或者 删除元素节点;是惰性的, 如果初始值为 false, 那么这些元素就直接不创建了, 节省一些初始渲染开销
v-else 和 v-else-if
v-model
给表单元素使用, 双向数据绑定
v-model 修饰符:
- v-model.number="age":自动将用户的输入值, 用parseFloat转成数字类型
- v-model.trim="msg":自动过滤用户输入的首尾空白字符
v-model 语法糖:==》等价于 给一个input框提供了 :value属性以及 @input事件
- 数据发生了改变,页面会自动变 v-bind:value
- 页面输入改变 , 数据会自动变化 v-on:input
<input type="text" v-model="msg">
<input type="text" :value="msg" @input="msg = $event.target.value">
v-text 和 v-html
- v-text:更新元素的
innerText
- v-html:更新DOM对象的
innerHTML
v-for
v-for="(item, index) in 数组名"
<ul>
<li v-for="(value, key) in user" :key="key">{{value}} ---{{key}}</li>
</ul>
v-for设置 和 不设置 key 有什么区别:==》 提高虚拟DOM的对比复用性能
计算属性(computed):计算属性必须是一个 function,计算属性必须有返回值
<p>{{ reverseMsg }}</p>
computed: {
reverseMsg () {
return this.msg.split('').reverse().join('')
}
}
计算属性的缓存的问题
- 计算属性只计算了一次,就会把结果缓存起来,以后多次使用计算属性,直接使用缓存的结果
- 如果计算属性依赖的属性发生了改变,计算属性会重新计算一次,并且缓存
属性监听(watch):只要监听的属性发生了改变,这个函数就会执行
watch: {
// 参数1: value 变化后的值
// 参数2: oldValue 变化前的值
msg (value, oldValue) {
console.log('你变了', value, oldValue)
}
}
watch的属性:
- deep: true 如果true,代表深度监听
- immediate: 立即 立刻 是否立即监听 默认是false
- handler 数据发生变化,需要执行的处理程序
data() {
return {
list: JSON.parse(localStorage.getItem('score-case')) || []
}
}
watch: {
list: {
deep: true,
handler() {
localStorage.setItem('score-case', JSON.stringify(this.list))
}
}
}
组件的全局注册与局部注册
全局注册的组件,可以在任意的组件模板范围中使用
//main.js
import HmHeader from './components/HmHeader'
// 全局注册
// Vue.component(名字, 组件)
Vue.component('HmHeader', HmHeader)
局部注册的组件,只能在当前注册的组件模板范围内使用
<template>
<div>
<hm-header></hm-header>
</div>
</template>
import HmHeader from './components/HmHeader'
export default {
components: {
HmHeader
}
}
通过 name 注册组件
<template>
<button>按钮组件</button>
</template>
<script>
export default {
name: 'HmButton'
}
</script>
import HmButton from './components/hm-button.vue'
Vue.component(HmButton.name, HmButton)
// 等价于 app.component('HmButton', HmButton)
组件的样式冲突 scoped
- 默认情况下,写在组件中的样式会
全局生,
会影响到整个 index.html 中的 dom 元素; - 可以给组件加上 scoped 属性, 可以让样式只作用于当前组件
组件通信
父传子 props 传值
父组件通过给子组件加属性传值
//父 属性传值
<Son price="100" title="不错" :info="msg"></Son>
//子 通过props属性接收
props: ['price', 'title', 'info']
传递给子组件的数据, 为了提高 子组件被使用时 的稳定性, 可以进行props校验
props: {
// 基础的类型检查
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值
propD: {
type: Number,
default: 100
}
}
子传父
子组件可以通过 this.$emit('事件名', 参数1, 参数2, ...)
触发事件的同时传参
//子 通过this.$emit()
this.$emit('sayPrice', 2)
//父
<son @sayPrice="sayPrice"></son>
methods: {
sayPrice (num) {
console.log(num)
}
}
ref 和 $refs:可以用于获取 dom 元素 或者组件实例
每个 vue 的组件实例上,都包含一个$refs 对象,里面存储着对应的DOM 元素
- 给需要获取的 dom 元素或者组件, 添加 ref 属性
<div>
<div ref="box">我是div盒子</div>
<jack ref="jack"></jack>
<button @click="fn">按钮</button>
</div>
- 通过
this.$refs.xxx
获取
import Jack from './jack.vue'
export default {
methods: {
fn () {
console.log(this.$refs.box)
console.log(this.$refs.jack)
this.$refs.jack.sayHi()
}
},
components: {
Jack
}
}
自定义指令:自定义全局指令跟自定义局部指令
插槽:允许在封装组件时,把不确定的、希望由用户指定的部分定义为插槽
- 默认插槽
// 父
<my-dialog>
<p>请输入正确的手机号码</p>
</my-dialog>
// 子
<template>
<div class="my-dialog">
<div class="header">
<h3>友情提示</h3>
</div>
<div class="content">
<slot></slot>
</div>
// 设置默认值
// <div class="content">
// <slot>设置的默认值</slot>
// </div>
<div class="footer">
<button>关闭</button>
</div>
</div>
</template>
- 具名插槽 :可以实现定向分发
- 给插槽起名字 ;
- 使用 template 标签, 将内容包裹成一个整体;
- 通过 v-slot:插槽名, 指定具体分发给谁
// 父
<my-dialog>
//具名插槽的简写 <template #header>
<template v-slot:header>
<h3>这是大标题</h3>
</template>
//具名插槽的简写 <template #default>
<template v-slot:default>
<p>这是内容</p>
</template>
//具名插槽的简写 <template #footer>
<template v-slot:footer>
<button>确认</button>
<button>取消</button>
</template>
</my-dialog>
// 子
<template>
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot>这是后备内容</slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
</template>
组件生命周期
- beforeCreate:data数据初始化之前,组件还没有数据
- created: data数据初始化之后,可以获取到组件的数据
- beforeMount:DOM渲染之前,DOM还没渲染
- mounted:DOM渲染之后,可以操作DOM了
- beforeUpdate: 数据更新,DOM更新前
- updated: 数据更新,DOM更新后
- beforeDestroy: 组件销毁前
- destroyed: 组件销毁后
单页应用程序的缺点:不利于 SEO 搜索引擎优化
vuex
多个组件共享状态,才存储在vuex中;对于某个组件中的私有数据,依旧存储在data中
配置项 | 含义 | 注意 |
---|---|---|
state | 单一状态树 | 类似data |
mutations | 数据管家(同步) | 唯一修改state地方 |
actions | 异步请求 | 要改state需要提交给mutations |
getters | vuex计算属性 | 类似computed |
modules | 模块拆分 |
state
// 方式1: 组件内视图 JS内直接 - 直接使用
this.$store.state.模块名.变量名
// 方式2:
import { mapState } from 'vuex';
...mapState("模块名", ['state变量名']) //同名直接获取
...mapState("模块名", {新名字:'state变量名'}) //改名
getters
// 方式1: 组件内 - **直接**使用
this.$store.getters['模块名/计算属性名']
// 方式2: 组件内 - **映射**使用
import { mapGetters } from 'vuex';
...mapGetters("模块名", ['getters里计算属性名']) // 同名获取
...mapGetters("模块名", {新名字:'getters里计算属性名'}) // 改名
mutations
// 方式1: 组件内 - **直接**使用
this.$store.commit("模块名/mutations里的函数名", 具体值)
// 方式2: 组件内 - **映射**使用
import { mapMutations } from 'vuex';
...mapMutations("模块名", ['mutations里方法名']) // 同名获取
...mapMutations("模块名", {新名字:'mutations里方法名'}) // 改名
actions
// 方式1: 组件内 - **直接**使用
this.$store.dispatch("模块名/actions里的函数名", 具体值)
// 方式2: 组件内 - **映射**使用
import { mapActions } from 'vuex';
...mapActions("模块名", ['actions里方法名']) // 同名获取
...mapActions("模块名", {新名字:'actions里方法名'}) // 改名