# 七.特殊窗口(对话框)

前言 --> 弹框组件特点

  • 下拉菜单组件应该由两部分组成:
    • 选中项的文本
    • 待选菜单(默认隐藏)
  • 它的主要功能包括:
    • 鼠标经过下拉菜单组件,显示待选菜单
    • 鼠标滑出下拉菜单组件,隐藏待选菜单
    • 鼠标点击待选菜单中的条目,选中项文本更新,组件派发 change 事件

# 1.目录结构

├── dialog
│   ├── dialog.vue
│   └── index.js
1
2
3

# 1.使用场景

平时我们开发时我们写的组件基本上都是通过#app 实现挂载在页面的。

import Vue from "vue"
import App from "./app.vue"
new Vue({
  el: "#app",
  render: (h) => h(App),
})
1
2
3
4
5
6

这样的组建使用方式,有几个特点:

  • 所有的内容,都是在#app节点内渲染的
  • 组件的模板,是事先定义好的
  • 由于组件的特性,注册的组件只能在当前位置渲染

如果在一些特殊的场景下就比较局限了:

  • 组件的模板是通过调用接口从服务端获取的,需要动态渲染组件
  • 实现类似原生window.alert()的提示框组件,它的位置是在<body>下,而非<div id="app">,并且不会通过常规的组件自定义标签的形式使用,而是像 JS 调用函数一样使用。

一般来说,在我们访问页面时,组件就已经渲染好了,对于第一个场景,组件的渲染是异步的,甚至预先不知道模板是什么。对于第二个场景,通过操作 dom 很容易实现。对于这两种场景,在 vue 中我们可以通过 Vue.extend 和 vm.$mount 来实现

# 2.用法

创建一个 vue 实例,都会有一个选项el,来指定实例的根节点,如果不写el选项,那组件就处于未挂载状态。Vue.extend的作用,就是基于 Vue 构造器,创建一个子类,它的参数和new Vue的基本一致,但data要和组件一样,是个函数,再配合$mount,就可以让组件渲染,并且挂载到任意指定的节点上。

创建一个构造器,解决异步获取 template 模板的问题

import Vue from "vue"
const AlertComponent = Vue.extend({
  template: "<div>{{message}}</div>",
  data() {
    return {
      message: "hello",
    }
  },
})
1
2
3
4
5
6
7
8
9

手动渲染组件,并且挂载到 body 下

const component = new AlertComponent().$mount()
document.body.appendChild(component.$el)
1
2

当然,除了 body,你还可以挂载到其他节点上

$mount也有一些快捷的挂载方式,以下两种都是可以的

// 在$mount里写参数来指定挂载的节点
new AlertComponent().$mount("#app")
// 不用$mount,直接在创建实例是指定el选项
new AlertComponent({ el: "#app" })
1
2
3
4

除了用 extend 外,也可以直接创建 Vue 实例,并且用一个 Render 函数来渲染一个.vue 文件:

import Vue from "vue"
import Notification from "./notification.vue"

const props = {}

const Instance = new Vue({
  render(h) {
    return h(Notification, {
      props: props,
    })
  },
})
const component = Instance.$mount()
document.body.appendChild(component.$el)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这样既可以使用.vue 来写复杂的组件,还可以根据需要传入适当的 props。渲染后,如果想操作 Render 的Notfication实例,也是很简单的:

const notification = Instance.$children[0]
1

因为 Instance 下只 render 了 Notification 一个子组件,所以可以额用$children[0]访问到

需要注意的是,我们使用$mount手动渲染组件,如果要销毁,也要用$destory来手动销毁实例,必要时,也可以用removeChild把节点从 DOM 中移除

使用场景

我们在写 Vue.js 时,不论是用 CDN 的方式还是在 Webpack 里用 npm 引入 Vue.js,都会有一个根节点,并且创建一个根实例,比如:

<body>
  <div id="app"></div>
</body>
<script>
const app = new Vue({
  el: "#app",
})
</script>
1
2
3
4
5
6
7
8

Webpack 也类似,一般在入口文件 main.js 里,最后会创建一个实列:

import Vue from "vue"
import App from "./app.vue"

new Vue({
  el: "#app",
  render: (h) => App,
})
1
2
3
4
5
6
7

因为用 Webpack 基本都是前端路由的,它的 html 里一般都只有一个根节点<div id="app"></div>,其余都是通过 JavaScript 完成,也就是许多的 Vue.js 组件(每个页面也是一个)。

有了初始化的实例,之后所有的页面,都由 vue-router 帮我们管理,组件也都是用import导入后局部注册(也有在 main.js 全局注册的),不管哪种方式,组件(或页面)的创建过程我们是无需关心的,只是写好.vue文件并导入即可。这样的组件使用方式,有几个特点:

  • 1.所有的内容,都是在#app节点内渲染的;
  • 2.组件的模板,是事先定义好的
  • 3.由于组件的特性,注册的组件只能在当前位置渲染