Vue项目环境搭建css
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项目建立vue
1) 进入存放项目的目录 d: 切换D盘 cd D:\python_workspace\day66\代码node
2) 建立项目 vue create v-projpython
3) 项目初始化 选择 Manually select features
默认选择的有Babel Linter / Formatter 在添加Router 和 Vuex
选择YES
选择第一个,直接进入
直接第一个进入
选择第一个In dedicated config files,第一个本身处理
选择NOT ios
加载环境ajax
选择改变端口号的地方,而后点击左边加号,选择npm,而后在右边Name中填写v-proj,vue-router
在pycharm中选择settings文件夹,选择Plugins,在右侧搜索框中搜索VUE,下载vuex
pycharm配置并启动vue项目npm
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>