美文网首页
vue(一):vue 知识点

vue(一):vue 知识点

作者: 林ze宏 | 来源:发表于2019-07-07 21:03 被阅读0次

目录

  • 1 Vue 实例
  • 2 Vue 的生命周期方法
  • 3 Vue 的数据绑定
  • 4 computed 和 watch 使用场景和方法
  • 5 Vue 的原生指令
  • 6 Vue 的组件之组件的定义
  • 7 Vue 的组件之组件的继承 extends
  • 8 Vue 的组件之自定义双向绑定
  • 9 Vue 的组件之高级属性
    9.1 插槽 slot
    9.2 跨级组件数据传递 provide 和 inject
  • 10 Vue 的组件之 render function
  • 11 vue-loader 相关配置

1 Vue 实例

官网:https://cn.vuejs.org/v2/api/index.html#vm-listeners

实例:也就是通过 new 创建出来的对象。也就是在各个组件中的 this 对象。

1:实例相关属性:

  • $data
  • $props
  • $el
  • $options
  • $parent
  • $root
  • $children
  • $refs
  • $slots
  • $scopedSlots
  • $isServer
  • $attrs
  • $listeners
const app = new Vue({
  // el: '#root',
  template: '<div ref="div">{{text}} {{obj.a}}</div>',
  data: {
    text: 0,
    obj: {}
  }
})

// app 即为 vue 的实例
app.$mount('#app')


组件:
<template>
  <div class="tab-container"></div>
</template>

<script>
export default {
  props: {
    todos: {
      type: Array,
      required: true
    },
    state: {
      type: String,
      required: true
    }
  },
  inheritAttrs: true,
  mounted() {
    const vm = this; // !!!this 即为 vue 实例
    console.log(vm.$attrs);
    console.log(vm.$listeners);
  }
};
</script>


例子

Todos.vue 父组件:

<template>
  <div class="main-content" ref="todos">
    <input
      class="ipt-text"
      autofocus="autofocus"
      @keyup.enter="addItem"
      v-model="iptText"
       ref="todos-input"
    />
    <item
      v-for="todo in filteredTodos"
      :key="todo.id"
      :todo="todo"
      @checkCompleted="checkCompleted"
      @removeItem="removeItem"
    />
    <tab
      :todos="todos"
      @clearNoActive111="clearNoActive"
      @filterItem="filterItem"
      :state="state"
      v-bind="$attrs"
    />

    <hr/>
    <blog-post>
      <template v-slot:header>
        <h1>About Me</h1>
      </template>

      <p>Here's some page content, which will be included in vm.$slots.default, because it's not inside a named slot.</p>

      <template v-slot:footer>
        <p>Copyright 2016 Evan You</p>
      </template>

      <p>If I have some content down here, it will also be included in vm.$slots.default.</p>.
    </blog-post>
  </div>
</template>

<script>
import Vue from 'vue';
import Item from './Item.vue';
import Tab from './Tab.vue';

Vue.component('blog-post', {
  render: function(createElement) {
    var header = this.$slots.header;
    var body = this.$slots.default;
    var footer = this.$slots.footer;
    return createElement('div', [
      createElement('header', header),
      createElement('main', body),
      createElement('footer', footer)
    ]);
  },
  mounted() {
    const vm = this;
    console.log(vm.$slots);
    console.log(vm.$scopedSlots);
  }
});

export default {
  name: 'main',
  components: { Item, Tab },
  data() {
    return {
      iptText: '',
      index: 0,
      state: 'all',
      todos: [],
      cacheTodos: []
    };
  },
  methods: {
    addItem(e) {
      this.todos.unshift({
        id: this.index++,
        name: e.target.value,
        completed: false
      });
      this.iptText = '';
    }
  },
  mounted() {
    const vm = this;
    console.log(vm.$data);
    console.log(vm.$props);
    console.log(vm.$el);
    console.log(vm.$options);
    console.log(vm.$parent);
    console.log(vm.$root);
    console.log(vm.$children);
    console.log(vm.$refs);
    console.log(vm.$isServer);
  }
};
</script>

<style scoped>
</style>


Tab.vue 子组件:

<template>
  <div class="tab-container">
    <div class="tab-content">
      <span>{{isCheckTodoNum}} item check</span>
      <div class="btn-group">
        <span :class="{active: state === 'all', btn : true}" @click="$emit('filterItem', 'all')">all</span>
        <span :class="{active: state === 'active', btn : true}" @click="$emit('filterItem', 'active')">active</span>
        <span :class="{active: state === 'noActive', btn : true}" @click="$emit('filterItem', 'noActive')">noActive</span>
      </div>
      <button @click="$emit('clearNoActive')">clear noActive</button>
    </div>
  </div>

</template>

<script>
export default {
  props: {
    todos: {
      type: Array,
      required: true
    },
    state: {
      type: String,
      required: true
    }
  },
  computed: {
    isCheckTodoNum: function() {
      return this.$parent.todos.filter(todo => todo.completed).length;
    }
  },
  inheritAttrs: true,
  mounted() {
    const vm = this;
    console.log(vm.$attrs);
    console.log(vm.$listeners);
  }
};
</script>

<style scoped>
</style>


说明:

  • $attrs

1:$attrs 即为父组件传递给子组件,而子组件没有使用过的 props 属性;

2:如果父组件中没有使用过的属性,想向下深层传递,例如:向孙组件传递没有使用过的 props 属性,则子组件需要使用 v-bind="$attrs"

3:$attrs 结合 inheritAttrs: true

例如:

父组件:
<template>
  <div class="hello">
    <Todos
      :dd="'dd'"
      :ss="'ss'"
    />
  </div>
</template>

子组件:Todos.vue
<template>
    <tab
      :todos="todos"
      @clearNoActive="clearNoActive"
      @filterItem="filterItem"
      :state="state"
      v-bind="$attrs" // !!!
    />
</template>

<script>
import Tab from './Tab.vue';
</script>


孙组件:Tab .vue
<script>
export default {
  props: {
    todos: {
      type: Array,
      required: true
    },
    state: {
      type: String,
      required: true
    }
  },
  inheritAttrs: true,
  mounted() {
    const vm = this;
    console.log(vm.$attrs);
  }
};
</script>

参考:https://www.jianshu.com/p/ce8ca875c337

2:实例方法

  • $watch(监听事件)
  • $on(注册事件)
  • $emit (触发事件 $emit$on 触发的事件必须为同一个实例对象)
  • $forceUpdate(强制重新更新渲染)
  • $set(设置数据,Vue 会监听该属性)
  • $delete(删除数据,会自动删除 Vue 绑定监听该属性上的事件)
  • $nextTick(获取真实DOM,回调)
  mounted() {
    const unWatch = this.$watch('iptText', (newText, oldText) => {
      // 组件销毁的时候,要手动注销
      console.log(`${newText} : ${oldText}`);
    });
    setTimeout(() => {
      unWatch();
    }, 5000);
  }

也可以通过 watch 方法。
<template>
  <div class="main-content" ref="todos">
    <input
      class="ipt-text"
      autofocus="autofocus"
      @keyup.enter="addItem"
      v-model="iptText"
       ref="todos-input"
    />
  </div>
</template>

<script>

export default {
  name: 'main',
  components: { Item, Tab },
  data() {
    return {
      iptText: ''
    }
  },
  watch: { // 主动注销
    iptText(newText, oldText) {
      console.log(`${newText} : ${oldText}`);
    }
  }
};
</script>


import Vue from 'vue'

const app = new Vue({
  // el: '#root',
  template: '<div ref="div">{{text}} {{obj.a}}</div>',
  data: {
    text: 0,
    obj: {}
  }
  // watch: { // 主动注销
  //   text (newText, oldText) {
  //     console.log(`${newText} : ${oldText}`)
  //   }
  // }
})

app.$mount('#root')

let i = 0
setInterval(() => {
  i++
  // app.text += 1
  // app.text += 1

  app.$set(app.obj, 'a', i)
  // app.obj.a = i
  // app.$forceUpdate()
  // app.$delete(app.obj, 'a')
}, 1000)

// $emit 和 $on 触发的事件必须为同一个实例对象

app.$on('test', (a, b) => {
  console.log(`test emited ${a} ${b}`)
})

// app.$once('test', (a, b) => {
//   console.log(`test emited ${a} ${b}`)
// })

setInterval(() => {
  app.$emit('test', 1, 2)
}, 1000)


// app.$forceUpdate() // 强制组件重新渲染

// $nextTick


2 Vue 的生命周期方法

生命周期
import Vue from 'vue'

const app = new Vue({
  // el: '#root',
  // template: '<div>{{text}}</div>',
  data: {
    text: 0
  },
  beforeCreate() {
    console.log(this.$el, 'beforeCreate')
  },
  created() {
    console.log(this.$el, 'created')
  },
  beforeMount() { // 服务端渲染不会触发,因为服务端渲染,根本就跟 DOM 挂载无关
    console.log(this.$el, 'beforeMount')
  },
  mounted() { // 服务端渲染不会触发,因为服务端渲染,根本就跟 DOM 挂载无关
    console.log(this.$el, 'mounted')
  },
  beforeUpdate() {
    console.log(this, 'beforeUpdate')
  },
  updated() {
    console.log(this, 'updated')
  },
  activated() { // 在组件章节讲解
    console.log(this, 'activated')
  },
  deactivated() { // 在组件章节讲解
    console.log(this, 'deactivated')
  },
  beforeDestroy() {
    console.log(this, 'beforeDestroy')
  },
  destroyed() {
    console.log(this, 'destroyed')
  },
  render(h) {
    throw new TypeError('render error')
    // console.log('render function invoked')
    // return h('div', {}, this.text)
  },
  renderError(h, err) { // 上线打包不能被调用
    return h('div', {}, err.stack)
  },
  errorCaptured() {
    // 如果在根组件定义,所有子组件报错都可以捕获到,除非子组件取消冒泡事件。
    // 会向上冒泡,并且正式环境可以使用
  }
})

app.$mount('#root')
// setInterval(() => {
//   app.text = app.text += 1
// }, 1000)

setTimeout(() => {
  app.$destroy() // 自动销毁组件
}, 1000)

3 Vue 的数据绑定

:class、:style、v-html、@click

参考 list-todo 例子:https://www.jianshu.com/writer#/notebooks/37329580/notes/48268401

import Vue from 'vue'

new Vue({
  el: '#root',
  // template: `
  //   <div :id="aaa" @click="handleClick">
  //     <p v-html="html"></p>
  //   </div>
  // `,
  template: `
    <div
      :class="[{ active: isActive }]"
      :style="[styles, styles2]"
    >
      <p>{{getJoinedArr(arr)}}</p>
    </div>
  `,
  data: {
    isActive: false,
    arr: [1, 2, 3],
    html: '<span>123</span>',
    aaa: 'main',
    styles: {
      color: 'red',
      appearance: 'none'
    },
    styles2: {
      color: 'black'
    }
  },
  methods: {
    handleClick () {
      alert('clicked') // eslint-disable-line
    },
    getJoinedArr (arr) {
      return arr.join(' ')
    }
  }
})


4 computed 和 watch 使用场景和方法

参考:https://cn.vuejs.org/v2/guide/computed.html

import Vue from 'vue'

new Vue({
  el: '#root',
  template: `
    <div>
      <p>Name: {{name}}</p>
      <p>Name: {{getName()}}</p>
      <p>Number: {{number}}</p>
      <p>FullName: {{fullName}}</p>
      <p><input type="text" v-model="number"></p>
      <p>FirstName: <input type="text" v-model="firstName"></p>
      <p>LastName: <input type="text" v-model="lastName"></p>
      <p>Name: <input type="text" v-model="name"></p>
      <p>Obj.a: <input type="text" v-model="obj.a"></p>
    </div>
  `,
  data: {
    firstName: 'Jokcy',
    lastName: 'Lou',
    number: 0,
    fullName: '',
    obj: {
      a: 0
    }
  },
  computed: { // 属性需要通过计算再显示,一般 computed 不要设置值,computed 就是通过计算重新组合需要显示的值。
    // name() {
    //   console.log('new name')
    //   return `${this.firstName} ${this.lastName}`
    // },
    name: {
      get() {
        console.log('new name')
        return `${this.firstName} ${this.lastName}`
      },
      set(name) {
        const names = name.split(' ')
        this.firstName = names[0]
        this.lastName = names[1]
      }
    }
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  },
  // watch: { // 主要场景:监听某个属性
  //   'obj.a': {
  //     handler() { // 实际执行就是 handler 方法
  //       console.log('obj.a changed')
  //       this.obj.a += 1
  //     },
  //     immediate: true // 声明该属性之后,立即执行,而不是等到属性变化后才执行
  //     // deep: true // 如果直接监听 obj,使用 deep: true,则对象的所有属性都会被监听,性能开销比较大;可以直接监听对应的属性 'obj.a'
  //   }
  // },
  methods: {
    getName() {
      console.log('getName invoked')
      return `${this.firstName} ${this.lastName}`
    }
  }
})


说明:

  • 1:computed:属性需要通过计算再显示,一般 computed 不要设置值,computed 就是通过计算重新组合需要显示的值。

  • 2:watch:主要场景是监听某个属性,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

watch 在首次加载时并不会马上触发,如果希望首次触发,可以配置:

immediate: true

watch 监听对象时,如果对象的某个属性发生改变是,是不会触发监听事件的,因为对象的地址并没有改变,监听的是对象的地址,但是,可以配置:

deep: true

如果直接监听 obj,使用 deep: true,则对象的所有属性都会被监听,性能开销比较大;可以直接监听对应的属性 'obj.a'

<p>Reversed message: "{{ reversedMessage() }}"</p>

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

好得多了,不是吗?

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

5 Vue 的原生指令

  • v-pre
  • v-once
  • v-html
  • v-if
  • v-else-if
  • v-else
  • v-model
  • v-model.number(输入为number)
  • v-model.trim(自动去除首尾空格)
  • v-model.lazy(input 事件变为 change,懒加载)
  • v-for

data 数组绑定复选框、以及单选框等。

import Vue from 'vue'

new Vue({
  el: '#root',
  template: `
    <div>
      <div v-pre>Text: {{text}}</div>
      <div v-once>Text: {{text}}</div>
      <div>Text: {{text}}</div>
      <div v-if="text === 0">Else Text: {{text}}</div>
      <div v-else>else content</div>
      <div v-html="html"></div>
      <input text="text" v-model="text">
      <input text="text" v-model.number="text">
      <input text="text" v-model.trim="text">
      <input text="text" v-model.lazy="text">
      <input type="checkbox" v-model="active">
      <div>
        <input type="checkbox" :value="1" v-model="arr">
        <input type="checkbox" :value="2" v-model="arr">
        <input type="checkbox" :value="3" v-model="arr">
      </div>
      <div>
        <input type="radio" value="one" v-model="picked">
        <input type="radio" value="two" v-model="picked">
      </div>
      <ul>
        <li v-for="(item, index) in arr" :key="item">{{item}}:{{index}}</li>
      </ul>
      <ul>
        <li v-for="(val, key, index) in obj">{{val}}:{{key}}:{{index}}</li>
      </ul>
    </div>
  `,
  data: {
    arr: [2, 3],
    obj: {
      a: '123',
      b: '456',
      c: '789'
    },
    picked: '',
    text: 0,
    active: false,
    html: '<span>this is html</span>'
  }
})


6 Vue 的组件之组件的定义

  • 方式一:全局注册
Vue.component('CompOne', compoent) 

规范:定义大写驼峰 CompOne,使用的时候,小写横杆 comp-one

  • 方式二:组件内部
components: {
  CompOne: compoent
},

例子:

import Vue from 'vue'

const compoent = {
  props: {
    // active: Boolean,
    active: {
      // type: Boolean,
      // required: true,
      validator(value) { // !!!自定义校验
        return typeof value === 'boolean'
      }
      // default() { // !!!默认值必须和 data 一样通过函数返回值
      //   return false
      // }
    },
    propOne: String
  },
  template: `
    <div>
      <input type="text" v-model="text">
      <span @click="handleChange">{{propOne}}</span>
      <span v-show="active">see me if active</span>
    </div>
  `,
  data() { // 必须是函数 return
    return {
      text: 0
    }
  },
  methods: {
    handleChange() {
      this.$emit('change')
    }
  }
}

// Vue.component('CompOne', compoent) // 方式一,全局;规范:定义大写驼峰 CompOne,使用的时候,小写横杆comp-one
/* eslint-disable no-new */

new Vue({
  components: { // 方式二
    CompOne: compoent
  },
  data: {
    prop1: 'text1'
  },
  methods: {
    handleChange() {
      this.prop1 += 1
    }
  },
  mounted() {
    console.log(this.$refs.comp1)
  },
  el: '#app',
  template: `
    <div>
      <comp-one ref="comp1" :active="true" :prop-one="prop1" @change="handleChange"></comp-one>
      <comp-one :active="true" propOne="text2"></comp-one>
    </div>
  `
})

** 说明:组件 props 属性定义和校验**

  props: {
    // active: Boolean,
    active: {
      // type: Boolean,
      // required: true,
      validator(value) { // !!!自定义校验
        return typeof value === 'boolean'
      }
      // default() { // !!!默认值必须和 data 一样通过函数返回值
      //   return false
      // }
    },
    propOne: String
  },

7 Vue 的组件之组件的继承 extends

extends: compoent,

const CompVue = Vue.extend(compoent)

组件继承之后,数据会覆盖,但是,组件各自的生命周期不会覆盖;如果要获取继承组件的 props,不能通过 props 获取,应该通过 propsData 属性。

例子:

import Vue from 'vue'

const compoent = {
  props: {
    active: Boolean,
    propOne: String
  },
  template: `
    <div>
      <input type="text" v-model="text">
      <span @click="handleChange">{{propOne}}</span>
      <span v-show="active">see me if active</span>
    </div>
  `,
  data() {
    return {
      text: 0
    }
  },
  mounted() {
    console.log('comp mounted')
  },
  methods: {
    handleChange() {
      this.$emit('change')
    }
  }
}

const componet2 = {
  extends: compoent,
  data() {
    return {
      text: 1
    }
  },
  mounted() {
    // this.$parent.text = 12345; // 可以改变父组件的数据,但是不要修改,会混乱!!!
    console.log(this.$parent.$options.name) // Root
  }
}

// const CompVue = Vue.extend(compoent)

// new CompVue({
//   el: '#root',
//   propsData: { // 通过 props 拿不到
//     propOne: 'xxx'
//   },
//   data: { // 合并属性会覆盖
//     text: '123'
//   },
//   mounted () { // 组件生命周期不会覆盖
//     console.log('instance mounted')
//   }
// })

const parent = new Vue({
  name: 'parent111'
})

/* eslint-disable no-new */
new Vue({
  parent: parent,
  name: 'Root',
  el: '#app',
  mounted() {
    console.log(this.$parent.$options.name) // parent111
  },
  components: {
    Comp: componet2
  },
  data: {
    text: 23333
  },
  template: `
    <div>
      <span>{{text}}</span>
      <comp></comp>
    </div>
  `
})


8 Vue 的组件之自定义双向绑定

  • 定义组件,给 input 输入框,绑定 @input 事件 和 :value 值
const component = {
  template: `
    <div>
      <input type="text" @input="handleInput" :value="value">
    </div>
  `,
  methods: {
    handleInput(e) {
      this.$emit('input', e.target.value)
    }
  }
}

使用组件:

import Vue from 'vue'

/* eslint-disable no-new */
new Vue({
  components: {
    CompOne: component
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  template: `
    <div>
      <comp-one :value="value" @input="value = arguments[0]">默认 value + input 事件</comp-one>
    </div>
  `
})

  • 等同于 v-model:( v-model 其实就是 value 值 + input 事件的绑定)
import Vue from 'vue'

/* eslint-disable no-new */
new Vue({
  components: {
    CompOne: component
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  template: `
    <div>
      <comp-one v-model="value"></comp-one>
    </div>
  `
})
  • input 事件改为 change:
const component = {
  model: {
    event: 'change' // 默认为 input
  },
  template: `
    <div>
      <input type="text" @change="handleInput" :value="value">
    </div>
  `,
  methods: {
    handleInput(e) {
      this.$emit('change', e.target.value)
    }
  }
}
<comp-one :value="value" @change="value = arguments[0]"></comp-one>
  • 更改绑定的 value 属性为自定义属性,需要配置 model、props
const component = {
  model: {
    prop: 'value1',
    event: 'change' // 默认为 input
  },
  props: ['value1'],
  // props: ['value'],  // 默认绑定为 value
  template: `
    <div>
      <input type="text" @input="handleInput" :value="value1">
    </div>
  `,
  methods: {
    handleInput(e) {
      this.$emit('change', e.target.value)
      // this.$emit('input', e.target.value)
    }
  }
}

<comp-one :value1="value" @change="value = arguments[0]"></comp-one>

例子:

import Vue from 'vue'

const component = {
  model: {
    prop: 'value1',
    event: 'change' // 默认为 input
  },
  props: ['value1'],
  // props: ['value'],  // 默认绑定为 value
  template: `
    <div>
      <input type="text" @input="handleInput" :value="value1">
    </div>
  `,
  methods: {
    handleInput(e) {
      this.$emit('change', e.target.value)
      // this.$emit('input', e.target.value)
    }
  }
}
/* eslint-disable no-new */
new Vue({
  components: {
    CompOne: component
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  template: `
    <div>
      <comp-one :value="value" @input="value = arguments[0]">默认 value + input 事件</comp-one>
      <comp-one :value1="value" @change="value = arguments[0]"></comp-one>
      <comp-one v-model="value"></comp-one>
    </div>
  `
})

 v-model 其实就是 value 值 + input 事件的绑定。


9 Vue 的组件之高级属性

9.1 插槽 slot

  • 通过 name 指定对应的 slot
  • 在组件配置 slot-scope 获取插槽 slot 属性
/* eslint-disable no-new */

import Vue from 'vue'

const component = {
  name: 'comp',
  // template: `
  //   <div :style="style">
  //     <div class="header">
  //       <slot name="header"></slot>
  //     </div>
  //     <div clas s="body">
  //       <slot name="body"></slot>
  //     </div>
  //   </div>
  // `,
  template: `
    <div :style="style">
      <slot></slot>

      <slot name="header"></slot>
      <slot name="footer"></slot>

      <slot :value="value" aaa="111"></slot>
    </div>
  `,
  data() {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  mounted() {
    console.log(this.$refs.comp.value, this.$refs.span) // 一般不要使用 ref,避免滥用
  },
  template: `
    <div>
      <comp-one>
        <span>默认</span>
      </comp-one>


      <comp-one>
        <span slot="header">name 指定 header 位置</span>
      </comp-one>
      <comp-one>
        <span slot="footer">name 指定 footer 位置</span>
      </comp-one>

      <comp-one>
        <span slot="footer">name 指定 footer 位置,变量: {{value}}</span>
      </comp-one>


      <comp-one ref="comp">
        <span slot-scope="props" ref="span">{{props.value}} {{props.aaa}} {{value}}</span>
      </comp-one>
      <input type="text" v-model="value" />
    </div>
  `
})


9.2 跨级组件数据传递 provide 和 inject

这种方式,孙组件通过 provide 注入的 value 值并不会跟随父组件实时渲染。

/* eslint-disable no-new */
import Vue from 'vue'

const ChildComponent = {
  template: '<div>child component: {{value}}</div>',
  inject: ['yeye', 'value'], // 孙子获取父辈实例,需要有上下级关系!!!
  mounted() {
    console.log(this.yeye, this.value)
  }
}

const component = {
  name: 'comp',
  components: {
    ChildComponent
  },
  template: `
    <div :style="style">
      <slot :value="value" aaa="111"></slot>
      <child-component />
    </div>
  `,
  data() {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  provide() {
    return { // value 默认不会提供渲染
      yeye: this, // !!!
      value: this.value
    }
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  mounted() {
    console.log(this.$refs.comp.value, this.$refs.span) // 一般不要使用 ref,避免滥用
  },
  template: `
    <div>
      <comp-one ref="comp">
        <span slot-scope="props" ref="span">{{props.value}} {{props.aaa}} {{value}}</span>
      </comp-one>
      <input type="text" v-model="value" />
    </div>
  `
})


provide 和 inject 改进,数据跟随实时渲染

/* eslint-disable no-new */
import Vue from 'vue'

const ChildComponent = {
  template: '<div>child component: {{data.value}}</div>',
  inject: ['yeye', 'data'], // 孙子获取父辈实例,需要有上下级关系!!!
  mounted() {
    console.log(this.yeye, this.data.value)
  }
}

const component = {
  name: 'comp',
  components: {
    ChildComponent
  },
  template: `
    <div :style="style">
      <slot :value="value" aaa="111"></slot>
      <child-component />
    </div>
  `,
  data() {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  provide() {
    const data = {}

    Object.defineProperty(data, 'value', {
      get: () => this.value,
      enumerable: true
    })

    return {
      yeye: this, // !!!
      data
    }
  },
  el: '#app',
  data() {
    return {
      value: '123'
    }
  },
  mounted() {
    console.log(this.$refs.comp.value, this.$refs.span) // 一般不要使用 ref,避免滥用
  },
  template: `
    <div>
      <comp-one ref="comp">
        <span slot-scope="props" ref="span">{{props.value}} {{props.aaa}} {{value}}</span>
      </comp-one>
      <input type="text" v-model="value" />
    </div>
  `
})


10 Vue 的组件之 render function

在组件中,template 模板最终会被 Vue 编译成 render function,render 方法接收一个 createElement 方法参数,也可以通过内部属性 this.$createElement;

createElement 方法接收三个参数,第一个为对应的节点名称,第二个为节点的属性,第三个为数组,可以是节点的数据或者内容,也可以继续通过 createElement 方法创建子节点。

import Vue from 'vue'

const component = {
  props: ['props1'],
  name: 'comp',
  // template: `
  //   <div :style="style">
  //     <slot></slot>
  //   </div>
  // `,
  render(createElement) {
    return createElement('div', {
      style: this.style
      // on: {
      //   click: () => { this.$emit('click') }
      // }
    }, [
        this.$slots.header, // this.$slots.default
        this.props1
      ])
  },
  data() {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  el: '#root',
  data() {
    return {
      value: '123'
    }
  },
  mounted() {
    console.log(this.$refs.comp.value, this.$refs.span)
  },
  methods: {
    handleClick() {
      console.log('clicked')
    }
  },
  // template: ` 编译成js函数render方法;
  //   <comp-one ref="comp">
  //     <span ref="span">{{value}}</span>
  //   </comp-one>
  // `,
  render(createElement) { // this.$createElement;template编辑成render,整个流程是通过vue-loader处理的
    return createElement(
      'comp-one',
      {
        ref: 'comp',
        props: {
          props1: this.value
        },
        // on: {
        //   click: this.handleClick
        // },
        nativeOn: { // 绑定在组件根节点原生DOM上,自动处理
          click: this.handleClick
        }
      },
      [
        createElement('span', {
          ref: 'span',
          slot: 'header',
          // domProps: {
          //   innerHTML: '<span>sssssss</span>'
          // },
          attrs: {
            id: 'test-id'
          }
        }, this.value)
      ]
    )
  }
})


11 vue-loader 相关配置

配置参考:

相关文章

  • Vue知识点合集

    Vue vue.js中el是什么vue 基础知识Vue杂七杂八的知识点(此篇比较老了)指令vue v-text &...

  • 118页Vue面试题总结,为面试提前做准备

    Vue面试题文档内容主要包括vue-cli工程,vue核心知识点,vue-router,vuex,http请求,U...

  • Vue部分基础知识点总结

    Vue基础知识点: 1.Vue:过滤html标签 ----{{{数据名}}}. 2.Vue:单次插值语法: ...

  • 学习链接

    vue-router教程总结 vue生命周期和钩子函数 详解vue生命周期 Vue2.0八——知识点整理 vuex...

  • vue学习(25)自定义指令

    知识点 1:局部指令new Vue({directives:{指令名,配置对象}})new Vue({direct...

  • Vue真是太好了 壹万多字的Vue知识点 超详细!

    《Vue真是太好了 壹万多字的Vue知识点 超详细!》 ---- ️️、Vue和其他两大框架的区别 Angular...

  • Vue核心小知识点

    一. Vue核心小知识点 1、vue中 key 值的作用 key 的特殊属性主要用在 Vue的虚拟DOM算法,在新...

  • (十)Vue中的is和操作DOM

    本节知识点 Vue 中的is 问题 (1) Vue中的is属性 vue中is的属性引入是为了解决dom结构中对放入...

  • Vue知识点

    本文主要总结了一些vue的知识点 , 可供面试和平时学习使用。 1. Vue的优点?Vue的缺点? 优点:渐进式,...

  • Plan

    一:Plan On Work:知识点 1、vue(MVVM、vue原理、使用中遇到的问题、相关插件、环境搭建) 2...

网友评论

      本文标题:vue(一):vue 知识点

      本文链接:https://www.haomeiwen.com/subject/sgbihctx.html