-
Vue实例
- Vue实例创建和作用
const app = new Vue({
// el: '#root',
template: '<div>{{text}}</div>',
data: {
text: 'text'
}
})
app.$mount('#root')
-- 1. app.$data
-> 所有data的数据 [obj](app.data)
-- 2. app.$props
-> 所有props的数据 [obj]
-- 3. app.$el
-> vue组件挂载到dom上的html节点
-- 4. app.$options
Vue实例对象的所有options, 包括默认和我们传进去的通过options.data是同一个对象
-- 5. app.$options.render = h => { return h(div, {}, 'new render function')}
-> 这里的 h其实是createElement()方法参数1. 标签名2. 存放标签属性的对象4.标签内容(如果还是标签,就会循环使用createElement())
,也可以不传参数 h
直接使用this.$createElement()方法
-- 6. app.$root
-> 指的的是挂载到真实html上的根实例对象
-- 7. app.$children
-> 在父组件里通过调用slots-> vue插槽 -> 在组件章节中会有讲解 -- 9.
app.refs-> 如果ref绑定组件到dom节点获取模板的真实dom节点, 如果绑定到组件, 获取组件的实例 -- 11.
app.parent, 获取父组件的实例. 可以改变父组件的属性, 不推荐, 在且只在new Vue实例的parent属性中定义一个父组件, 这样就可以获取这个组件的实例, 普通组件中的组件间的关系是vue渲染中指定了, 无法修改
// comp1 可以通过this.$parent获取comp0的实例
new Vue {
parent: comp0
}
2.Vue实例的属性
- Vue实例的方法
-- 1.app.$mount()
-> 将Vue实例挂载到真实Dom上
-- 2.app.$watch()
-> 监听data里的数据,作用同组件内的watch, 在组件销毁的同事, 需要注销掉, 否则会导致内存溢出, 在组件内生命的watch伴随这组件的消亡而注销, 而调用的$watch方法需要手动注销
// 监听data数据并得到注销watch的函数
const unWatch = app.$watch('text', (newText, oldText) => {})
// 注销watch
unWatch()
-- 3. app.$on
和app.$emit
-> 用来派发(emit)和监听(on)事件, 必须同事作用于同一vue对象否则不会冒泡 app.$once
-> 只触发一次
app.$on('test', (a, b) => {
console.log(`test emited ${a} ${b}`)
})
app.$emit('test', 1, 2)
-- 4. app.$forceUpdate -> 强制组件重新渲染一次
new Vue({
...,
data: {
obj: {} // vue是响应式的框架, 如果没有声明对象里边的属性而直接给这个属性赋值, 就不是响应式的, 可以在每次调用这个属性的时候, 执行一次app.$forceUpdate(), 但是这样性能太低
}
})
-- 5. app.$set()
-> 用来给date里的数组和对象添加响应式的属性, 相当于把某些值, 补到了data里
app.$set(app.obj, 'a', 123)
-- 6. app.$delete(app.obj, 'a')
-> app.$set()
的对应方法: 如果直接删除nextTick(callback())-> 这个和js的事件队列有关, vue的渲染过程是异步的,举个例子: 在setInterval()里, 声明一个变量
i并让
i++五次, 最后打印
i的值, 得到的结果不会是, 一个一个往上加, 而是5个5个的往上加, 这就是js事件队列 -> js会把当前的队列都执行一遍, 然后在循环一遍, 执行新添加的队列,如果是需要操作dom节点的, 肯定是要在dom全渲染完之后才行, $nextTick就是再dom完全渲染完之后, 才执行回调(如果你拿不到dom或者值跟新了页面没更新, 这个时候你可能会用到$nextTick) -- 8.
app.$destroy()` -> 主动销毁组件
-
vue组件的生命周期
- beforeCreate() -> 组件初始化就会执行
-- 初始化events和 lifecycle, reactive并没有初始化, 不要做数据的修改 - created()
-- created()之后会判断是否有el属性, 没有就等待$mount方法被调用,
初始化injections 和reactive - beforeMount() -> 将组件的template编译挂载到dom上 -> $el是vue绑定到的真事dom
中间有个 render(createElement) -> 在render之前会vue会查看是否有template属性, 有的话, 会通过render把template渲染到页面上, 没有的话就会显示vue挂载的节点 - mounted() -> $el是组件template里的数据
- beforeUpdate() -> 数据更新的时候执行
- updated()
- activated() -> keepalive激活的时候执行
- deactivated() - keepalive停用的时候执行
- beforeDestroy() - 组件销毁的时候触发
- destroyed()
- renderError(h, err){return h('div', {}, err.stack)} -> render()报错时会调用此方法, 只会在开发环境调用, 只管自己的组件的错误, 不能冒泡
- errorCaptured () {} -> 可以捕捉到子组件的报错, 用法类似renderError() 可以在线上使用
调用一次的: create类, mount类, destroy类
ssr调用的: create类
生命周期中vue实例有哪些区别? -> $el会有所不同
注: 在使用.vue开始方式, 都没有template, vue-loader会直接将.vue文件中的template解析成render()方法, 这样更快
-
vue的数据绑定
- class的数据绑定
:class="{active: isActive}" -> 如果isActive是true就添加active的类名
:class="['header', {active: isActive}, isActive ? 'red' : 'green']"
- style的数据绑定: 动态绑定的style会自动添加浏览器前缀
:style="[s1, s2, {color: 'red'}]"
data () {return { s1, s2 }}
-
computed和watch
- computed是vue的计算属性, 可以对data进行处理, 拥有缓存机制, 只有当相关数据改变的时候, 才会执行computed里的相关函数, 这也是为什么要用computed而不是在methods创建一个方法
computed写在标签上时候, 不用加(), 这是因为, computed是通过Object.defineProperty(obj, 'abc', { get: () => xxx, value: xxx }), 通过get方法可以直接以变量名的方式调用函数
{
computed: {
name () {
return first + last
}
}
}
// computed内部支持Object.defineProperty()的get和set函数
{
computed: {
name: {
get () {},
set (name) {在这里可以改变data中的数据(不推荐, 容易搞出死循环)}
}
}
}
- watch: 监听数据的变化, 当数据变化时才执行这个watch里的方法
watch: {
first (newData, oldData) {
console.log('change', newData, oldData)
}
}
// watch可以通过设置参数去立刻执行
在data里声明对象时, 有几个坑, 1. 如果为声明对象的属性, 那么只有第一次给obj属性赋值时会触发watch, 2. 即便在data里声明了obj.a,通过obj.a的方式修改数据, 也不会触发watch监听, watch只会监听obj的对象引用, 通过this.obj = {a:1}的方式才能被watch监听到,
watch: {
// obj: {
// handler (newData, oldData) {
// console.log('obj.a change')
// },
// immediate: true // 立即执行watch
// deep: true // 为true时候, watch会递归循环obj里的说有属性, 性能低
// }
// 这种方式可以监听obj的属性, 性能高用字符串里去写对象的深入调用
'obj.a': () => {
console.log('obj.a change')
}
}
- vue原生指令
指令修饰符: @click.stop: 组织冒泡, @keydown.13: 按回车, v-model.number="": 输入的数字为number类型, v-model.trim="": 输入的内容清除前后空格, @touchend.capture=""或@touchstart.capture="": 事件捕获
-
v-text
: 显示文本, 标签只能显示v-text里的内容 -
v-html
: 显示未转义的内容, 可以解析标签 -
v-show
: 原理display:none -
v-if
: 节点不放在dom流里, 动态增删节点, 性能低重绘 -
v-for
: 遍历数组和对象, 性能优化添加:key="内容不要index" -> 通过:key里边的内容(唯一的id)去判断是否重新渲染这一行:数组:v-for="(item, index) in items"
, 对象:v-for-"(val, key, index) in obj"
-
v-on:
简写@
: 绑定事件, 如果是dom节点:使用addEventListener, 如果是组件使用$on, 修饰符: .stop组织冒泡 -
v-bind:
简写:
:绑定变量 -
v-model
: 表单响应式的改变数据, 修饰符 .number: 数字为Number类型, .lazy: blur时显示数据, .trim: 清除前后空格
-- checkbox的v-model绑定一个变量, 只要这个变量为true就是选中状态, 2. 可以对多个checkbox使用数组, 每一个checkbox的value就是这数组对应的值
-- radio: v-model绑定的变量和radio的value相等时, 为选中状态 -
v-once
: {{}}只执行一次, 之后就不会检测 -
v-pre
: 直接输出{{}}和里边的内容
-
组件的定义
- 全局定义: Vue.component('ComP', component1)
- 组件内定义子组件: components: { ComP: component1 }
- props: 指定子组件的可配置行为
props: ['active', 'data']
props: {
active: {
type: Boolean,
default: false,
required: true
},
data: {
type: [String, Number]
}
}
// props的自定义核验
props: {
active: {
// type: Boolean,
validator (value) {
return typeof value === Boolean
}
}
}
-
组件的extend => 扩展vue,两种extend方式,
-- 第一种, 实例化扩展: 类似于子组件, 可以通过 propsData
传递props, 钩子函数先调用, component里的, data会覆盖component里的
const compVue = Vue.extend(component)
new CompVue({
el: '#root',
propsData: {
prop1: 'xxx'
}
})
-- 第二种在组件内的extends属性中什么要继承的组件
extends: component
如何去用呢? 在一个比较完善的组件里, 配置项太多, 或者还需要扩展, 这时候, 就可以在你的组件上extends这个组件, 然后去覆盖或拓展他的配置
-
组件的高级属性
-
slot
插槽:
-- 具名插槽:
const component = {
template = '<div>
<slot name="header"></slot>
<slot></slot>
</div>'
}
new Vue({
components: { ComP: component},
template: '
<com-p>
<p slot="header">我是header</p>
<p>我是默认的插槽</p>
</com-p>
'
})
-- 插槽传值
const component = {
template = '<div>
<slot name="header"></slot>
<slot :a="aaa" b="bbb"></slot>
</div>',
data () {
return {
a: 'a的值'
}
}
}
new Vue({
components: { ComP: component},
template: '
// 组件的ref是组件的实例(不推荐, 应该使用props), dom几点的ref是dom节点
<com-p ref="comp">
<p slot="header" ref="p">我是header</p>
<p scope-slot="obj">{{obj.a}}我是默认的插槽{{obj.b}}</p>
</com-p>
'
})
-
provide
和inject
: 往任意后代组件传值,provide
原生不支持reactive的, 需要用到Object.defineProperty()方法
const sunCom = {
inject: ['yeye', 'data'],
template: `<div>我是孙子</div>`,
mounted () {
console.log(this.$parent.$options.name) // er-com
console.log(this.yeye, this.data.value) //
}
}
const erCom = {
name: 'er-com',
components: {sunCom},
template: `<sun-com></sun-com>`
}
new Vue({
// provide必须是一个函数, 否则this实例不会出现的
provide () {
// 官方不推荐, 有可能会改变写法或弃用
// 通过defineProperty()自己指定一个get方法
const data = {}
// 解释一下下边的代码: 每次获取 value的时候, 都会直接调用get()方法, 设置为可枚举后, get方法就会每次获取最新的数据
// vue的reactive基础就是Object.defineProperty()里的get()方法
Object.defineProperty(data, 'value', {
get: () => this.value,
enumerable: true // 把属性设置成可枚举的, (可遍历)
})
return {
yeye: this,
data
}
},
components: {erCom},
template: `<er-com><er-com>`,
data: {
value: 'yeye的value'
}
})
-
vue之render()
{
...,
render() {
return this.$createElement('div', {标签属性}, 标签内容(还有标签就在调用[$createElement)]多节点用数组)
}
}
-
vue的原生组件:
-- <router-view>
-- <router-link>
-- <keep-alive>
-- <slot>
-- <transition>