• 十、组件(7)


    本章概要

    • 单文件组件
    • 组件通信的其它方式
      • 访问根实例
      • 访问父组件实例
      • 访问子组件实例或子元素
      • provide 和 inject

    10.10 单文件组件

    在很多 Vue 项目中,全局组件使用 app.component() 方法定义,然后使用 app.mount(‘#app’) 在页面内绑定一个容器元素。
    这种方式在很多小规模的项目中运作的很好,在这些项目里 JavaScript 只是被用来加强特定的视图。然而,在更复杂的项目中,或者前端完全由JavaScript驱动时,以下缺点将变得非常明显:

    • 全局定义强制要求每个组件的命名不能重复
    • 字符串模板缺乏语法高亮显示,在 HTML 有多行的时候,需要用到反斜杠,或者 ES6 中的反引号“`”,而后者依赖于支持 ES6 的浏览器
    • 没有 CSS 的支持意味着当 HTML 和 JavaScript 被模块化为组件时,CSS 明显被遗漏了
    • 没有构建步骤,这限制为只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如果 Pug一千的(Jade)和 Babel

    在 Vue.js 中,可以使用单文件组件解决上述所有问题。在一个文件扩展名为 .vue 的文件中编写组件,可以将组件模板代码以 HTML 的方式书写,同时 JavaScript 与 CSS 代码也在同一个文件中编写。
    例如:

    <template>
        <div>
            <ul class="item">
                <li class="username">用户名:{{ post.user.username }},留言时间:{{ gstTime }}li>
                <li class="title">主题:{{ post.title }},li>
                <li>内容:{{ post.content }}li>
            ul>
        div>
    template>
    <style scoped>
        .item {
            border-top: solid 1px grey;
            padding: 15px;
            font-size: 14px;
            color: grey;
            line-height: 21px;
        }
        .username{
            font-size: 16px;
            font-weight: bold;
            line-height: 24px;
            color: #009a61;
        }
        .title {
            font-size: 16px;
            font-weight: bold;
            line-height: 24px;
            color: #009a61;
        }
        ul li {
            list-style: none;
        }
    style>
    <script>
       export default{
        name:'postItem',
        data() {
            return {}
        },
        props:['post'],
        computed:{
            gstTime:function(){
                let d = new Date(this.post.gstTime);
                d.setHours(d.getHours - 8);
                return d.toLocaleString();
            }
        }
       }
    script>
    
    • 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

    在单文件组件中编写 CSS 样式规则时,可以添加一个 scoped 属性。该属性的作用是限定 CSS 样式只作用于当前组件元素,相当于是组件作用域的 CSS。

    10.11 杂项

    介绍组件开发中一些不常用但特殊需求下会用到的功能。

    10.11.1 组件通信的其它方式

    总结一下前面介绍的组件通信的 3 种方式:

    • 父组件通过 prop 向子组件传递数据
    • 子组件通过自定义事件向父组件发起通知或进行数据传递
    • 子组件通过 slot 元素充当占位符,获取父组件分发的内容;也可以在子组件的 slot 元素上使用 v-bind 指令板顶一个插槽 prop ,向父组件提供数据

    而此处将介绍组件通信的其它实现方式。

    1. 访问根实例

    在每一个根组件实例的子组件中,都可以通过 root 属性访问根实例。如下:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>title>
    head>
    
    <body>
    	<div id="app">
    		<parent>parent>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const app = Vue.createApp({
    			data() {
    				return {
    					price: 188
    				}
    			},
    			computed: {
    				totalPrice() {
    					return this.price * 10;
    				}
    			},
    			methods: {
    				hello() {
    					return "葵花宝典";
    				}
    			}
    		})
    
    		app.component('parent', {
    			template: ''
    		})
    
    		app.component('child', {
    			methods: {
    				accessRoot() {
    					console.log("单价:" + this.$root.price);
    					console.log("总价:" + this.$root.totalPrice);
    					console.log(this.$root.hello());
    				}
    			},
    			template: ''
    		})
    
    		app.mount('#app');
    	script>
    body>
    
    html>
    
    • 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

    在浏览器中点击“访问根实例”按钮,在 Console 窗口中的输出如下:

    单价:188
    总价:1880
    葵花宝典
    
    • 1
    • 2
    • 3

    不管组件是根实例的子组件,还是更深层次的后代组件,root 属性总是代表了根实例。

    2. 访问父组件实例

    与 root 类似, parent 属性用于在一个子组件中访问父组件的实例,这可以代替父组件通过 prop 想子组件传递数据的方式。

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>title>
    head>
    
    <body>
    	<div id="app">
    		<parent>parent>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const app = Vue.createApp({});
    		app.component('parent', {
    			data() {
    				return {
    					price: 188
    				}
    			},
    			computed: {
    				totalPrice() {
    					return this.price * 10;
    				}
    			},
    			methods: {
    				hello() {
    					return "重生之门";
    				}
    			},
    			template: ''
    		})
    
    		app.component('child', {
    			methods: {
    				accessParent() {
    					console.log("单价:" + this.$parent.price);
    					console.log("总价:" + this.$parent.totalPrice);
    					console.log(this.$parent.hello());
    				}
    			},
    			template: ''
    		})
    
    		app.mount('#app')
    	script>
    body>
    
    html>
    
    • 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

    parent 属性只能用于访问父组件实例,如果父组件之上还有父组件,那么该组件是访问不到的。

    3. 访问子组件实例或子元素

    父组件要访问子组件实例或子元素,可以给子组件或子元素添加一个特殊的属性 ref,为子组件或子元素分配一个引用 ID ,然后父组件就可以通过 refs 属性访问子组件实例或子元素。
    如下:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>title>
    head>
    
    <body>
    	<div id="app">
    		<parent>parent>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const app = Vue.createApp({});
    		app.component('parent', {
    			mounted() {
    				// 访问子元素,让其具有焦点
    				this.$refs.inputElement.focus();
    				// 访问子组件的message数据属性
    				console.log(this.$refs.childComponent.message)
    			},
    			template: `
    					

    `
    }) app.component('child', { data() { return { message: 'Java无难事' } }, template: '

    {{message}}

    '
    }) app.mount('#app');
    script> body> html>
    • 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

    需要注意的是,refs属性只在组件渲染完成之后生效,并且它们不是响应式的。要避免在模板和计算属性中访问 refs。

    4. provide 和 inject

    root 属性用于访问根实例,parent 属性用于访问父组件实例,但如果组件嵌套的层级不确定,某个组件的数据或方法需要被后代组件所访问,又该如何实现?
    此时需要用到两个新的实例选项:provide 和 inject 。
    provide 选项允许指定要提供给后代组件的数据或方法,在后代组件中使用 inject 选项接收要添加到该实例中的特定属性。
    如下:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>title>
    head>
    
    <body>
    	<div id="app">
    		<parent>parent>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const app = Vue.createApp({});
    		app.component('parent', {
    			data() {
    				return {
    					msg: 'Java无难事'
    				}
    			},
    			methods: {
    				sayHello(name) {
    					console.log("Hello, " + name);
    				}
    			},
    			provide() {
    				return {
    					// 数据message和sayHello方法可供后代组件访问
    					message: this.msg,
    					hello: this.sayHello
    				}
    			},
    			template: '',
    		})
    
    		app.component('child', {
    			// 接收message数据属性和hello方法
    			inject: ['message', 'hello'],
    			mounted() {
    				// 当自身的方法来访问
    				this.hello('zhangsan');
    			},
    			// 当自身的数据属性来访问
    			template: '

    {{message}}

    '
    }) const vm = app.mount('#app')
    script> body> html>
    • 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

    使用 provide 和 inject ,父组件不需要知道哪些后代组件要使用他提供的属性,后代组件不需要知道被注入的属性来自哪里。
    不过上述代码也存在一些问题。首先,注入的 message 属性并不是响应式的,当修改父组件的 msg 数据属性时,message 属性并不会跟着改变。
    这是因为默认情况下,provide/inject 绑定的并不是响应式的,可以通过传递 ref属性或 reactive 对象更改这个行为。
    其次,provide 和 inject 将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。
    如果数据需要在多个组件中访问,并且能够响应更新,可以考虑真正的状态管理解决方案-Vuex。

  • 相关阅读:
    【Java小知识点】类加载器的区别
    vue使用slot封装navBar
    分享从零开始学习网络设备配置--2.4 利用三层交换机实现部门间网络互访
    uniapp 获取地理位置(uni#getLocation和高德sdk获取中文地址)
    clang入门大全以及clang全家桶介绍
    php 开发微信 h5 支付 APIv3 接入超详细流程
    C++ static_cast、dynamic_cast、const_cast 和 reinterpret_cast 用处和区别
    连接池及Druid(德鲁伊) 数据库连接池
    基于 Thingsboard 定制开发,国产化企业级、低代码 AIoT 物联网平台
    html CSS 循环旋转的圆圈
  • 原文地址:https://blog.csdn.net/GXL_1012/article/details/127831435