建立项目
首先咱们要建立项目并安装相关的组件html
1. vue init webpack luffy 2. 安装依赖: cd luffy npm install vuex --save 用于:多组件之间数据共享 npm install vue-cookies --save 用于:操做cookie npm install axios --save 用于:发送ajax请求 3. 快速使用 程序入口(manage.py): App.vue - 组件名称【组件1(/index ),组件2(/course),组件3】 - 空容器 main.js 路由(urls.py): router/index.js /index Course1.vue /course Course2.vue 组件(views): Course1.vue (每一个py文件) Course2.vue (每一个py文件) Course3.vue (每一个py文件)
建立基本的路由和组件
首先咱们要在App.vue中展示一些基本的组件,须要建立路由和相关的组件前端
index.jsvue
import Vue from 'vue' import Router from 'vue-router' import Index from '@/components/Index' import Course from '@/components/Course' import Detail from '@/components/Detail' import Micro from '@/components/Micro' import News from '@/components/News' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'index', component: Index }, { path: '/course', name: 'course', component: Course }, { path: '/detail/:cid', name: 'detail', component: Detail }, { path: '/micro', name: 'micro', component: Micro }, { path: '/news', name: 'news', component: News } ], mode:'history' })
mode:'history'参数能够去掉url中的#号webpack
App.vueios
<template> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/course">课程</router-link> <router-link to="/micro">学位课</router-link> <router-link to="/news">深科技</router-link> </div> <router-view/> </div> </template> <script> export default { name: 'App' } </script> <style> </style>
这样咱们就能够在页面上经过点击来切换路由和组件了web
展现课程列表
当咱们点击course组件时应该能看到课程列表,因此在course组件中咱们须要向后头发送ajax请求,获取课程的数据,并使用v-for显示到页面上ajax
course.vuevue-router
<template> <div> <h1>{{msg}}</h1> <div v-for="item in courseList"> <!--<router-link to="/detail">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>--> <!--<router-link :to="{path: '/detail/'+item.id }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>--> <router-link :to="{ name:'detail',params:{cid:item.id} }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link> </div> </div> </template> <script> export default { name: "Course", data(){ return { msg:'课程列表', courseList:[] } }, mounted(){ this.init() }, methods:{ init(){ // 发送Ajax this.courseList = [ {id:11,name:'21天学会Python',img: require('../assets/logo.png')}, {id:21,name:'21天学会Java',img:require('../assets/logo.png')}, {id:31,name:'21天学会Linux',img:require('../assets/logo.png')}, ] } } } </script> <style scoped> .img{ width: 30px; height: 30px; } </style>
这里咱们先不发送ajax请求了,直接模拟了数据,在定义图片的src属性时,能够直接写上图片的路径,可是若是像上面那样使用绑定属性时,可能会看不到图片,这时须要使用require,img: require('../assets/logo.png'),这样就能正常看到图片了vuex
显示课程详细
在course组件中咱们经过v-for的方式展现了课程列表,如今咱们须要经过点击某一门课程来看到它的详细信息,首先咱们要在路由中定义一个Detail组件npm
{ path: '/detail/:cid', name: 'detail', component: Detail },
因为须要某一门课的详细信息,因此须要知道这门课的id,在路径中咱们在/detail后加了:cid来区分id,这里的cid能够换成其它的,在course中咱们经过router-link来点击跳转
<div v-for="item in courseList"> <!--<router-link to="/detail">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>--> <!--<router-link :to="{path: '/detail/'+item.id }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>--> <router-link :to="{ name:'detail',params:{cid:item.id} }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link> </div>
这里的to属性咱们能够经过绑定方法来定义:
第一种方式是经过:to="{path: '/detail/'+item.id }",经过path再拼接id的形式来实现
第二种方式是经过:to="{ name:'detail',params:{cid:item.id} }",经过name来肯定url,并经过params来肯定url的参数
经过上面的两种方式咱们均可以访问到Detail组件
Detail.vue
<template> <div> <h1>课程详细 - {{currentCourseId}}</h1> <ul> <li>课程名称: {{courseDetail.title}}</li> <li>课程简介: {{courseDetail.summary}}</li> </ul> <h1>推荐课程</h1> <ul v-for="item in courseDetail.recommends"> <li v-on:click="recommendCourse(item.id)">{{item.name}}</li> <!--<router-link :to="{name:'detail',params:{cid:item.id}}">{{item.name}}</router-link>--> </ul> </div> </template> <script> export default { name: "detail", data(){ return { currentCourseId:this.$route.params.cid, courseDetail:{ title: '', summary:'', recommends:[] } } }, mounted(){ this.init() }, methods:{ init(){ // 根据当前ID发送Ajax请求,获取课程详细信息 this.currentCourseId this.courseDetail = { title: '21天学会Python'+this.currentCourseId, summary:'休想休想休想休想休想休想休想休想休想', recommends:[ {id:100,name:'rest api'}, {id:200,name:'vue'} ] } }, recommendCourse(cid){ // 主动将内容根据最新的课程ID进行更新 this.currentCourseId = cid this.init() // URL进行重定向 this.$router.push({name:'detail',params:{cid:cid}}) } } } </script> <style scoped> </style>
在详细页中咱们能够经过this.$route.params.cid来获取url中的参数,也就是课程的id值,经过这个id咱们能够向后端发送ajax请求来获取数据,并在页面上显示,在这里咱们要注意一点,在课程详细中包含有推荐课程,点击推荐课程咱们应该能看到推荐课程的详细
信息,可是若是还使用上面的方法,也就是<router-link :to="{name:'detail',params:{cid:item.id}}">{{item.name}}</router-link>,因为都是在Detail组件中,因此这时点击它,页面是不会变化的,只能看到url的变化,因此在这里咱们给推荐课程的标签绑定了一个事件,
点击后会修改this.currentCourseId为推荐课程的id,而后再次进行初始化(发送ajax请求),这样就能获取到推荐课程的数据并显示到页面上,可是这样只是页面内容变了,url没有变,因此咱们还要用this.$router.push({name:'detail',params:{cid:cid}})来进行url
重定向
相关命令
this.$route.params 获取url中的参数 this.$router.push() url重定向,跳转
利用vuex和vue-cookies实现用户登陆、注销
登陆按钮应该在每一个页面都能看到,因此咱们将组件放到App.vue中,首先建立路由
{ path: '/login', name: 'login', component: Login }
Login.vue
<template> <div> <input type="text" v-model="username" placeholder="用户名"> <input type="text" v-model="password" placeholder="密码"> <input type="button" value="登陆" v-on:click="doLogin"> {{error}} </div> </template> <script> export default { name: "Login", data(){ return { username:'', password:'', error:'' } }, methods:{ doLogin(){ if(this.username === 'alex' && this.password === '123'){ this.$store.commit('saveToken',{username:'alex',token:'sdfsdfsdf'}) this.$router.push({name:'index'}) }else{ this.error = '用户名或密码错误' } } } } </script> <style scoped> </style>
在这个组件中咱们经过v-model来进行数据的双向绑定,当用户输入了用户名和密码点击登陆后,咱们应该拿到数据,并发送给后端进行验证,这里咱们在前端简单验证一下,就不发送后端了,当验证成功后咱们跳转到index首页,验证失败则显示错误信息,这里咱们
其实还调用了$store中的一个方法,这个后面再说
组件写完后咱们就能够把他加到App.vue中了
<template> <div id="app"> <div> <router-link to="/">首页</router-link> <router-link to="/course">课程</router-link> <router-link to="/micro">学位课</router-link> <router-link to="/news">深科技</router-link> <div v-if="this.$store.state.username"> <a>{{this.$store.state.username}}</a> <a v-on:click="doLogout">注销</a> </div> <div v-else> <router-link to="/login">登陆</router-link> </div> </div> <router-view/> </div> </template> <script> export default { name: 'App', methods:{ doLogout(){ this.$store.commit('clearToken') } } } </script> <style> </style>
这里咱们能够看到在App.vue中作了一步判断,其实就是为了实现当用户没有登陆时,能够看到登陆按钮,若是用户登陆了就能看到本身的用户名和注销按钮,要实现这个功能必需要咱们的App.vue能使用到Login组件中的用户登陆信息,因此这里咱们使用vuex来实现
首先导入vuex并生成store对象
import Vue from 'vue' import Vuex from 'vuex' import Cookie from 'vue-cookies' Vue.use(Vuex) export default new Vuex.Store({ // 组件中经过 this.$store.state.username 调用 state: { username: Cookie.get('username'), token: Cookie.get('token'), apiList: { auth: 'http://127.0.0.1:8000/api/v1/auth/', courses: 'http://127.0.0.1:8000/api/v1/courses/', pricePolicy: 'http://127.0.0.1:8000/api/v1/price_policy/', shopCar: 'http://127.0.0.1:8000/api/v1/shop_car/', } }, mutations: { // 组件中经过 this.$store.commit('saveToken',{username:'alex',token:'sdfsdfsdf'}) 调用 saveToken: function (state, userToken) { state.username = userToken.username; state.token = userToken.token; Cookie.set("username", userToken.username, "20min") Cookie.set("token", userToken.token, "20min") }, clearToken: function (state) { state.username = undefined state.token = undefined Cookie.remove('username') Cookie.remove('token') } } })
能够看到这里state中定义了username和token,当用户登陆后咱们应该将这两个信息保存,并写到cookie中,因此在mutations中咱们定义了一个方法saveToken,当用户登陆成后咱们就能够调用这个方法来将username和token写到cookie中
Cookie.set("username", userToken.username, "20min") Cookie.set("token", userToken.token, "20min")
而后state中的username和token能够直接从cookie中获取
username: Cookie.get('username'), token: Cookie.get('token'),
注意,这里若是不从cookie中获取,而是写死的话,页面刷新后可能就取不到原来的值了,这样在App.vue中咱们就能够判断state.username是否存在,来显示登陆按钮或用户信息,当显示用户信息时,还有一个注销按钮,咱们给它绑定一个点击事件,当点击时,执行
mutations中的clearToken方法,注销cookie
state.username = undefined state.token = undefined Cookie.remove('username') Cookie.remove('token')
课程详细页面:tab切换
在课程详细页中咱们能够经过点击切换一些显示的内容,以下图所示
这里咱们能够给这些标签都绑定一个点击事件,而后经过v-show来实现
<h1>tab切换</h1> <div class="tab-menu"> <div> <a v-on:click="changeTab('detail')">课程概述</a> <a v-on:click="changeTab('chapter')">课程章节</a> <a v-on:click="changeTab('review')">用户评价</a> <a v-on:click="changeTab('question')">常见问题</a> </div> </div> <div> <div v-show="tabs.detail">课程概述内容</div> <div v-show="tabs.chapter">课程章节内容</div> <div v-show="tabs.review">用户评价内容</div> <div v-show="tabs.question">常见问题内容</div> </div>
这里的详细内容也应该经过ajax请求从后台获取,这里咱们直接写死了
tabs数据
tabs: { detail: true, chapter: false, //chapter review: false, question: false, },
changeTab方法
changeTab(name) { for (let item in this.tabs) { if (item === name) { this.tabs[item] = true } else { this.tabs[item] = false } } },
点击某一个时就将其对应的tabs中的值变为true,其它的变为false,这样经过v-show就能将点击的内容显示出来
咱们用到的式样
.tab-menu { border-bottom: 1px solid #ddd; padding-top: 30px; text-align: center; } .tab-menu a { display: inline-block; padding: 20px; border-bottom: 2px solid transparent; cursor: pointer; } .tab-menu a:hover { border-bottom: 2px solid darkseagreen; }
价格策略效果
在课程详细页中咱们还应该显示页面的价格策略
<h1>价格策略</h1> <ul class="price-policy"> <li v-bind:class="[{active:num==selectCourseIndex} ]" v-on:click="choiceCourse(num)" v-for="(pri,num) in prices">¥{{pri.price}} (有效期 {{pri.period}} ) </li> </ul>
这里的prices数据咱们本身模拟,就不存后端拿了,而后给每一个标签绑定一个点击事件,点击时将selectCourseIndex的值改成当前标签的索引,这样当前的标签就能得到一个active的class属性
prices: [ {id: 11, price: 100, period: '2个月'}, {id: 21, price: 200, period: '3个月'}, {id: 31, price: 300, period: '4个月'}, ] choiceCourse(index) { this.selectCourseIndex = index }
active样式
.price-policy .active { background-color: darkseagreen; }
播放cc视频
在cc视频上传了视频后cc视频会给咱们一段html代码,咱们只要把他复制到咱们的页面上就能够了
在课程详细页组件中
<h1>视频描述</h1> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="400" height="300" v-bind:id="'cc_'+video_brief_link"> <param name="movie" v-bind:value="'https://p.bokecc.com/flash/single/C2401FCB0F73D923_'+video_brief_link + <param name="allowFullScreen" value="true"/> <param name="allowScriptAccess" value="always"/> <param value="transparent" name="wmode"/> <embed v-bind:src="'https://p.bokecc.com/flash/single/C2401FCB0F73D923_'+video_brief_link + '_false_35E7C8085202EBC3_1/player.swf'" width="400" height="300" name="cc_ECC9954677D8E1079C33DC5901307461" allowFullScreen="true" wmode="transparent" allowScriptAccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"/> </object>
其中的video_brief_link参数就是cc提供的一段字符串,咱们能够在data中定义
video_brief_link: 'ECC9954677D8E1079C33DC5901307461'
基于router的拦截器实现用户登陆成功后才能访问页面
router有一个功能相似于django中的中间件,能够在访问某一个url前写一些逻辑,这里咱们用来实现一些页面的访问控制,对于一些页面,咱们只给登陆后的用户访问,未登陆的则直接跳转到登陆页面
main.js
router.beforeEach(function (to,from,next) { if(to.meta.requireAuth){ // 须要登陆的路由 if (store.state.token) { // 已经登陆 next() } else { // 未登陆 next({ name: 'login' }) } }else{ // 不须要登陆的路由 next() } } )
router.beforeEach就是在访问url前会执行的方法,这里的function有3个参数,to from next,to表示要访问的url,from表示从哪一个url来,next是接下来实际会访问的url,通常状况下next和to是同样的
这里咱们判断用户是否登陆了,可是不能对每一个页面都判断,只有一些特定的页面须要作这个判断,这时咱们就能够在url中添加一些参数了
{ path: '/micro', name: 'micro', component: Micro, meta:{ requireAuth:true, } },
对于须要登陆才能访问的url咱们能够加一个meta参数,里面有一个requireAuth为true,在router.beforeEach咱们就能够先判断to.meta.requireAuth,若是为true说明须要登陆后才能访问,而后再判断token是否存在,存在表示已经登陆,则直接next(),不存在则跳转到
登陆页面next({ name: 'login' })
二级路由
前面咱们用的都是一级路由,如今咱们来使用一次二级路由
{ path: '/help', name: 'help', component: Help, children: [ { path: 'about-us', name: 'about-us', component: AboutUs }, { path: 'user-note', name: 'user-note', component: UserNote }, { path: 'feedback', name: 'feedback', component: Feedback } ] }
首先先写一个一级路由help,而后加一个children属性,里面写二级路由,注意,这里二级路由的path前面不要加/
写一级路由组件
<template> <div> <h2>{{msg}}</h2> 预留二级路由的位置 <router-view></router-view> </div> </template>
注意在一级路由组件中须要给二级路由预留位置<router-view></router-view>
写二级路由
<template> <div> <h2>{{msg}}</h2> </div> </template>
使用
<router-link to="/help/about-us">关于咱们</router-link> <router-link to="/help/feedback">用户反馈</router-link> <router-link to="/help/user-note">使用手册</router-link>
点击后就能看到在页面上有一级路由组件的内容,而且内容中还包含有二级路由组件的内容
访问登陆页面时,添加backurl
为了提升用户体验,当咱们访问登陆页面登陆成功后,应该跳转回以前的页面,而不该该老是跳转到首页,咱们能够经过在访问login的url时添加backurl参数来实现
首先咱们要修改登陆按钮的to属性
<router-link v-else :to="{name:'login',query:{backUrl: this.$route.path}}">登陆</router-link>
这里咱们增长了一个query属性,这个属性就是在url后加?k1=v1这类的参数的,而后咱们经过this.$route.path取到当前页的url,并添加到backUrl参数中
在登陆组件中,登陆成功后咱们要取到backUrl属性并判断
methods: { doLogin() { if (this.username === 'oldboy' && this.password === '123') { let obj = {username: 'oldboy', token: 'jsudfnksdjwe8234sdfsdkf'} this.$store.commit('saveToken', obj) let backUrl = this.$route.query.backUrl if (backUrl) { this.$router.push({path: backUrl}) } else { this.$router.push('/index') } } else { this.error = '用户名或秘密错误' } }
经过this.$route.query取到url中的参数backUrl,并判断是否存在,若是存在则使用this.$router.push({path: backUrl})跳转回原来的页面,不存在则跳转首页
在router拦截器中咱们也应该实现这个功能
router.beforeEach(function (to, from, next) { if (to.meta.requireAuth) { if (store.state.token) { next() } else { next({ path: '/login', query: {backUrl: to.fullPath} }) } } else { next() } })
当要跳转登陆页面时须要加backUrl参数