前言
哈喽大家周五好, 马上又是一个周末了, 下周就是中秋了, 下下周就是国庆啦, 这里先祝福大家一个比一个假日嗨皮啦~~ 转眼我们的专题已经写了第 23 篇了, 好几次都坚持不下去想要中断, 不过每当看到群里的交流, 看到博客下边好多小伙伴提出问题, 我又燃起了斗志, 不过这两天感冒了, 所以更新的比较晚, 这里也提醒大家, 节日要照顾好自己哟~~~, 好多人说我写的上不了台面, 哈哈这里表示赞同, 本系列的宗旨就是, 给大家一个学习的点, 让大家去自学一个面, 然后大家一起学, 把面交流成一个立体, 就达到一个体系了. 好啦, 言归正传 (一直告诫自己, 不能写心情贴, 哈哈要写技术文章), 昨天呢, 不知道有几个小伙伴按照教程把自己的之前的 .net core api 教程里的项目给展示出来了呢, 一定要自己动手试试哟, 只要成功了, 就是棒棒哒, 今天咱们继续往下走, 来说说一个一直让人头大的东西, 就是表单以及 vuex 的使用, 好啦, 开始今天的讲解!
0, 今天要完成实战 1 中的红色部分
一, 常见的 Vue 表单提交是如何设计的?
说到了 web 开发, 一定几乎所有人都能说到表单提交, 这个是真的少不了, 而且也让人写的头晕眼花, 心身疲惫, 自然在 Vue 开发中, 也是少不了的一部分工作, 常见的表单是什么样子的呢?
1, 表单, 按钮等在一个组件内
这个时候机智如你一定会说: 这有什么难的是吧, Vue 提供了完美的 双向数据绑定, 可以很好的实现数据的更新, 获取和提交, 嗯~ 没错, 你说的很对, 我们再也不用费心的操作 DOM 了, 用户填好数据就可以直接 axios 到 后端 api 接口了, 多好, 当然, 这也是一个处理方式.
这里的代码就不写了, 很简单, 把所有的写到一个页面内就行, 大家可以自己试一试.
可是想一想, 如果页面内有很多组件, 有很多的表单, 或者更直接点儿, 想要表单单独是一个组件用作弹窗, 你会怎么办呢~ 为什么呢?
2, 按钮在父组件, 表单在单独的子组件内
这种开发利于开发, 易于维护, 可是就是不适合数据传输, 因为父子组件内的数据通讯是很麻烦的, 虽然 Vue 支持双向数据绑定, 但是父子通讯是: 组件之间的数据只能是单项流通的, 而且由父组件传递给子组件, 如果你看过我之前写的文章, 有关组件的《从 1 开始前后端分离 [ Vue2.0+.NET Core2.1] 二十║Vue 基础终篇: 组件详解 + 项目说明》, 你应该知道, 之所以这么麻烦 , 是因为父组件可以通过 props 给子组件传递参数 , 但子组件内却不能直接修改父组件传过来的参数. 只能通过自定义方法, 向上提交时数据, 今天, 咱们说下第二种方法, 子组件可以使用 $emit 触发父组件的自定义事件.
二, 通过 $emit 修改父组件数据
1, 在原来代码里 About.vue 修改成 Form.vue
注意, 如果你是使用 Webstorm 的话, 重命名的时候, 会自动的把当然文件的全部应用的地方都会修改 (举栗子: router 中的名字), 如果是手动修改的文件夹中的, 请确保其他地方都被修改了.
在 Form.vue 组件内, 添加以下代码
- <!-- 父组件 Form.vue -->
- <template>
- <div class="parent">
- <h3 > 问卷调查 </h3>
- <!-- 注意这里, formData 是自定义属性, 用来向子组件传递数据, 如果要想被子组件控制, 必须加上 .sync-->
- <child :formData.sync="form"></child>
- <div class="">
- <p > 姓名:{{form.name}}</p>
- <p > 年龄:{{form.age}}</p>
- <p > 地址:{{form.address}}</p>
- </div>
- </div>
- </template>
- <script>
- import child from "../components/dialog.vue";// 导入子组件
- export default {
- components: {
- child
- },
- data: function() {// 定义返回 data
- return {
- form: {
- name: "",
- namePla: "姓名不能为空",
- address: "",
- age: ""
- }
- };
- }
- };
- </script>
2, 在 components 文件夹内, 添加窗口子组件 dialog.vue(之所以叫窗口, 就是你可以把它设计成弹窗)
- <!-- 子组件 dialog.vue -->
- <template>
- <div class="child">
- <label>
- 姓名:<input :placeholder="form.namePla" type="text" v-model="form.name">
- </label>
- <label>
- 年龄:<input type="text" v-model="form.age">
- </label>
- <label>
- 地址:<input type="text" v-model="form.address">
- </label>
- </div>
- </template>
- <script>
- export default {
- data: function() {// 子组件返回 data
- return {
- form: {
- name: "",
- namePla: "",
- age: "",
- address: ""
- }
- };
- },
- props: {
- // 这个 prop 属性用来接收父组件传递进来的值
- formData: Object// 对象的形式
- },
- watch: {
- // 因为不能直接修改 props 里的属性, 所以不能直接把 formData 通过 v-model 进行绑定到 input 上
- // 在这里我们需要监听 formData, 当它发生变化时, 立即将值赋给 data 里的 form, 这个时候才可以用来 绑定 input
- formData: {
- immediate: true,
- handler(val) {
- this.form = val;
- }
- }
- },
- mounted() {// 挂载完成
- // props 是单向数据流, 通过触发 update 事件修改 formData,
- // 将 data 里的 form 指向父组件通过 formData 绑定的那个对象
- // 父组件在绑定 formData 的时候, 需要加上 .sync
- this.$emit("update:formData", this.form);
- }
- };
- </script>
这里要说下 几个概念:
1,watch : 用来监听 父组件传递过来的值, 当传递过来的时候, 赋给 form, 然后用 from 才可以双向绑定 DOM
2,mounted : 挂载完成后执行, 如果有不明白的小伙伴, 可以看看我之前的有关生命周期的文章 -- 《从 1 开始前后端分离 [ Vue2.0+.NET Core2.1] 十九║Vue 基础: 样式动态绑定 + 生命周期》.
3,$emit : this.$emit("自定义事件名", 要传送的数据), 用来触发父组件的自定义事件, 这里不是很明白没关系, 因为它要配合 .sync 来使用.(注意: 如果你在父组件里写了一个事件, 这里就是那个事件的名字)
4,.sync : .sync 修饰符所提供的功能. 当一个子组件改变了一个 prop 的值时, 这个变化也会同步到父组件中所绑定, 就是说我们可以直接在我们需要传的 prop 后面加上 .sync. 也就是说我们在子组件内, 用过 update:formData 来改变了父组件的 属性 prop 的值, 通过 .sync 来同步到了父组件的 form 对象里. 嗯~ 大概就是这个意思. 官网 : 地址
5, update:my-prop-name 的模式触发事件. 举个栗子, 在一个包含 title prop 的假设的组件中, 我们可以用以下方法表达对其赋新值:
this.$emit('update:title', newTitle)
然后父组件可以监听那个事件并根据需要更新一个本地的数据属性. 例如:
- <text-document
- v-bind:title="doc.title"
- v-on:update:title="doc.title = $event"
- ></text-document>
为了方便起见, 我们为这种模式提供一个缩写, 即 .sync 修饰符:
<text-document text- v-bind:title.sync="doc.title"></text-document>
3, 这个时候, 我们看我们看我们的项目, 结果就出来了
不仅可以把父组件的 "姓名不能为空" 传递到子组件, 还可以, 把子组件内的数据发送到父组件, 是不是感觉很神奇?! 而且也可以做成一个弹窗的, 大家可以自己试试.
但是呢, 这里是一个小 DEMO 还好, 要是多的话, 要设置总感觉不是很舒服, 还需要 用到那么多的新的东西, 欸! 机智如你, Vuex 就这么登台了.
三, 使用 Vuex 来实现父子通讯
1, 老规矩, 什么是 Vuex?
Vuex 是一个专为 vue.js 应用程序开发的状态管理模式. 它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化. 说人话就是, 它就像是一个容器, 一个第三方, 我们可以把内容存进去, 然后在别的任何地方去取出来, 这个是不是正好就是我们的父子组件通讯?! 大家再看看上面, 父子组件之间的通信是不是比较麻烦, 改变数据还要用 $emit. 如果有一个地方跟仓库一样就存放着 form 的值, 谁要用谁去请求 form 的值, 谁想改就改该多好是吧, vuex 就是一个管理仓库, 有点全局变量的意思. 任何组件需要拿, 改东西, 都可以找他.
2, 首先我们需要安装 Vuex
利用 npm 下载 vuex 包, 在命令行工具中输入以下命令, cd 到你的项目目录
npm install vuex --save
3, 还记得那个 store.js 么, 终于用到了, 修改内容
- import Vue from "vue";
- import Vuex from "vuex";
- Vue.use(Vuex);
- const store = new Vuex.Store({
- // 初始化的数据
- state: {
- formDatas: null// 定义一个变量 formDatas
- },
- // 改变 state 里面的值得方法
- mutations: {
- getFormData(state, data) {
- state.formDatas = data;
- }
- }
- });
- // 输出模块
- export default store;
4, 在 views 文件夹下, 新建 FormVuex.vue 页面
内容和 Form.vue 主要内容差不多
- <!-- 父组件 parent.vue -->
- <template>
- <div class="parent">
- <h3 > 问卷调查 </h3>
- <child></child><!-- 注意: 这里已经没有 .sync 了 -->
- <div class="">
- <br>
- <br>
- <p > 数据:{{_formData}}</p>
- </div>
- </div>
- </template>
- <script>
- import child from "../components/dialogVuex.vue";
- export default {
- components: {
- child
- },
- data: function() {
- return {
- form: {
- name: "",
- namePla: "姓名不能为空",
- address: "",
- age: ""
- }
- };
- },
- computed: {// 挂载完成后
- _fatherData() {// 获取全局 store 仓库中的 formDatas 值
- // 读取 store 里面的值, 这里是重点
- return this.$store.state.formDatas;
- }
- }
- };
- </script>
5, 在 components 文件夹下, 新增 dialogVuex.vue 页面
- <!-- 子组件 child.vue -->
- <template>
- <div class="child">
- <label>
- 姓名:<input type="text" v-model="form.name">
- </label>
- <label>
- 年龄:<input type="text" v-model="form.age">
- </label>
- <label>
- 地址:<input type="text" v-model="form.address">
- </label>
- </div>
- </template>
- <script>
- export default {
- data: function() {
- return {
- form: {
- name: "",
- namePla: "",
- age: "",
- address: ""
- }
- };
- },
- mounted() {
- // 将数据提交到 store
- this.$store.commit('fatherData', this.form)
- }
- };
- </script>
6, 修改 App.vue 和 路由 router.js, 提供 Vuex 页面入口
- import Vue from "vue";
- import Router from "vue-router";
- import Home from "./views/Home.vue";
- import FormVuex from "./views/FormVuex.vue";
- Vue.use(Router);
- export default new Router({
- mode: "history",
- base: process.env.BASE_URL,
- routes: [
- {
- path: "/",
- name: "home",
- component: Home
- },
- {
- path: "/Vuex",
- name: "Vuex",
- component: FormVuex
- },
- {
- path: "/about",
- name: "about",
- // route level code-splitting
- // this generates a separate chunk (about.[hash].js) for this route
- // which is lazy-loaded when the route is visited.
- component: () =>
- import(/* webpackChunkName: "about" */ "./views/Form.vue")
- }
- ]
- });
7, 浏览页面, 搞定!
就这样就完成啦! 是不是很简单, 原理就是: 在子组件内, 把双向数据绑定的数据, 提交到 store 里, 然后再在父组件里获取, 剩下的大家可以在处理
四, 其他扩展
1,store.js 各部分单独管理
从上边可以看出, 虽然我们的 store 单独在一个文件里, 那组件多了之后 , 状态也多了 , 这么多状态都堆在 store.js 不好维护怎么办 ?
我们可以使用 vuex 的 modules , 把 store.js 改成 :
- import Vue from 'vue'
- import vuex from 'vuex'
- Vue.use(vuex);
- import dialog_store from '../components/dialog_store.js';// 引入某个 store 对象
- export default new vuex.Store({
- modules: {
- dialog: dialog_store
- }
- })
这里我们引用了一个 dialog_store.js , 在这个 js 文件里我们就可以单独写 dialog 组件的状态了 :
- export default {
- state: {
- formDatas: null
- },
- // 改变 state 里面的值得方法
- mutations: {
- getFormData(state, data) {
- state.formDatas = data;
- }
- }
- }
做出这样的修改之后 , 我们将之前我们使用的 $store.state.formDatas 统统改为 $store.state.dialog.formDatas 即可. 这个大家可以自由的尝试, 这里就不一一说明了.
2, 什么情况下我应该使用 Vuex?
虽然 Vuex 可以帮助我们管理共享状态, 但也附带了更多的概念和框架. 这需要对短期和长期效益进行权衡.
如果您不打算开发大型单页应用, 使用 Vuex 可能是繁琐冗余的. 确实是如此 -- 如果您的应用够简单, 您最好不要使用 Vuex. 一个简单的 store 模式就足够您所需了. 但是, 如果您需要构建一个中大型单页应用, 您很可能会考虑如何更好地在组件外部管理状态, Vuex 将会成为自然而然的选择.
3, 其他扩展问题补充中
五, 结语
今天因为时间的问题, 就暂时说到了这里, 是不是感觉很简单, 这个就是 vuex 的使用, 当然, 还有其他的一些扩展使用, 我还没有来得及准备, 明天就是周末了, 我再整理后, 再修改下吧, 如果你以上的都看懂了, 那父子组件通讯, 各种表单提交, 你已经没有问题啦~ 好啦下次再见咯.
六, CODE
https://github.com/anjoy8/Blog.Vue
注意: 如果下载好了, 首先需要 执行 npm install 安装依赖
请确保已经把 webpack 和 vue-cli 都安装了.
来源: https://www.cnblogs.com/laozhang-is-phi/p/9647008.html