路费学城之 vue

目录

路费学城之 vue

vue笔记

一、vue 框架内容大纲

"""
Vue框架:前台界面,页面逻辑
	1)指令
	2)实例成员
	3)组件
	4)项目开发

DRF框架:数据(接口)
	1)基础的模块:请求、响应、解析、渲染
	2)序列化、三大认证
	3)过滤、搜索、排序、分页
	4)异常、第三方jwt、restful接口规范
	
Luffy项目:前后台分离项目
	1)项目的登录认证、课程商机销售、项目完成
	2)短信、支付宝、服务器、上线、redis、celery、git
"""

vue 框架

"""
1)定义:javascript渐进式框架
	渐进式:可以控制一个页面的一个标签,也可以控制一系列标签,也可以控制整个页面,甚至可以控制整个前台项目

2)优势:
	有指令(分支结构,循环结构...),复用页面结构等
	有实例成员(过滤器,监听),可以对渲染的数据做二次格式化
	有组件(模块的复用或组合),快速搭建页面
	
	虚拟DOM
	数据的双向绑定
	单页面应用
	数据驱动
	
3)为什么学习vue:
	前台框架:Angular(庞大)、React(精通移动端)、Vue(吸取前两者优势,轻量级)
	Vue一手文档是中文
	实现前后台分离开发,节约开发成本
	
4)如何使用Vue
	简单使用
"""

Vue环境简单搭建:通过script标签导入vue文件即可

"""
1)cdn
<script src="https://cn.vuejs.org/js/vue.js"></script>

2)本地导入
<script src="js/vue.js"></script>
"""

挂载点el

"""
/** el: 挂载点
* 1)一个挂载点只能控制一个页面结构(优先匹配到的结构)
* 2)挂载点挂载的页面标签严格建议用id属性进行匹配(一般习惯用app)
* 3)html标签与body标签不能作为挂载点(html和body标签不可以被替换,组件中详细介绍)
* 4)是否接受vue对象,是外界是否要只有vue对象的内容决定的
*/
"""
<div id="app">
    <div class="d1">
        {{ num }}
    </div>
    <div class="d1">
        {{ num }}
    </div>
</div>
<div id="main">
    {{ n }}
</div>

<script>
 var app = new Vue({
     el: '#app',
     data: {
         num: 100
     }
 });
 console.log(app.$data.num, app.num);
 new Vue({
     el: '#main',
     data: {
         n: app.num
     }
 });
</script>

插值表达式

"""
 /** 插值表达式
     * 1)空插值表达式:{{ }}
     * 2)中渲染的变量在data中可以初始化
     * 3)插值表达式可以进行简单运算与简单逻辑
     * 4)插值表达式符合冲突解决,用delimiters自定义(了解)
     */
"""
<div id="app">
    <p>{{ info }}</p>
    <p>{{ msg }}</p>
    <p>{{ }}</p>
    <p>{{num}}</p>
    <p>{{num + 10 * 2}}</p>
    <p>{{ msg.length + num }}</p>
    <p>{{ msg[4] }}</p>
    <p>{{ msg.split('')[4] }}</p>
    <p>[{ num }]</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            info: '信息',
            msg: 'message',
            num: 10,
        },
        // 控制vue插值表达式符合
        delimiters: ['[{', '}]'],
    })
</script>

过滤器

"""
/** 过滤器
     * 1)用实例成员filters来定义过滤器
     * 2)在页面结构中,用 | 来标识使用过滤器
     * 3)过滤方法的返回值就是过滤器过滤后的结果
     * 4)过滤器可以对1~n个变量进行过滤,同时还可以传入辅助的变量,
     *      过滤器方法接受参数是安装传入的位置先后
     */
"""
<body>
    <div id="app">
        <!-- 简单使用:过滤的对象会作为参数传给过滤器 -->
        <p>{{ num | add(20) }}</p>
        <!-- 串联使用:将第一个过滤器结果作为参数给第二个过滤器 -->
        <p>{{ num | add(100) | jump(2) }}</p>
        <!-- 究极使用 -->
        <p>{{ n1, n2 | fn(99, 77) }}</p>
        <!-- 你品,你细品 -->
        <p>{{ n1, n2 | fn(99, 77), n1, n2 | fn(100) }}</p>
    </div>
</body>
<script src="js/vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            num: 10,
            n1: 66,
            n2: 88
        },
        filters: {
            add: function (a, b) {
                console.log(a, b);
                return a + b;
            },
            jump: function (a, b) {
                return a * b
            },
            fn: function (a, b, c, d) {
                console.log(a, b, c, d);
                return a + b + c + d;
            }
        }
    })
</script>

以上所学实例成员(实例属性,自我完成小结)

{
el:
data:
delimiters:
filters:
}

文本指令

"""
/**
* 1) v-* 是vue指令,会被vue解析,v-text="num"中的num是变量(指令是有限的,不可以自定义)
* 2)v-text是原样输出渲染内容,渲染控制的标签自身内容会被替换掉(<p v-text="num">123</p>会被num替换)
* 3)v-html可以解析渲染html语法的内容
*/


4)补充
<!-- js基本数据类型:字符串、数字、布尔、undefined -->
<p v-text="'abc' + num + 10"></p>
<p>{{ 'abc' + num + 10 }}</p>
"""

<body>
    <div id="app">
        <p>{{ num | add(300) }}</p>

        <p v-text="num"></p>
        <p v-text="num">123</p>

        <p v-text="info"></p>
        <p v-html="info"></p>
    </div>
</body>
<script src="js/vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            num: 100,
            info: '<i style="color: red">info内容</i>'
        },
        filters: {
            add: function (a, b) {
                return a + b;
            }
        }
    })
</script>

事件指令

"""
/**
 * 一、数据驱动
 *  1)操作是一个功能,使用需要一个方法来控制 2)方法名是变量,所以控制变量就可以控制该方法
 *
 *
 * 二、事件指令
 *  1)在实例成员methods中声明事件方法
 *  2)标签通过事件指令绑定声明的方法: v-on:事件名="事件方法名"
 *      eg: <button v-on:click="btnClick">按钮</button>
 *  3)标签通过事件指令绑定声明的方法,且自定义传参: v-on:事件名="事件方法名()"
 *      eg: <button v-on:click="btnClick()">按钮</button>  不传任何参数
 *      eg: <button v-on:click="btnClick($event)">按钮</button>  传入事件对象,同不写()
 *      eg: <button v-on:click="btnClick(10)">按钮</button>  只传入自定义参数,当然也可以传入事件对象
 */
"""

<body>
    <div id="app">
        <button v-on:click="btnClick">{{ btn1 }}</button>

        <button v-on:click="btnClick">{{ btn2 }}</button>
        <hr>

        <!-- 直接绑定事件名:系统会在触发事件时(点击时)调用事件方法(fn1),传给事件方法一个参数(事件对象) -->
        <button v-on:click="fn1">按钮3</button>

        <!-- 绑定的事件名后跟着(),不是主动调用事件方法,而是表示在触发事件调用时,传入的参数全由用户自己决定 -->
        <button v-on:click="fn2($event, 10, 20)">按钮4</button>

        <hr>
        <button v-on:click="fn(btn1)">{{ btn1 }}</button>

        <button v-on:click="fn(btn2)">{{ btn2 }}</button>
    </div>
</body>
<script src="js/vue.js"></script>
<script>
    // 对比DOM驱动:1)js选择器获取目标标签 2)为目标标签绑定事件 3)在事件中完成相应逻辑
    // var btn = document.getElementsByTagName('button')[0];
    // btn.onclick = function () {
    //     console.log(111111111111);
    // };
    new Vue({
        el: '#app',
        data: {
            btn1: '按钮1',
            btn2: '按钮2',
        },
        methods: {
            btnClick () {
                console.log(666)
            },
            fn1 (ev) {
               console.log(ev.clientX, ev.clientY);
            },
            fn2(ev, n1, n2) {
                console.log(ev, n1, n2);
                console.log(ev.clientX, ev.clientY);
            },
            fn (msg) {
                console.log(msg);
            }
        }
    })
</script>

总结

"""
1、路飞项目所有知识点概况(了解)

2、Vue框架的介绍
	what:渐进式 javascript 框架
	where:前后台分离项目的前台开发(pc、移动端)
	why:有其他框架的优点、轻量、中文API(低学习成本)
	how:在页面中导入vue环境,用变量控制挂载点,各种指令与实例成员配合
	
3、基础实例成员
	1)挂载点el:id选择器唯一绑定
	2)插值表达式:{{ 内部可以写基本类型与变量,还可以完成简单运算与逻辑 }}
	3、标识符:delimiters修改插值表达式符号(了解)
	4、过滤器:filters自定义过滤器,可以串联使用,可以一次性过滤多个变量
	5、方法:methods自定义vue控制的方法,给事件指令绑定的

4、基础指令
	1)v-text:基本同{{}}
	2)v-html:可以解析html语法的内容
	3)v-on:
		v-on:事件="方法"   =>  系统传参,只默认传$event
		v-on:事件="方法($event, ...)"  => 完全自定义传参,可以手动传入$event
		
5、js对象补充:
	1)对象就可以作为字典,属性名都是字符串,可以省略引号
	2)属性值时变量同属性名,可以简写
	3){}中的属性值为函数,称之为方法,也可以简写
	
	4)定义的函数,函数名大写,且函数内部有this,代表类
	5)可以通过 类.prototype.类属性 实现所以对象共享
	
"""


二、内容大纲

"""
1)指令
	属性指令:v-bind
	表达指令:v-model
	条件指令:v-show v-if
	循环指令:v-for
	斗篷指令:v-cloak
	
2)实例成员
	计算:computed
	监听:watch
	
3)vue项目环境
"""

斗篷指令(了解)

"""
v-cloak:避免屏幕闪烁
1)属性选择器,会将v-cloak属性所在的标签隐藏
2)当vue环境加载后,会将v-cloak属性解析移除,所以内容{{ num }}就会显示出来
3)而现在vue已经准备完毕,所以用户会直接看到数值10,而不会看到 页面从{{ num }}闪烁成数值10    """

<style>
    [v-cloak] {
        display: none;
    }
</style>
<div id="app" v-cloak>
    <p>{{ num }}</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            num: 10
        },
    })
</script>

属性指令

"""
/** 属性指令
* 1)语法:v-bind:属性名="变量"
* 2)针对不同属性,使用方式稍微有一丢丢区别
*      i)自定义属性以及title这些,直接赋值的,使用方式如下(t是变量,'o'是常量)
*          <p v-bind:title="t" v-bind:owen="'o'">段落</p>
*      ii)class属性(重点):
*          绑定的变量:值可以为一个类名 "p1",也可以为多个类名 "p1 p2"
*          绑定的数组:数组的每一个成员都是一个变量
*          绑定的字典:key就是类名,value是绝对该类名是否起作用
*      iii)style属性(了解):
*          绑定的变量:值是一个字典
*/
"""

<p v-bind:title="t" v-bind:owen="'o'">段落</p>
<script>
    new Vue({
        el: '#app',
        data: {
            t: '悬浮提示',
        },
    })
</script>

<!-- 
a是变量,值就是类名
b就是类名,不是变量
c是变量,值为布尔,决定b类是否起作用
d是变量,值可以为一个类名 'p1' 也可以为多个类名 "p1 p2 ..."
calss="p1 b p2 p3"
-->
<p v-bind:class="[a, {b: c}]" v-bind:class="d"></p> 
<script>
    let app = new Vue({
        el: '#app',
        data: {
            a: 'p1',
            c: true,
            d: 'p2 p3',
        },
    })
</script>

<p v-bind:style="myStyle"></p>
<script>
    let app = new Vue({
        el: '#app',
        data: {
            myStyle: {
                 '50px',
                height: '50px',
                backgroundColor: 'pink',
                borderRadius: '50%'
            }
        },
    })
</script>

案例
<button v-bind:class="{live: isLive == 1}" v-on:click="changeLive(1)">1</button>
<button v-bind:class="{live: isLive == 2}" v-on:click="changeLive(2)">2</button>
<button v-bind:class="{live: isLive == 3}" v-on:click="changeLive(3)">3</button>
<script>
    let app = new Vue({
        el: '#app',
        data: {
            isLive: 1,
        },
        methods: {
            changeLive (index) {
                // this就代表当前vue对象,和app变量等价
                // app.isLive = index;
                this.isLive = index;
            }
        }
    })
</script>  

重点:事件指令与属性指令都可以简写
<!--
1)v-bind: 可以简写为 :
2)v-on: 可以简写为 @
-->

<button v-bind:class="{live: isLive == 1}" v-on:click="changeLive(1)">1</button>
<button :class="{live: isLive == 2}" @click="changeLive(2)">2</button>
<button :class="{live: isLive == 3}" @click="changeLive(3)">3</button>

事件补充

<style>
    body {
        /* 不允许文本选中 */
        user-select: none;
    }
    .d1:hover {
        color: orange;
        /* 鼠标样式 */
        cursor: pointer;
    }
    /* 只有按下采用样式,抬起就没了 */
    .d1:active {
        color: red;
    }
    /* div标签压根不支持 :visited 伪类 */
    .d1:visited {
        color: pink;
    }

    .d2.c1 {
        color: orange;
    }
    .d2.c2 {
        color: red;
    }
    .d2.c3 {
        color: pink;
    }
</style>
<div id="app">
    <div class="d1">伪类操作</div>
    <br><br><br>
    <!--
    click: 单击
    dblclick:双击
    mouseover:悬浮
    mouseout:离开
    mousedown:按下
    mouseup:抬起
    -->
    <div :class="['d2', c]" @click="hFn('c1')" @mouseover="hFn('c2')" @mousedown="hFn('c3')">事件处理</div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            c: '',
        },
        methods: {
            hFn (c) {
                this.c = c
            }
        }
    })
</script>


表单指令

"""
1)语法:v-model="变量"
2)v-model绑定的变量控制的其实就是value属性值
3)v-model要比v-bind:value要对一个监听机制
4)数据的双向绑定:
	v-model可以将绑定的变量值映射给表单元素的value
	v-model还可以将表单元素的新value映射给报道的变量
"""

<!-- 两个输入框内容会同时变化 -->
<input name="n1" type="text" v-model="v1">
<input name="n2" type="text" v-model="v1">
<script>
    new Vue({
        el: '#app',
        data: {
            v1: ''
        }
    })
</script>

条件指令

"""
/**
* 1)语法:v-show="变量"  |  v-if="变量"
* 2)两者的区别:
*      v-show在隐藏标签时,采用display:none渲染标签,标签通过css隐藏
*      v-if在隐藏标签时,不会渲染在页面上
*
* 3)v-if有家族:v-if | v-else-if | v-else
*      v-if是必须的,必须设置条件
*      v-else-if可以为0~n个,必须设置条件
*      v-else可以为0~1个
*      上方分支成立会屏蔽下方所有分支,从上至下依次类推
*/
"""

<div id="app">
    <div>
        <p v-show="isShow">show控制显隐</p>
        <p v-if="isShow">if控制显隐</p>
    </div>

    <div>
        <p v-if="0">你是第1个p</p>
        <p v-else-if="0">你是第2个p</p>
        <p v-else>你是第3个p</p>
    </div>

</div>
<script>
    new Vue({
        el: '#app',
        data: {
            isShow: false,
        }
    })
</script>

案例
<style>
    body {
        margin: 0
    }
    button {
         60px;
        line-height: 40px;
        float: right;
    }
    .bGroup:after {
        display: block;
        content: '';
        clear: both;
    }
    .box {
        /* vw: view width  vh: view height*/
         100vw;
        height: 200px;
    }
    .red {
        background-color: red;
    }
    .green {
        background-color: green;
    }
    .blue {
        background-color: blue;
    }

    button.active {
        background-color: cyan;
    }
</style>

<div id="app">
    <div class="bGroup">
        <button :class="{active: isShow === 'red'}" @click="isShow = 'red'">红</button>
        <button :class="{active: isShow === 'green'}" @click="isShow = 'green'">绿</button>
        <button :class="{active: isShow === 'blue'}" @click="isShow = 'blue'">蓝</button>
    </div>
    <div>
        <div v-if="isShow === 'red'" class="box red"></div>
        <div v-else-if="isShow === 'green'" class="box green"></div>
        <div v-else class="box blue"></div>
    </div>
</div>

<script>
    new Vue({
        el: '#app',
        data: {
            isShow: 'red'
        }
    })
</script>

循环指令

"""
/**
* 1)语法:v-for="ele in obj"  obj是被遍历的对象,ele是遍历得到的每一次结果
* 2)遍历可迭代对象的首要结果,都是可迭代对象容器中的值,其次还可以遍历得到索引及键等数据
*      字符串:v-for="v in str"  |  v-for="(v, i) in str"
*      数组:v-for="v in arr"  |  v-for="(v, i) in arr"
*      对象:v-for="v in obj"  |  v-for="(v, k) in obj"  |  v-for="(v, k, i) in obj"
* 注:v-for遍历要依赖于一个所属标签,该标签及内部所有内容会被遍历复用
*/
"""

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>循环指令</title>
</head>
<body>
    <div id="app">
        <!-- 遍历数字
		5
		【1】【2】【3】【4】【5】
		-->
        <p>{{ d1 }}</p>
        <i v-for="e in d1">【{{ e }}】</i>
        <hr>

        <!-- 遍历字符串
		abc
		【a】【b】【c】
		【0a】【1b】【2c】
		-->
        <p>{{ d2 }}</p>
        <i v-for="e in d2">【{{ e }}】</i>
        <i v-for="(e, i) in d2">【{{ i }}{{ e }}】</i>
        <hr>

        <!-- 遍历数组
		[ 1, 3, 5 ]
		【1】【3】【5】
		【01】【13】【25】
		-->
        <p>{{ d3 }}</p>
        <i v-for="e in d3">【{{ e }}】</i>
        <i v-for="(e, i) in d3">【{{ i }}{{ e }}】</i>
        <hr>

        <!-- 遍历对象
		{ "name": "Bob", "age": 17.5, "gender": "男" }
		【Bob】【17.5】【男】
		【name-Bob】【age-17.5】【gender-男】
		【name-Bob-0】【age-17.5-1】【gender-男-2】
		-->
        <p>{{ d4 }}</p>
        <i v-for="e in d4">【{{ e }}】</i>
        <i v-for="(e, k) in d4">【{{ k }}-{{ e }}】</i>
        <i v-for="(e, k, i) in d4">【{{ k }}-{{ e }}-{{ i }}】</i>
        <hr>

    </div>
</body>
<script>
    new Vue({
        el: '#app',
        data: {
            d1: 5,
            d2: 'abc',
            d3: [1, 3, 5],
            d4: {
                name: "Bob",
                age: 17.5,
                gender: "男"
            }
        }
    })
</script>


商品循环案例
<style>
    .box {
         280px;
        border: 1px solid #eee;
        border-radius: 5px;
        overflow: hidden; /* 隐藏超出父级显示范围外的内容 */
        text-align: center; /* 文本相关的属性大多默认值是inherit */
        float: left;
        margin: 10px;
    }
    .box img {
         100%;
    }
</style>

<div id="app">
    <div class="box" v-for="obj in goods">
        <img :src="obj.img" alt="">
        <p>{{ obj.title }}</p>
    </div>
</div>

<script>
    let goods = [
        {
            "img": "https://***1.jpg",
            "title": "纯种拆家专家1"
        },
        {
            "img": "https://***2.jpg",
            "title": "纯种拆家专家2"
        },
    ];
    
    new Vue({
        el: '#app',
        data: {
            goods,
        }
    })
</script>

面试题:todolist

js的Array操作
"""
尾增:arr.push(ele)  
首增:arr.unshift(ele)
尾删:arr.pop()
首删:arr.shift()
增删改插:arr.splice(begin_index, count, args)
"""

前台数据库
"""
// 存
// 持久化化存储,永远保存
localStorage.name = "Bob";
// 持久化化存储,生命周期同所属标签(页面),页面关闭,重新打开就会丢失
sessionStorage.name = "Tom";

// 取
console.log(localStorage.name);
console.log(sessionStorage.name);

// 清空
localStorage.clear();
sessionStorage.clear();

// 短板:只能存储字符串,所有对象和数组需要转换为json类型字符串,再进行存储
let a = [1, 2, 3];
localStorage.arr = JSON.stringify(a);
let b = JSON.parse(localStorage.arr);
console.log(b);
"""

案例:留言板
<style>
    li:hover {
        color: red;
        cursor: pointer;
    }
</style>

<div id="app">
    <form>
        <input type="text" v-model="info">
        <button type="button" @click="sendInfo">留言</button>
    </form>
    <ul>
        <li v-for="(info, index) in info_arr" @click="deleteInfo(index)">{{ info }}</li>
    </ul>
</div>

<script>
    new Vue({
        el: '#app',
        data: {
            info: '',
            // 三目运算符: 条件 ? 结果1 : 结果2
            info_arr: localStorage.info_arr ? JSON.parse(localStorage.info_arr) : [],
        },
        methods: {
            sendInfo () {
                // 完成留言:将info添加到info_arr
                // 增 push unshift | 删 pop shift
                if (this.info) {
                    // 留言
                    this.info_arr.push(this.info);
                    // 清空输入框
                    this.info = '';
                    // 前台数据持久化(缓存)
                    localStorage.info_arr = JSON.stringify(this.info_arr);
                }
            },
            deleteInfo(index) {
                // 删
                this.info_arr.splice(index, 1);
                // 同步给数据库
                localStorage.info_arr = JSON.stringify(this.info_arr);
            }
        }
    })
</script>

小结

"""
1、v-cloak斗篷指令
2、属性指令
	v-bind:title="变量"
	:class="变量" | :class="[变量1, ..., 变量n]"  |  :class="{类名: 布尔变量}"
	:style="字典变量"
3、事件:@click @dblclick @mouseover|out|down|up
	鼠标单击、双击、悬浮、移开、按下、抬起
4、表单指令:
	v-model绑定变量控制value属性,可以实现双向绑定
5、条件指令:
	v-show | v-if
	v-if | v-else-if | v-else
6、循环指令:
	字符串:v-for="v in str"  |  v-for="(v, i) in str"
    数组:v-for="v in arr"  |  v-for="(v, i) in arr"
    对象:v-for="v in obj"  |  v-for="(v, k) in obj"  |  v-for="(v, k, i) in obj"
    
7、Array操作
	arr.push(ele)  arr.unshift(ele)
	arr.pop()  arr.shift()
	arr.splice(begin_index, count, args)

8、前台数据库
	localStorage | sessionStorage
	1)操作就类似于obj,直接 .key 语法访问 value
	2)localStorage永久存储
	3)sessionStorage生命周期同所属页面标签
	
"""


三、内容大纲

"""
1)computed
2)watch
3)项目环境
4)项目开发:请求生命周期、组件、组件的生命周期、路由...
"""

字符串补充

"""
1)双引号:
	"前缀" + 变量 + "后缀"
	
2)单引号:
	'前缀' + 变量 + '后缀'

3)反引号:
	`前缀${变量}后缀`
	注:在反引号中可以用 ${} 来包裹变量,实现字符串拼接
"""

实例成员:计算属性

"""
/** 计算属性:
* 1)其实就是vue中的方法属性,方法名可以作为属性来使用,属性值为方法的返回值
* 2)在computed中声明的方法属性,不能在data中重复声明,比data中声明的属性要多出写逻辑的地方
* 3)方法属性,自带监听机制,在方法属性中出现的变量,都会被监听,一旦有任何被监听的变量值发生更新,
*      方法属性都会被调用更新方法属性的值
* 4)方法属性一定要在页面中渲染一次,方法属性采用意义,多次渲染,方法属性只会被调用一次
* 案例:计算器
* 方法属性的应用场景:一个变量依赖于多个变量,且需要进行一定的逻辑运算
*/
"""

<div id="app">
    <!-- type="number"表示只能写数字 -->
    <input type="number" v-model="num1" max="100" min="0">
    +
    <input type="number" v-model="num2" max="100" min="0">
    =
    <button>{{ sum }}</button>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            // sum: '',  // 重复声明
            num1: '',
            num2: '',
        },
        computed: {
            sum () {
                // num1和num2都在该方法属性中,所以有一个更新值,该方法都会被调用
                if (this.num1 && this.num2) {
                    return +this.num1 + +this.num2;  // +this.num1是将字符串快速转换澄数字
                }
                return '结果';
            }
        }
    })
</script>


监听属性

"""
/**
* 1)watch中不定义属性,只是监听属性,所以方法的返回值没有任何意义,只是监听变量值是否发生更新
* 2)watch中的方法名,就是被监听的属性(方法名同被监听属性名)
* 3)被监听的变量值一旦发生更新,监听方法就会被调用
* 应用场景:
*      i)k线图:股票数据变化,页面的k线图重新渲染(需要逻辑将数据转换为图形)
*      ii)拆分姓名:录入姓名,拆分为姓和名(需要逻辑将一个数据拆分为多个数据)
*/
"""

<div id="app">
    姓名:<input type="text" v-model="full_name">
    <hr>
    姓:<button>{{ first_name }}</button>
    名:<button>{{ last_name }}</button>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            full_name: '',
            first_name: '未知',
            last_name: '未知'
        },
        watch: {
            full_name () {
                if (this.full_name) {
                    // 只是实现简易的拆分逻辑
                    this.first_name = this.full_name.split('')[0];
                    this.last_name = this.full_name.split('')[1];
                } else {
                    this.first_name = '未知';
                    this.last_name = '未知';
                }
            }
        }
    })
</script>


vue的项目开发

环境:安装vue的脚手架cli环境

"""
node 	~~ 		python
npm 	~~		pip

python:c语言编写,解释执行python语言的
node:c++语言编写,解释执行JavaScript语言的
npm类似于pip,是为node环境安装额外功能的

"""

"""
1)官网下载并安装node,附带npm
	https://nodejs.org/zh-cn/
2)换源:将npm欢迎为cnpm
	npm install -g cnpm --registry=https://registry.npm.taobao.org
3)安装vue脚手架	
	cnpm install -g @vue/cli
注:如果第二三步异常,基本都是由网络导致的,可以重新执行第二三步,如果一直有问题,可以清理缓存后重复
	npm cache clean --force
"""

项目创建:在终端中创建

各插件作用
Babel:将ES6语法解析为ES5语法给浏览器
Router:前台路由
Vuex:前台仓库,相当于单例,完成个组件间传参的

项目移植:将公司代码在自己电脑上跑起来

"""
1)拷贝出环境 node_modules 意外的文件与文件夹到目标文件夹
2)终端进入目标文件夹所在位置
3)执行:npm install 重新构建依赖(npm可以用cnpm替换)
"""

pycharm配置并启动vue项目

1) 用pycharm打开vue项目
2) 添加配置npm启动

vue项目目录结构分析

├── v-proj
|	├── node_modules  	// 当前项目所有依赖,一般不可以移植给其他电脑环境
|	├── public			
|	|	├── favicon.ico	// 标签图标
|	|	└── index.html	// 当前项目唯一的页面
|	├── src
|	|	├── assets		// 静态资源img、css、js
|	|	├── components	// 小组件
|	|	├── views		// 页面组件
|	|	├── App.vue		// 根组件
|	|	├── main.js		// 全局脚本文件(项目的入口)
|	|	├── router
|	|	|	└── index.js// 路由脚本文件(配置路由 url链接 与 页面组件的映射关系)
|	|	└── store	
|	|	|	└── index.js// 仓库脚本文件(vuex插件的配置文件,数据仓库)
|	├── README.md
└	└── package.json等配置文件

vue组件(.vue文件)

"""
注:pycharm安装vue.js插件,就可以高亮显示vue文件了

1)一个.vue文件就是一个组件
2)组件都是由三部分组成:html结构、js逻辑、css样式
    html结构都是在template标签中,页面结构有且只有一个根标签(template一级结构下)
    js逻辑都是在script标签中,必须设置导出,export default {...}
    css样式都是在style标签中,必须设置scoped属性,是样式组件化
"""

<template>
    <div class="first-cp">
         <h1>第一个组件</h1>
    </div>
</template>

<script>
    // .vue文件类似于模块,可以直接相互导入,所以在组件内部要设置导出
    export default {

    }
</script>

<style scoped>
    /* scoped可以使样式组件化,只在自己内部起作用 */

</style>

全局脚本文件main.js(项目入口)

"""
1)main.js是项目的入口文件
2)new Vue()就是创建根组件,用render读取一个.vue文件,$mount渲染替换index.html中的占位
3)项目所依赖的环境,比如:vue环境、路由环境、仓库环境、第三方环境、自定义环境都是在main.js中完成
"""

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')


改写
import Vue from 'vue'  // 加载vue环境
import App from './App.vue'  // 加载根组件
import router from './router'  // 加载路由环境
import store from './store'  // 加载数据仓库环境

Vue.config.productionTip = false;  // tip小提示

import FirstCP from './views/FirstCP'
new Vue({
    el: '#app',
    router: router,
    store: store,
    render: function (readVueFn) {
        return readVueFn(FirstCP);  // 读取FirstCP.vue替换index.html中的占位
    }
});


路由与根组件(重点)

路由核心配置:router/index.js

// import 别名 from '文件'
import Home from '../views/Home'
import About from '../views/About'
import First from '../views/FirstCP'

// 路由配置
// 1)当用户在浏览器中访问的路由是 / ,router插件就会加载 Home.vue文件,同理 /about 就是 About.vue文件
// 2)将加载的 Home.vue文件 或者 About.vue文件,去替换App.vue文件中的 <router-view />占位符
// 3)用redirect配置来实现路由的重定向
const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home
    },
    {
        path: '/home',
        redirect: '/',  // 路由的重定向
    },
    {
        path: '/about',
        name: 'About',
        component: About
    },
    {
        path: '/first',
        name: 'First',
        component: First
    }
];

根组件占位渲染页面组件
<!--
1)App.vue是项目的根组件,是唯一由main.js加载渲染的组件,就是替换index.html页面中的<div id="app"></div>的占位标签
2)实际开发中App.vue文件中,只需要书写下方五行代码即可(可以额外增加其他代码)
3)router-view是一个占位标签,由router插件控制,可以在router的配置文件中进行配置
4)router-view就是根据router在index.js中配置的路由关系被 指定路径 匹配 指定页面组件 渲染
    router-view或被不同的页面组件替换,就形成了页面跳转
-->
<template>
    <div id="app">
        <!-- 前台路由占位标签,末尾的/代表单标签的结束 -->
        <router-view />
    </div>
</template>

页面组件渲染小组件(重点)

页面组件作为父组件:views/*.vue

<template>
    <div class="home">
        <!-- vue项目下的html是支持大小写,且区分大小写 -->
        <Nav />
    </div>
</template>

<script>
    // 父组件加载子组件:父组件通常是页面组件,是被一个个小组件这些子组件组装成的页面
    // 1)导入语法要在 export default{} 之上完成
    // 2)@符合标识 项目src文件夹 绝对路径
    // 3)要在export default{} 的括号中用 components 注册
    // 4)在该组件的模板中就可以渲染子组件了(html代码中是区别大小写的)
    // 5)步骤:i)导入子组件 ii)注册子组件 iii)使用子组件
    import Nav from '@/components/Nav'

    export default {
        components: {
            Nav,
        }
    }
</script>

<style scoped>
    .home {
         100vw;
        height: 100vh;
        background-color: orange;
    }
</style>

导航栏小组件(封装路由跳转):components/*.vue

<template>
    <div class="nav">
        <img src="" />
        <ul>
            <li>
                <!-- router控制的路由,不是用a标签完成跳转:
                    1)a标签会刷新页面,错误的
                    2)router-link标签也能完成跳转,且不会刷新页面,就是router提供的a标签(最终会被解析为a标签,还是用a来控制样式)
					3)router-link标签的to属性控制跳转路径,由两种方式
                        i) to="路径字符串"
                        ii :to="{name: '路由名'}"
                -->
                <a href="/">主页</a>
            </li>
            <li>
                <router-link to="/about">关于页</router-link>
            </li>
            <li>
                <!-- to="字符串",v-bind:to="变量",可以简写为 :to="变量" -->
                <router-link :to="{name: 'First'}">第一页</router-link>
            </li>
        </ul>
    </div>
</template>

<style scoped>
    .nav {
         100%;
        height: 80px;
        background: rgba(0, 0, 0, 0.3);
    }
    img {
         200px;
        height: 80px;
        background: tan;
        float: left;
    }
    ul {
        float: left;
        list-style: none;
        margin: 0;
        padding: 0;
        height: 80px;
        background: pink;
    }
    ul li {
        float: left;
        height: 80px;
        padding: 30px 20px 0;
    }
    a {
        color: black;
    }
</style>

总结

"""
1)计算属性:computed
	定义方法属性,返回值为属性值,方法内的变量都会被监听
	案例:计算器
	
2)监听属性:watch
	监听与方法名同名的属性,被监听的数据更新调用方法
	案例:拆分姓名,k线图
	
3)vue项目环境:
	node -> npm -> cnmp -> vue/cli
	
4)新建vue项目:在当前终端所在目录下
	vue create 项目
	
5)pycharm配置项目启动
	在pycharm中设置npm启动
	
6)项目目录结构

7)组件:vue文件

8)入口文件:main.js

9)根组件、页面组件、小组件 与 路由关系(router-view、router-link)
"""

四、内容大纲

"""
1)路由:逻辑跳转、路由传参
2)项目组件的数据局部化处理:data: {} => data: function(){ return {} } => data(){ return{} }
3)组件的声明周期
4)组件间通信
5)各种第三方插件:vuex、axios、element-ui、(jq+bs) 
"""

项目初始化

"""
1)根组件:App.vue
<template>
    <div id="app">
        <router-view />
    </div>
</template>

2)路由配置:router/index.js
const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home
    }
];

3)组件:views和components文件夹
	i)删除除Home.vue以为的所有组件
	ii)初始化Home.vue
	<template>
        <div class="home">
        </div>
    </template>

4)分类管理资源:assets文件夹
	建立img、css、js子文件夹,删除原来的资源
	
5)如果要修改页面标签图标
	替换public文件夹下的favicon.ico图片文件
"""

组件数据局部化处理

"""
1)不管页面组件还是小组件,都可能会被多次复用
2)复用组件的原因,其实就是复用组件的 页面结构、页面样式、页面逻辑
3)但是页面上的数据需要区分(被复用的两个组件数据多少是有区别的),所以组件的数据要做局部化处理
4)借助函数可以产生局部作用域的特点,为每一次复用组件产生一个独立的作用域
语法:
data () {
	return {
		// 数据们
	}
}
"""

子组件
<template>
    <div class="beat" @click="count += 1">
        {{ count }}下
    </div>
</template>

<script>
    export default {
        name: "Beat",
        // 不管是页面组件还是小组件,都可能被复用,页面结构与样式都可以采用一套,但是数据一定要相互独立
        data () {
            return {
                count: 0
            }
        }
    }
</script>

<style scoped>
    .beat {
         100px;
        height: 100px;
        background-color: orange;
        text-align: center;
        line-height: 100px;
        border-radius: 50%;
    }
</style>


父组件
<template>
    <div class="home">
        <Beat/>
        <Beat/>
    </div>
</template>
<script>
    import Beat from '@/components/Beat'
    export default {
        components: {
            Beat,
        }
    }
</script>

路由逻辑跳转

"""
1)很多时候,我们需要通过普通按钮的逻辑,或是直接在某些逻辑中完成页面的跳转
2)可以通过在逻辑中用 this.$router.push() 来完成前往目标页,两种语法如下
	this.$router.push('路径')
	this.$router.push({name: '路由名'})
3)在做移动端项目时,没有像浏览器那样的前进后台键,页可以用 this.$router.go() 来完成前进后退,语法如下
	前进后退:this.$router.go(正负整数),正式代表前进,负数代表后台,数值就是步长
"""

案例
<template>
    <div class="home">
        <Nav/>
        <h1>主页</h1>
        <button @click="goPage('/first')">前往第一页</button>
        |
        <button @click="goPage('/second')">前往第二页</button>
        |
        <button @click="goBack(-1)">后退一页</button>
        |
        <button @click="goBack(-2)">后退二页</button>
        |
        <button @click="goBack(1)">前进一页</button>
    </div>
</template>

<script>
    import Nav from '@/components/Nav'

    export default {
        methods: {
            goPage(path) {
                // 可以通过 this.$router 完成逻辑跳转
                this.$router.push();
            },
            goBack(num) {
                // 一般在移动端项目上运用
                this.$router.go(num);  
            }
        },
        components: {
            Nav,
        }
    }
</script>

组件传参

父传子

"""
一、组件传参 - 父传子
1)在子组件内部通过props设置组件的自定义属性
    props: ['abc', 'goods']
2)在父组件渲染子组件是对自定义属性赋值即可
    <GoodsBox v-for="goods in goods_list" :abc="goods" :goods="goods"/>
"""

子组件
<template>
    <div class="goods-box">
        <img :src="goods.img" alt="">
        <p>{{ goods.title }}</p>
    </div>
</template>

<script>
    export default {
        name: "GoodsBox",
        // 在组件内部通过props定义组件的自定义属性
        props: ['abc', 'goods'],
    }
</script>

<style scoped>
    .goods-box {
         260px;
        height: 300px;
        border: 1px solid black;
        border-radius: 5px;
        margin: 20px;
        float: left;
        overflow: hidden;
        text-align: center;
    }
    img {
         260px;
        height: 260px;
    }
</style>

父组件
<template>
    <div class="goods">
        <div class="main">
            <!-- 在使用子组件是对自定义属性赋值即可 -->
            <GoodsBox v-for="goods in goods_list" :abc="goods" :goods="goods" />
        </div>
    </div>
</template>
<script>
    import GoodsBox from "../components/GoodsBox";

    let goods_list = [
        {
            img: require('@/assets/img/001.jpg'),
            title: '小猫',
        },
        {
            img: require('@/assets/img/002.jpg'),
            title: '小猫儿',
        },
        {
            img: require('@/assets/img/003.jpg'),
            title: '小狗',
        },
        {
            img: require('@/assets/img/004.jpg'),
            title: '小狗儿',
        },
    ];

    export default {
        name: "Goods",
        data () {
            return {
                goods_list,
            }
        },
        components: {
            GoodsBox,
        },
    }
</script>


子传父

"""
二、组件传参 - 子传父
前提:子组件是被父组件渲染的,所以子组件渲染要晚于父组件
1)子组件一定要满足一个条件,才能对父组件进行传参(某个时间节点 === 某个被激活的方法)
    eg:i)子组件刚刚加载成功,给父组件传参 ii)子组件某一个按钮被点击的时刻,给父组件传参 iii)子组件要被销毁了,给父组件传参

2)在子组件满足条件激活子组件的方法中,对父组件发生一个通知,并将数据携带处理(自定义组件事件)
    <div class="goods-box" @click="boxClick"></div>
    methods: {
        boxClick () { this.$emit('receiveData', this.goods.title, '第二个数据', '第三个数据') }
    }

3)在父组件渲染子组件时,为自定义事件绑定方法
    <GoodsBox @receiveData="recFn"/>

4)在父组件实现绑定方法时,就可以拿到子组件传参的内容(接收到了通知并在父组件中相应)
    recFn(title, data2, data3) {
        console.log('接收到了' + title);
    }

组件标签不能绑定系统定义的事件,没有意义,子组件的事件都是在自己内部完成
"""

子组件
<template>
    <div class="goods-box" @click="boxClick">
        <img :src="goods.img" alt="">
        <p>{{ goods.title }}</p>
    </div>
</template>

<script>
    export default {
        props: ['abc', 'goods'],
        methods: {
            boxClick () {
                // 通知父级 - 自定义组件的事件
                this.$emit('receiveData', this.goods.title)
            }
        }
    }
</script>

父组件
<template>
    <div class="goods">
        <div class="main">
            <!-- 实现自定义事件,接收子组件通知的参数 -->
            <GoodsBox v-for="goods in goods_list" @receiveData="recFn"/>
        </div>
    </div>
</template>
<script>
    import GoodsBox from "../components/GoodsBox";
    export default {
        name: "Goods",
        data () {
            return {
                goodsTitle: '哪个',
            }
        },
        methods: {
            recFn(title) {
                console.log('接收到了' + title);
                this.goodsTitle = title;
            }
        },
        components: {
            GoodsBox,
        },
    }
</script>


组件的生命周期钩子

"""
一、组件的生命周期:一个组件从创建到销毁的整个过程
二、生命周期钩子:在一个组件生命周期中,会有很多特殊的时间节点,且往往会在特定的时间节点完成一定的逻辑,特殊的事件节点可以绑定钩子
注:钩子 - 提前为某个事件绑定方法,当满足这个事件激活条件时,方法就会被调用 | 满足特点条件被回调的绑定方法就称之为钩子
"""

<template>
    <div class="goods">
        <Nav />
    </div>
</template>
<script>
    import Nav from "../components/Nav";
    export default {
        name: "Goods",
        components: {
            Nav,
        },
        beforeCreate() {
            console.log('该组件要被加载了')
        },
        created() {
            console.log('该组件要被加载成功了')
        },
        updated() {
            console.log('数据更新了')
        },
        destroyed() {
            console.log('该组件销毁了')
        }
    }
</script>


路由传参

"""
路由传参:
一、通过url正则传递数据
i)设置
    路由: path: '/goods/detail/:pk'   |   '/goods/:pk/detail/:xyz'
    请求: '/goods/detail/任意字符'    |    '/goods/任意字符/detail/任意字符'
ii)如何传
    <router-link :to="`/goods/detail/${pk}`"></router-link>
    this.$router.push(`/goods/detail/${pk}`)

iii)如何取
    this.$route对象是管理路由参数的,传递的参数会在this.$route.params字典中
    this.$route.params.pk

二、通过url参数传递数据
i)设置
    路由: path: '/goods/detail'
    请求: '/goods/detail?pk=数据'
ii)如何传
    <router-link :to="`/goods/detail?pk=${pk}`"></router-link>
    <router-link :to="{name:'GoodsDetail', query:{pk: pk}}"></router-link>

    this.$router.push(`/goods/detail?pk=${pk}`)
    this.$router.push({name:'GoodsDetail', query:{pk: pk}})

iii)如何取
    this.$route对象是管理路由参数的,传递的参数会在this.$route.query字典中
    this.$route.query.pk
"""

第一种

配置:router/index.js
const routes = [
    {
        path: '/goods/detail/:pk',
        name: 'GoodsDetail',
        component: GoodsDetail
    },
]

传递:GoodsBox.vue
<router-link class="goods-box" :to="`/goods/detail/${goods.pk}`">
    <img :src="goods.img" alt="">
    <p>{{ goods.title }}</p>
</router-link>

<!------------------- 或者 ------------------->

<div class="goods-box" @click="goDetail(goods.pk)">
    <img :src="goods.img" alt="">
    <p>{{ goods.title }}</p>
</div>
<script>
    export default {
        name: "GoodsBox",
        methods: {
            goDetail (pk) {
                this.$router.push(`/goods/detail/${pk}`);
            }
        }
    }
</script>

接收:GoodsDetail.py
<script>
    export default {
        name: "GoodsDetail",
        data () {
            return {
                pk: '未知',
            }
        },
        // 通常都是在钩子中获取路由传递的参数
        created() {
            this.pk = this.$route.params.pk || this.$route.query.pk;
        }
    }
</script>

第二种

配置:router/index.js
const routes = [
    {
        path: '/goods/detail',
        name: 'GoodsDetail',
        component: GoodsDetail
    },
]

传递:GoodsBox.vue
<router-link class="goods-box" :to="`/goods/detail?pk=${goods.pk}`">
    <img :src="goods.img" alt="">
    <p>{{ goods.title }}</p>
</router-link>

<!------------------- 或者 ------------------->

<div class="goods-box" @click="goDetail(goods.pk)">
    <img :src="goods.img" alt="">
    <p>{{ goods.title }}</p>
</div>
<script>
    export default {
        name: "GoodsBox",
        methods: {
            goDetail (pk) {
                // this.$router.push(`/goods/detail?pk=${goods.pk}`);
                
                // 或者
                this.$router.push({
                    name: 'GoodsDetail',
                    query: {
                        pk,
                    }
                });
            }
        }
    }
</script>

接收:GoodsDetail.py
<script>
    export default {
        name: "GoodsDetail",
        data () {
            return {
                pk: '未知',
            }
        },
        // 通常都是在钩子中获取路由传递的参数
        created() {
            this.pk = this.$route.params.pk || this.$route.query.pk;
        }
    }
</script>

全家配置自定义css与js

global.css
html, body {
    margin: 0;
}

a {
    color: black;
    text-decoration: none;
}

ul {
    margin: 0;
    padding: 0;
}

settings.js
export default {
    base_url: 'https://127.0.0.1:8000'
}

main.js
//1) 配置全局css
import '@/assets/css/global.css'
// import global_css from '@/assets/css/global.css'  // 资源需要用变量保存,方便以后使用
// require('@/assets/css/global.css')
// let global_css = require('@/assets/css/global.css')  // 资源需要用变量保存,方便以后使用


// 2) 配置自定义js设置文件
import settings from '@/assets/js/settings.js'
Vue.prototype.$settings = settings;
// 在任何一个组件中的逻辑,可以通过 this.$settings访问settings.js文件的{}数据

小结

"""
项目:
	环境;node -> npm -> cnpm -> vue/cli
	创建:vue create proj
	配置:配置npm启动项
	项目目录结构:依赖、环境、入口、核心代码们
	
组件:
	构成:template + script + style
	导入:import 别名 from '路径'
	父加子:1)导入 2)注册 3)使用
	组件数据:组件化处理 data(){ return {} }
	传参:父传子 - 自定义组件属性  |  子传父 - 自定义组件事件
	生命周期钩子:created() { //完成后台请求等 }

路由:
	根组件中的页面占位:<router-view />
	导航栏中的页面跳转:<router-link to=""></router-link>
	代码中的逻辑跳转:this.$router.push() | this.$router.go()
	路由传参:两种方式
	两个路由对象:
		this.$router - 控制路由跳转
        this.$route - 控制路由数据
"""

五、内容大纲

"""
第三方插件:
1)vuex:组件间交互的(移动端)
2)axios - 前后台(django):ajax
3)element-ui:页面布局
4)jq+bs:jQuery+BootStrap
"""

vuex插件:可以完成任意组件间信息交互(移动端) - 了解

"""
1)vuex是实现任何组件间的信息交互的,可以理解为全局的一个单例,为任何一个组件共享vue仓库里的数据
2)在任何一个组件的逻辑里,都可以访问仓库
	i)现在仓库里(store/index.js)定义变量,用来存储共享数据
	state: {
        info: '共享数据的初始值'
    },
    ii)在组件逻辑中获取仓库值
    let 变量 = this.$store.state.info
    iii)在组件逻辑中更新仓库值
    this.$store.state.info = '新值'

注:vuex通常运用在开发移动端项目,pc端项目可以用localStorege和localStorege数据库来替换
原因:vuex中的数据,会在页面刷新后,重置到store/index.js配置的默认值
"""

前端存储数据大汇总

"""
1)cookie:以字符串形式存储,数据有过期时间(过期时间到,数据失效吗,否则永远有效)

2)localStorage:以对象形式存储,数据永久保存

3)sessionStorage:以对象形式存储,生命周期同所属页面标签(页面不关闭,数据就有效)

4)vuex(store):以对象形式存储,当页面刷新数据就重置(移动端不能刷新,所以只有应用大退才会重置)
"""

前后台交互方式(重点)

"""
1)form表单方式
	i)get | post 两种请求方式,get请求包含直接在浏览器中输入url回车后发送的请求
	ii)该方式的特点是一定会发生页面的跳转(刷新页面叫本页跳转) - 后台决定页面路由
	
2)ajax异步方式
	i)get | post | patch | put | delete 等众多请求方式,请求的目的都是异步获取后台的数据
	ii)该方式的特点是不会刷新页面,只是得到新的数据,前台自己完成页面的局部刷新、整体刷新、页面跳转 - 前台决定页面路由
	
注:
i)前后台不分离项目,采用form表单请求,可以完成页面跳转,同步ajax异步请求完成页面局部刷新
ii)前后台分离项目,不采用form表单请求,页面刷新、页面跳转的请求,都是由ajax完成,只不过页面跳转,后台相应的是跳转的目标url,前台再自己完成跳转

iii)前后台分离项目,前台也会出现大量的form表单,但是form表单的提交按钮,走的不是form表单的submit提交,而是ajax请求
"""

axios插件:完成前后台ajax交互的

"""
1)安装:在前端项目根目录下的终端
cnpm install axios

2)项目配置:main.js
import axios from 'axios'
Vue.prototype.$axios = axios;

3)在任何一个组件的逻辑中,都可以访问 this.$axios()
beforeMount() {
	// 请求后台
	this.$axios({
    	url: this.$settings.base_url + '/test/',
        method: 'delete',
    })
}
"""

同源策略 - 跨域问题

"""
一、django默认是同源策略,所以前后台分离项目,访问django会出现CORS跨域问题的报错

二、什么叫跨域
i)ip不同:前后台(两个服务器)不在一台主机上运行的
ii)port不同:前后台(两个服务器)是相互独立的,运行在不同的端口之上
iii)协议不同:http与https之间也同样是跨域问题
注:三者满足一个,就是跨域

三、解决跨域
i)伪装:将前台请求伪装成后台自己对自己发生的请求
ii)后台主动允许跨域:后台配置允许跨域即可(在响应头中处理)

四、Django解决跨域
i)安装模块:
	pip install django-cors-headers
ii)注册app:
INSTALLED_APPS = [
	...
	'corsheaders'
]
iii)添加中间件
MIDDLEWARE = [
	...
	'corsheaders.middleware.CorsMiddleware'
]
iv)允许跨域源
CORS_ORIGIN_ALLOW_ALL = True
"""

前后台分离项目交互流程

"""
1)启动前后台项目

2)前台配置页面路由,渲染前台页面 | 后台配置数据路由,响应数据(处理好跨域问题)

3)前台通过ajax请求后台接口
	i)将前台数据提交给后台
	ii)得到后台的响应结果
	iii)根据响应结果的数据,最后完成页面的局部刷新、整体刷新、页面跳转
"""

异步请求细节

"""
1)vue框架用axios完成ajax异步请求
	语法:this.$axios().then().catch();
    解读:$axios()是请求逻辑 | then()是正常响应逻辑 | catch()是错误响应逻辑
    具体语法:
    this.$axios({
    	url: '后台接口链接',
    	method: '请求方式',
    	params: {},  // url拼接参数
    	data: {},  // 数据包参数
    	headers: {}  // 请求头参数
    }).then(response => {
    	// response是http状态2xx的响应结果,响应数据是response.data
    }).catch(error => {
    	// error是http状态4xx、5xx的响应结果,错误响应对象是error.response,错误响应数据是error.response.data
    })

2)前台提交数据的两种方式:
	i)url拼接参数:
		所有请求都拥有的提交数据的方式
		该方式会将数据都在请求url后用?拼接的方式提交给后台
		提交数据只能采用url字符串方式提交给后台,数据是不安全的
		axios插件可以用params属性携带url拼接参数
		
	ii)数据包参数:
		除get请求外的所有请求都拥有的提交数据的方式
		该方式会将数据进行加密,打包成数据包方式提交给后台
		打包加密数据有三种方式:urlencoded(form默认的方式)、form-data(可以提交文件)、json(提交json数据)
		axios插件可以用data属性携带数据包参数
		
"""

"""
注意项:
1)this.$axios({}).then(response => {}).catch(error => {}) 中的then和catch回调函数,不能写function,因为实际vue项目开发,一定会在回调逻辑用到this语法(代表vue对象),而在function中使用了this语法,function就不是普通函数了(可以理解为类,this就不能代表vue对象了)

2)原生django没有提供所有类型的数据包数据解析规则,但是数据会在request.body中,可以自己解析;Django-rest-framework框架是提供了三种类型的数据包参数解析
"""

element-ui插件

"""
element-ui就类似于BootStrap框架,前者是在vue项目中运用,后者是在原生项目中运用,当然也可以在vue项目中运用

环境搭建:
1)安装:在前端项目根目录下的终端
cnpm install element-ui

2)配置:main.js
import ElementUI from 'element-ui'
Vue.use(ElementUI);
import 'element-ui/lib/theme-chalk/index.css';

3)使用:根据视频和官方API接口
"""

jq+bs插件

"""
一、jq环境搭建:
1)安装:在前端项目根目录下的终端
cnpm install jquery

2)配置:自己在项目根目录下新建 vue.config.js
const webpack = require("webpack");
module.exports = {
    configureWebpack: {
        plugins: [
            new webpack.ProvidePlugin({
                $: "jquery",
                jQuery: "jquery",
                "window.$": "jquery",
                "window.jQuery": "jquery",
                Popper: ["popper.js", "default"]
            })
        ]
 	}
};

二、bs环境搭建:
1)安装:在前端项目根目录下的终端
cnpm install bootstrap@3

2)配置:自己在项目根目录下新建 vue.config.js
import BootStrap from "bootstrap"
import "bootstrap/dist/css/bootstrap.css"
Vue.use(BootStrap)
"""

Django国际化配置

"""
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
"""

小结

"""
1)vuex
	仓库,拥有组件间传参
	前台存储数据的位置:cookie、localStorage、sessionStorage、vuex

2)axios
	配置:安装 => 配置给Vue.prototype.$axios
	使用:this.$axios({}).then(response => {}).catch(error => {})
	两种请求:form(页面跳转,前后台不分离) | ajax(只刷新数据,所有类型项目都可以)
	前后台分离项目工作流程:前台启动提供页面 => 后台启动提供数据 => 前台渲染页面并请求后台数据
	请求方式:get、post、patch、put、delete...
	请求数据:
		url拼接数据:所有请求都可以携带
		数据包数据:只有get不能携带
	数据包三种格式:urlencoded、form-data、json	

3)element-ui
	提供一堆布局

4)jq+bs
	提供一堆布局
	

"""

原文地址:https://www.cnblogs.com/WQ577098649/p/12633725.html