0x00 前言
本文是 Django 全栈开发教程的第四篇
目录在这里, 已经更新的文章如下
Django 全栈开发教程之目录篇 - 2018 年不容错过的 Django 全栈项目
Django 全栈开发教程之 01 - YaDjangoBlog 的开发环境配置
Django 全栈开发教程之 02 - YaDjangoBlog 的前后端设计
Django 全栈开发教程之 03 - YaDjangoBlog 之前后端分离篇
本文需要完成三件事情:
第一件事情, 介绍为什么选择 vuejs?
第二件事情, 介绍 vue 项目的一些注意点
第三件事情, 蜻蜓点水搬的带大家过一编, YaDjangoBlog 前端的项目结构, 静态资源管理, 路由以及组件
0x01 为什么是 VueJS
国产框架 + 语法简洁是我入坑 VueJS 初衷
后来却是 Vue 的丰富的生态和简洁的语法吸引了继续用下去
这里要感谢为 VueJS 持续贡献代码的人, 从 Vue 本身, 到 VueCLI, 到 Router, 到 VueX, 如果没有那么多人为之贡献代码, 可能今天这一小节就变成了, 为什么是 React 了逃
Vue 自称为 Vue 渐进式 JavaScript 框架
什么是渐进式?
就是你可以逐步按照 Vue 的方式逐渐引入一些 Vue 的组件到项目中没有必要上来就是 Vue 全家桶, 依据场景逐步引入
参考链接 www.zhihu.com/question/51
然而, 依据我的经验, vue 全家桶用起来还是很舒服的这里必须要感谢 Vue 社区
模板语法, 数据驱动, 双向绑定写起代码来简直就是一个字, 爽
0x02 Vue 项目的一些注意点
从项目角度, 我们想想前端项目有哪些地方是需要注意的:
开发环境和线上环境区分
前端资源打包
Vue 项目资源打包
DLL 打包
字体文件打包
CSS/JS 如何管理
有哪些必要的依赖, 如何引入第三方库
有哪些页面级组件, 有哪些小组件? 应该安排这些组件? 组件与组件应该怎么通讯?
路由怎么管理
状态怎么管理
登录, 鉴权怎么做
限于篇幅, 我就不一一讲解了挑在 YaDjangoBlog 中使用到的技术简单介绍一下
再次感谢 Vue 社区出品的 VueCLI 以及 webpack 模板
下面依次介绍:
项目结构
静态资源管理
路由
组件
0x03 项目结构
首先, YaDjangoBlog 文件的前端目录如下:
- .
- README.md
- build
- build.js
- build_iconfont.js # 构建 iconfont 脚本
- check-versions.js
- logo.png
- utils.js
- vue-loader.conf.js
- webpack.base.conf.js
- webpack.dev.conf.js # 增加了 AutoDllPlugin 用于自动打包 DLL
- webpack.prod.conf.js
- config
- dev.env.js # 可以在这里添加开发环境的环境变脸
- index.js
- prod.env.js
- test.env.js
- extra
- svg-icon # 这里存放需要生成 iconfont 的字体文件
- index.html
- package.json # 这里添加了一些构建脚本
- packages
- theme-future # 注意, 这里是另一个子项目, 使用 Gulp 构建的纯 CSS 子项目
- src
- App.vue
- api # 对 axios 进行初步封装
- assets # 从使用 Gulp 生成的 CSS 可以放在这里
- components # 跨页面的组件放在这里
- directives # 指令
- filters # 过滤器
- main.js # 初始化 Vue 实例
- pages # 页面
- router # 路由
- store # vuex
- utils # 常用工具类
- static
- hightlight # hightlight 脚本
- iconfont # 本地构建的 iconfont
- images
- js
- test # 没写测试, 大家开源项目不要学我.... 逃
- e2e
- unit
- yarn.lock
0x04 静态资源管理
静态资源管理, 主要涉及到 JS/CSS/ 图片 / 字体
首先, 由于使用了 VueCli 的模板, 所以大可以按照 VueCli 提供的写法来写
- <template>
- <div id="app">
- <Header></Header>
- <router-view/>
- <Footer></Footer>
- </div>
- </template>
- <script>
- export default {
- name: 'app',
- components: {
- Header: () => import('./pages/commons/Header.vue'),
- Footer: () => import('./pages/commons/Footer.vue'),
- }
- }
- </script>
- <style lang="scss">
- $primary-color: #37b24d;
- $dark-color: #2b5732;
- $body-bg: #f9f9f9;
- @import '~spectre.css/src/spectre-icons.scss';
- @import '~spectre.css/src/spectre.scss';
- @import '~spectre.css/src/spectre-exp.scss';
- @import './assets/theme-future/index.css';
- a {
- &:focus,
- &:hover,
- &:active,
- &.active {
- text-decoration: none;
- box-shadow: 0 0 0 0;
- }
- }
- #app {
- }
- </style>
依据我个人经验, 做了一部分的微调:
第一 在代码中新建一个主题 CSS, 单独用于处理 SCSS 编译 CSS. 即除了 App.vue, 其他地方的 CSS 直接写在同一个地方
第二 对于字体文件, 不引入 IconFont 在线字体, 而是使用 SVG 本地编译字体这样减少对 iconfont cdn 的依赖, 可以以后直接迁移这个字体到其他 CDN 上
第三 对于依赖库管理, 分为 npm 依赖库和外部 JS 依赖库两种
对于 NPM 依赖库, 如果有使用过 ECharts3.0 的 SPA 开发者应该对于万恶的 DLL 非常熟悉了最早的时候, 我们是这样做的:
先写一个编译脚本, 指定相关依赖包, 打包出 dll 和一个 manifest 文件
然后从 index.html 里引入打包好的 dll.
再从 webpack 的配置文件中引入这个文件
这种恶心的配置随着 autodll-webpack-plugin 的出现从而得到缓解, 于是现在的你只需要配置:
- new AutoDllPlugin({
- inject: true, // will inject the DLL bundles to index.html
- debug: true,
- filename: '[name]_[hash].js',
- entry: {
- vendor: [
- '@antv/data-set',
- '@antv/g2',
- '@antv/g6',
- 'highlight.js',
- 'markdown-it',
- 'markdown-it-abbr',
- 'markdown-it-deflist',
- 'markdown-it-emoji',
- 'markdown-it-footnote',
- 'markdown-it-ins',
- 'markdown-it-katex',
- 'markdown-it-mark',
- 'markdown-it-sub',
- 'markdown-it-sup',
- 'markdown-it-task-lists',
- 'markdown-it-toc-and-anchor',
- 'typed.js',
- 'vue',
- 'vue-router',
- 'vuex'
- ]
- },
- plugins: [new webpack.optimize.UglifyJsPlugin()],
- })
当然, 如果你用了 ECharts, 有的时候会出现莫名其妙的
__DEV__ is not defined
解决方法就是在这上面的插件里面加个插件定义一个 Global 的变量
- new webpack.DefinePlugin({
- __DEV__: false
- }),
PS: 去年的版本由于依赖库的一个路径问题导致 autodll-webpack-plugin 不能在 Windows 上使用, 今年可以啦还不快快用起来?
对于外部的 JS/CSS 依赖库:
直接拷贝到 static 下面, 然后从 index.html 引入即可
动态创建 script 标签 (比如动态引入高德地图)
0x04 路由
博客项目, 实际上路由比较简单
- export default new Router({
- mode: 'history',
- base: '/',
- // 注释掉这里是因为和引入的 smooth-scroll 冲突
- // scrollBehavior (to, from, savedPosition) {
- // return { x: 0, y: 0 }
- // },
- routes: [
- {
- path: '/',
- name: 'home',
- component: () => import('../pages/Home.vue')
- },
- // 解决手贱带来的问题
- {
- path: '/index:suffix*',
- name: 'index',
- component: () => import('../pages/Home.vue')
- },
- {
- path: '/blog',
- name: 'blog',
- component: () => import('../pages/Blog.vue')
- },
- {
- path: '/blog/post/:title',
- name: 'post',
- component: () => import('../pages/Blog/ArticlePost.vue')
- },
- {
- path: '/blog/:category(category/\\d+)?/:tags(tags/\\d+)?/:page(page/\\d+)?',
- name: 'blogposts',
- component: () => import('../pages/Blog.vue')
- },
- {
- path: '/archive',
- name: 'archive',
- component: () => import('../pages/Archive.vue')
- },
- {
- path: '/gallery',
- name: 'gallery',
- component: () => import('../pages/Gallery.vue')
- },
- {
- path: '/works',
- name: 'works',
- component: () => import('../pages/Works.vue')
- },
- {
- path: '/about',
- name: 'about',
- component: () => import('../pages/About.vue')
- }
- ]
- })
除了 import 语法之外, 需要注意的就是 '/blog/:category(category/\d+)?/:tags(tags/\d+)?/:page(page/\d+)?' 这个奇怪的表达式
这个表达式可以用于匹配下面的路由
- /blog/category/1/tags/2/page/3
- /blog/category/1/page/3
- /blog/tags/3/page/3
- /blog/page/3
匹配完毕之后, 就可以拿到 categroy tags page 的值然后提交数据库拿数据咯
0x05 组件
博客里面需要注意的就三个组件
ArticlePost 组件
分页组件
打字终端组件
第一个, ArticlePost 组件
- <template>
- <div class="p-article-post">
- <div class="columns">
- <div class="col-1 hide-xl">
- </div>
- <div class="col-2 col-xl-3">
- <div class="g-sidebar">
- <h4 > 本文目录 </h4>
- <div v-html="articleToc"></div>
- </div>
- </div>
- <div class="col-6 col-xl-8">
- <ArticleCard :article="article" @articleTocReady="initArticleToc"></ArticleCard>
- </div>
- <div class="col-2 col-xl-3">
- <div class="g-sidebar">
- <h4 > 公告 </h4>
- <div>
- <p>
MG 的编程小屋, 其实就是我整理笔记, 写写文章的地方
- </p>
- <p>
专注 Python / JavaScript , 爱折腾的全干工程师 (Full Stuff Engineer)
- </p>
- <p>
如果我的文章给您的日常开发带来很大帮助的话
- </p>
- <p>
您可以关注我的公众号
- </p>
- <div>
- <img src="/static/images/mp_wechat.jpg" alt=""style="width: 200px">
- </div>
- <p>
也可以扫描二维码进行投喂
- </p>
- <div>
- <img src="/static/images/tips_wechat.jpeg" alt=""style="width: 200px">
- </div>
- <p>
听说关注或者进行投喂的人, 技术都越来越牛咯
- </p>
- </div>
- </div>
- </div>
- <div class="col-1 hide-xl">
- </div>
- </div>
- </div>
- </template>
- <script>
- import {
- fetchBlogPost
- }
- from '../../api/blog';
- export
- default {
- name:
- 'BlogPage',
- components: {
- ArticleCard: () = >import('../../components/Common/ArticleCard.vue'),
- },
- data() {
- return {
- article: '',
- articleToc: undefined
- }
- },
- watch: {
- '$route.params': function() {
- this.initArticle()
- }
- },
- created() {
- this.initArticle()
- },
- mounted() {},
- methods: {
- initArticleToc: function(v) {
- this.articleToc = v;
- },
- initArticle: function() {
- let title = this.$route.params.title;
- fetchBlogPost(title).then((res) = >{
- this.article = res;
- })
- }
- }
- }
- </script>
嗯, 其实就是监听 url, 如果匹配上 url 的话, 从 url 中取 title, 然后发送请求, 接着取回响应的内容交给子组件处理子组件处理完毕会 emit 出一个 toc 的值, 将这个值赋值给左侧 toc 即可
分页组件
见地址吧 github.com/twocucao/Ya
打字终端组件
终端的样式, 当然是抄别人的 CSS, 打字效果, 来源于 typed.js 依赖库
0x06. 扩展
对于其他的实现, 自然是要多多看代码咯
其实前端工程化是一个很广的概念, 本文没有提到代码风格团队开发工作流 CSS 编写规范组件优化 Webpack 详细配置等等这都需要在日常开发中多多练习的
笔者最近换了份工作, 以 React 为技术栈 加上篇幅和精力有限, 也就是不在以 Vue 为前端这一块详细展开了
下面的文章还是聚焦在后端上面
0xEE. 参考链接
还犹豫啥, Django 前后端分离最佳实践, 点赞后, 快上车吧
前端代码 https://github.com/twocucao/YaVueBlog
后端代码 https://github.com/twocucao/YaDjangoBlog
来源: https://juejin.im/entry/5aade0a2f265da23a335016c