Flask+Vue 初试牛刀

------------------------------------------------------------------------------------------------------------------

工具:

pycharm

python

vscode

vue

-----------------------------------------------------------------------------------------------------------------

flask 环境准备:

 

一:python,pycharm 安装, 已有不做介绍;

二:新建项目,并创建虚拟环境;

 

 

          解释器选择前面安装的版本,项目路径自己定义;

三:安装flask;

 

 

 四:在项目路径下新建 app.py文件,内容 如下:

 

 五:开启测试服务器;

        打开command prompt,   cd 到app.py 文件所有文件夹,运行

                      set FLASK_APP=app.py

        再运行

                       python -m flask run

    就开启了测试服务器http://127.0.0.1:5000

 

六:浏览器访问 http://127.0.0.1:5000 ,可以访问的话,说明flask工作正常;

 ——————————————————————————————————————————————————————————————

 

 

vue环境准备:

 

一: 后续准备采用 npm【Nodejs下的包管理器】的方式安装vue,所以第一步安装 node , 官网下载安装即可;

     安装完成之后在 command prompt  运行  node -v , 便可查看到安装的nodejs 的版本,说明安装成功;

      

 

     npm 是集成在node中的,也可以运行: npm -v 查看安装的npm 版本:

       

二:安装cnpm;

        命令行运行   npm install -g cnpm --registry=http://registry.npm.taobao.org  没报错的话表示安装成功;

       

三:安装vue-cli 脚手架构建工具

命令行运行    npm install -g vue-cli   然后等待安装完成,

        安装成功之后 运行    vue -v   可查看版本,版本出来的话说明安装成功;

        

 

       

         命令行再运行  webpack -v 

         若提示webpack 不是内部命令,需要在cmd中运行 npm install webpack -g 安装webpack;

 

四:命令行工具cd 要创建项目的文件夹路径下,运行 vue creat myproject [myproject 为项目的文件夹名字] ,

        然后就会出现脚手架配置工具,初步配置如下:

       

 

 

五:命令行工具cd 到刚创建的 myproject 路径下, 然后运行  npm run serve  即开始编译;

 

六:完成之后浏览器访问:http://127.0.0.1:8080/,便可以访问项目默认的页面:

       

 

 

  到此说明vue 环境准备完成;

——————————————————————————————————————————————————————————

尝试用 vscode直接打开myproject文件夹:

                  

 

 主要尝试说明src下面几个文件和文件夹的作用:

 

 

下面单独对每个文件尝试理解一下:

App.vue 这个文件内容如下:

 

 

 从结构上来看,有两部分内容,一部分是 <template>  ...  </template> , 这部分定义的渲染要用到的其他模块和访问路径:

 

 

 主路径下的Home, /about 路径下 About 

对应显示的页面是这样,根地址http://127.0.0.1:8080/显示的Home的内容,http://127.0.0.1:8080/about 路径显示的 About的内容:

 

 

另外一部分是 <style> ...  </style>, 定义的主页的css 样式,

#app 定义的是   div id = app 结构的样式,

#nav 定义的是   div id =  nav 结构的样式

#nav a 定义的是有链接的文字样式

#nav a.router-link-exact-active 定义的是链接激活之后的样式

 

 

 

------------------------------------------------------------------------------------------------------------------------------

实际上我们看到的内容应该在views文件夹中,打开views文件,有Home.vue 和 About.vue 两个模板:

 

 

 

 Home.vue 文件内容如下,还是由两部分组成 <template> ...</template>  和 <script>  </script>  模板和js脚本两部分; 

 

 

 

其中<template>...</template>  中home 类定义了两个内容,一个是图片,另一个是HelloWord 组件,

这个组件的提示信息通过 msg 显示,另外的内容通过 <script> ... </script> 导入:

@表示的是/src 

 

 

 

下面看下 HelloWorld.vue, 还是由三部分组成,<template>  <script> <style> 

 

 

 模板定义的是HTML显示的内容

 

 

 

About.vue 文件内容相对简单,

 

 

 

————————————————————————————————————————————————————————————————

 

下面看下 router/index.js , 这个文件定义的是      模块和URL映射

 

const routes 定义了 path ,name, component  , path是路由路径,component 定义的是渲染模板,

模板可以通过例如: import Home from '../views/Home.vue' 的方式定义;

模板也可以通过例如:component: ()  => import (/* webpackChunkName: "about" */ '../views/About.vue')  的方式定义;

 

 

 ---------------------------------------------------------------------------------------------------------------------------------------------------

 

 

下面我们开始改造工作,把基础的环境改成成我们想要的。

 

1:通过axios库来连接Vue前端和Flask后端使用AJAX请求进行通信;

              命令行工具cd 到myproject 路径,然后命令行运行: npm  install axios 

 

2:  对HelloWorld.vue 模板进行改造;

     script 中导入 axios , 同时增加空置的对象data ,  获取的method:

     const path = http://localhost:5000/ 这个路径为flask中app.py 定义的内容路由路径;

     

 3.   pycharm  ,vscode 同时都保存文件,同时确保服务器在运行状态。

      

 

 

      

 4.   访问网址http://127.0.0.1:8080/, 在网址下看flask中定义的内容,说明连通成功,返回了flask 后台的内容;

       

       

 

 

 

 

 ---------------------------------------------------------------------------------------------------------------------------------------------

 

 

安装Bootstrap 样式

命令行工具运行  npm install bootstrap --save

client/src/main.js中引入Bootstrap

 

添加Bootstrap按钮控件,测试一下效果;

 

 

 

 

下面尝试做个改造例子,了解一下过程:

 1. 在components文件夹中添加一个新的模块Books.vue

     

 

 

 

 2. 更新一下路由router/index.js将首页/路由指向Books模块

       

 

 

 3.  刷新http://127.0.0.1:8080/  可看到新的模板;

      

 

 

 4.  将Books.vue 里面的模板替换为Bootstrap风格表格

      

 

 

 5. 刷新http://127.0.0.1:8080/  可看到新的模板;

     

 

 

 6. 下面要实现的是 前端界面和后端的API进行交互

      RESTfu API规范的后端服务,使用HTTP中的GET,POST,PUT,DELETE方法进行通信

 

      Server端(Flask)设置

    1. app.py中添加一个存放图书的数组变量模拟数据

      

 

 

      2. 修改Flask主文件app.py的路由

      

 

 

     3.  刷新http://127.0.0.1:5000/,可看到返回的json 话的数组数据;

        

 

Client端(VUE)设置

    1. 更新一下components/Books.vue模块的script 

        

 

 

        备注:

        当Books.vue模块created()完成的时候,调用getBooks()函数从后台获取图书数据
         模板部分使用了v-for标签,在table中循环输出行,将循环过程中的index作为了key主键(该主键设置是VUE规范推荐的,据说可以提高渲染效率). 使用v-if标签来判断渲染read是否的值.

     

        更新后的页面如下:

        

 

 

        2. 使用Bootstrap中的模态框控件来实现添加新书的界面

                 1 . npm安装bootstrap-vue包,命令行运行:npm install bootstrap-vue --save

                 2.  修改client/src/main.js启用bootstrap-vue

                      

 

    POST路由设置

        1. 更新app.py处理添加新书时POST请求的路由,修改之前添加过的路由, methods增加POST类型,然后通过判断request.method来确定接下来要进行的操作;

             

 

 

          2. 更新一下import部分的引入:  from flask import Flask, jsonify, request

           

Client端设置

       1. 在主<div>里面添加一个模态框来实现增加新图书的界面

               

                 这里使用了v-model标签来绑定input输入框中的值和VUE中data数据对象中的addBookForm变量(这个变量在下面代码中声明)

       2.  更新下script部分的代码

       3.  最终的Books.vue 的代码如下:

             <template>

  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr />
        <br />
        <br />
        <button type="button" class="btn btn-success btn-sm" v-b-modal.book-modal>Add Book</button>
        <br />
        <br />
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(bookindexin books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <div class="btn-group" role="group">
                  <button type="button" class="btn btn-warning btn-sm">Update</button>
                  <button type="button" class="btn btn-danger btn-sm ml-2">Delete</button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <b-modal ref="addBookModal" id="book-modal" title="Add a new book" hide-footer>
      <b-form @submit="onSubmit" @reset="onResetclass="w-100">
        <b-form-group id="form-title-group" label="Title:" label-for="form-title-input">
          <b-form-input
            id="form-title-input"
            type="text"
            v-model="addBookForm.title"
            required
            placeholder="Enter title"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-author-group" label="Author:" label-for="form-author-input">
          <b-form-input
            id="form-author-input"
            type="text"
            v-model="addBookForm.author"
            required
            placeholder="Enter author"
          ></b-form-input>
        </b-form-group>
        <b-form-group id="form-read-group">
          <b-form-checkbox-group v-model="addBookForm.readid="form-checks">
            <b-form-checkbox value="true">Read?</b-form-checkbox>
          </b-form-checkbox-group>
        </b-form-group>
        <b-button-group>
          <b-button type="submit" variant="primary">Submit</b-button>
          <b-button type="reset" variant="danger">Reset</b-button>
        </b-button-group>
      </b-form>
    </b-modal>
  </div>
</template>

<script>
import axios from "axios";

export default {
  data() {
    return {
      books: [],
      addBookForm: {
        title: "",
        author: "",
        read: []
      }
    };
  },
  methods: {
    getBooks() {
      const path = "http://localhost:5000/";
      axios
        .get(path)
        .then(res => {
          this.books = res.data.books;
        })
        .catch(error => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
    addBook(payload) {
      const path = "http://localhost:5000/";
      axios
        .post(pathpayload)
        .then(() => {
          this.getBooks();
        })
        .catch(error => {
          // eslint-disable-next-line
          console.log(error);
          this.getBooks();
        });
    },
    initForm() {
      this.addBookForm.title = "";
      this.addBookForm.author = "";
      this.addBookForm.read = [];
    },
    onSubmit(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      let read = false;
      if (this.addBookForm.read[0]) read = true;
      const payload = {
        title: this.addBookForm.title,
        author: this.addBookForm.author,
        read // property shorthand
      };
      this.addBook(payload);
      this.initForm();
    },
    onReset(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      this.initForm();
    }
  },
  created() {
    this.getBooks();
  }
};
</script>

 

 

 

            代码解释一下:

                1data中新创建了一个addBookForm对象,用来和表单中的input控件使用v-model标签进行数据双向绑定(这正是VUE的魅力所在)

               2. onSubmit()函数和<b-form>中的submit属性进行了绑定,用户在提交表单的时候会调用.

                   其中evt.preventDefault()是用来屏蔽浏览器默认的提交表单操作,this.$refs.addBookModal.hide()用来关闭模态框,随                 

                   后调用addBook()提交数据给后台添加新图书,最后通过initForm()重置表单数据

                3. addBook()函数发送POST请求给后台的/books路径用来添加新图书;

 

          

Alert提醒模块

       1.  添加一个用来反馈提醒用户操作结果的模块../src/components/Alert.vue

            

 

       2.  client/src/components/Books.vue模块中修改script部分代码注册上面添加的Alert.vue模块

             

 

 

        3. 刷新一下浏览器,可以看到Alert模块引用并显示成功

           

 

 

 

        4. Alert模块使用b-alert标签进行Bootstrap样式改造

          <template>

  <div>
    <b-alert variant="success" show>{{ message }}</b-alert>
    <br>
  </div>
</template>

<script>
export default {
  props: ['message'],
};
</script>

          b-alert标签中使用了variant来定义颜色样式, show定义了显示时候使用渐变动画

          这里注意一下在script中使用了props来定义声明模块中所包含的属性变量,

          这样我们自定义的这个模块就可以在被调用的时候,message会作为<alert></alert>标签中的一个属性 来接收参数.

 

       5.  回到Books模块中,可以带参数形式的调用Alert模块了,例如这样:

           <alert message="hi"></alert>

    6. 为了为了让alert模块中的message变量实现动态化,使用:变量名=语法进行数据双向绑定

           <alert :message="message"></alert>

7. 在VUE的data变量中声明message变量:

            

 

       8. 改造一下addBook()函数,加入对message变量的操作

              addBook(payload) {

       const path = 'http://localhost:5000/';
       axios.post(pathpayload)
         .then(() => {
          this.getBooks();
          this.message = 'Book added!';
        })
        .catch((error=> {
          // eslint-disable-next-line
         console.log(error);
         this.getBooks();
     });
    },

      9. 使用v-if标签,实现只有showMessage变量为真的时候才显示Alert模块

            <alert :message=message v-if="showMessage"></alert>>

      10. 需要在data中声明这个showMessage变量,并且修改addBook()函数,加入对showMessage的操作

           

 

PUT路由设置

      1. 更新已有的数据需要添加唯一的id标识,可以使用Python标准库中的uuid来生成

           修改server/app.pyBOOKS数组变量,别忘了引入uuid

         

 

       2. 修改路由中的all_books()函数,为新增加的书添加随机的唯一id

        

 

       3.  增加一条新的路由,用来根据id查看单个书信息

       

 

      4. 再添加个删除书操作的函数(内部调用的,不用关联路由)

       

 

    

 

Client端设置

     实现修改书籍操作功能的待办事项:

  1. 添加一个模态框表单
  2. 处理Update按钮的点击事件
  3. 通过AJAX发送请求
  4. 通知用户操作结果处理取消按钮的点击事件

1.添加模态框表单

 

Books.vue模块的template区域之前写好的模态框后面再写个模态框:

 

 

            

 

                      

    

 

 

 

 
<script>
import axios from "axios";

export default {
  data() {
    return {
      books: [],
      addBookForm: {
        title: "",
        author: "",
        read: []
      }
    };
  },
  methods: {
    getBooks() {
      const path = "http://localhost:5000/";
      axios
        .get(path)
        .then(res => {
          this.books = res.data.books;
        })
        .catch(error => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
    addBook(payload) {
      const path = "http://localhost:5000/";
      axios
        .post(pathpayload)
        .then(() => {
          this.getBooks();
        })
        .catch(error => {
          // eslint-disable-next-line
          console.log(error);
          this.getBooks();
        });
    },
    initForm() {
      this.addBookForm.title = "";
      this.addBookForm.author = "";
      this.addBookForm.read = [];
    },
    onSubmit(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      let read = false;
      if (this.addBookForm.read[0]) read = true;
      const payload = {
        title: this.addBookForm.title,
        author: this.addBookForm.author,
        read // property shorthand
      };
      this.addBook(payload);
      this.initForm();
    },
    onReset(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      this.initForm();
    }
  },
  created() {
    this.getBooks();
  }
};
</script>
posted @ 2020-04-04 10:37  Nsubmarine  阅读(17545)  评论(1编辑  收藏  举报