Vue组件开发-高级玩法

时间:2022-07-25
本文章向大家介绍Vue组件开发-高级玩法,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在文章《Vue组件开发三板斧:prop、event、slot》中聊了常用的组件开发常用API和一些采坑心得,这里,再说说一些可能不太常用的高级玩法,可参考https://cn.vuejs.org/v2/api/

1. 组件挂载

方式一:components属性

我们常用的创建组件方式就是文件声明,例如,在一个假设的 headTop.js 或 headTop.vue 文件中定义组件。然后通过components 引入组件,将其挂载在DOM节点上。

// layout.vue文件
<template>
  <div class="fillcontain">
    <head-top></head-top>
    <div class="table_container container">
      <div class="container_wrap">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script>
import headTop from '@/components/headTop'

export default {
  name: 'layout',
  components: {
    headTop // 引用组件
  }
}
</script>

<style lang="less">
@import '../style/mixin';
.table_container {
  min-height: calc(100% - 100px);
}
</style>

组件headTop是挂载在组件layout中某个DOM节点下。

方式二:$mount

还有两种方式可以创建组件:

  • new Vue()
  • Vue.extend()

new Vue()创建一个 Vue 实例时,都会有一个选项 el,可以用来指定实例的根节点。如果不写 el 选项,那组件就处于未挂载状态。看看最顶层的App.vue是如何挂载到根节点上的:

import App from './App'
......
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

Vue.extend 是基于 Vue 构造器,创建一个“子类”,它的参数跟 new Vue 的基本一样,但是data写法和组件类似,需要返回一个函数。

import Vue from 'vue';

const AlertComponent = Vue.extend({
  template: '<div>{{ message }}</div>',
  data () {
    return {
      message: 'Hello world!'
    };
  },
});

Vue.extend是无法挂载组件的,此时需要:

  1. 使用$mount 渲染组件或者渲染并挂载组件
  2. 使用JS原生方法,挂载组件
// 方式一:仅仅渲染
const component = new AlertComponent().$mount();
// 通过JS方法组件添加到body节点上
document.body.appendChild(component.$el);

// 方式二:渲染挂载同时做
// 创建并挂载到 #app (会替换 #app)
new AlertComponent().$mount('#app')

应用场景:最常见的应该是自定义全局消息弹窗了。需要将组件挂载在body根节点上,此时,就可以通过$mount指定挂载节点。


同步歪歪一下React......

React 16 的portal也有异曲同工之妙。 portal可以帮助我们在JSX中跟普通组件一样直接使用dialog, 但是又可以让dialog内容层级不在父组件内,而是显示在独立于原来app在外的同层级组件。

HTML:

<div id="app-root"></div>
// 这里为我们定义Dialog想要放入的位置
<div id="modal-root"></div>

JS:

const modalRoot = document.getElementById('modal-root');

// Let's create a Modal component that is an abstraction around the portal API.
class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    // Append the element into the DOM on mount. We'll render
    // into the modal container element (see the HTML tab).
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    // Remove the element from the DOM when we unmount
    modalRoot.removeChild(this.el);
  }
  
  render() {
    // Use a portal to render the children into the element
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

2. 渲染函数 render

Vue.js 2.0使用了 Virtual DOM(虚拟 DOM)来更新 DOM 节点,提升渲染性能。

一般我们写 Vue.js 组件,模板都是写在 <template> 内的,但它并不是最终呈现的内容,在 Vue.js 编译阶段,会解析为 Virtual DOM。与 DOM 操作相比,Virtual DOM 是基于 JavaScript 计算的,所以开销会小很多。下图演示了 Virtual DOM 运行的过程(来自网络):

vue.png

Vue.js 的 Render 函数就是将template 的内容改写成一个 JavaScript 对象。官网文档上有个极好的例子:https://cn.vuejs.org/v2/guide/render-function.html

Vue.component('my-component', {
  render: (h)=> {
    return h('div', {
            style: {
              color: 'red'
            }
          }, '自定义内容');
  }
})

应用场景:如果模板条件太多,用JS处理比HTML处理更加便利时,推荐使用render函数。

3. 递归组件

递归组件就是指组件在模板中调用自己,其核心是:在组件中设置一个 name 选项。如下:

<template>
  <div>
   这是一个组件,递归调用自己
   <my-component></my-component> 
  </div>
</template>
<script>
  export default {
    name: 'my-component'
  }
</script>

当然,上面的代码是有问题的。如果直接运行,会抛出 max stack size exceeded 的错误,因为没有终止条件,所以组件会无限的递归下去,循环至死。

所以,递归组件的第二个核心:设置终止条件。 改造一下上面的代码:

<template>
  <div>
   这是一个组件,递归调用3次
   <my-component :count="count + 1" v-if="count <= 3"></my-component>
  </div>
</template>
<script>
  export default {
    name: 'my-component',
    props: {
      count: {
        type: Number,
        default: 1
      }
    }
  }
</script>

应用场景:树形组件

4. 组件通信:provide / inject

这对选项需要一起使用!( Vue.js 2.2.0 版本后新增的 API) 允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

是不是和React context很相似!!

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

应用场景:某种意义上可以代替Vuex。如果你的项目只是需要全局共享一些公共状态信息,比如用户名,那么,用provide / inject足够了。

比如,在app.vue中注入根组件。

<script>
  export default {
    provide () {
      return {
        app: this
      }
    },
    data () {
      return {
        userInfo: null
      }
    },
    methods: {
      getUserInfo () {
        // 通过 ajax 获取用户信息后,赋值给 this.userInfo
        $.ajax('/user/info', (data) => {
          this.userInfo = data;
        });
      }
    },
    mounted () {
      this.getUserInfo();
    }
  }
</script>

然后,任何组件都可以使用到userInfo数据:

<template>
  <div>
    {{ app.userInfo }}
  </div>
</template>
<script>
  export default {
    inject: ['app']
  }
</script>

是不是比用Vuex简洁多了!

5. 数据更新:$set

之前提过,向响应式对象中添加一个属性,该新属性是非响应式的,视图也无法更新。所以为了保证新属性的响应性,可以用此API。

this.$set(data, 'checked', true);

小结

https://cn.vuejs.org/v2/api/是个好东西,多翻翻里面的api,可以发现很多有趣的功能。