深入Vue

构建Vue实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 手动构建元素
import Vue from 'vue/dist/vue.js' //注意,需要制定vue运行版本
const div = document.createElement('div')
document.body.appendChild(div)

new Vue({
el: div,
template: '<div>some content</div>'
})
如果在new实例时,没有指定el,可以进行,跟上面一样的效果
const app = new Vue({
template: '<div>some content</div>'
})
app.$mount('#root')
// 当执行的时候,模板文件会将el的节点进行替换

2. 通过html-webpack-plugin插件进行生成插入
const HtmlWebpackPlugin = require('html-webpack-plugin')
new HtmlWebpackPlugin({
template: './index.html'
}),

Vue实例属性

1
2
3
4
5
const app = new Vue({
el: div,
template: '<div>some content</div>'
})
该app常量就是Vue实例自动绑定的this,意味着下面的app.$属性,即为this.$属性
  • app.$data data()定义的值
  • app.$props 组件传值
  • app.$el ATS抽象树
  • app.$options 合成过后的属性-即为整个实例
  • app.$options.render = (h) => {return h(‘div’, {}, ‘new render content’ )} 当页面数据进行更新的时候,会触发这个函数
  • app.$root 就是app本身
  • app.$children 类似于react的props.children <div>这个div就是children值</div>
  • app.$parent 查找父组件 app.$ $parent.$options.name 一般配合extend使用
  • app.$slots
  • app.$scopedSlots
  • app.$refs ref指定的元素数组集合,可以进行操纵DOM
  • app.$isServer 服务端渲染用
  • app.$on(‘事件名’, (子组件params1, paramsTwo) => {})
  • app.$emit(‘事件名’, 传给父组件参数1, 参数2)
  • app.$set(app.obj, ‘a属性’, 修改的值)
  • app.$delete(app.obj, 要删除的属性)
  • app.$forceUpdate() 强制更新视图,与react类似
  • app.$destroy() 销毁实例,一般不会去做

内存溢出

如果使用watch方法,没有控制好,有可能会造成内存溢出,所以,在某些情况下可能需要移除watch

watch和computed都不要去修改监听的值,否则会造成无限循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const unWatch = this.$watch('text', (newVal,oldVal) => {})
unWatch() 注销watch

如果使用Vue模板文件,可以这样写
watch: {
text (newVal,oldVal) {})
}
模板文件的watch,会被编译成handler方法,如果我们手动写handler方法,可以使用immediate:true使watch立即执行
watch: {
text: {
handler (newVal, oldVal) {
do some thing
},
immediate: true,
deep: true // 默认为false 是否watch 对象键值 -遍历对象,消耗性能,解决办法监听'text.a'
}
}

生命周期

Vue跟react的更新生命周期形式很类型, Vue:beforeUpdate | update — React: componentWillUpdate | compoentDidUpdate。不同的是,react在执行componentDidUpdate前,会先执行render()方法,Vue没有

  • vue跟react都有强制更新视图的操作this.forceUpdate()
1
2
3
4
5
6
7
8
9
10
Vue编译我们的template模板,最后为render方法输出。render方法在mounted前进行调用,所以beforeMount()生命周期this-不指向Vue实例
render (h) {
throw new TypeError('render error')
},
renderError (h, err) {
return h('div', {}, err.stack)
// 这个不会捕获子组件的报错,只能在生产环境下使用
},
errorCaptured (h, err) {}
//这个会向上冒泡,并能在正式环境中使用

原生指令

  • v-model.number 转换字符串为数字
  • v-model.trim 去除空格
  • v-model.lazy change的时候才进行改变
  • v-pre 不进行解析表达式,页面展示模板字符串
  • v-once 只绑定一次
  • v-cloak 使用场景少

props严格验证

1
2
3
4
5
6
7
8
9
props: {
active: {
type: Boolean,
validator (value) {
// value就是传进来的值
return typeof value === 'boolean'
}
}
}

双向绑定

vue的双向绑定和react基本一致,都是采用e.target.value的方式进行绑定

子组件=

1
2
3
4
5
<input type="text" @input="handleInput">

function handleInput(e) {
this.$emit('input', e.target.value)
}

父组件 =

value=”value” @input=”value = arguments[0]”

插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
定义一个组件
const component = {
template: `
<div>
<slot></slot>
<slot name="body"></slot>
</div>
`
}
调用组件
<comp-one>
<span>show the slot</span>
<span slot="body">show the bodyName slot</span>
</comp-one>
作用域插槽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
作用域插槽的概念
一般来讲,组件内部的变量是根据当前引用组件的变量来变化的
如果使用了slot-scope,就可以使用当前组件内部定义的变量+引用组件的变量
定义组件
const component = {
template: `
<div>
<slot value="456"></slot>
</div>
`
}
引用组件-通过props.定义值来调用,引用组件的值可以直接写
<comp-one>
<span slot-scope="props">{{props.value}}{{currentData}}</span>
</comp-one>

跨组件通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
爷爷级组件
provide() {
const data = {}
Object.defineProperty(data, 'value', {
get: () => this.value,
enumerable: true //提供可读
})
return {
yeye: this,
value: this.value
}
}
孙级组件
inject: ['yeye', 'data']
调用-- <span>{{data.value}}</span>

这种方法适用于简单的组件级通信,解决只能父子组件通信的问题。如果交互的数据量比较多,可以采用vuex通信

render方法

首先我们需要明白:构建Vue组件有三种方式 :template / renderFunction / js

在mounted生命周期前调用render方法,默认传入this.$createElement方法,该方法可以创建一个vnode节点,用来跟DOM节点进行对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
模板文件为
template: `
<comp-one ref="comp">
<span ref="span">{{value}}</span>
</comp-one>
`
---------------------转换成render方法---------------------
render(createElement) {
return createElement(
'comp-one',
{
ref: 'comp'
},
[
createElement('span', {
ref: 'span'
}, this.value)
]
)
}
注意点: 加入子节点的的时候,需要使用数组createdElement('节点名称',{属性}, 值)
如果是slot,则是
template: `
<div :style="style">
<slot></slot>
</div>
`
render(createdElement) {
return createdElement('div', {style: this.style}, this.$slots.default)
}
事件的渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
------------------组件内部------------------
props: ['props1'],
name: 'comp',
template: `
<div :style="style">
<slot></slot>
</div>
`
--------------------转换render--------------
render(createdElement) {
return createdElement('div',
{
style: this.style,
on: {
click: () => {this.$emit('click')}// 注意与nativeOn的区别
}
}, [
this.$slots.default,
this.props1
])
}

--------------------------引用组件-------------------------
methods: {
handleClick() {}
},
data() {
return {
value: 123
}
}
render(createElement) {
return createElement(
'comp-one',
{
ref: 'comp',
props: {
props1: this.value
},
on: {
click: this.handeleClick
}
},
[
createElement('span', {
ref: 'span'
}, this.value)
]
)
}

如果采用nativeOn: {
click: this.handleClick//是将事件直接绑定在根节点上,不需要采用on/emit的方式
//意味着组件上不需要定义on事件就可以直接触发了
}
具名插槽的render方式
1
2
3
4
5
6
7
8
9
10
11
12
普通插槽使用this.$slots.default生成一个vnode节点
具名插槽使用this.$slots.名称生成vnode节点
------定义组件------
render (createElement) {
return createElement('div', {}, this.$slots.header)
}
------引用组件-------
render (createElement) {
return createElement('div', {}, [
createElement('span', {slot: 'header'},'123')
])
}
添加原生DOM属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
------引用组件-------
render (createElement) {
return createElement('div', {}, [
createElement('span', {
slot: 'header',
domProps: {
innerHTML: '<span>原生DOM添加了元素节点</span>'
},
attrs: {
id: 'test-id'//给原生DOM上添加了id属性
}
},'123')
])
}
文章目录
  1. 1. 构建Vue实例
  2. 2. Vue实例属性
    1. 2.0.1. 内存溢出
    2. 2.0.2. 生命周期
    3. 2.0.3. 原生指令
    4. 2.0.4. props严格验证
    5. 2.0.5. 双向绑定
    6. 2.0.6. 插槽
      1. 2.0.6.0.0.1. 作用域插槽
  • 2.0.7. 跨组件通信
  • 2.0.8. render方法
    1. 2.0.8.0.0.1. 事件的渲染
    2. 2.0.8.0.0.2. 具名插槽的render方式
    3. 2.0.8.0.0.3. 添加原生DOM属性
  • |