Vue項目環境搭建
1) 安裝node
官網下載安裝包,傻瓜式安裝:https://nodejs.org/zh-cn/
2) 換源安裝cnpm
>: npm install -g cnpm --registry=https://registry.npm.taobao.org
3) 安裝vue項目腳手架
>: cnpm install -g @vue/cli
注:2或3終端安裝失敗時,可以清空 npm緩存 再重復執行失敗的步驟
npm cache clean --force
Vue項目創建
1) 進入存放項目的目錄 d: 切換D盤 cd D:\python_workspace\day66\代碼
2) 創建項目 vue create v-proj
3) 項目初始化 選擇 Manually select features
默認選擇的有Babel Linter / Formatter 在添加Router 和 Vuex
選擇YES
選擇第一個,直接進入
直接第一個進入
選擇第一個In dedicated config files,第一個自己處理
選擇NOT
加載環境
選擇改變端口號的地方,然后點擊左邊加號,選擇npm,然后在右邊Name中填寫v-proj,
在pycharm中選擇settings文件夾,選擇Plugins,在右側搜索框中搜索VUE,下載
pycharm配置並啟動vue項目
1) 用pycharm打開vue項目
2) 添加配置npm啟動
終端啟動vue cd D:\python_workspace\day66\代碼\v-proj cnpm run serve
vue組件(.vue文件)
# 1) template:有且只有一個根標簽
# 2) script:必須將組件對象導出 export default {}
# 3) style: style標簽明確scoped屬性,代表該樣式只在組件內部起作用(樣式的組件化)
App.vue
<template> <div id="app"> <!--url路徑會加載不同的組件 /red => RegPage | /blue => BluePage 替換router-view標簽,完成也買你切換--> <router-view/> </div> </template>
全局腳本文件main.js(項目入口)
import Vue from 'vue' //加載vue環境 import App from './App.vue' //加載根組件 import router from './router' //加載路由環境 import store from './store' //加載數據倉庫環境 Vue.config.productionTip = false; //配置全局樣式 import '@/assets/css/global.css' new Vue({ el: '#app', router, store, render: function (readFn) { return readFn(App); }, });
vue項目啟動生命周期
1) 加載mian.js啟動項目
i) import Vue from 'vue' 為項目加載vue環境
ii) import App from './App.vue' 加載根組件用於渲染替換掛載點
iii) import router from './router' 加載路由腳本文件,進入路由相關配置
2) 加載router.js文件,為項目提供路由服務,並加載已配置的路由(鏈接與頁面組件的映射關系)
注:不管當前渲染的是什么路由,頁面渲染的一定是根組件,鏈接匹配到的頁面組件只是替換根組件中的
<router-view></router-view>
新增頁面三步驟
1) 在views文件夾中創建視圖組件
2) 在router.js文件中配置路由
3) 設置路由跳轉,在指定路由下渲染該頁面組件(替換根組件中的router-view標簽)
組件生命周期鈎子
# 1)一個組件從創建到銷毀的整個過程,就稱之為組件的生命周期
# 2)在組件創建到銷毀的過程中,會出現眾多關鍵的時間節點,如 組件要創建了、組件創建完畢了、組件數據渲染完畢了、組件要被銷毀了、組件銷毀完畢了 等等時間節點,每一個時間節點,vue都為其提供了一個回調函數(在該組件到達該時間節點時,就會觸發對應的回調函數,在函數中就可以完成該節點需要完成的業務邏輯)
# 3)生命周期鈎子函數就是 vue實例 成員
views\Home.vue
<template> <div class="home"> <Nav /> <div class="router"> <button type="button" @click="goPage('/')">主頁</button>
<button type="button" @click="goPage('/red')">紅頁</button>
<button type="button" @click="goPage('/blue')">藍頁</button> <button type="button" @click="goBack('/')">返回上一頁</button> </div> </div> </template> <script> import Nav from '@/components/Nav.vue' export default { name: 'home', components: { Nav }, methods: { goPage(page) { let currentPage = this.$route.path; if (currentPage !== page){ this.$router.push(page); } }, goBack(){ this.$router.go(-1) }, goPageName(pageName) { // alert(name) this.$router.push({ name: pageName }) } } } </script>
views\BluePage.vue
<template>
<div class="blue-page">
<Nav></Nav>
</div>
</template>
<script>
import Nav from '@/components/Nav'
export default {
name: "BluePage",
components: {
Nav
}
}
</script>
<style scoped>
.blue-page {
width: 100vw;
height: 100vh;
background-color: blue;
}
</style>
views\RedPage.vue
<template>
<div class="red-page">
<Nav></Nav>
<h1 class="title" @click="alterTitle">{{ title }}</h1>
</div>
</template>
<script>
import Nav from '@/components/Nav'
export default {
name: "RedPage",
data() {
return {
title: '紅頁'
}
},
methods: {
alterTitle() {
alert(this.title)
}
},
components: {
Nav
},
beforeCreate() {
console.log('組件創建了,但數據和方法還未提供');
// console.log(this.$data);
// console.log(this.$options.methods);
console.log(this.title);
console.log(this.alterTitle);
},
// 該鈎子需要掌握,一般該組件請求后台的數據,都是在該鈎子中完成
// 1)請求來的數據可以給頁面變量進行賦值
// 2)該節點還只停留在虛擬DOM范疇,如果數據還需要做二次修改再渲染到頁面,
// 可以在beforeMount、mounted鈎子中添加邏輯處理
created() {
console.log('組件創建了,數據和方法已提供');
// console.log(this.$data);
// console.log(this.$options.methods);
console.log(this.title);
console.log(this.alterTitle);
console.log(this.$options.name);
},
destroyed() {
console.log('組件銷毀完畢')
}
}
</script>
<style scoped>
.red-page {
width: 100vw;
height: 100vh;
background-color: red;
}
.title {
text-align: center;
cursor: pointer;
}
</style>
components/Nav.vue
<template>
<div class="nav">
<ul>
<li :class="{active: currentPage === '/'}">
<router-link to="/">主頁</router-link>
</li>
<li :class="{active: currentPage === '/red'}">
<router-link to="/red">紅頁</router-link>
</li>
<li :class="{active: currentPage === '/blue'}">
<router-link to="/blue">藍頁</router-link>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "Nav",
data(){
return {
currentPage:''
}
},
created() {
this.currentPage = this.$route.path;
}
}
</script>
<style scoped>
.nav {
width: 100%;
height: 60px;
background-color: orange;
}
.nav li {
float: left;
font: normal 20px/60px '微軟雅黑';
}
.nav li:hover {
cursor: pointer;
background-color: aquamarine;
}
.nav li.active {
cursor:pointer;
background-color: aquamarine;
}
.nav li a {
display: block;
height: 60px;
padding: 0 20px;
}
</style>
router.js
import Vue from 'vue' import Router from 'vue-router' import Home from './views/Home.vue' import RedPage from './views/RedPage.vue' import BluePage from './views/BluePage.vue' Vue.use(Router); export default new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home }, { path: '/red', name: 'red', component: RedPage }, { path: '/blue', name: 'blue', component: BluePage }, ] })
路由跳轉
src/views/Home.vue
<template> <div class="home"> <Nav /> <h1>{{ hTitle }}</h1> <hr> <div class="router"> <button type="button" @click="goPage('/course')">課程頁</button> <!--<button type="button" @click="goPage('/red')">紅頁</button>--> <button type="button" @click="goPage('/')">主頁</button> <button type="button" @click="goBack('/')">返回上一頁</button> <button type="button" @click="goPageName('/course')">課程頁(name)</button> <router-link :to="{name: 'course'}">課程頁(name)</router-link> </div> </div> </template> <script> import Nav from '@/components/Nav.vue' export default { name: 'home', data(){ return { hTitle: '主頁' } }, components: { Nav }, methods: { goPage(page) { let currentPage = this.$route.path; if (currentPage !== page){ this.$router.push(page); } }, goBack(){ this.$router.go(-1); // this.$router.go(1); }, goPageName(pageName) { // alert(name) this.$router.push({ name: pageName }) } } } </script>
路由傳參
router.js 配置路由
routes: [ { path: '/course', name: 'course', component: Course }, { path: '/course/:id/detail', name: 'course-detail', component: CourseDetail }, ]
src/views/Course.vue
<template>
<div class="course">
<Nav />
<h1>{{ cTitle }}</h1>
<hr>
<div class="main">
<CourseCard v-for="course in course_list" :key="course.name" :course="course" />
</div>
</div>
</template>
<script>
import Nav from '@/components/Nav'
import CourseCard from '@/components/CourseCard'
let course_list = [
{
id: 1,
name: 'Python入門到入土'
},
{
id: 2,
name: '前端放棄攻略'
},
{
id: 3,
name: '你最棒,他最強'
},
{
id: 4,
name: '基佬修煉法則'
},
];
export default {
name: "Course",
components: {
Nav,
CourseCard,
},
data() {
return {
course_list,
cTitle: '課程頁',
}
},
}
</script>
<style scoped>
</style>
src/views/CourseDetail.vue
<template>
<div class="course-detail">
<h1>{{ course.name }}</h1>
<p>{{ course.info }}</p>
<p>{{ course.price }}</p>
</div>
</template>
<script>
let course_list = [
{
id: 1,
name: 'Python入門到入土',
price: 6.66,
info: '三分鍾入門,一分鍾入土!學了你不吃虧,不學你就廢了!'
},
{
id: 2,
name: '前端放棄攻略',
price: 3.66,
info: '學習前端,忘掉所有痛苦!'
},
{
id: 3,
name: '你最棒,他最強',
price: 5.22,
info: '別做夢了!'
},
{
id: 4,
name: '基佬修煉法則',
price: 80000,
info: '就是他,錯不了!'
},
];
export default {
name: "CourseDetail",
data () {
return {
course: {},
cTitle: '',
hTitle: '',
}
},
created() {
let id = this.$route.params.id || this.$route.query.id || 1 ;
// for of 遍歷的值 | for in 遍歷的是取值的依據(arr是索引,obj是key)
for (let course of course_list) {
if (id == course.id) {
this.course = course;
break
}
}
}
}
</script>
<style scoped>
</style>
src/components/CourseCard.vue
<template>
<div class="course-card">
<h1 @click="goDetail">{{ course.name }}</h1>
</div>
</template>
<script>
export default {
name: "CourseCard",
props: ['course'],
methods: {
goDetail() {
this.$router.push({
name: 'course-detail',
});
// 第一種傳參
// this.$router.push({
// name: 'course-detail',
// params: {
// id: this.course.id
// }
// });
// 第二種傳參
// this.$router.push({
// name: 'course-detail',
// query: {
// id: this.course.id
// }
// });
// 第三種
this.$router.push(`/course/${this.course.id}/detail`);
}
}
}
</script>
<style scoped>
.course-card h1, .course-card a {
width: 200px;
height: 200px;
border-radius: 50%;
background-color: coral;
font: normal 20px/200px 'STSong';
float: left;
text-align: center;
cursor: pointer;
display: block;
}
</style>
跨組件傳參
store.js
export default new Vuex.Store({ state: { cTitle: '課程頁' }, mutations: { //mutations 為 state 中的屬性提供setter方法 //setter方法名隨意,但是參數列表固定兩個:state,newValue setCTitle(state, newValue) { state.cTitle = newValue; } }, actions: { } })
src/views/CourseDetail.vue
<template>
<div class="course-detail">
<h1>課程詳情頁</h1>
<hr>
<p>
修改課程頁標題 <input type="text" v-model="cTitle"> <button @click="changeCTitle">修改</button>
</p>
<p>
修改主頁標題 <input type="text" v-model="hTitle"> <button @click="changeHTitle">修改</button>
</p>
<hr>
<h1>{{ course.name }}</h1>
<p>{{ course.info }}</p>
<p>{{ course.price }}</p>
</div>
</template>
<script>
export default {
methods: {
changeCTitle() {
// 通過一種存儲數據的方式,完成組件間的數據交互(組件可以有父子關系,也可以無關系)
// 跨組件傳參可以有4種方式
// 1) localStorage:永久存儲數據
// 2) sessionStorage:臨時存儲數據(刷新頁面數據不重置,關閉再重新開啟標簽頁數據重置)
// 3) cookie:臨時或永久存儲數據(由過期時間決定)
// 4) vuex的倉庫(store.js):臨時存儲數據(刷新頁面數據重置)
// 1)
// this.cTitle && (localStorage.cTitle = this.cTitle);
// 4)
// console.log(this.$store)
// this.$store.state.cTitle = this.cTitle;
this.$store.commit('setCTitle', this.cTitle);
},
changeHTitle() {
this.hTitle && (localStorage.hTitle = this.hTitle);
}
},
created() {
console.log(this.$route);
let id = this.$route.params.id || this.$route.query.id || 1 ;
}
}
</script>
vue的cookie操作
安裝 cnpm install vue-cookies
main.js 配置
import cookies from 'vue-cookies' // 導入插件
Vue.prototype.$cookies = cookies; // 直接配置插件原型 $cookies
router.js
routes: [ { path: '/test', name: 'test', component: TestPage }, ]
src/views/TestPage.vue
<template>
<div class="test-page">
<Nav />
<h1>測試頁面</h1>
<hr>
<p>
<input type="text" v-model="tokenInput">
<button @click="setToken">設置token</button>
</p>
<p>
<input type="text" v-model="token">
<button @click="getToken">獲取token</button>
</p>
<p>
<button @click="deleteToken">刪除token</button>
</p>
<hr>
</div>
</template>
<script>
import Nav from '@/components/Nav.vue'
export default {
name: "TestPage",
components: {
Nav
},
data () {
return {
tokenInput: '',
token: '',
}
},
},
methods: {
setToken() {
// 1) 什么是token:安全認證的字符串
// 2) 誰產生的:后台產生
// 3) 誰來存儲:后台存儲(session表、文件、內存緩存),前台存儲(cookie)
// 4) 如何使用:服務器先生成反饋給前台(登陸認證過程),前台提交給后台完成認證(需要登錄后的請求)
if (this.tokenInput) {
let token = this.tokenInput;
// token的cookie存儲都需要前台自己完成:增(改)、查、刪 => vue-cookies
// 增(改): key,value,exp
// 300 = '300s' | '1m' | '1h' | '1d'
this.$cookies.set('token', token, '1y');
this.tokenInput = '';
}
},
getToken() {
// 查:key
this.token = this.$cookies.get('token');
},
deleteToken() {
// 刪:key
this.$cookies.remove('token');
},
}
}
</script>
<style scoped>
</style>
cookie一般都是用來存儲token的
vue的ajax操作
axios插件: 在vue框架中安裝 cnpm install axios
main.js配置
import axios from 'axios' // 導入插件
Vue.prototype.$axios = axios; // 直接配置插件原型 $axios
src/views/TestPage.vue
<template>
<div class="test-page">
<Nav />
<h1>測試頁面</h1>
<div class="ajax">
<input type="text" v-model="username">
<button @click="ajaxAction">提交ajax</button>
</div>
</div>
</template>
<script>
import Nav from '@/components/Nav.vue'
export default {
name: "TestPage",
components: {
Nav
},
data () {
return {
tokenInput: '',
token: '',
username: '',
}
},
methods: {
setToken() {
// 1) 什么是token:安全認證的字符串
// 2) 誰產生的:后台產生
// 3) 誰來存儲:后台存儲(session表、文件、內存緩存),前台存儲(cookie)
// 4) 如何使用:服務器先生成反饋給前台(登陸認證過程),前台提交給后台完成認證(需要登錄后的請求)
if (this.tokenInput) {
let token = this.tokenInput;
// token的cookie存儲都需要前台自己完成:增(改)、查、刪 => vue-cookies
// 增(改): key,value,exp
// 300 = '300s' | '1m' | '1h' | '1d'
this.$cookies.set('token', token, '1y');
this.tokenInput = '';
}
},
getToken() {
// 查:key
this.token = this.$cookies.get('token');
},
deleteToken() {
// 刪:key
this.$cookies.remove('token');
},
ajaxAction() {
if (this.username) {
this.$axios({
url: 'http://127.0.0.1:8000/test/ajax/',
method: 'get',
params: {
username: this.username
}
}).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
});
this.$axios({
url: 'http://127.0.0.1:8000/test/ajax/',
method: 'post',
data: {
username: this.username
}
}).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
});
}
}
}
}
</script>
<style scoped>
</style>
新建 dg_proj django框架
跨站問題 后台接收到前台的請求,可以接收前台數據與請求信息,發現請求的信息不是自身服務器發來的請求,拒絕響應數據,這種情況稱之為 - 跨域問題(同源策略 CORS)
導致跨域情況有三種:1) 端口不一致 2) IP不一致 3) 協議不一致
settings.py文件夾
Django如何解決 - django-cors-headers模塊
1) 安裝:pip3 install django-cors-headers
2) 注冊:
INSTALLED_APPS = [ 'corsheaders' ] 3) 設置中間件: MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware' ] 4) 設置跨域: CORS_ORIGIN_ALLOW_ALL = True
dg_proj/urls.py
from django.conf.urls import url from django.contrib import admin from api import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^test/ajax/$', views.test_ajax), ]
vue的element-ui插件
安裝 cnpm i element-ui -S
main.js配置
import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);
router.js
routes: [ { path: '/eleui', name: 'eleui', component: EleUIPage }, ]
src/views/ElePage.vue
<template>
<div class="ele-ui-page">
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1', '3']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-message"></i>導航一</template>
<el-menu-item-group>
<template slot="title">分組一</template>
<el-menu-item index="1-1">選項1</el-menu-item>
<el-menu-item index="1-2">選項2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分組2">
<el-menu-item index="1-3">選項3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">選項4</template>
<el-menu-item index="1-4-1">選項4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-menu"></i>導航二</template>
<el-menu-item-group>
<template slot="title">分組一</template>
<el-menu-item index="2-1">選項1</el-menu-item>
<el-menu-item index="2-2">選項2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分組2">
<el-menu-item index="2-3">選項3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">選項4</template>
<el-menu-item index="2-4-1">選項4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title"><i class="el-icon-setting"></i>導航三</template>
<el-menu-item-group>
<template slot="title">分組一</template>
<el-menu-item index="3-1">選項1</el-menu-item>
<el-menu-item index="3-2">選項2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分組2">
<el-menu-item index="3-3">選項3</el-menu-item>
</el-menu-item-group>
<el-submenu index="3-4">
<template slot="title">選項4</template>
<el-menu-item index="3-4-1">選項4-1</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header>
<el-row>
<Nav/>
</el-row>
</el-header>
<el-main>
<i @click="clickAction" class="elm el-icon-platform-eleme"></i>
</el-main>
<el-footer>Footer</el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
import Nav from '@/components/Nav.vue'
export default {
components: {
Nav
},
methods: {
clickAction() {
// this.$message({
// message: '恭喜你,這是一條成功消息',
// type: 'warning',
// duration: 1000,
// });
this.$confirm('此操作將永久刪除該文件, 是否繼續?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '刪除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消刪除'
});
});
}
}
}
</script>
<style scoped>
.elm {
font-size: 50px;
color: tomato;
}
.el-row {
margin: 0 -20px;
}
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
