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>
来源:oschina
链接:https://my.oschina.net/u/4361197/blog/3361317