This page looks best with JavaScript enabled

Vue3 + ElementUI + Flask 速成网站

 ·  ☕ 5 min read · 👀... views

Vue Install

Install Node.js

先安装 nodejs 下载 LTS Windows Installer (.msi)然后运行安装就可以了。安装时有一个勾选项是“是否安装其他依赖库”可以不勾选

安装完毕后 node -v 和 npm -v 查看是否安装成功

config NodeJs

npm包管理器对包的管理分为全局包和局部包两种。局部包下载安装在当前目录下的 node_modules 下,由package.json维护,和go的mods管理模式类似。而全局包会安装在指定目录下,运行时会去该目录link。这里我们需要将vue安装在全局(vue应该是全局可用的而不是隶属于一个项目),所以应该需要配置一下安装目录

安装目录默认在 C:\Users\YourUser\AppData\Roaming\npm,我将其应用到自己的指定目录下,然后顺便配一下缓存目录和镜像目录

npm config set prefix "C:\Program Files\nodejs\node_global"     // 设置全局包目录
npm config set cache "C:\Program Files\nodejs\node_cache"       // 设置缓存目录
npm config set registry https://registry.npm.taobao.org         // 配置淘宝镜像
npm config list                                                 // 查看基本配置看是否配置成功

然后要把全局包目录添加到系统环境变量

Install Vue

全局安装下vue

npm install -g @vue/cli@next
vue --version

Intall yarn

yarn是一个更先进的、取代npm的包管理器,会在项目目录下创建yarn.lock以树状方式管理项目依赖。用法上几乎和npm一致

npm install -g yarn
yarn config set registry https://registry.npm.taobao.org 

基本用法和npm一致,除了安装是用add,这里给出一些常用的命令备忘

// yarn安装依赖
yarn add 包名                   // 局部安装
yarn global add 包名            // 全局安装

// yarn 卸载依赖
yarn remove 包名                // 局部卸载
yarn global remove 包名         // 全局卸载(如果安装时安到了全局,那么卸载就要对应卸载全局的)

// yarn 查看全局安装过的包
yarn global list  

// 初始化项目 
yarn init                       // 同npm init,执行输入信息后,会生成package.json文件

// 安装包
yarn install                    //安装package.json里所有包,并将包及它的所有依赖项保存进yarn.lock
yarn install --force            //强制重新下载所有包

// 缓存 
yarn cache 
yarn cache list # 列出已缓存的每个包 

Install Vscode Plugin

  1. Vetur

Vue Project

Create Project

vue create <YourProject>        // 选择Vue3
yarn serve
yarn build

然后就好了,非常简单

项目结构

然后简单看一下项目结构。创建后的Vue项目,从结构上需要我们重点关注的主要有四个部分:

  1. src里的App.vue:vue代码写里面
  2. src里的main.js:运行时的入口文件,同时也是在这里面初始化vue实例和各插件
  3. public里的index.html:这个html是主页的html文件,也是框架自动生成出来的
  4. 外层的config.js配置文件:会定义一些build时行为等

在index.html中可以看到

,而main.js中往往会有这样两句话

1
2
const app = createApp(App)
app.mount('#app')

这里意思就是创建了vue app,并将其挂载到index.html的app节点上。也就是说,mount方法执行时,会将编译后的vue文件(html代码)替换到这个id=app的div上。所以我们一般实现好App的导出就可以了,也就是生成例子中的App.vue

Vue+Flask

前后端分离的项目,进行整合开发主要要做好前后端的请求交互和整合build

交互

交互我用的是axios库,用法非常简单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Get
axios
  .get("/getapi")
  .then((res) => {
    console.log(res.data);
    for (const [key, text] of Object.entries(res.data)) {
      // Pass
    }
  })
  .catch((err) => {
    console.log("Get error : " + err);
  });

// Post
axios
  .post("/post_api", { key: params })
  .then((res) => {
    console.log(res.data);
  })
  .catch((err) => {
    console.log("Post error : " + err);
  });

然后在交互的时候也遇到一些坑点这里记录一下。首先是使用axios请求下载一个文件,浏览器没反应。先看我的后端实现:

1
2
3
4
5
6
7
8
9
@app.route('/downloadapi', methods=['POST'])
def download():
    # print(request.form.get("data"))             # Ajax传值接收方式
    data = request.get_json()["data"]             # axios传值接收方式
    
    if create_file({'data' : data}, filepath) == True:
        return send_file(filepath, download_name="file.dat", as_attachment=True)
    else:
        return abort(500)

后端从前端接收数据,保存到一个文件里,然后用send_file发送文件让用户下载,用户保存的名字应该是file.dat,并且开启了as_attachment选项提示浏览器这是一个附件应当保存下载它。如果直接浏览器内请求这个url是完全没有问题的,但是如果通过按钮事件触发用axios等方法去请求,就会出现浏览器无反应不下载附件的情况。最神奇的是,是否下载还很“风水”,如果download_name指定的下载后缀是 .exe .pdf 这种很常见的二进制格式文件,浏览器就会去下载它,而如果是 .dat .bin .txt 这些,浏览器就毫无反应;并且是否下载还和浏览器版本有关,新版的Chrome就能下载很多后缀的附件,而edge则只能下载少数几个后缀。

后面查了很多资料发现这是由于页面内的点击事件不会触发下载这一种浏览器的内置操作导致的,因为页面内的点击事件都是通过XHR请求实现,而非浏览器那层。所以正确的做法应该是(懒得解释了,直接上通用代码其实是我也说不清楚,毕竟前后端半吊子

 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
axios
  .post("downloadapi", { "data": data }, {responseType: 'blob'})
  .then((res) => {
    console.log(res.data);

    // 兼容浏览器下载模板代码
    const { data, headers } = res
    // console.log(headers);
    const fileName = headers['content-disposition'].replace(/.*filename=(.*)/, '$1')
    // 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
    //const blob = new Blob([JSON.stringify(data)], ...)
    const blob = new Blob([data], {type: headers['content-type']})
    let dom = document.createElement('a')
    let url = window.URL.createObjectURL(blob)
    dom.href = url
    dom.download = decodeURI(fileName)
    dom.style.display = 'none'
    document.body.appendChild(dom)
    dom.click()
    dom.parentNode.removeChild(dom)
    window.URL.revokeObjectURL(url)

  })
  .catch((err) => {
    console.log("Download error : " + err);
  });

Build

build实际上只需要前端项目中 npm build 完然后把生成的东西复制到后端目录就行了,这边我写了一个powershell脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
mkdir backend/templates

pushd frontend
yarn build
cp -Force dist/index.html ../backend/templates
cp -R -Force dist/static ../backend
cp -Force dist/favicon.ico ../backend
popd

python backend/app.py

ElementUI

Element UI 是一套以 Vue 作为基础框架实现的组件库,里面实现了大量的常用组件,可以通过调用这些组件以非常快的速度拼出一个页面。

ElementUI是Vue2.0的组件库,如果用的是Vue3,应该用 ElementPlus

使用起来也非常简单,先装包

yarn add element-plus

然后在main.js中,app mount之前,将插件引入

1
2
3
4
5
6
7
8
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import 'element-plus/dist/index.css'

app.use(ElementPlus)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }

然后就可以在.vue文件中用它的组件了,比如我创建一个删除按钮,并设置点击函数为removeAt

<el-icon @click="removeAt(index)">
  <Delete style="cursor: pointer" />
</el-icon>
Share on

Qfrost
WRITTEN BY
Qfrost
CTFer, Anti-Cheater, LLVM Committer