重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。

本文原文:【Vue,Vuex】为你的vue项目添加vuex支持,并实现全局动态主题颜色修改。本文更新不及时建议到原文地址浏览。

为了让读者更加容易理解并快速掌握相关知识点,本文将从项目的创建开始进行内容的介绍。本文使用@vue/cli 3.x工具来创建vue项目,这是笔者在撰写本文时的版本。对于此工具的安装,点击这里 查看详细信息。

本文通过改变主题色来讲解vuex的使用,篇幅上可能vuex不是很多,你可以使用目录直接查看vuex部分,也可以根着内容实现效果。本文的主题效果不是最佳解决方案,只为效果演示,读者了解原理后需自己考究实用性,并加以自身优化。

一、创建项目

通过 cli 工具,可直接使用命令创建项目,本项目名为 vuex-theme , 那么使用命令:

 vue create vuex-theme

就可以很方便的创建好项目了。项目创建好之后,就可以先来一波测试运行,进入创建好的项目目录 vuex-theme, 打开命令窗口,运行命令 npm run serve 即可开启测试服务,在浏览器输入提示的地址即可看到初始项目的运行结果,如下所示:

图1

看到了效果还不够,接下来再看看项目的目录结构:

图2

为了不让初始项目内容对本文介绍的内容产生影响,先将自动生成的文件内容进行清理。

首先清理App.vue,删除里面的多余内容,最终效果:

<!-- App.vue -->
<template>
  <div id="app">

  </div>
</template>

<script>

export default {
  name: 'app',
  components: {}
}
</script>

然后清理 HelloWorld.vue 文件,此文件直接删除,不需要它。再将 assets 目录下的 logo.png 删除,我们也不需要它。

二、加入路由支持

默认创建的项目没有自动加入路由支持,一个单页面前端应用,既然咱们要配合vuex实现主题色切换,那必然会是多界面的,就必然需要路由帮我们导航多页面支持了。

1、安装 vue-router

使用命令 npm install vue-router --save-dev 即可完成安装。使用 --save-dev 参数,可将依赖保存到 package.json 文件,别人down了你的项目才不会把依赖落下。

2、路由代码加入

安装完成之后,便可以开始编写路由代码了。考虑到本案例虽然将会使用路由导航多个页面,但不会是很多页面。因此路由表就全部写在 main.js 里,如果自己的项目路由表较多,建议分出为一个独立的路由管理文件。打开 main.js 文件,修改内容为:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'

Vue.config.productionTip = false
Vue.use(VueRouter)

// 这个数组目前还没有内容,因为还没有添加任何界面。
const routes = [];

new Vue({
  render: h => h(App),
  router: new VueRouter({
    routes: routes
  }),
}).$mount('#app')

继续打开文件 App.vue ,修改内容为:

<!-- App.vue -->
<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'app',
  components: {}
}
</script>

代码中添加了 router-view 组件,通过它就可以实现将 main.js 里路由数组里面的页面动态渲染到界面上。这两处代码修改完成了之后,就相当于完成了路由的加入。接下来的事情就是添加各个界面了。

三、基本功能实现 - todo事项

为了能有一个不错的功能实现,笔者选用了 TODO事项 这个知名而又小巧的功能需求作为演示。考虑到此程序将实现主题色的更换,所以,在代码实现上要兼顾颜色字段的设计,即便现在还没有加入vuex的使用,也要为后期留下可操作的途径。

1、建立默认配置文件

在项目src目录下新建一个 pageCfg.js 文件,此文件作文页面颜色配置文件,在没有任何颜色设置时,此文件可作为默认颜色配置文件。文件内容如下:

// pageCfg.js
export default {
    pageBackgroundColor: '#FFFFFF', // 页面背景色
    primaryColor:        '#1E9FFF', // 主要点缀颜色
    textPrimaryColor:    '#000000', // 主要文本颜色
    textSecondColor:     'gray',    // 次要文本颜色
}

然后修改 main.js 文件,引入配置,将其挂载到 Vue 的 prototype 上,这样方便每一个页面都能读取到配置内容:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import PageCfg from './pageCfg'

Vue.config.productionTip = false
Vue.use(VueRouter)

// 将配置挂载到 prototype 上。
Vue.prototype.pageCfg = PageCfg;

// 这个数组目前还没有内容,因为还没有添加任何界面。
const routes = [];

new Vue({
  render: h => h(App),
  router: new VueRouter({
    routes: routes
  }),
}).$mount('#app')

2、添加用户登录页

todo事项,每个人都有自己要做的事情,所以每一个todo事项列表都必须先确定一个用户,因此专门为用户登入实现一个界面,在项目目录 src 下新建目录 pages 用于保存各个不同的界面。在pages下新建文件 login.vue 实现用户登录功能,这里简单的只需要提供一个用户名即可:

<!-- login.vue -->
<template>
    <div>
        <!-- 登录输入框 -->
        <div style="margin: 15px">
            <input placeholder="请输入你的用户名" v-model="user"/>
            &nbsp;<button @click="onLoginClick" :style="getBtnThemeColor()">登录</button>
        </div>
    </div>
</template>

<script>
    export default {
        methods: {

            // 登录按钮点击
            onLoginClick () {
                if (!this.user) return

                // 在 pageCfg 对象上为此用户分配一个存储数据的对象。
                this.pageCfg[this.user] = this.pageCfg[this.user] || [];

                // 转到首页
                this.$router.replace({
                    path: "/",
                    query: {
                        user: this.user
                    }
                });
            },

            // 获取配置的主题颜色。设置给按钮。
            getBtnThemeColor () {
                return {
                    color:           '#FFFFFF',
                    backgroundColor: this.pageCfg.primaryColor,
                    borderColor:     this.pageCfg.primaryColor
                }
            }
        },
        data () {
            return {
                user: ''
            }
        }
    }
</script>

代码中记录了用户名称,保存在 user 字段,用户必须输入一个值才能下一步。当用户输入值下一步时,会在 pageCfg 对象上为该用户分配一个存储数据的空间,该用户的所有数据就都会保存在这个空间里。然后会引导用户进入todo事项列表页,并将该用户的名称传递到下一个界面。

同时,为了迎合主题切换达到效果,特意为按钮加入了 getBtnThemeColor 方法,此方法返回配置的颜色数据,设置给按钮,这样按钮就可以通过配置修改颜色了。

3、添加todo事项列表页

用户进入todo事项列表界面时,就应该展示出用户的所有数据,并且提供一个新增数据的方式,因此使用如下代码实现基本功能:

// todo.vue
<template>
    <div>

        <!-- 根据配置文本主色改变此标题颜色 -->
        <h1 :style="getTxtPriMaryColor()">
            todo事项&nbsp;
            <button @click="onExitClick" :style="getBtnThemeColor()">退出</button>
        </h1>
        <ul>

            <!-- 根据配置文本主色改变此标题颜色 -->
            <li v-for="item in todo" :key="item.key" :style="getTxtPriMaryColor()">
                {{item.txt}}

                <!-- 根据配置文本次要色改变此事项颜色 -->
                <small :style="getTxtSecondColor()">{{item.date.toLocaleString()}}</small>
            </li>
            <li>
                <input ref="newTodoInput" placeholder="输入新事项内容"/>&nbsp;
                <button @click="onAddNewClick" :style="getBtnThemeColor()">添加新事项</button>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        created () {
            this.user = this.$route.query.user
            this.todo = this.pageCfg[this.user]

            if (!this.user || !this.todo) {
                this.$router.replace("/login")
                return
            }
        },
        methods: {
            onAddNewClick () {
                let txt = this.$refs.newTodoInput.value
                if (!txt) return

                this.todo.push({
                    key: String(Date.now()),
                    txt: txt,
                    date: new Date()
                });
                this.$refs.newTodoInput.value = ''
            },
            onExitClick () {
                this.$router.replace("/login")
            },

            // 获取页面文本主颜色
            getTxtPriMaryColor () {
                return {
                    color: this.pageCfg.textPrimaryColor
                }
            },

            // 获取文本次要颜色
            getTxtSecondColor () {
                return {
                    color: this.pageCfg.textSecondColor
                }
            },

            // 获取配置的主题颜色。设置给按钮。
            getBtnThemeColor () {
                return {
                    color:           '#FFFFFF',
                    backgroundColor: this.pageCfg.primaryColor,
                    borderColor:     this.pageCfg.primaryColor
                }
            }
        },
        data () {
            return {
                user: '',
                todo: []
            }
        }
    }
</script>

可以看到,代码中首先读取传递进入的用户名,然后根据用户名将本页的列表数据进行初始化,这样页面即可对应显示出来。同时针对页面上的按钮,也同样提供了主题色设置方法。以及h1标题、li列表事项等,都加入了主题色配置获取方法。为了更明确的展示出颜色的应用方法,我并没有将按钮提取为一个公共组件,当然在读者实际开发中有需要的话,建议将共同操作都提取为一个组件去实现。然后本页提供了一个输入框,用户可以随时新增事项。

4、页面放入路由

现在我们把页面完成了,可将这两个页面放入路由映射表了。修改 main.js 内容如下:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import PageCfg from './pageCfg'

Vue.config.productionTip = false
Vue.use(VueRouter)

// 将配置挂载到 prototype 上。
Vue.prototype.pageCfg = PageCfg;

// 加入写好的页面。 这里是使用了懒加载的方式。
const routes = [
    {path: "/login", component: () => import('./pages/login')},
    {path: "/",      component: () => import('./pages/todo')}
];

new Vue({
  render: h => h(App),
  router: new VueRouter({
    routes: routes
  }),
}).$mount('#app')

5、加入基本样式

为了让vue文件代码篇幅较小,我将一些样式写在了html文件中,打开项目目录public文件夹,编辑 index.html 文件,在其head内添加如下代码:

<style>
  span,i,input,button,small,strong,b,img {
    vertical-align: middle;
  }

  button {
    display: inline-block;
    border: 1px solid #d9d9d9;
    padding: 6px 10px;
    cursor: pointer;
  }
  input {
    padding: 6px 8px;
    outline: none;
  }
</style>
6、预览初形

当上面几步完成后,再次运行npm run serve开启测试,输入提示的地址,即可看到效果,这只是一个简单的示列,仅仅支持显示和新增,并且刷新页面将会导致数据全部丢失。效果如下:

图3

1、尝试修改主题

看起来还不错,至少没出什么大问题。现在我们尝试更改一下主题,我们的修改并不是直接在 pageCfg.js 文件里把配置给修改了,而是在 main.js 里,将 pageCfg.js 的值进行修改,从而达到目的,修改如下:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import PageCfg from './pageCfg'

Vue.config.productionTip = false
Vue.use(VueRouter)

// 将配置挂载到 prototype 上。
Vue.prototype.pageCfg = PageCfg;

Vue.prototype.pageCfg.primaryColor     = "red";  // 将点缀主色修改为红色。
Vue.prototype.pageCfg.textPrimaryColor = "blue"; // 将文本主色修改为蓝色。

const routes = [
    {path: "/login", component: () => import('./pages/login')},
    {path: "/",      component: () => import('./pages/todo')}
];

new Vue({
  render: h => h(App),
  router: new VueRouter({
    routes: routes
  }),
}).$mount('#app')

我们将点缀主色修改为红色。将文本主色修改为蓝色。再次运行,发现已经生效了:

图4

2、适配背景

上面的一系列主题色的应用都只是将显示主色修改了,但背景一直都是白色,实际上到目前为止配置的背景色都没有应用起来。现在将其使用起来。由于背景只需要设置给网页背景,document.body 是一个不做的设置位置,但是该在哪个界面完整设置背景色这个任务?似乎在任何一个页面里实现这样的逻辑都显得有点不合理,那不如将代码放在 main.js 的 new Vue 的代码逻辑里面。修改 main.js 的代码:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import PageCfg from './pageCfg'

Vue.config.productionTip = false
Vue.use(VueRouter)
Vue.prototype.pageCfg = PageCfg;

Vue.prototype.pageCfg.primaryColor     = "red";  // 将点缀主色修改为红色。
Vue.prototype.pageCfg.textPrimaryColor = "blue"; // 将文本主色修改为蓝色。

const routes = [
    {path: "/login", component: () => import('./pages/login')},
    {path: "/",      component: () => import('./pages/todo')}
];

new Vue({
    created() {

        // 页面初始时就设置一下页面背景色。
        document.body.style.backgroundColor = this.bgColor
    },
    render: h => h(App),
    router: new VueRouter({
        routes: routes
    }),
    watch: {

        // 监听这个颜色的改变,实现背景动态修改。
        bgColor(newvalue) {
            document.body.style.backgroundColor = newvalue
        }
    },
    data() {
        return {

            // 将背景色赋值给根vue对象的数据区块里。
            bgColor: this.pageCfg.pageBackgroundColor
        }
    }
}).$mount('#app')

现在,适配完成,但是看不到任何效果,因为咱们配置的背景颜色也是白色的,所有没有发生改变。这时如果再次修改颜色的值,并且添加一些新的颜色修改,就可以实现各种主题效果,现在实现一个夜间效果模式,修改颜色如下:

// main.js
import ...

...

Vue.prototype.pageCfg.pageBackgroundColor     = "#02010c"
Vue.prototype.pageCfg.primaryColor            = "#6e6e6e"
Vue.prototype.pageCfg.textPrimaryColor        = "#ffffff"
Vue.prototype.pageCfg.textSecondColor         = "#858585"

const routes = [...]

new Vue(...)

再次运行程序,发现效果已经有了:

图5

四、加入Vuex

目前看来,每次主题更换时,我们都需要去修改一下代码,然后重新运行(你可能感觉并没有重新运行,只是速度比较,快你感觉就是没有重新启动一样)才能看到效果。如何能做到不用修改代码,直接程序运行时,去修改状态值,并且让效果立即展示出来。当然有办法,

Vuex的加入就可以瞬间解决这个问题。vuex的作用是状态保持。可能不好理解,做一个比较你就明白了。每一个vue页面里,有一个data区域,这里面的数据和页面模板里面进行了绑定,你修改data的数据,页面就可以同时进行修改。那么这个data相当于就为这个页面做了状态保持。同样道理,vuex的作用就不仅是某个界面了,而是扩大到了整个vue应用内。

1、安装 vuex

和安装 vue-router 类似,使用命令 npm install vuex --save-dev 即可进行安装。安装完成后便可以在代码里进行使用了。

使用方式也和 vue-router 类似,直接创建对象然后注入根vue实例里。修改 main.js 文件,引入vuex(部分代码已隐藏):

// main.js
// ...
import Vuex from 'vuex'

// ...
Vue.use(Vuex);

// 直接创建对象
const store = new Vuex.Store({
    state: {},
    mutations: {}
});

new Vue({
    created() {...},
    render: h => h(App),
    router: new VueRouter({
        routes: routes
    }),
    watch: {...},
    data() {...},
    store: store // 将上面创建的store对象注入根vue实例
}).$mount('#app')

上面的代码就算是将vuex加入到了咱们的程序里,但还没有具体实现任何功能。可能你对此已经产生了一些疑惑。state 和 mutations 是拿来干嘛的?

思考一下,在每个页面的data数据区域里的数据,要修改它们时,是不是通常都是由页面里的 method 提供的某方法,或者对应生命周期方法里,但都离不开对应的页面。同样的道理,在这个为整个应用提供数据的store区域里如果没有一个统一的操作数据的地方,那岂不乱套了。各个页面都有各自的数据要处理,这个全局数据还搅和在里面岂不更加混乱。所以,store对象有一个固有的格式。其包含了一个 state 数据具体存放的对象、以及一个 mutations。所有操作state数据的方法写在mutations里面。代码最后通过 store 字段将其注入 vue根实例里面,这样你就可以在每个页面通过this.$store拿到store实例了。

现在有了这个基础,就可以继续完善 store 创建部分的代码,加入主题色相关的字段:

// main.js - 部分代码
const store = new Vuex.Store({
    state: {
        // 初始值就都使用配置的默认值。
        pageBackgroundColor: Vue.prototype.pageCfg.pageBackgroundColor,
        primaryColor:        Vue.prototype.pageCfg.primaryColor,
        textPrimaryColor:    Vue.prototype.pageCfg.textPrimaryColor,
        textSecondColor:     Vue.prototype.pageCfg.textSecondColor
    },
    mutations: {
        updateColor(state, payload) {
            state[payload.key] = payload.value
        }
    }
});

可以看到,我们为 state 添加了一些颜色字段。为 mutations 添加了一个更新颜色的方法。这个方法有两个参数,一个state,和一个payload,payload就是更新数据时传递进来的参数。

那么如何在页面里使用这个 updateColor 方法呢? 其实你无法直接使用到这个方法,vuex的store提供了一个commit方法,这个方法可以让你执行到 updateColor 方法,例如在某个页面里,想要更新页面背景色,就可以通过 this.$store.commit('updateColor', {key: 'pageBackgroundColor', value: 'pink'}), 从而将state里的pageBackgroundColor字段进行修改,也就达到了修改背景为粉色的效果了。

2、实现动态更改主题颜色

上一节已经了解了vuex的用法和相关代码的实现。现在可以将每个页面的与获取颜色有关的方法进行一些修改,好适配主题色的动态变化。

1)、全局背景适配

全局背景色的应用我们在第三节里写在了 main.js 里,因此,继续修改main.js文件。我们使用vuex加入后,就可以不用复杂的在main.js里绕这么多代码了,去除一些代码,然后使用store的subscribe实现每次修改主题颜色后就尝试修改背景:

// main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import PageCfg from './pageCfg'
import Vuex from 'vuex'

Vue.config.productionTip = false
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.prototype.pageCfg = PageCfg;

Vue.prototype.pageCfg.pageBackgroundColor     = "#02010c";  // 修改默认背景色。
Vue.prototype.pageCfg.primaryColor            = "#6e6e6e";  // 将点缀主色修改为红色。
Vue.prototype.pageCfg.textPrimaryColor        = "#ffffff";  // 将文本主色修改为蓝色。
Vue.prototype.pageCfg.textSecondColor         = "#858585";  // 将文本主色修改为蓝色。

const routes = [
    {path: "/login", component: () => import('./pages/login')},
    {path: "/",      component: () => import('./pages/todo')}
];

// 直接创建对象就好了
const store = new Vuex.Store({
    state: {
        // 初始值就都是用配置的默认值。
        pageBackgroundColor: Vue.prototype.pageCfg.pageBackgroundColor,
        primaryColor:        Vue.prototype.pageCfg.primaryColor,
        textPrimaryColor:    Vue.prototype.pageCfg.textPrimaryColor,
        textSecondColor:     Vue.prototype.pageCfg.textSecondColor
    },
    mutations: {
        updateColor(state, payload) {
            state[payload.key] = payload.value
        }
    }
})

store.subscribe((mutation, state) => {
    // 每次修改state值之后,这里都会执行,不如就在这里设定背景色。
    document.body.style.backgroundColor = state.pageBackgroundColor
})

new Vue({
    render: h => h(App),
    router: new VueRouter({
        routes: routes
    }),
    store: store
}).$mount('#app')

上述代码中,使用 subscribe 订阅了state的修改事件,每次修改都会调用传入的方法,如果改了背景色,自然就会自动修改背景。

2)、登录页颜色适配

现在修改登录页面,使其适配vuex的加入:

login.vue
<template>
    <div>
        <!-- 登录输入框 -->
        <div style="margin: 15px">
            <input placeholder="请输入你的用户名" v-model="user"/>
            &nbsp;<button @click="onLoginClick" :style="getBtnThemeColor">登录</button>
        </div>
    </div>
</template>

<script>
    export default {
        methods: {

            // 登录按钮点击
            onLoginClick () {
                if (!this.user) return

                // 在 pageCfg 对象上为此用户分配一个存储数据的对象。
                this.pageCfg[this.user] = this.pageCfg[this.user] || [];

                // 转到首页
                this.$router.replace({
                    path: "/",
                    query: {
                        user: this.user
                    }
                });
            }
        },
        computed: {
            // 获取配置的主题颜色。设置给按钮。
            getBtnThemeColor () {
                return {
                    color:           '#FFFFFF',
                    backgroundColor: this.$store.state.primaryColor,
                    borderColor:     this.$store.state.primaryColor
                }
            }
        },
        data () {
            return {
                user: ''
            }
        }
    }
</script>

可以看到,在模板代码区,按钮的style将原来后面的括号移除了,js代码区,将原本在methods里面的getBtnThemeColor方法移动到了computed里面,并在getBtnThemeColor方法里面修改为使用store提供的数据。

3)、事项列表页颜色适配

适配方法和登录页相似,去除布局代码里的方法括号,将方法移动到computed里面,并修改一下数据来源,即可完成:

todo.vue
<template>
    <div>

        <!-- 根据配置文本主色改变此标题颜色 -->
        <h1 :style="getTxtPriMaryColor">
            todo事项&nbsp;
            <button @click="onExitClick" :style="getBtnThemeColor">退出</button>
        </h1>
        <ul>

            <!-- 根据配置文本主色改变此标题颜色 -->
            <li v-for="item in todo" :key="item.key" :style="getTxtPriMaryColor">
                {{item.txt}}

                <!-- 根据配置文本次要色改变此事项颜色 -->
                <small :style="getTxtSecondColor">{{item.date.toLocaleString()}}</small>
            </li>
            <li>
                <input ref="newTodoInput" placeholder="输入新事项内容"/>&nbsp;
                <button @click="onAddNewClick" :style="getBtnThemeColor">添加新事项</button>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        created () {
            this.user = this.$route.query.user
            this.todo = this.pageCfg[this.user]

            if (!this.user || !this.todo) {
                this.$router.replace("/login")
                return
            }
        },
        methods: {
            onAddNewClick () {
                let txt = this.$refs.newTodoInput.value
                if (!txt) return

                this.todo.push({
                    key: String(Date.now()),
                    txt: txt,
                    date: new Date()
                });
                this.$refs.newTodoInput.value = ''
            },
            onExitClick () {
                this.$router.replace("/login")
            }
        },
        computed: {

            // 获取页面文本主颜色
            getTxtPriMaryColor () {
                return {
                    color: this.$store.state.textPrimaryColor
                }
            },

            // 获取文本次要颜色
            getTxtSecondColor () {
                return {
                    color: this.$store.state.textSecondColor
                }
            },

            // 获取配置的主题颜色。设置给按钮。
            getBtnThemeColor () {
                return {
                    color:           '#FFFFFF',
                    backgroundColor: this.$store.state.primaryColor,
                    borderColor:     this.$store.state.primaryColor
                }
            }
        },
        data () {
            return {
                user: '',
                todo: []
            }
        }
    }
</script>
4)、查看效果

现在我们再次运行,就可以看到通过vuex加入后的主题效果,所实话,和没加vuex前完全一致。。。由于我们没有再次修改颜色值,所以颜色一直保持夜间主题模式。效果就不用再展示了,和之前一样的。

5)、加入颜色动态修改

为了更好的展示效果,将在todo事项列表页添加四个颜色修改视图,这样可以非常直观的看到vuex发挥的作用。开源组件 vue-color 就帮我们完成了这样一个控件,通过它就可以方便的实现颜色选择。
第一步先安装它 npm install vue-color --save-dev。然后就可以在页面中使用了,修改todo.vue代码如下:

// todo.vue
<template>
    <div>

        <!-- 根据配置文本主色改变此标题颜色 -->
        <h1 :style="getTxtPriMaryColor">
            todo事项&nbsp;
            <button @click="onExitClick" :style="getBtnThemeColor">退出</button>
        </h1>
        <ul>

            <!-- 根据配置文本主色改变此标题颜色 -->
            <li v-for="item in todo" :key="item.key" :style="getTxtPriMaryColor">
                {{item.txt}}

                <!-- 根据配置文本次要色改变此事项颜色 -->
                <small :style="getTxtSecondColor">{{item.date.toLocaleString()}}</small>
            </li>
            <li>
                <input ref="newTodoInput" placeholder="输入新事项内容"/>&nbsp;
                <button @click="onAddNewClick" :style="getBtnThemeColor">添加新事项</button>
            </li>
        </ul>

        <div style="margin-top: 15px">
            <div style="display: inline-block">
                <span :style="getTxtPriMaryColor">背景</span>
                <ChromeColorPicker v-model="pageBackgroundColor"/>
            </div>&nbsp;
            <div style="display: inline-block">
                <span :style="getTxtPriMaryColor">主色</span>
                <ChromeColorPicker v-model="primaryColor"/>
            </div>&nbsp;
            <div style="display: inline-block">
                <span :style="getTxtPriMaryColor">文本主色</span>
                <ChromeColorPicker v-model="textPrimaryColor"/>
            </div>&nbsp;
            <div style="display: inline-block">
                <span :style="getTxtPriMaryColor">文本次色</span>
                <ChromeColorPicker v-model="textSecondColor"/>
            </div>
        </div>
    </div>
</template>

<script>
    import { Chrome } from 'vue-color'

    export default {
        components: {
            ChromeColorPicker: Chrome
        },
        created () {
            this.user = this.$route.query.user
            this.todo = this.pageCfg[this.user]

            if (!this.user || !this.todo) {
                this.$router.replace("/login")
                return
            }
        },
        methods: {
            onAddNewClick () {
                let txt = this.$refs.newTodoInput.value
                if (!txt) return

                this.todo.push({
                    key: String(Date.now()),
                    txt: txt,
                    date: new Date()
                });
                this.$refs.newTodoInput.value = ''
            },
            onExitClick () {
                this.$router.replace("/login")
            }
        },
        computed: {

            // 获取页面文本主颜色
            getTxtPriMaryColor () {
                return {
                    color: this.$store.state.textPrimaryColor
                }
            },

            // 获取文本次要颜色
            getTxtSecondColor () {
                return {
                    color: this.$store.state.textSecondColor
                }
            },

            // 获取配置的主题颜色。设置给按钮。
            getBtnThemeColor () {
                return {
                    color:           '#FFFFFF',
                    backgroundColor: this.$store.state.primaryColor,
                    borderColor:     this.$store.state.primaryColor
                }
            }
        },
        watch: {
            pageBackgroundColor (newval) {
                this.$store.commit('updateColor', {
                    key:  "pageBackgroundColor",
                    value: newval.hex8
                })
            },
            primaryColor (newval) {
                this.$store.commit('updateColor', {
                    key:  "primaryColor",
                    value: newval.hex8
                })
            },
            textPrimaryColor (newval) {
                this.$store.commit('updateColor', {
                    key:  "textPrimaryColor",
                    value: newval.hex8
                })
            },
            textSecondColor (newval) {
                this.$store.commit('updateColor', {
                    key:  "textSecondColor",
                    value: newval.hex8
                })
            }
        },
        data () {
            return {
                user: '',
                todo: [],
                // -------------------
                pageBackgroundColor: this.$store.state.pageBackgroundColor,
                primaryColor:        this.$store.state.primaryColor,
                textPrimaryColor:    this.$store.state.textPrimaryColor,
                textSecondColor:     this.$store.state.textSecondColor,
            }
        }
    }
</script>

由于 vue-color 控件更新回执值是一个对象,因此我是用watch的方式,然后将值通过commit提交给updateColor方法,实现更新。现在来看看效果吧:

图7

Perfect!

五、总结及源代码

使用vuex的store实现了主题颜色切换,正是基于vuex的状态保持特性,使整个app内都可以响应其变化产生的效果。除了使用它保存颜色这些全局属性,按照项目逻辑可以将更多字段保存在vuex里面。vuex是让数据在整个app内贯通及响应,它不会负责持久化保存数据,它在每次运行页面时创建。持久化数据需要自行保存并在vuex创建时将值又赋值给vuex。vuex的用法还有很多,还可以分模块进行状态管理,本文定位vuex入门,更多vuex知识请点击官网文档进行查阅。本文涉及的项目代码你可已通过此链接下载运行:百度网盘,提取码:b2u4

Logo

前往低代码交流专区

更多推荐