1. 为什么要采用SPA复合项目

1.1 前后端分离

  1. 目前前后端分离已经成为主流,除了为了SEO或首屏加载速度基本没有一定要服务端渲染HTML的必要了,即使是要前服务端渲染,前后端分离。解耦也利于项目的维护。

  2. 以前一个HTML页面可能包含多个不同的API,在小型MCV项目上,经常每个View里都有若干不同的逻辑去获取并渲染不同的数据。前后端分离后每个RESTful API就可以只做一件事情。

  3. 现在的前端应用也越发复杂,有大量的用户交互而不仅仅是展示,用jQuery去处理的话就就相对比较难维护,而且又要写页面又要写API。

1.2 Vue.js SPA应用

  1. Vue SPA应用体验优于嵌入vue.js的写法。主要原因在嵌入的写法在页面跳转时需要重新加载HTML,有一个页面重载的过程。SPA应用的跳转仅仅改变了页面的URL,没有进行实际的跳转。
  2. 可以使用ES新特性的语法。诸如async/await,class等。这些语法能降低JavaScript的难度,提升开发效率。

1.3 复合项目

  1. 当前蓝鲸支持多模块部署,前端应用使用node.js启用,即使全部是静态文件。这样一定程度的浪费了资源。
  2. 前后端开发者为一人的情况下便于管理代码。

从性能来说,用Django处理静态文件性能也不是很高,最好的办法应该是挂到nginx下,但目前蓝鲸不支持这种做法。

2. Vue.js

2.1 不使用BKUI-CLI,只用Vue

BKUI-CLI是独立部署前端应用的最佳实践,如果只是复合项目,鉴权等功能可以交给Django来做。mock功能在前后端开发为同一人的情况下显得不重要。单纯的Vue已经基本够用。

2.2 vue.config.js

使用vue-cli创建的vue项目隐藏了webpack配置文件,需要在vue.config.js中配置,详见(https://cli.vuejs.org/zh/guide/webpack.html),主要要修改几个地方:

  1. chainWebpack:项目入口文件,如果还在用src/mian.js的话,容易造成误解,修改为front_src/mian.js,同时修改目录名。
  2. devServer:Vue默认使用localhost访问,devserver有跨域检测。关闭devServer.disableHostCheck在本地开发时。
  3. outputDir:打包后的文件要打包到static目录,即Django默认的静态文件目录。
  4. publicPath:默认的是是/,要改成/static
  5. html:HTML文件输出的位置设为Django Home page view的模板(后面会讲)。

最终配置如下:

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
27
28
29
30
31
32
33
34
35
const path = require('path')
const SITE_TITLE = 'Marmot'

// 是否在蓝鲸环境
const inBkEnv = process.env.BKPAAS_ENVIRONMENT === 'stag' || process.env.BKPAAS_ENVIRONMENT === 'prod'

const customConfig = {
chainWebpack: config => {
config.entry('app')
.clear()
.add('./front_src/main.js')
.end()
config.resolve.alias
.set('@', path.join(__dirname, './front_src'))
if (inBkEnv) {
config.plugin('html')
.tap(_ => [{
title: SITE_TITLE,
template: path.join(__dirname, './front_src/public/index.html'),
filename: path.join(__dirname, './backend/app_webpage/templates/index.html')
}])
}
config.plugins.delete('copy')
},
devServer: {
disableHostCheck: true
},
outputDir: path.join(__dirname, './static')
}

if (inBkEnv) {
customConfig.publicPath = '/static/'
}

module.exports = customConfig

3. Django

3.1 使用blueapps:

没有直接使用Django,使用了蓝鲸blueapps框架,适当修改了目录结构。

3.2 index.html

在本地开发时和线上部署使用的index.html和静态文件均不同。本地开发时,使用Django反向代理vue serve,线上部署时,使用编译好的html文件。实现方法如下建立一个view。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
from django.http.response import HttpResponse
from django.shortcuts import render
from requests.exceptions import ConnectionError
from settings import ENVIRONMENT


def homepage(request):
if ENVIRONMENT == 'dev':
url = 'http://localhost:8001' + request.path

try:
res = requests.get(url)
return HttpResponse(res.content, content_type=res.headers.get('content-type'))
except ConnectionError:
return HttpResponse("前端项目未启动,请执行npm run serve启动")
else:
return render(request, 'index.html')

4. 部署

4.1 部署环境

enter image description here

4.2 前置命令的坑

蓝鲸在打包前端应用和后端应用时,前置命令会执行两次,我这里使用一个文件front_builded来判断是在哪个阶段执行:

bin/post-compile

1
2
3
4
5
6
7
8
#!/bin/bash
if [ -f 'front_builded' ]; then
echo "------> python manage.py migrate --no-input"
python manage.py migrate --no-input
else
echo "------> touch front_builded"
touch front_builded
fi