构建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' 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')
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 } }
|
生命周期
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) { 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')} } }, [ 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 }
|
具名插槽的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' } },'123') ]) }
|