通过 props 和 Vue 原型实例在不同组件之间共享数据状态


在前面的表单组件中,包含了一个作者字段,通常该字段的值就是当前登录用户的用户名,如果应用包含多个类似表单,每次都要手动填写有些麻烦,我们可否通过从外部传入或者全局共享变量来自动填充作者字段对应的输入框?

当然可以。

通过 props 从父作用域传递

从外部传入的话,使用前面 Vue 组件基本语法里介绍的 props 属性即可。

我们在视图层引入表单组件的父级作用域中,在 form-component 元素上设置一个 username 属性,通过 Blade 语法填充当前认证用户的用户名作为属性值,如果用户未认证的话,显示匿名用户:

<form-component username="{{ auth()->check() ? auth()->user()->name : '匿名用户' }}"></form-component>

然后在 FormComponent.vue 中通过 props 属性将其引入 Vue 组件,并将其作为 form.author 字段的默认值:

<script>
export default {

    props: ['username'],

    data() {
        return {
            form: new Form({
                title: '',
                author: this.username,
                content: ''
            })
        }
    },
    
    ...

接下来,就可以基于 Vue 框架的数据绑定完成将其填充到文章发布表单的作者字段输入框。

我们重新编译前端资源,刷新表单视图,就可以看到未认证的情况下,作者输入框默认填充了「匿名用户」:

-w781

如果是认证用户的话,则会填充对应的认证用户名:

-w762

注:laravel/ui 包默认提供了认证脚手架代码,运行 php artisan ui bootstrap --auth 即可自动生成,然后运行 php artisan migrate 创建对应的用户表,就可以通过 /register 路由在注册表单注册一个新用户并基于此用户进行登录认证了。

非常简单,不过,这种方式需要在每个引入 form-component 的地方设置 username 属性(当然,你可以通过 Blade 模板的组件和包含子视图功能规避同一个表单组件的重复设置,不过不同的表单组件还是要这么做),除此之外,我们还可以通过为 Vue 设置原型实例以便在每个 Vue 组件中共享数据状态。

添加 Vue 原型实例

引入 User 类

开始之前,我们在 resources/js 目录下新建一个 user.js 文件定义 User 类:

class User {
    constructor(data) {
        this.new(data);
    }

    new(data) {
        Object.keys(data).forEach((field) => {
            this[field] = data[field];
        })
    }

    data() {
        let data = {};
        Object.keys(this).forEach((field) => {
            data[field] = this[field];
        });
        return data;
    }

    get(field) {
        return this[field];
    }

    set(field, value) {
        this[field] = value;
    }
}

export default User;

然后在 resources/js/app.js 中引入它。

window.User = require('./user').default;

这样,我们就可以在后续代码中直接使用 User 类了:

-w526

注册后端路由

为了初始化 User 实例,需要从后端接口读取用户数据,我们在 routes/web.php 注册这个 /user 路由,路由处理逻辑也非常简单,我们通过 PHP 的箭头函数直接返回当前认证用户的数据即可,如果未认证返回空数组:

Route::get('/user', fn() => auth()->check() ? auth()->user() : []);

你可以先在浏览器测试这个接口是否可以正常工作:

-w699

通过 Vue 原型实例共享全局变量

接下来,回到 resources/js/app.js,我们在 Vue 组件初始化和挂载之前通过 axios 库异步读取后端 /user 接口数据,并将其赋值给 Vue.prototype.$user

window.User = require('./user').default;

axios.get('/user').then(resp => {
    Vue.prototype.$user = new User(resp.data);
}).catch(error => {
    console.log(error);
});

这样一来,我们就在 Vue 原型上新增了一个 $user 实例属性,后续就可以在所有组件中共享这个属性了,以 FormComponent.vue 组件为例,我们定义一个 mounted 钩子函数 基于 Vue.prototype.$user 初始化 form.author 字段值:

data() {
    return {
        form: new Form({
            title: '',
            author: '匿名用户',
            content: ''
        })
    }
},

mounted() {
   let timer = setInterval(() => {
       if (this.$user) {
           this.form.author = this.$user.get('name');
           clearInterval(timer);
       }
    }, 100);
},

可以看到,在 Vue 组件中可以通过 this.$user 引用 Vue.prototype.$user,由于 Vue.prototype.$user 是异步读取后端接口数据设置的,所以这里也定义了一个定时器,每隔 100ms 刷新下,读取到 $user 完成 form.author 字段初始化后,清除这个定时器。

最后,我们刷新表单视图,就可以看到这个作者字段刷新的过程:

hie1b-69

如果引用的是一个静态的实例数据,则不需要编写这些异步代码,直接读取 Vue 原型实例数据即可。

小结

当然,关于组件之间的数据状态共享 Vue 官方还提供更加优雅的系统解决方案 —— Vuex,我们将在后续单页面应用实战中详细介绍它的使用。这篇教程的意义是对于非常小的功能,有时候没有必要引入 Vuex,我们可以基于 Vue 本身提供的一些基本语法来实现组件间的数据共享。


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: 通过 ES6 新语法对 Vue 表单组件进行面向对象重构

>> 下一篇: SOLID 原则在 Vue 组件开发中的应用:将单个表单组件拆分成可复用的子组件组合