Vue

Vue

vue多数用的都是es6语法

前戏

let的特点
	1.局部作用域,(let代码块内部生效,外部不能使用这个变量)
        if (1){
            let a = 'xxoo';
        }
        console.log(a); //报错

        if (1){
            var b = 'xxoo';
        }
        console.log(b); // 可以使用,但是函数不行
	2.不存在变量提升
		console.log(xx); //undefined
		var xx = 'oo';
		
		console.log(xxx);  //报错
		let xxx = 'ooo';
	3.不能重复声明(与var不能同时声明一个变量)
        var s = 6;
        let s = 6; // 会报错
	4.let声明的变量不从属于window对象,var声明的变量从属于window对象
		var th = 5
		window.th
		5
		
		let sh = 6;
		window.sh
		undefined
	关于第4个特点的简单说明:
		ES5声明变量只有两种方式: var和function
		ES6有let、const、import、class、var、function共有6种声明变量的方式。
		还需要了解顶层对象:浏览器环境中顶层对象是window
		ES5中,顶层对象的属性等价于全局变量。
		ES6中,有所改变var、function声明的是全局变量,依然是顶层对象的属性;let、const、class声明的全局变量不属于顶层对象的属性,也就是说ES6开始,全局变量和顶层对象的属性开始分离,脱钩,目的是以防声明的全局变量将window对象的属性造成污染,因为window对象是顶层对象,它里面的属性是各个js程序中都可以使用的,不利于模块化开发,并且window对象有很多的自有属性,也为了防止对window的自有属性的污染,所以在ES6中将顶层对象和全局变量进行隔离。

const的特点
	1.局部作用域
	2.不存在变量提升
	3.不能重复声明(与var和let)
	4.不能重新赋值
	5.一般用来声明不可变的量(常量)

模板字符串:tab键上面的反引号,${变量名}来插入值,类似于python中的三引号,可以写多行文本
示例:
	let bb = 'jj';
	var ss = `你好${bb}`;
  console.log(ss); -- '你好jj'

ES5全局和局部变量的作用域

函数的全局变量和局部变量
	局部变量:
		在JavaScript函数内部声明的变量是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。
	全局变量:
		在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。
	变量生存周期:
		JavaScript变量的声明周期从它们被声明的时间开始
		局部变量会在函数运行以后被删除
		全局变量会在页面关闭后被删除
	作用域
		首先在函数内部查找变量,找不到则到外层函数查找,逐步找到最外层。

ES5和ES6的函数对比

    //ES5写法
    function add(x){
        return x
    }
    add(5);
    //匿名函数
    var add = function (x) {
        return x
    };
		add(5);
    //ES6的匿名函数
    let add = function (x) {
        return x
    };
    add(5);
    //ES6的箭头函数,就是上面方法的简写形式
    let add = (x) => {
        
        return x
    };
    console.log(add(20));
    //更简单的写法,但不是很易阅读
    let add = x => x;
    console.log(add(5));
    多个参数的时候必须加括号,函数返回值还是只能有一个,没有参数的,必须写一个()
    let add = (x,y) => x+y;

自定义对象中封装函数的写法

   //es5对象中封装函数的方法
    var name = '子俊';
    var person1 = {
        name:'超',
        age:18,
        f1:function () {  //在自定义的对象中放函数的方法
            console.log(this);//this指向的是当前的对象,{name: "超", age: 18, f1: ƒ}
            console.log(this.name)  // '超'
        }
    };
	person1.f1();  //通过自定对象来使用函数


	//ES6中自定义对象中来封装箭头函数的写法
    let username = '子俊'; //-- window.username
    let person2 = {
        name:'超',
        age:18,
        //f1:function(){};
        f1: () => {  //在自定义的对象中放函数的方法
            console.log(this); //this指向的不再是当前的对象了,而是指向了person的父级对象(称为上下文),而此时的父级对象是我们的window对象,Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
            console.log(window);//还记得window对象吗,全局浏览器对象,打印结果和上面一样:Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
            console.log(this.username)
        }
    };
    person2.f1(); //通过自定对象来使用函数

	//而我们使用this的时候,希望this是person对象,而不是window对象,所以还有下面这种写法
	let person3 = {
        name:'超',
        age:18,
        //f1:function(){};
        //f1(){}
        f1(){  //相当于f1:function(){},只是一种简写方式,称为对象的单体模式写法,写起来也简单,vue里面会看用到这种方法
            console.log(this);//this指向的是当前的对象,{name: "超", age: 18, f1: ƒ}
            console.log(this.name)  //'超'
        }
    };
    person3.f1()

一.Vue的准备工作

1.1 vue.js库的下载

vue.js是目前前端web开发最流行的工具库,由尤雨溪在2014年2月发布的

另外几个常见的工具库: react.js/angular.js

官方网站:

​ 中文:https://cn.vuejs.org/

​ 英文:https://vuejs.org/

​ 官方文档:https://cn.vuejs.org/v2/guide/

在github下载:https://github.com/vuejs/vue/releases

在官网下载地址: https://cn.vuejs.org/v2/guide/installation.html

1.2vue.js库的基本使用

vue的引入类似于jQuery,开发中可以使用开发版本vue.js,产品上线要换成vue.min.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="js/vue.min.js"></script>
    
</head>
<body>
<div id="app">
    <!-- {{ message }} 表示把vue对象里面data属性中的对应数据输出到页面中 -->
    <!-- 在双标签中显示数据要通过{{  }}来完成 -->
    <p>{{ message }}</p>
</div>
</body>
  <script>
    
      	// vue.js的代码开始于一个Vue对象。所以每次操作数据都要声明Vue对象开始。
        let vm = new Vue({
            el:'#app',   // 设置当前vue对象要控制的标签范围。
          	// data属性写法方式1
            data:{  // data是将要展示到HTML标签元素中的数据。
              message: 'hello world!',
            }
          	// 方式2
            // data:function () {
            //     return {
            //         'msg':'掀起你的盖头来1!'
            //     }
            // }
						// 方式3
            data(){   // 单体模式  这种写法用的居多
                  return {
                      'msg':'掀起你的盖头来2!',
                  }
              }
            });
    
    </script>
</html>

总结:

1.vue的使用要从创建Vue对象开始
	let vm = new Vue();
2.创建vue对象的时候,需要传递参数,是自定义对象,自定义对象必须至少有两个属性成员
	let vm = new Vue({
		el:'#app',
		data(){
			
		},
	})
	
	el:圈地,划分地盘,设置vue可以操作的html内容范围,值就是css的id选择器,其他选择器也可以,但是多用id选择器。
	data:保存vue.js中要显示到html页面的数据
3.vue.js要控制其内容,外围必须通过id来设置
    <div id="app">
    <h1>{{message}}</h1>
    <p>{{message}}</p>
    </div>

vue中的变量可以直接进行一些简单直接的js操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板语法,和django的模板语法类似 -->
    <h2>{{ msg }}</h2> <!-- 放一个变量,会到data属性中去找对应的值 -->
    <!-- 有人说,我们直接这样写数据不就行吗,但是请注意,我们将来的数据都是从后端动态取出来的,不能写死这些数据啊 -->
    <h2>{{ 'hello beautiful girl!' }}</h2>  <!-- 直接放一个字符串 -->
    <h2>{{ num+1 }}</h2>  <!-- 四则运算 -->
  	<h2>{{ 2+1 }}</h2>  <!-- 四则运算 -->
    <h2>{{ {'name':'chao'} }}</h2> <!-- 直接放一个自定义对象 -->
    <h2>{{ person.name }}</h2>  <!-- 下面data属性里面的person属性中的name属性的值 -->
    <h2>{{ 1>2?'真的':'假的' }}</h2>  <!-- js的三元运算 -->
    <h2>{{ msg2.split('').reverse().join('') }}</h2>  <!-- 字符串反转 -->

</div>

<!-- 1.引包 -->
<script src="vue.js"></script>
<script>
//2.实例化对象
    new Vue({
        el:'#app',
        data(){
            return{
                msg:'黄瓜',
                person:{
                    name:'超',
                },
                msg2:'hello Vue',
                num:10,
            }
        }
    })

</script>
</body>
</html>

1.3Vue.js的M-V-VM思想

MVVM是Model-View-ViewMode的缩写,它是一种基于前端开发的架构模式。

Model指代的是vue对象的data属性里面的数据。这里的数据要显示到页面中。

View指代的就是vue中数据要显示的HTML页面,在vue中,也称之为"视图模板"。

ViewModel指代的是vue.js中我们编写代码时的vm对象,它是vue.js的核心,负责连接View和Model,保证视图和数据的一致性,所以前面代码中,data里面的数据被显示到标签中就是vm对象自动完成的。

在浏览器中可以通过console.log对vm对象可以直接访问el和data属性,甚至可以访问data里面的数据

console.log(vm.$el)		# vm对象可以控制的范围
console.log(vm.#data);	# vm对象要显示到页面中的数据
console.log(vm.message)	# 这个message就是data里面声明的数据,也可以使用vm.变量名显示其他数据

总结:

1.如果要输出data里面的数据作为普通标签的内容,需要使用{{ }}
	用法:
		vue对象的data属性:
			data(){ //单体模式
				return{
					name:'小明',
				}
			}
			标签元素:
				<h1>{{ name }}</h1>
2.如果要输出data里面的数据作为表单元素的值,需要使用vue.js提供的元素属性v-model
	用法:
		vue对象的data属性:
			data(){
				return{
					name:'小明',
				}
			}
		表单元素:
				<input v-model='name'>  # 一定不要加{{ }}
	使用v-model把data里面的数据显示到表单元素以后,一旦用户修改表单元素的值,则页面中凡是用了这个数据的地方都会发生变化,但是data里面对应的值不会改变,页面刷新恢复原来最初设置的值。

二.Vue指令系统的常用指令

指令(Directives)是带有"v-"前缀的特殊属性,每一个指令在vue中都有固定的作用。

在vue中提供了很多指令,常用的有:v-html,v-text,v-if,v-model,v-for等等。

2.1文本指令v-html和v-text

​ v-text相当于js代码的innerText,直接在html中插入文本内容,若果data里面写了个标签,那么通过模板语法渲染的是文本内容。

​ v-html相当于innerHtml,可以识别标签并生成标签。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test vue</title>
</head>
<body>

<div id="app">
    <!-- vue的模板语法 -->
    <div>{{ msg }}</div>
    <div v-text="msg"></div>
    <div v-html="msg"></div>

</div>
</body>
    
<script src="vue.js"></script>
<script>

    new Vue({
        el:'#app',
        data(){
            //记着data中是一个函数,函数中return一个对象,可以是一个空对象,但必须return
            return{
                msg:'<h2>超</h2>', //后端返回的是标签,那么我们就可以直接通过v-html渲染出来标签效果
            }
        }
    })

</script>
</html>

指令会在vm对象的data属性的数据发生变化时,同时改变元素中的其控制的内容或属性

因为vue的历史版本原因,所以有一部分指令都有两种写法:

vue1.x写法			 vue2.x写法
v-html					{{}} # vue2.x 也支持v-html
v-bind:属性名			  :属性
v-on:事件名			  @事件名

2.2条件渲染指令v-if和v-show

vue中提供了两个指令用于判断是否要显示元素,分别是v-if和v-show

2.2.1 v-if
标签元素:
	<!-- vue对象最终会把条件的结果编程布尔值 -->
		<h1 v-if="ok">yes</h1>
		
	data数据
        data(){
        	return{
        		ok:false,  // true则显示,false是隐藏
        	}
        }
2.2.2 v-else

v-else指令来表示v-if的else块,v-else元素必须紧跟在带v-if或者v-else-if的元素后面,否则它将不会被识别。

  标签元素:
			<h1 v-if="ok">Yes</h1>
			<h1 v-else>No</h1>
  data数据:
  		data:{
      		ok:false    // true则是显示,false是隐藏
      }
2.2.3 v-else-if

在vue2.1.0版本之后,又添加了v-else-if,v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用。

可以出现多个v-else-if语句,但是v-else-if之前必须有一个v-if开头。后面可以跟着v-else,也可以没有。

  标签元素:
		<h1 v-if="num===1">num的值为1</h1>
		<h1 v-else-if="num===2">num的值为2</h1>
		<h1 v-else>num的值是{{num}}</h1>
  data数据:
  		data:{
      		num:2
      }
2.2.4 v-show
标签元素:
		<h1 v-show="ok">Hello!</h1>
  data数据:
  		data:{
      		ok:false    // true则是显示,false是隐藏
      }

简单总结v-if和v-show

v-show后面不能接v-else-if和v-else

v-show隐藏元素时,使用的是display:none来隐藏的,而v-if是直接从HTML文档中移除元素[DOM操作中的remove]

v-if 是'真正'的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建

v-if 也是惰性的: 如果在初始渲染时条件为假,则什么也不做一直到条件第一次变为真时,才会开始渲染条件块

相比之下,v-show就简单得多,不管初始条件是什么,元素总会被渲染,并且只是简单地基于CSS进行切换

一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销,因此,如果需要非常频繁地切换,则使用v-show,如果在运行时条件很少改变,则使用v-if较好

2.3 操作属性v-bind

格式:

<标签名 :变迁属性='data属性'></标签名>
<p :title="str1">{{ str1 }}</p> <!-- 也可以使用v-html显示双标签的内容,{{  }} 是简写 -->
<a :href="url2">淘宝</a>
<a v-bind:href="url1">百度</a>  <!-- v-bind是vue1.x版本的写法 -->

例1.显示密码切换效果:配合v-on事件绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <input v-bind:type="type">
    <button @click="f1"  v-text="tip"></button>
</div>
</body>
<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el:'#app',
        data(){
            return {
                type:'password',
                tip:'睁眼',
            }
        },
        methods:{
            f1(){
                if (this.type === 'password'){
                    this.type = 'text';
                    this.tip = '闭眼';
                }
                else{
                    this.type = 'password';
                    this.tip = '睁眼';
                }
            }
        }
    })
</script>

</html>

2.4事件绑定v-on和methods属性

有两种事件操作的写法,@事件名和v-on:事件名

<button v-on:click="num++">按钮</button>   <!-- v-on 是vue1.x版本的写法 -->
<button @click="num+=5">按钮2</button>

总结:

1. 使用@事件名来进行事件的绑定
   语法:
      <h1 @click="num++">{{num}}</h1>

2. 绑定的事件的事件名,全部都是js的事件名:
   @submit   --->  onsubmit
   @focus    --->  onfocus
   ....

例2.商城的商品增减数量

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <button @click="add">+</button>
    <input type="text" v-model="num">
    <button @click="sub">-</button>
</div>
</body>
<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el:'#app',
        data(){
            return{
                num:0,
            }
        },
        methods:{
           add(){
               this.num++
            },
            sub(){
                if (this.num > 0){
                    this.num--
                }
            }
        }
    })
</script>
</html>

2.5操作样式

2.5.1控制标签class类名
格式:
	<h1 :class="值">元素</h1>	值可以是对象,对象名,数组(数组的方式比较少)	
				
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/vue.js"></script>
    <style>
    .box1{
        color: red;
        border: 1px solid #000;
    }
    .box2{
        background-color: orange;
        font-size: 32px;
    }
    </style>
</head>
<body>
    <font color='red'><!-- 第一种形式 --></font></font> 
    <div id="box">
        <!--- 添加class类名,值是一个对象
        {
         class类1:布尔值变量1,
         class类2:布尔值变量2,
        }
         -->
        <p :class="{box1:myclass1}">一个段落</p>
        <p @click="myclass3=!myclass3" :class="{box1:myclass2,box2:myclass3}">一个段落</p>
    </div>
    <script>
        let vm1=new Vue({
            el:"#box",
            data:{
                myclass1:false, // 布尔值变量如果是false,则不会添加对象的属性名作为样式
                myclass2:true,  // 布尔值变量如果是true,则会添加对象的属性名作为样式
                myclass3:false,
            },
        })
    </script>
    <font color='red'><!-- 第二种形式 --></font></font>
    <!-- 上面的代码可以:class的值保存到data里面的一个变量,然后使用该变量作为:class的值 -->
    <style>
    .box4{
        background-color: red;
    }
    .box5{
        color: green;
    }
    </style>
    <div id="app">
        <button @click="mycls.box4=!mycls.box4">改变背景</button>
        <button @click="mycls.box5=!mycls.box5">改变字体颜色</button>
        <p :class="mycls">第二个段落</p>
      	<!-- 还有下面这种将类值当作属性值来操作的方式 -->
        <p :class="status?'bg1':'bg2'">一段文本</p> <!-- 注意,bg1必须加引号 -->

    </div>
    <script>
        let vm2 = new Vue({
            el:"#app",
            data:{
                mycls:{
                    box4:false,
                    box5:true
                },
              	status:true,
            }
        })
    </script>
	
	<font color='red'><!-- 第三种形式 --></font></font>
    <!-- 批量给元素增加多个class样式类 -->
    <style>
    .box6{
        background-color: red;
    }
    .box7{
        color: green;
    }
    .box8{
        border: 1px solid yellow;
    }
    </style>
    <div id="app2">
            <p :class="[mycls1,mycls2]">第三个段落</p>
    </div>
    <script>
        let vm3 = new Vue({
            el:"#app2",
            data:{
                mycls1:{
                    box6:true,
                    box7:true,
                },
                mycls2:{
                    box8:true,
                }
            }
        })
    </script>
</body>
</html>

总结:

1. 给元素绑定class类名
	vue对象的data数据:
		data:{
			myobj:{
				complete:true,
				uncomplete:fasle,
			}
		}
		html元素:
			<div class="box" :class="myObj">2222</div>
    最终浏览器效果:
		    <div class="box complete">2222</div>
2.5.2控制标签style样式
格式1:	值是json对象,对象写在元素的:style属性中
	标签元素:
		<div :style="{color:activeColor,fontSize:fontSize + "px"}"></div>
		<!-- 注意: 不能出现横杠 - , 有的话就套上引号如 'font-size',或者去掉横杠, 后一个单词的首字母大写,比如fontSize -->
		data数据如下:
			data:{
				acriveColor:'red',
				fontSize:30
			}

格式2:	值是对象变量名,对象在data中进行声明
	标签元素:
		<div v-bind:style="styleObject"></div>
	data数据如下:
		data:{
			styleObject:{
				color:'red',
				fontSize:'13px',
			}
		}

格式3:	值是数组
	标签元素:
		<div v-bind:style='[style1,style2]'></div>
	data数据如下:
		data:{
			style1:{
				color:'red',
			},
			style2:{
				backgroundColor:"yellow",
				fontSize:"21px",
			}
		}

实例-vue版本选项卡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #app{
            width:500px;
            height:350px;
        }
        .title{
            height:50px;
        }
        .title span{
            width:100px;
            height:50px;
            background-color:#ccc;
            display: inline-block;
            line-height:50px; /* 设置行和当前元素的高度相等,就可以让文本内容上下居中 */
        }
        .content .list{
            width: 500px;
            height: 300px;
            background-color: yellow;
            display:none;
        }
        .content .active{
            display: block;
        }
        .title .current{
            background-color:yellow;
        }
    </style>
</head>
<body>
    <div id='app'>
        <div class='title'>
            <span @click='num=0' :class="num==0?'current':''">国内新闻</span>
            <span @click='num=1' :class="num==1?'current':''">国际新闻</span>
            <span @click='num=2' :class="num==2?'current':''">银河新闻</span>
        </div>
        <div class='content'>
        	<span class='list' :class="num==0?'active':''">国内新闻列表</span>
            <span class='list' :class="num==1?'active':''">国际新闻列表</span>
            <span class='list' :class="num==2?'active':''">银河新闻列表</span>
        </div>
    </div>
</body>
    <script src='vue.js'></script>
    // 思路: 当用户点击标题栏的按钮[span]时,显示对应索引下标的内容块[.list]
    <script>
    	let vm = new Vue({
            el:'#app',
            data(){
                return{
                    num:0,
                }
            },
        })
    </script>
</html>

2.6 列表渲染指令v-for

在vue中,通过v-for指令可以将一组数据渲染到页面中,数据可以是数组或者对象

<font color='red'>数据是数组:</font>
	<ul>
        <li v-for="book in book_list" :key='book.id'>{{ book.title }}</li>
	</ul>
	<!-- v-for不仅可以遍历数组,还可以遍历对象,写v-for后面一定要加上:key,就是v-bind:key,这个key就是为了给已经渲染好的li标签做个标记,以后即便是有数据更新了,也可以在这个li标签里面进行数据的更新,不需要再让Vue做重新生成li标签的dom操作,提高页面渲染的性能,因为频繁的添加和删除dom操作对性能是有影响的,如果数据里面有id,一般绑定id,没有id则绑定v-for里面的index(可以自己起名字),这里面用的是diff算法, v-for的优先级最高,先把v-for遍历完,然后给:key加数据,还有如果没有bind这个key,有可能页面后期用动态数据渲染的时候,会出现问题,一定要加上v-bind:key -->
	<li v-for="(item,index) in book_list" :key="item.id"> 第{{index+1}}本图书:{{item.title}}</li>
	<script>
            var vm1 = new Vue({
                el:"#app",
                data:{
                    book_list:[
                        {"id":1,"title":"图书名称1","price":200},
                        {"id":2,"title":"图书名称2","price":200},
                        {"id":3,"title":"图书名称3","price":200},
                        {"id":4,"title":"图书名称4","price":200},
                    ]
                }
            })
        </script>

数据是对象:
	<ul>
            <!--i是每一个value值-->
            <li v-for="value in book">{{value}}</li>
        </ul>
        <ul>
            <!--value是每一个value值,attr是每一个键名-->
            <li v-for="value,attr in book">{{attr}}:{{value}}</li>
        </ul>
        <script>
            var vm1 = new Vue({
                el:"#app",
                data:{
                    book: {
                        // "attr属性名":"value属性值"
                        "id":11,
                        "title":"图书名称1",
                        "price":200
                    },
                },
            })
        </script>

练习:

goods:[
	{"name":"python入门","price":150},
	{"name":"python进阶","price":100},
	{"name":"python高级","price":75},
	{"name":"python研究","price":60},
	{"name":"python放弃","price":110},
]

# 把上面的数据采用table表格输出到页面,价格大于60的那一条数据需要添加背景色

练习答案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>

    </style>
</head>
<body>
<div id="app">
    <table border="1px">
        <thead>
            <tr>
                <th>name</th>
                <th>price</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="good in goods" :key="good.id">
                <td>{{good.name}}</td>
                <td v-if="good.price>60" style="background-color:red;">{{good.price}}</td>
                <td v-else>{{good.price}}</td>
            </tr>
        </tbody>
    </table>
</div>

</body>
    <script src='vue.js'></script>
    <script>
        let vm = new Vue({
            el:'#app',
            data(){
                return{
                    goods:[
                        {"name":"python入门","price":150},
                        {"name":"python进阶","price":100},
                        {"name":"python高级","price":75},
                        {"name":"python研究","price":60},
                        {"name":"python放弃","price":110},
                    ]
                }
            },
        })
    </script>
</html>

三.Vue对象提供的属性功能

3.1过滤器

过滤器,就是vue允许开发者自定义的文本格式化函数,可以用在输出内容和操作数据中

定义过滤器的方式有两种,全局和局部过滤器

3.1.1使用Vue,filter()进行全局定义
Vue.filter("RMB1",function(v){
    //就是用来格式化处理v这个数据的
    if(v==0){
        return v
    }
    return v+'元'
})
3.1.2在vue对象中通过filters属性来定义
let vm = new Vue({
    el:'#app',
    data(){
        return{
            
        }
    },
    filters:{
        RMB2:function(value){
            if(value==''){
                return;
            }else{
                return '¥ ' +value;
            }
        }
    }
})

示例:数据小数点保留3位,并在后面加上一个元字

//全局过滤器
// Vue.filter('过滤器名称','调用过滤器时执行的函数')
Vue.filter("RMB",function(value){
    return value.toFixed(3)+'元'; // toFixed()保留几位小数
})

局部过滤器写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/vue.js"></script>
    <script src="js/filters.js"></script>
</head>
<body>

    <div id="app">
        价格:{{price.toFixed(3)}}<br>
        价格:{{price|keepdot2(3)|RMB}}<br>
        价格:{{price|keepdot2(3)|RMB}}<br>  
        价格:{{price|keepdot2(3)|RMB}}<br>
        价格:{{price|keepdot2(3)}}<br>
    </div>

    <script>

        var vm1 = new Vue({
            el:"#app",
            data:{
                price: 20.3
            },
            methods:{},
            // 普通过滤器[局部过滤器]
            filters:{
                keepdot2(value,dot){  //这里有两个参数,第二个参数必须是调用过滤器时过滤器函数名称括号里面的值
                    return value.toFixed(dot)
                }
            }
        })
    </script>

</body>
</html>

3.2计算和监听属性

3.2.1计算属性
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <p>{{add}}</p>
</div>
</body>
<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el:'#app',
        data(){
            return{
                price1:100,
                price2:100.126
            }
        },
        // 计算属性
        computed:{
            add(){
                return this.price1 + this.price2
            }
        }
    })
</script>
</html>
3.2.2监听属性

监听属性可以帮助我们监听data某个数据的变化从而做响应的自定义操作

监听属性是一个对象,它的键是要监听的对象或者变量,值一般是函数,当监听的data数据发生变化时,会自动执行对应函数,这个函数在被调用时,vue会传入两个形参,第一个是变化后的数据值,第二个是变化前的数据值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    {{price}}
    <input type="text" v-model="price">
    <button @click="info.xx='ss'">走你</button>
</div>
</body>
<script src="vue.js"></script>
<script>
    let vm = new Vue({
        el:'#app',
        data(){
            return{
                price:100,
                info:{
                    xx:'oo',
                }
            }
        },
        watch:{
            // price:function(newv,oldv){
            //     if (newv == 200){
            //         alert('200块钱了')
            //     }
            // },
            'info.xx':function(newv,oldv){
                if (newv != 'oo'){
                    alert('不是oo了')
                }
            }

        }
    })
</script>
</html>

示例:用户名长度限制4-10位

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/vue.js"></script>
    <script src="js/filters.js"></script>
</head>
<body>

    <div id="app">
        <form action="">
            账号:<input type="text" v-model="form.username"><span :style="user_style">{{user_text}}</span><br><br>
            密码:<input type="password" v-model="form.password"><br><br>
            确认密码:<input type="password" v-model="form.password2"><br><br>
        </form>
    </div>

    <script>

        var vm1 = new Vue({
            el:"#app",
            data:{
                form:{
                    username:"",
                    password:"",
                    password2:"",
                },
                user_style:{
                    color: "red",
                },
                user_text:"用户名长度只能是4-10位"
            },
            // 监听属性
            // 监听属性的变化
            watch:{
                "form.username":function(value){ //注意,使用数据属性中的某个属性的时候,如果使用的是该数据中的内部属性,别忘了加双引号
                    if(value.length>=4 && value.length<=10){
                        this.user_style.color="blue";
                        this.user_text="用户名长度合法!";
                    }else{
                        this.user_style.color="red";
                        this.user_text="用户名长度只能是4-10位!";
                    }
                }
            }
        })
    </script>

</body>
</html>

3.3vue对象的生命周期钩子函数

​ 每个Vue对象在创建时都要经过一系列的初始化过程,在这个过程中Vue.js会自动运行一些叫做生命周期的钩子函数,我们可以使用这些函数,在对象创建的不同阶段加上我们需要的代码,实现特定的功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/vue.min.js"></script>
    <script>
    window.onload = function(){
        //$(function(){})()  $.ready() -- window.onload = function(){}
        var vm = new Vue({
            el:"#app",
            data:{
                num:0
            },
            beforeCreate:function(){
                console.log("beforeCreate,vm对象尚未创建,num="+ this.num);  //undefined,就是说data属性中的值还没有放到vm对象中
                this.name=10; // 此时没有this对象呢,所以设置的name无效,被在创建对象的时候被覆盖为0
              	console.log(this.$el) //undefined
            },
            created:function(){  //重点 ,获取到数据但还没找到圈地
              	// 用的居多,一般在这里使用ajax去后端获取数据,然后交给data属性
                console.log("created,vm对象创建完成,设置好了要控制的元素范围,num="+this.num );  // 0 也就是说data属性中的值已经放到vm对象中
                this.num = 20;
                console.log(this.$el) //undefined
            },
            beforeMount:function(){  //找到圈地了
                console.log( this.$el.innerHTML ); // <p>{{num}}</p> ,vm对象已经帮我们获取到了这个视图的id对象了
                console.log("beforeMount,vm对象尚未把data数据显示到页面中,num="+this.num ); // 20,也就是说vm对象还没有将数据添加到我们的视图中的时候
                this.num = 30;
            },
            mounted:function(){  //数据在html页面渲染了
              	// 用的居多,一般在这里使用ajax去后端获取数据然后通过js代码对页面中原来的内容进行更改 
                console.log( this.$el.innerHTML ); // <p>30</p>
                console.log("mounted,vm对象已经把data数据显示到页面中,num="+this.num); // 30,也就是说vm对象已经将数据添加到我们的视图中的时候
            },
            
          	// 后面两个简单作为了解吧,测试的时候最好单独测试下面两个方法
            beforeUpdate:function(){
                // this.$el 就是我们上面的el属性了,$el表示当前vue.js所控制的元素#app
                console.log( this.$el.innerHTML );  // <p>30</p>
                console.log("beforeUpdate,vm对象尚未把更新后的data数据显示到页面中,num="+this.num); // beforeUpdate----31
                
            },
            updated:function(){
                console.log( this.$el.innerHTML ); // <p>31</p>
                console.log("updated,vm对象已经把过呢更新后的data数据显示到页面中,num=" + this.num ); // updated----31
            },
        });
    }
    </script>
</head>
<body>
    <div id="app">
        <p>{{num}}</p>
        <button @click="num++">按钮</button>
    </div>
</body>
</html>

总结:

在vue使用的过程中,如果要初始化操作,把初始化操作的代码放在mounted中执行,mounted阶段就是在vm对象已经把data数据实现到页面以后,一般页面初始化使用.例如:用户访问页面加载成功后,就要执行的ajax请求

另一个就是created,这个阶段就是在vue对象创建以后,把ajax请求后端数据的代码放进created中

3.4阻止事件冒泡和刷新页面

使用.stop或者.prevent

示例1:阻止事件冒泡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box1{
            width: 200px;
            height: 200px;
            background: #ccc;
        }
        .box2{
            width: 100px;
            height: 100px;
            background: pink;
        }
    </style>

</head>
<body>
    <div id="app">
        <div class="box1" @click="show1('box1')">
            <div class="box2" @click.stop="show2('box2')"></div>   <!-- @click.stop来阻止事件冒泡,下面再加个prevent也可以 -->
<!--          <div class="box2" @click.stop.prevent="show2('box2')"></div>-->
        </div>

        <form action="">
            <input type="text">
            <input type="submit">
            <input type="submit" value="提交02" @click.prevent=""> <!-- @click.prevent来阻止表单提交,从而不刷新页面 -->
        </form>
    </div>

</body>
<script src="vue.js"></script>
  <script>
    let vm = new Vue({
        el:'#app',
        data(){
            return {
                currentIndex:1,
                'msg':'hello',
                'price':200.1,
                inp:'',
            }
        },
        methods:{
            show1(val){
                alert('box1');
            },
            show2(val){
                alert('box2');
            }
        }
    })

</script>
</html>

示例2:阻止form表单提交动作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

<!--    <script src="filters.js"></script>-->
</head>
<body>
    <div id="app">
        <form action="">
        账号:<input type="text" v-model="user"><br><br>
        密码:<input type="password" v-model="pwd"><br><br>
        <button @click="loginhander">登录</button>
        </form>
    </div>



</body>
<script src="vue.js"></script>
    <script>
        var vm1 = new Vue({
            el:"#app",
            data:{
                user:"",
                pwd:"",
            },
            methods:{
                loginhander(){
                    if(this.user.length<3 || this.pwd.length<3){
                        // 长度太短不能登录
                        alert("长度太短不能登录");
                        // 也可以写个return false; 也可以不写,prevent直接就是阻止form表单的提交动作
                    }else{
                        // 页面跳转
                        //  加 return false 或者prevent 阻止表单提交动作页面刷新,下面代码不执行
                        //prevent
                        location.assign("http://www.baidu.com");
                        //或者:
                        //location.href="http://www.baidu.com";
                    }
                }
            }
        })
    </script>
</html>

3.5综合案例-todolist

我的计划列表

html代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>todolist</title>
	<style type="text/css">
		.list_con{
			width:600px;
			margin:50px auto 0;
		}
		.inputtxt{
			width:550px;
			height:30px;
			border:1px solid #ccc;
			padding:0px;
			text-indent:10px;
		}
		.inputbtn{
			width:40px;
			height:32px;
			padding:0px;
			border:1px solid #ccc;
		}
		.list{
			margin:0;
			padding:0;
			list-style:none;
			margin-top:20px;
		}
		.list li{
			height:40px;
			line-height:40px;
			border-bottom:1px solid #ccc;
		}

		.list li span{
			float:left;
		}

		.list li a{
			float:right;
			text-decoration:none;
			margin:0 10px;
		}
	</style>
</head>
<body>
	<div class="list_con">
		<h2>To do list</h2>
		<input type="text" name="" id="txt1" class="inputtxt">
		<input type="button" name="" value="增加" id="btn1" class="inputbtn">

		<ul id="list" class="list">
			<!-- javascript:; # 阻止a标签跳转 -->
			<li>
				<span>学习html</span>
				<a href="javascript:;" class="up"> ↑ </a>
				<a href="javascript:;" class="down"> ↓ </a>
				<a href="javascript:;" class="del">删除</a>
			</li>
			<li><span>学习css</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
			<li><span>学习javascript</span><a href="javascript:;" class="up"> ↑ </a><a href="javascript:;" class="down"> ↓ </a><a href="javascript:;" class="del">删除</a></li>
		</ul>
	</div>
</body>
</html>

特效实现效果:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>todolist</title>
	<style type="text/css">
		.list_con{
			width:600px;
			margin:50px auto 0;
		}
		.inputtxt{
			width:550px;
			height:30px;
			border:1px solid #ccc;
			padding:0px;
			text-indent:10px;
		}
		.inputbtn{
			width:40px;
			height:32px;
			padding:0px;
			border:1px solid #ccc;
		}
		.list{
			margin:0;
			padding:0;
			list-style:none;
			margin-top:20px;
		}
		.list li{
			height:40px;
			line-height:40px;
			border-bottom:1px solid #ccc;
		}

		.list li span{
			float:left;
		}

		.list li a{
			float:right;
			text-decoration:none;
			margin:0 10px;
		}
	</style>
</head>
<body>
	<div id="todolist" class="list_con">
		<h2>To do list</h2>
		<input type="text" v-model="message" class="inputtxt">
		<input type="button" @click="addItem" value="增加" class="inputbtn">
		<ul id="list" class="list">
			<li v-for="item,key in dolist">
				<span>{{item}}</span>
				<a @click="upItem(key)" class="up" > ↑ </a>
				<a @click="downItem(key)" class="down"> ↓ </a>
				<a @click="delItem(key)" class="del">删除</a>
			</li>
		</ul>
	</div>
</body>
    <script src="vue.js"></script>
    <script>
    // 计划列表代码
    let vm = new Vue({
        el:"#todolist",
        data:{
            message:"",
            dolist:[
                "学习html",
                "学习css",
                "学习javascript",
            ]
        },
        methods:{
            addItem(){
                if(this.messsage==""){
                    return false;
                }

                this.dolist.push(this.message);
                this.message = ""
            },
            delItem(key){
                this.dolist.splice(key, 1);
            },
            upItem(key){
                if(key==0){
                    return false;
                }
                // 向上移动
                let result = this.dolist.splice(key,1); // 注意返回的是数组
                this.dolist.splice(key-1,0,result[0]);
            },
            downItem(key){
                // 向下移动
                let result = this.dolist.splice(key, 1);
                console.log(result); 
                this.dolist.splice(key+1,0,result[0]);
            }
        }
    })
    </script>
</html>

splice方法的简单使用

删除
let xx = [11,22,33,44,55];
xx.splice(2,2)

删除并替换
let xx = [11,22,33,44,55];
xx.splice(2,2,'aa','bb','cc','dd');
[11, 22, "aa", "bb", "cc", "dd", 55]

a标签href属性

<a href="#">百度</a>
<!-- href属性为空时,刷新页面 -->
<!-- 不写href属性,就是普通文本标签 -->
<!-- 当href属性等于某个值是,页面跳转到对应的页面 -->
<!-- 当href属性等于javascript:void(0);简写javascript:;时,不刷新也不跳转 -->
<!-- 当href属性等于#时,不刷新页不跳转,但会在url上加个#号结尾 -->

四.通过axios实现数据请求

vue.js默认是没有提供ajax功能的

所以使用vue的时候,一般都会使用axios的插件来实现ajax与后端服务器的数据交互。

注意,axios本质上就是javascript的ajax封装,所以会被同源策略限制。

下载地址:

https://unpkg.com/axios@0.18.0/dist/axios.js
https://unpkg.com/axios@0.18.0/dist/axios.min.js

axios提供发送请求的常用方法有两个:axios.get() 和 axios.post() 。

增 post

删 delete

改 put

查 get

// 发送get请求
// 参数1:必填,字符串,请求的数据接口的url地址,例如请求地址:http://www.baidu.com?id=200
// 参数2: 可选,json对象,要提供给数据接口的参数
// 参数3: 可选,json对象,请求头信息
axios.get('服务器的资源地址// http://www.baidu.com
    params:{
    	参数名:'参数值' . //id:200,      
	}
}).then(function(response){ // 请求成功以后的回调函数
    console.log('请求成功');
    console.log(response);
    
}).catch(function(error){ // 请求失败后的回调函数
    console.log('请求失败');
    console.log(error.response);
});

// 发送post请求,参数和使用axios.get()一样
// 参数1:  必填,字符串,请求的数据接口的url地址
// 参数2: 必填, json对象,要提供给数据接口的参数,如果没有参数,则必须使用{}
// 参数3: 可选,json对象,请求头信息
axios.post('服务器的资源地址',{
    username: 'xiaoming',
    password: '123456',
},{
    responseData: 'json',
})
.then(function(response){  //请求成功以后的回调函数
	console.log(response);
})
.catch(function(error){    // 请求失败后的回调函数
    console.log(error);
});

4.1json

​ json是JavaScript Object Notation 的首字母缩写,单词的意思是javascript对象表示法,这里说的json值得是类似于javascript对象的一种数据格式

​ json的作用:在不同的系统平台,或不同编程语言之间传递数据

4.1.1json数据的语法

json数据对象类似于JavaScript中的对象,但是它的键对应的值里面是没有函数方法的,值可以是普通变量,不支持undefined,值还可以是数组或者json对象

// json数据的对象格式:  最好用双引号
{
    "name":"tom",
    "age":18
}
// json数据的数组格式:
["tom",18,"programmer"]

复杂的json格式数据可以包含对象和数组的写法

{
    "name":"小明",
  	"age":200,
  	"fav":["code","eat","swim","read"],
  	"son":{
        "name":"小小明",
        "age":100,
        "lve":["code","eat"],
  	}
}

json数据可以保存在.json文件中,一般里面就只有一个json对象

总结:

1. json文件的后缀是.json
2. json文件一般保存一个单一的json数据
3. json数据的属性不能是方法或者undefined,属性值只能是:数值,字符串,json和数组
4. json数据只能使用双引号,每一个属性成员之间使用逗号隔开,并且最后一个成员没有逗号

工具:postman可以用于测试开发的数据接口

4.1.2js中提供的json数据转换方法

javascript提供了一个JSON对象来操作json数据的数据转换

方法 参数 返回值 描述
stringify json对象 字符串 json对象转成字符串
parse 字符串 json对象 字符串格式的json数据转成json对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // json语法
    let humen = {
        "username":"xiaohui",
        "password":"1234567",
        "age":20
    };

    console.log(humen);
    console.log(typeof humen);

    // JSON对象提供对json格式数据的转换功能
    // stringify(json对象)  # 用于把json转换成字符串
    let result = JSON.stringify(humen);
    console.log(result);
    console.log(typeof result);

    // parse(字符串类型的json数据)  # 用于把字符串转成json对象
    let json_str = '{"password":"1123","age":20,"name":"xiaobai"}';
    console.log(json_str)
    console.log(typeof json_str)

    let json_obj = JSON.parse(json_str);
    console.log(json_obj);
    console.log(typeof json_obj)

    console.log(json_obj.age)
</script>
</body>
</html>

4.2ajax

​ ajax,一般中文称为:'阿贾克斯',是英文“Async Javascript And Xml”的简写,译作:异步js和xml传输数据

​ ajax的作用:ajax可以让js代替浏览器向后端程序发送http请求,与后端通信,在用户不知道的情况下操作数据和信息,从而实现页面局部刷新数据/不刷新页面的情况更新数据。所以开发中ajax是很常用的技术,主要用于操作后端提供的数据接口,从而实现网站的前后端分离。

​ ajax技术的原理是实例化js的XMLHttpResquest对象,使用此对象提供的内置方法就可以与后端进行数据通信

4.2.1数据接口

数据接口,也叫api接口,表示后端提供操作数据/功能的url地址给客户端使用

客户端通过发起请求向服务端提供url地址申请操作数据(增删改查)

同时在工作中,大部分数据接口都不是手写,而是通过函数库/框架来生成

4.2.2ajax的使用

ajax的使用必须与服务端程序配合使用,我们可以使用别人写好的数据接口进行调用

jQuery将ajax封装成了一个函数$.ajax(),可以直接用这个函数来执行ajax请求

接口 地址
天气接口 http://wthrcdn.etouch.cn/weather_mini?city=城市名称
音乐接口搜索 http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=歌曲标题
音乐信息接口 http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.song.play&songid=音乐ID

编写代码获取接口提供的数据:

jQ版:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="btn">点击获取数据</button>
</body>
    <script src='jquery.js'></script>
    <script>
    	$(function(){
            $("#btn").on("click",function(){
                $.ajax({
                    // 后端程序的url地址
                    url:'http://wthrcdn.etouch.cn/weather_mini',
                    type:"get",
                    dataType:'json', // 返回的数据格式,常用的有json,html,jsonp
                    data:{ // 设置发送给服务器的数据,如果是get请求,也可以写在url地址的?号后面
                        "city" : '北京'
                    }
                })
                .done(function(resp){ // 请求成功以后的操作,新的ajax是这样写的,以前是通过属性success:function(){}的形式写的
                    console.log(resp);
                })
                .fail(function(error){	//请求失败以后的操作.以前通过 error:function(){}
                    console.log(error);
                })
            })
        })
    </script>
</html>

Vue版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="city">
        <button @click="get_weather">点击获取天气</button>
    </div>
</body>
    <script src="vue.js"></script>
    <script src="axios.js"></script>
    <script>
    	let vm = new Vue({
            el:'#app',
            data(){
                return{
                    city:'',
                }
            },
            methods:{
                get_weather(){
                    axios.get("axios.get(r"http://wthrcdn.etouch.cn/weather_mini?city="+this.city")
                    .then(response=>{
                              console.log(response);
                    }).catch(error=>{
                		console.log(error.response)
            		});
                }
            }
        })
    </script>
</html>
4.2.3同源策略

同源策略,是浏览器为了保护用户信息的一种安全机制,所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器端页面地址)之间比较,是否协议,域名(ip)和端口号相同,不同源的客户端脚本(javascript)在没有明确授权的情况下,没有权限读写对方的信息。

ajax本质上还是javascript,是运行在浏览器中的脚本语言,所以会受到浏览器的同源策略所限制

前端地址:
http://www.oldboy.cn/index.html
是否同源 原因
http://www.oldboy.cn/user/login.html 协议,域名,端口号相同
http://www.oldboy.cn/about.html 协议,域名,端口号相同
https://www.oldboy.cn/user/login.html 协议不同
http:/www.oldboy.cn:5000/user/login.html 端口号不同
http://bbs.oldboy.cn/user/login.html 域名不同

同源策略针对ajax的拦截代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
    <script src="axios.js"></script>
</head>
<body>
    <div id="app">
        <button @click="get_music">点击获取音乐</button>
    </div>
    <script>
        let vm = new Vue({
            el:"#app",
            data:{},
            methods:{
                get_music(){
                    axios.get("http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=我的中国心")
                        .then(response=>{
                            console.log(response);

                        }).catch(error=>{
                            console.log(error.response)
                    });
                }
            }
        })
    </script>
</body>
</html>

上面代码运行错误如下:

Access to XMLHttpRequest at 'http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.catalogSug&query=%E6%88%91%E7%9A%84%E4%B8%AD%E5%9B%BD%E5%BF%83' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

关键词: Access-Control-Allow-Origin

出现这个就是访问受限,出现同源策略的拦截问题

4.2.4 ajax跨域(跨源)方案之CORS

​ CORS是一个W3C标准,全称是"跨域资源共享",它允许浏览器向跨源的后端服务器发出ajax请求,从而克服了ajax只能同源使用的限制

​ 实现CORS主要依靠后端服务器中响应数据中设置响应头信息返回的

django的视图:

def post(request):
	ret = HttpResponse('ok')
    ret['Access-Control-Allow-Origin'] = '*' # 允许所有源请求
    return ret
// 在响应行信息里面设置以下内容:
Access-Control-Allow-Origin: ajax所在的域名地址

Access-Control-Allow-Origin: www.oldboy.cn  # 表示只允许www.oldboy.cn域名的客户端的ajax跨域访问

// * 表示任意源,表示允许任意源下的客户端的ajax都可以访问当前服务端信息
Access-Control-Allow-Origin: *

总结:

0. 同源策略:浏览器的一种保护用户数据的一种安全机制。
   浏览器会限制脚本语法不能跨源访问其他源的数据地址。
   同源:判断两个通信的地址之间,是否协议,域名[IP],端口一致。
   
   ajax:  http://127.0.0.1/index.html
   api数据接口:  http://localhost/index
   
   这两个是同源么?不是同源的。是否同源的判断依据不会根据电脑来判断,而是通过协议、域名、端口的字符串是否来判断。
   
1. ajax默认情况下会受到同源策略的影响,一旦受到影响会报错误如下:
	 No 'Access-Control-Allow-Origin' header is present on the requested resource

2. 解决ajax只能同源访问数据接口的方式:
   1. 在服务端的响应行中设置:
      Access-Control-Allow-Origin: 允许访问的域名地址
   2. jsonp
   3. 是否服务端代理
      思路:通过python来请求对应的服务器接口,获取到数据以后,

五.组件化开发

5.1组件[component]

​ 组件(Component)是自定义封装的功能,在前端开发过程中,经常出现多个网页的功能是重复的,而且很多不同网站之间也存在相同的功能。

​ 在网页中实现一个功能,需要使用html定义功能的内容结构,使用css声明功能的外观样式,还要使用js来定义功能的特效,因此就产生了把一个功能相关的【HTML,css和javascript】代码封装在一起组成一个整体的代码块封装模式,我们称之为组件。

​ 组件就是一个html网页中的功能,一般就是一个标签,标签中有自己的html内容结构,css样式和js特效,这样前端人员就可以在开发的时候,只需要书写一次代码,随处引入即可使用。

​ 我们在进行vue开发的时候,还记得我们自己创建的vm对象吗,这个vm对象我们称为一个大组件,根组件(页面上叫Root),在一个网页的开发中,根据网页上的功能区域我们又可以细分成其他组件,或称为子组件

组件有两种:默认组件(全局组件)和单文件组件(局部组件)

5.1.1局部组件

三步:声子,用子,挂子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <div class="vheader">   <!--代码要放在标签里  -->
        这是头部{{msg}}
    </div>
    <App></App>
</div>
</body>
<script src="vue.js"></script>
<script>

    // 1. 声明子组件
    let App = {
        data(){
            return{ //组件中必须写成函数形式
                appmsg:'这是app组件!',
            }
        },
        template:`
            <div class="content">
                {{appmsg}}
            </div>
        `
    }
    let vm = new Vue({
        el:'#app',
        data(){
            return{
                msg:'hello',
            }
        },
        components:{
            App, // 2.挂载子组件
        }
    })
</script>
</html>

5.1.2默认组件(全局组件)

局部组件使用时需要挂载,全局组件使用时不需要挂载,局部组件就在某个局部使用,全局组件是公用的,或者说每个页面都有这么一个功能的时候,在哪里都可能会用到

<div id="app">
    <addnum></addnum>
</div>
<script src="vue.js"></script>
<script>
	Vue.component("addnum",{
        data(){
            return{
                num: 1,
            }
        },
        template:`
			<div>
    			<input type="text" v-model="num">
				<button @click="num+=1">点击</button>
    		</div>
		`
    });
    var vm = new Vue({
        el:'#app',
        data(){
            return{
                
            }
        },
    })
    
</script>

5.2 组件传值

通过prop属性进行传值

5.2.1 父组件往子组件传值

两步:

​ 1.在子组件中使用props属性声明,然后可以直接在子组件中任意使用

​ 2.父组件要定义自定义的属性

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <App></App>
</div>
</body>
<script src="vue.js"></script>
<script>
    let Naver = {
        data(){
            return{
                navMsg:'这是顶部导航栏'
            }
        },
        template:`
            <div class="naver">
                <h1>{{navMsg}}</h1>
                <h2>222</h2>
                <h2>{{xx}}</h2>
            </div>>
        `,
        props:['xx',]
    }
    
    let App = {
        data(){
            return{
                Msg:'这是App组件',
                num:100
            }
        },
        template:`
            <div class="nav">
                <h1 style="color:blue;">{{Msg}}--{{num}}</h1>
<!--                <Naver xx="xiaobei"></Naver>  静态传值   -->        
<!--                <Naver :xx="num"></Naver>     动态传值-->
                <Naver :xx="num"></Naver>
            </div>
        `,
        components:{
            Naver,
        }
    }

    let vm = new Vue({
        el:'#app',
        data(){
            return{

            }
        },
        components:{
            App,
        }
    })
</script>
</html>
使用父组件传递数据给子组件时,注意以下几点:
1.传递数据是变量,则需要在属性左边添加冒号
  传递数据是变量,这种数据称之为 <font color='red'>'动态数据传递'</font>
  传递数据不是变量,这种数据称之为 <font color='red'>静态数据传递'</font>
2.父组件中修改了数据,在子组件中会被同步修改,但是,子组件中的数据修改了是不会影响到父组件中的数据的,这种情况在开发时被称为 '单向数据流'

5.2.2子组件往父组件传值

两步:
	a.子组件中使用this.$emit('fatherHandler',val); fatherHandler是父组件内中使用子组件的地方添加的绑定自定义事件,注意如果fatherHandler报错了,可能是vue版本不支持自定义键名称fatherHandler中有大写字母,所以我们改成father-handler或者直接就全部小写就可以了
<Vheader @fatherHandler="appFatherHandler"></Vheader>
	b.父组件中的methods中写一个自定义的时间函数:appFatherHandler(val){},在函数里面使用这个val,这个val就是上面子组件传过来的数据

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <h1>你好</h1>
    <App></App>
</div>
</body>
<script src="vue.js"></script>
<script>

    let Nav = {
        data(){
            return{
                msg:'这是子组件导航栏',
                num:80,
            }
        },
        template:`
            <div class="nav">
                <h1>{{msg}}</h1>
                <button @click="chufa">走你</button>
            </div>
        `,
        methods:{
            chufa(){
                console.log(this)
                this.$emit('fatherHandler',this.num);
            }
        }
    };

    let App = {
        data(){
            return{
                msg:'这是App组件',
                num:100,
                xx:'',
            }
        },
        template:`
            <div class="app">
                <h1 style="color:blue;">{{msg}}------子组件的num为:{{xx}}</h1>
                <Nav @fatherHandler="fuckSon"></Nav>
            </div>
        `,
        components:{
            Nav,
        },
        methods:{
            fuckSon(val){
                this.xx = val;
            }
        }
    };

    let vm = new Vue({
        el:'#app',
        data(){
            return{

            }
        },
        components:{
            App,
        }
    })
</script>
</html>

5.2.3 平行组件传值

平行组件:两个或多个子组件同时挂载到一个父组件上

代码(声明两个全局组件T1和T2),将T1组件数据传递给T2组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <App></App>
</div>
</body>
<script src="vue.js"></script>
<script>

    let bus = new Vue();

    Vue.component('T1',{
        data(){
            return{
                t1Msg:'我是t1组件',
                t1Num:120,
            }
        },
        template:`
            <div class="t1">
                <h3>{{t1Msg}}-----{{t1Num}}</h3>
                <button @click="zouni">走你</button>
            </div>
        `,
        methods:{
            zouni(){
                bus.$emit('kkk',this.t1Num);
            }
        }
    });

    Vue.component('T2',{
        data(){
            return{
                t2Msg:'我是t2组件',
                t2Num:130,
                t1Msg:'',
            }
        },
        template:`
            <div class="t2">
                <h3>{{t2Msg}}</h3>
                <h3>t1组件传递过来的数据:{{t1Msg}}</h3>
            </div>
        `,
        created(){
            bus.$on('kkk',(val)=>{
                console.log(this);
                this.t1Msg = val;
            })
        },
    });

    let App = {
        data(){
            return{
                Msg:'这是App组件',
                num:100,
                xx:'',
            }
        },
        template:`
            <div class="nav">
                <T1></T1>
                <T2></T2>
            </div>
        `,
    };

    let vm = new Vue({
        el:'#app',
        data(){
            return{

            }
        },
        components:{
            App,
        }
    });
    console.log(vm)
</script>
</html>

6. vut-router的使用

6.1 介绍

	vue就是我们前面学习的vue基础,vue + vue-router主要用来做SPA(Single Page Application),单页面应用,为什么要使用单页面应用呢?因为传统的路由跳转,如果后端资源过多,会导致页面出现'白屏现象',所以我们希望让前端来做路由,在某个生命周期的钩子函数中,发送ajax来请求数据,进行数据驱动,比如之前我们使用django的MTV模式,我们是将后端的数据全部渲染给了模板,然后模板再发送给前端进行浏览器页面的渲染,一下将所有的数据都给了页面,而我们现在使用vue,我们可以在组件的钩子函数中发送对应的ajax请求去获取对应的数据,而不是一下子把数据都放到页面上了,单页面应用给我们提供了很多的便利。django模板渲染做大型应用的时候,页面很复杂,数据很大的页面时,是不太适合的,使用vue的网站如:**掘金**

什么是单页面应用:

	例如使用vue的网站的导航栏标签,每点击一个标签都会发送一个请求,也就是说每个标签里面肯定有url,但是对于前后端分离的项目来讲,这些标签的url不再是向django一样通过django的模板语言来渲染了,这些路由是由前端来写的,点击一个标签获得一些组件,这些组件里面发送ajax请求去后端获取数据,这个路由分发交给前端来写了,这就是单页面应用,当然ajax请求数据的节后还是后端写好的数据接口。

vue + vue-router就是完成单页面应用的,vue-router(路由)是vue的核心插件

vue-router: 文档地址: https://router.vuejs.org/zh/

下载vue-router的cnd链接地址:https://unpkg.com/vue-router/dist/vue-router.js

官网简单操作:

// 1. 如果使用模块化机制编程, 导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 2. 定义(路由)组件
// 下面两个组件可以从其他文件import进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 3. 定义路由
// 每个路由应该映射一个组件,其中 component 可以是通过Vue.extend()创建的组件构造器,或者只是一个组件配置对象.还可以有嵌套路由
const routes = [
	{ path: '/foo',component: Foo},
	{ path: '/bar', component: Bar }
]

// 4.创建router实例,然后`routes`配置
const router = new VueRouter({
	routes // (缩写)相当于routes:routes
})

// 5.创建和挂载跟实例
// 记得要通过router配置参数注入路由从而让整个应用都有路由功能
const app = new Vue({
	router
})

6.2 简单示例

示例:通过不同的路径访问到不同的组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <h1>你好</h1>
    <App></App>
</div>

</body>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
<script>

    let Home = {
        data(){
            return {
                msg:'这是home页面'
            }
        },
        template:`
        <div class="home">
            <h1>{{msg}}</h1>
        </div>
        `
    };

     let Course = {
        data(){
            return {
                msg:'这是Course页面'
            }
        },
        template:`
        <div class="course">
            <h1>{{msg}}</h1>

        </div>
        `
    };

    let App = {
        data(){
            return {

            }
        },
        template:
         `
          <div class="nav">
                <router-link to="/home">首页</router-link>
                <router-link to="/course">课程页</router-link>

                <router-view></router-view>
        </div>
        `,
    };

    const routes = [
        {path:'/home', component:Home},
        {path:'/course', component:Course},
    ];

    let router = new VueRouter({
        routes,
    });

    let  vm = new Vue({
        el:'#app',
        router,
        data(){
            return {

            }
        },
        // 2 挂载子组件  挂子
        components:{
            // 挂载组件的简写形式
            App,
            // Naver,

        }
    });
    console.log(vm);
</script>
</html>

7. Vue自动化工具(Vue-cli)

​ 前面学习了普通组件后,接下来我们继续学习单文件组件需要提前安装准备一些组件开发工具,否则无法使用和学习单文件组件

​ 一般情况下,我们在运行自动化工具vue-cli时,可以帮我们编译单文件组件,需要我们现在系统中搭建vue-cli工具

官网:https://cli.vuejs.org/zh/

Vue CLI 需要Node.js8.9或更高版本(推荐8.11.0+),你可以使用nvmnvm-windows

7.1 nvm

由于node.js的版本一直处于不断更新中,所以我们需要一个版本管理器来更好的使用node.js
nvm是一个开源的node版本管理器,通过它,你可以下载任意版本的node.js,还可以在不同版本之间切换使用

注意:安装nvm之前,要确保当前机器中不存在任何版本的node,如果有,则卸载掉

nvm工具的下载和安装:

https://www.jianshu.com/p/d0e0935b150a

https://www.jianshu.com/p/622ad36ee020

安装记录:

https://github.com/coreybutler/nvm-windows/releases

7.2 node

使用nvm的相关命令安装node

# 查看官方提供的可安装node版本
nvm ls-remote

# 安装执行版本的node, 例如: nvm install v10.15.2
nvm install <version>

# 卸载node版本, 例如: nvm uninstall v10.15.2
nvm uninstall <version>

# 查看已安装的node列表
nvm ls

# 切换node版本, 例如: nvm use v10.15.2
nvm use <version>

# 设置默认版本,如果没有设置,则开机是默认node是没有启动的
nvm alias default v10.15.2

# 查看当前使用的版本
nvm current

关于nvm和npm切换国内源:https://blog.csdn.net/qq_14815199/article/details/104610163?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160360743819724813229561%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160360743819724813229561&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2

7.3 npm

npm(node package manager)是nodejs的包管理工具,用于node插件管理(包括安装,卸载,管理依赖等),安装node以后就自动安装了npm(不一定是最新版本)

npm --version					# 查看当前npm版本
npm install -g 包名              # 安装模块   -g表示全局安装,如果没有-g,则表示在当前项目安装
npm list                        # 查看当前目录下已安装的node包
npm view 包名 engines            # 查看包所依赖的Node的版本 
npm outdated                    # 检查包是否已经过时,命令会列出所有已过时的包
npm update 包名                  # 更新node包
npm uninstall 包名               # 卸载node包
npm 命令 -h    

官方:https://www.npmjs.com

文档:https://www.npmjs.com.cn/

7.4安装Vue-cli

npm install -g vue-cli
npm install -g vue-cli --registry https://registry.npm.taobao.org

如果安装速度过慢,一直超时,可以考虑切换npm镜像源 http://npm.taobao.org/

指令:

# 1. 临时使用
npm install jquery --registry https://registry.npm.taobao.org
    
# 2. 可以把这个选项配置到文件中,这样不用每一次都写
npm config set registry https://registry.npm.taobao.org

# 3. 验证是否配置成功
npm config list 或者 npm config get registry 

# 4. 在任意目录下都可执行, --global是全局安装,不可省略
npm install --global cnpm 或者 npm install -g cnpm --registry=https://registry.npm.taobao.org
 
# 5. 安装后之后使用
cnpm install jquery

npm和cnpm介绍

参考博客:https://blog.csdn.net/jack_bob/article/details/80644376

小结:

安装vue-cli
nvm是node.js的版本管理工具
安装node.js,自带npm
通过npm安装vue-cli ,他运行时需要依赖node.js的环境

7.5 使用vue-cli初始化创建项目

7.5.1 生成项目目录

使用vue自动化工具可以快速搭建单页应用项目目录

该工具为现代化的前端开发工作同乐开箱即用的构建配置,只需几分钟即可创建并启动一个带热重载,保存时静态检查以及可用于生产环境的构建配置的项目

// 生成一个基于 webpack模板的新项目
vue init webpack 项目名

// 启动开发服务器 ctrl+c 停止服务
cd 项目名
npm run dev # 运行这个命令就可以启动node提供的测试http服务器

那么访问一下命令执行完成后提示出来的网址就可以看到网站了:http://localhost:8080/

7.5.2 项目目录结构

  • src 主开发目录,要开开发的单文件组件全部在这个目录下的components目录下
  • static 静态资源目录,所有的css,js,图片资源等文件都放在这个文件夹
  • dist 项目打包发布文件夹,最后要上限的单文件项目文件都在这个文件夹中(后面打包项目,让项目中的vue组件经过编译编程js代码以后,dist就出现了)
  • node_modules 是node的包目录
  • config 是配置目录
  • bulid 项目打包时依赖的目录
  • src/router 路由,后面需要我们在使用Router路由的时候,自己声明

7.5.3 项目执行流程图

整个项目是一个主文件index.html,index.html会引入src文件夹中的main.js,
main.js会引入单文件组件App.vue,App.vue会通过组件嵌套或者路由来引用components文件夹中的其他单文件组件

8. 单文件组件的使用

组件有两种: 普通组件, 单文件组件

普通组件的缺点:

  • html代码是作为js的字符串进行编写的,所以组装和开发的时候不易理解,而且没有高亮效果
  • 普通组件在小项目中非常合适,但是在复杂的大项目中,如果把更多的组件放到html文件中,那么维护成本就会变得非常昂贵
  • 普通组件只是整合了js和html,但是css代码被剥离出去了,使用的时候不好处理

单文件组件:

​ 将一个组件相关的html结构,css样式,以及交互的JavaScript代码从html文件中剥离出来,合成一个文件,相当于一个组件具有了结构,变现和行为的完整功能,方便组件之间随意组合以及组件的重用,这种文件的扩展名为".vue"

8.1 创建组件

在组件中编辑三个标签,编写视图、vm对象和css样式代码。

template 编写html代码的地方

<template>
  <div id="Home">
    <span @click="num--" class="sub">-</span>
    <input type="text" size="1" v-model="num">
    <span @click="num++" class="add">+</span>
  </div>
</template>

script编写vue.js代码

<script>
  export default {
    name:"Home",
    data: function(){
      return {
        num:0,
      }
    }
  }
</script>

style编写当前组件的样式代码

<style scoped>
  .sub,.add{
    border: 1px solid red;
    padding: 4px 7px;
  }
</style>

9. 在组件中使用axios获取数据

默认情况下,我们的项目中并没有对axios包的支持,所以需要下载

在项目根目录下使用npm安装包

npm install axios

接着在main.js文件中,导入axios并把axios对象挂载到vue属性中多为一个子对象,这样我们才能在组件中使用

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App' // 这里表示从别的目录下导入 单文件组件
import axios from 'axios'; // 从node_modules目录中导入包
Vue.config.productionTip = false

Vue.prototype.$axios = axios; // 把对象挂载vue中

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
});

9.1 在组件中使用axios获取数据

<template>
    <div id="Header" class="header">
      {{message}}
      <p>num: <input type="text" v-model="num"></p>
      <p>深圳的天气情况: {{weather_info}}</p>
    </div>
</template>

<script>
  export default {
    name:"Header", // 组件名称,用于以后路由跳转
    props:["num"],
    data(){      // 当前组件中需要使用的数据
      return {
        message:"页面头部",
        weather_info:"",
      }
    },
    // 钩子方法,在页面中vue挂在data数据以后,自动执行
    created() {
      // 使用axios发送请求获取数据
      this.$http.get("http://wthrcdn.etouch.cn/weather_mini?city=北京").then(response=>{
        console.log(response.data);
        console.log(response.data.data.ganmao);
        this.weather_info = response.data.data.ganmao;
      }).catch(error=>{

      });
    }

  }

</script>

// scoped 表示当前style的样式只作用于当前组件的template代码中,其他地方不会被影响
<style scoped>
  .header{
    height: 100px;
    line-height: 100px;
    background-color: #eee;
    text-align: center;
  }
</style>

注意:
使用的时候,因为本质上说,我们还是用的原来的ajax,所以也会受到同源策略的影响,后面开发项目的时候,可以使用cors来解决跨域的问题

路飞学成项目前端

笔记

1.项目分析

首页
	导航栏,登录注册栏,轮播图,底部导航栏
登录注册
	选项卡
免费课
	课程分类,筛选,课程列表
免费课详情
	课程封面视频,优惠活动倒计时,选项卡
我的购物车
	全选,商品价格统计
购买结算

购买成功

我的订单

课程\课时播放页面

2.项目搭建

2.1创建项目目录

例如我们要把项目保存在桌面下 ~/Desktop/luffy

cd Destop
vue init webpack luffycity

还可以使用git管理项目

给git仓库配置用户

git小结:

git指令
1 创建git本地仓库
	创建文件夹,cd到文件夹中,执行
	git init
	
2 git status 查看仓库状态

3 git add 文件名称(.表示所有文件和目录)

commit之前必须配置用户
git config --global user.name chao
git config --global user.email 1069696250@qq.com

4 git commit -m '描述'  生成版本

根据需要在生成项目时,选择对应的选项

接下来,根据终端上效果显示的对应地址来访问项目(如果有多个vue项目在运行,8080端口被占据了,服务器会自动改端口,所以根据自己实际在操作中看的地址来访问)

2.2 初始化项目

main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

清除默认的HelloWord.vue组件和APP.vue中的默认模板代码和默认样式

如下效果:

<template>
  <div id="app">

    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',

}
</script>

<style>
  body{
    margin: 0;
    padding: 0;
  }

</style>

在settings中的plugins下载一下vue插件,才能看到相关vue的效果

pycharm运行脚本的配置

在 edit configurations中

设置好之后就可以在终端运行项目了

npm run dev

2.3 安装路由vue-router

如果是在终端的项目目录下创建的项目,里面直接就有选择是否下载 vue-router,选择是就不需要自己手动下载了,可略过以下步骤

vue init webpack 项目名

2.3.1 下载路由组件

npm i vue-router -S # -S就是给当前项目的环境添加vue-router包

2.3.2 配置路由

2.3.2.1 初始化路由对象

在src目录下创建router路由目录,在router目录下创建index.js路由文件

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Home from "../components/Home";

export default new Router({
  mode:'history',
  routes: [
    {
      path: '/home/',
      // name: 'HelloWorld',  路由别名  url('',name='')
      component: Home,
    }
  ]
})

打开main.文件,把router路由规则对象注册到vue中

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router/index'  1、引入,这样引入文件那么会直接拿到这个文件中export default后面的对象,export default的意思是将对象暴露出去,让别人可以引入
import router from '@/router/index'   @符号表示的是src的目录路径,这样写起来更靠谱一些,因为引入src目录里面的内容的时候就不需要知道当前文件和src目录里面的文件隔着基层了
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,  2、注册
  components: { App },
  template: '<App/>'
});
2.3.2.3 在视图中显示路由对应的内容

在App.vue组件中,添加显示路由对应的内容

<template>
  <div id="app">
    <router-view/>  <!-- 在组件中使用子组件的写法 -->
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {

  }
}
</script>
<style>
</style>

Home.vue组件初始化

<template>
  <div class="home">
    <Header></Header>
    <h1>这是home页面</h1>

  </div>
</template>

<script>
    import Header from './common/Header'
    export default {
        name: "Home",
        components:{
          Header,
        }
    }
</script>

<style scoped>

</style>

Header顶部导航栏组件初始化

<template>
    <div class="header">
      这是顶部导航栏
    </div>
</template>

<script>
    export default {
        name: "Header"
    }
</script>

<style scoped>

</style>

3. 引入ElementUI

​ 对于前段页面布局,我们可以使用一些开源的UI框架来配合开发,vue开发前端项目中,比较常用的就是ElementUI了

​ ElementUI是饿了么团队开发的一个UI组件框架,这个框架提前帮我们提供了很多已经写好的通用模块,我们可以在vue项目中引来使用,这个框架的使用类似于bootstrap框架,直接把官方文档中的组件代码拿来就可以用,有定制性的内容,可以直接通过样式进行覆盖修改就可以了

中文官网:http://element-cn.eleme.io/#/zh-CN

文档快速入门:http://element-cn.eleme.io/#/zh-CN/component/quickstart

3.1 快速安装ElementUI

项目根目录下执行命令:

npm i element-ui -S --registry https://registry.npm.taobao.org

上面的命令等同于npm install element-ui --save

3.2 配置ElementUI到项目中

在main.js中导入ElementUI,并调用

// elementUI 导入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';  // 需要import引入一下css文件,和我们的link标签引入是一个效果,而import .. from ..是配合export default来使用的
// 调用插件
Vue.use(ElementUI);

成功引入ElementUI以后,就可以进行前端页面的开发

4. 首页

首页采用了上下页面布局,首先是导航栏,轮播图,底部导航栏等几个小模块,所以可以把首页作为一个足迹开发,然后把首页的这些小模块作为单独的组件进行开发

4.1创建首页组件

在src/components目录下创建文件Home.vue

代码:

<template>
  <div id="home">
    首页
  </div>
</template>

<script>
export default {
  name:"Home",
  data(){
    return {

    }
  }
}
</script>

<style scoped>
</style>

4.1.1创建首页对应的路由

在router/index.js中引入Home组件,并设置Home组件作为首页路由

import Vue from "vue"
import router from "vue-router"  //注意这里起名必须叫做router,因为vue是有命名规范的

// 后面这里引入可以被用户访问的页面组件
import Home from "../components/Home"

Vue.use(router);

export default new Router({
  // 路由跳转模式,注意使用 history
  mode: "history",

  // 路由规则
  routes:[
    {
      // name:"路由别名",
      name:"Home",
      // path: "路由地址",
      path: "/",
      // component: 组件类名,
      component: Home,
    },{
      // name:"路由别名",
      name:"Home",
      // path: "路由地址",
      path: "/home",
      // component: 组件类名,
      component: Home,
    },
  ]
})

4.2 开发导航栏子组件

​ 经过前面的观察,可以发现导航栏不进在首页出现,其他页面也有,所以对于这些不同页面中公共的内容,可以创建一个单独的组件目录存放

创建src/components/common/Header.vue

<template>

</template>

<script>
  export default {
    name: "Header",
    data(){
      return {
        
      };
    }
  }
</script>

<style scoped>

</style>

4.2.1 在首页引入导航栏组件

<template>
  <div class="home">
    <Header/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  export default {
    name: "Home",
    data(){
      return {

      };
    },
    components:{
      Header,
    }
  }
</script>

<style scoped>

</style>

接下来我们就可以在组件中参考ElementUI文档进行样式开发了

App.vue中设置一些公共样式的代码

<style>
  body{
    padding: 0;
    margin:0;
  }
  a{
    text-decoration: none;
    color: #4a4a4a;
  }
  a:hover{
    color: #000;
  }
  .header .el-menu li .el-submenu__title{
    height: 26px!important;
    line-height: 26px!important;
  }
  .el-menu--popup{
    min-width: 140px;
  }
</style>

4.2.2 新版的导航栏子组件Header

<template>
  <div class="total-header">
    <div class="header">
    <el-container>
      <el-header height="80px" class="header-cont">
        <el-row>
          <el-col class="logo" :span="3">
            <a href="/">
              <img src="@/assets/head-logo.svg" alt="">

            </a>
          </el-col>
          <el-col class="nav" :span="10">
            <el-row>
                <el-col :span="3"><router-link to="/" class="active">免费课</router-link></el-col>
                <el-col :span="3"><router-link to="/">轻课</router-link></el-col>
                <el-col :span="3"><router-link to="/">学位课</router-link></el-col>
                <el-col :span="3"><router-link to="/">题库</router-link></el-col>
                <el-col :span="3"><router-link to="/">教育</router-link></el-col>
              </el-row>

          </el-col>
          <el-col :span="11" class="header-right-box">
            <div class="search">
              <input type="text" id="Input" placeholder="请输入想搜索的课程" style="" @blur="inputShowHandler" ref="Input" v-show="!s_status">
              <ul @click="ulShowHandler" v-show="s_status" class="search-ul">
                <span>Python</span>
                <span>Linux</span>
              </ul>
              <p>
                <img class="icon" src="@/assets/sousuo1.png" alt="" v-show="s_status">
                <img class="icon" src="@/assets/sousuo2.png" alt="" v-show="!s_status">
                <img class="new" src="@/assets/new.png" alt="">
              </p>
            </div>
            <div class="register" v-show="!token">
              <router-link to="/login"><button class="signin">登录</button></router-link>
              &nbsp;&nbsp;|&nbsp;&nbsp;
              <a target="_blank" href="https://www.luffycity.com/signup">
                <router-link to="/register"><button class="signup">注册</button></router-link>

              </a>
            </div>
            <div class="shop-car" v-show="token">
              <router-link to="/cart">
                <b>6</b>
                <img src="@/assets/shopcart.png" alt="">
                <span>购物车 </span>
              </router-link>
            </div>
            <div class="nav-right-box" v-show="token">
                <div class="nav-right">
                  <router-link to="/myclass">
                    <div class="nav-study">我的教室</div>
                  </router-link>
                  <div class="nav-img" @mouseover="personInfoList" @mouseout="personInfoOut">
                    <img src="@/assets/touxiang.png" alt="" style="border: 1px solid rgb(243, 243, 243);" >
                    <ul class="home-my-account" v-show="list_status" @mouseover="personInfoList">
                  <li>
                    我的账户
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>
                  <li>
                    我的订单
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>
                  <li>
                    贝里小卖铺
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>
                  <li>
                    我的优惠券
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>
                  <li>
                    <span>
                      我的消息
                      <b>(26)</b>
                    </span>
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>
                  <li>
                    退出
                    <img src="https://hcdn1.luffycity.com/static/frontend/activity/back_1568185800.821227.svg" alt="">
                  </li>

                </ul>
                  </div>

                </div>

              </div>


          </el-col>
        </el-row>

      </el-header>


    </el-container>

  </div>
  </div>

</template>

<script>
    export default {
      name: "Header",
      data(){
        return {
          // 设置一个登录状态的标记,因为登录注册部分在登录之后会发生变化
          token:true,
          s_status:true,
          list_status:false,
        }
      },
      methods:{
        ulShowHandler(){
          this.s_status = false;
          console.log(this.$refs.Input);

          // this.$refs.Input.focus();
          this.$nextTick(()=>{  //延迟回调方法,Vue中DOM更新是异步的,也就是说让Vue去显示我们的input标签的操作是异步的,如果我们直接执行this.$refs.Input.focus();是不行的,因为异步的去显示input标签的操作可能还没有完成,所有我们需要等它完成之后在进行DOM的操作,需要借助延迟回调对DOM进行操作,这是等这次操作对应的所有Vue中DOM的更新完成之后,在进行nextTick的操作。
            this.$refs.Input.focus();
          })

        },
        inputShowHandler(){
          console.log('xxxxx')
          this.s_status = true;
        },
        personInfoList(){
          this.list_status = true;
        },
        personInfoOut(){
          this.list_status = false;
        }
      }
    }



</script>

<style scoped>
  .header-cont .nav .active{
    color: #f5a623;
    font-weight: 500;
    border-bottom: 2px solid #f5a623;
  }
  .total-header{
    min-width: 1200px;
    z-index: 100;
    box-shadow: 0 4px 8px 0 hsla(0,0%,59%,.1);
  }
  .header{
    width: 1200px;
    margin: 0 auto;
  }
  .header .el-header{
    padding: 0;
  }
  .logo{
    height: 80px;
    /*line-height: 80px;*/
    /*text-align: center;*/
    display: flex; /* css3里面的弹性布局,高度设定好之后,设置这个属性就能让里面的内容居中 */
    align-items: center;
  }
  .nav .el-row .el-col{
    height: 80px;
    line-height: 80px;
    text-align: center;

  }
  .nav a{
    font-size: 15px;
    font-weight: 400;
    cursor: pointer;
    color: #4a4a4a;
    text-decoration: none;
  }
  .nav .el-row .el-col a:hover{
    border-bottom: 2px solid #f5a623
  }

  .header-cont{
    position: relative;
  }
  .search input{
    width: 185px;
    height: 26px;
    font-size: 14px;
    color: #4a4a4a;
    border: none;
    border-bottom: 1px solid #ffc210;

    outline: none;
  }
  .search ul{
    width: 185px;
    height: 26px;
    display: flex;
    align-items: center;
    padding: 0;

    padding-bottom: 3px;
    border-bottom: 1px solid hsla(0,0%,59%,.25);
    cursor: text;
    margin: 0;
    font-family: Helvetica Neue,Helvetica,Microsoft YaHei,Arial,sans-serif;
  }
  .search .search-ul,.search #Input{
    padding-top:10px;
  }
  .search ul span {
    color: #545c63;
    font-size: 12px;
    padding: 3px 12px;
    background: #eeeeef;
    cursor: pointer;
    margin-right: 3px;
    border-radius: 11px;
  }
  .hide{
    display: none;
  }
  .search{
    height: auto;
    display: flex;
  }
  .search p{
    position: relative;
    margin-right: 20px;
    margin-left: 4px;
  }

  .search p .icon{
    width: 16px;
    height: 16px;
    cursor: pointer;
  }
  .search p .new{
    width: 18px;
    height: 10px;
    position: absolute;
    left: 15px;
    top: 0;
  }
  .register{
    height: 36px;
    display: flex;
    align-items: center;
    line-height: 36px;
  }
  .register .signin,.register .signup{
    font-size: 14px;
    color: #5e5e5e;
    white-space: nowrap;
  }
  .register button{
    outline: none;
    cursor: pointer;
    border: none;
    background: transparent;
  }
  .register a{
    color: #000;
    outline: none;
  }
  .header-right-box{
    height: 100%;
    display: flex;
    align-items: center;
    font-size: 15px;
    color: #4a4a4a;
    position: absolute;
    right: 0;
    top: 0;
  }
  .shop-car{
    width: 99px;
    height: 28px;
    border-radius: 15px;
    margin-right: 20px;
    background: #f7f7f7;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    cursor: pointer;
  }
  .shop-car b{
    position: absolute;
    left: 28px;
    top: -1px;
    width: 18px;
    height: 16px;
    color: #fff;
    font-size: 12px;
    font-weight: 350;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    background: #ff0826;
    overflow: hidden;
    transform: scale(.8);
  }
  .shop-car img{
    width: 20px;
    height: 20px;
    margin-right: 7px;
  }

  .nav-right-box{
    position: relative;
  }
  .nav-right-box .nav-right{
    float: right;
    display: flex;
    height: 100%;
    line-height: 60px;
    position: relative;
  }
  .nav-right .nav-study{
    font-size: 15px;
    font-weight: 300;
    color: #5e5e5e;
    margin-right: 20px;
    cursor: pointer;

  }
  .nav-right .nav-study:hover{
    color:#000;
  }
  .nav-img img{
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-block;
    cursor: pointer;
  }
  .home-my-account{
    position: absolute;
    right: 0;
    top: 60px;
    z-index: 101;
    width: 190px;
    height: auto;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 4px 8px 0 #d0d0d0;
  }
  li{
    list-style: none;
  }
  .home-my-account li{
    height: 40px;
    font-size: 14px;
    font-weight: 300;
    color: #5e5e5e;
    padding-left: 20px;
    padding-right: 20px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    box-sizing: border-box;
  }
  .home-my-account li img{
    cursor: pointer;
    width: 5px;
    height: 10px;
  }
  .home-my-account li span{
    height: 40px;
    display: flex;
    align-items: center;
  }
  .home-my-account li span b{
    font-weight: 300;
    margin-top: -2px;
  }
</style>

4.3 开发轮播图子组件

4.3.1 创建Banner.vue组件文件

代码:

<template>
  <div class="banner">

  </div>
</template>

<script>
  export default {
    name:"Banner",
    data(){
      return {};
    }
  }
</script>

<style scoped>

</style>

4.3.2 在Home组件引入Banner子组件

<template>
  <div class="home">
    <Header/>
    <Banner/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Banner from "./common/Banner"
  export default{
    name:"Home",
    data(){
      return {};
    },
    components:{
      Header,
      Banner,
    }
  }
</script>

<style scoped>
.home{
  padding-top: 80px;
}
</style>

接下来,在ElementUI中油对应的轮播图[跑马灯]效果,可以直接提取过来使用

注意:图片保存在assets目录下,等同于保存在static/img目录下

对于图片的使用,如果是vue代码直接要使用的图片,可以保存在assets目录下,如果是第三方插件要使用到的图片,需要保存在static目录下,其实本质上说,所有的图片都是保存在static目录下的,而assets目录下的内容,最终被vue解析成地址的时候,也是在static目录下的。

4.3.3 新版的轮播图组件

<template>
<!--    <h1>轮播图组件</h1>-->
  <el-carousel indicator-position="outside" height="400px">
    <el-carousel-item v-for="(value,index) in banner_list" :key="value.id">

      <a :href="value.link" style="display: inline-block;height: 400px;width: 100%">
        <img :src="value.img_src" alt="" width="100%" height="400px">
      </a>
<!--      <img src="@/assets/banner/banner1.png" alt="">-->
    </el-carousel-item>
  </el-carousel>
</template>

<script>
    //router-link主要用于站内页面跳转,使用的是相对路径
    //a标签用于外部链接页面跳转
  
    export default {
        name: "Banner",
      data(){
          return {
            banner_list:[

              // {id:1,img_src:require('@/assets/banner/banner1.png'),link:'http://www.baidu.com'},
              // {id:2,img_src:'@/assets/banner/banner2.png',link:'http://www.baidu.com'},
              // {id:3,img_src:'@/assets/banner/banner3.png',link:'http://www.baidu.com'},
            //数据属性中写图片路径的方式
              // 方式1:通过require映射成static路径下的图片路径
            //   {id:1,img_src:require('@/assets/banner/banner1.png'),link:'http://www.baidu.com'},
            //   {id:2,img_src:require('@/assets/banner/banner2.png'),link:'http://www.baidu.com'},
            //   {id:3,img_src:require('@/assets/banner/banner3.png'),link:'http://www.baidu.com'},

              //方式2:直接使用static目录
            {id:1,img_src:'../../../static/banner/banner1.png',link:'http://www.baidu.com'},
              {id:2,img_src:'../../../static/banner/banner2.png',link:'http://www.baidu.com'},
              {id:3,img_src:'../../../static/banner/banner3.png',link:'http://www.baidu.com'},

            ]
          }
      }
    }
</script>

<style scoped>

</style>

4.4 页面脚部

4.4.1 创建脚部组件文件

<template>
  <div class="footer">

  </div>
</template>

<script>
  export default {
    name:"Footer",
    data(){
      return {}
    }
  }
</script>

<style scoped>

</style>

4.4.2 在Home组件中引入Footer组件

<template>
  <div class="home">
    <Header/>
    <Banner/>
    <Footer/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Banner from "./common/Banner"
  import Footer from "./common/Footer"
  export default{
    name:"Home",
    data(){
      return {};
    },
    components:{
      Header,
      Banner,
      Footer,
    }
  }
</script>

<style scoped>
.home{
  padding-top: 80px;
}
</style>

4.4.3 新版底部组件

<template>
    <div class="footer">
      <div class="footer-item">
        <div class="foot-left">
        <div class="foot-content">
          <p>
            <span>关于我们</span>
            &nbsp;&nbsp;|&nbsp;&nbsp;
            <span>贝里小卖铺</span>
          </p>
          <p>
            地址:北京市昌平区顺沙路八号院汇德商厦402  邮箱:customer@luffycity.com
          </p>
          <p>
             © 2017-2020 北京路飞学城教育科技有限公司版权所有
            <a class="copyright" href="http://beian.miit.gov.cn" target="_blank" >京ICP备17072161号-1</a>
          </p>
          <p>
            <a class="report-link" target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010102002019&amp;token=1ee3034d-ba3a-4cc3-89d4-89875e2dd0b1">
              <img class="report-img" src="//img.alicdn.com/tfs/TB1..50QpXXXXX7XpXXXXXXXXXX-40-40.png" style="float:left" >
              京公网安备 11010102002019号
            </a>
          </p>
        </div>

      </div>
        <img src="@/assets/code.png" alt="" class="code">
      </div>
    </div>
</template>

<script>
    export default {
        name: "Foot"
    }
</script>

<style scoped>

  .footer{
    display: flex;
    align-items: center;
    width: 100%;
    height: 140px;
    flex-direction: column;
    overflow: hidden;
    background: #191c25;
  }
  .footer .foot-left{
    display: flex;
    align-items: center;
  }
  .footer .foot-left .foot-content p{
    margin-bottom: 10px;
    font-size: 13px;
    font-family: PingFangSC-Regular;
    font-weight: 400;
    color: #d0d0d0;
  }
  .footer-item{
    width: 960px;
    height: 100%;
    justify-content: space-between;
  }
  .foot-left{
    display: flex;
    align-items: center;
    float: left;
  }
  .foot-content a{
    color: #d0d0d0;
  }
  .footer .foot-left .foot-content p:first-child span{
    font-size: 16px;
    font-weight: 400;
    display: inline-block;
    font-family: PingFangSC-Regular;
    cursor: pointer;
  }
  .foot-content .report-img{
    display: inline-block;
    height: 20px;
    margin-right: 12px;
  }
  .footer-item .code{
    width: 74px;
    height: auto;
    margin-right: 100px;
    float: right;
    padding-top: 30px;
  }
</style>

5. 免费课程

在组件目录components下创建Courses.Vue组件,

<template>
  <div class="courses">

  </div>
</template>

<script>
  export default {
    name:"Courses",
    data(){
      return {

      }
    }
  }
</script>

<style scoped>
  
</style>

5.1 在router/index.js路由中注册路由

import Vue from "vue"
import Router from "vue-router"

// 导入可以被用户访问的组件
import Home from "@/components/Home"
import Courses from "@/components/Courses"

Vue.use(Router);

export default new Router({
  mode: "history",
  routes:[
    // 路由列表
    {
      path: "/",
      name: "Home",
      component:Home,
    },
    {
      path: "/home",
      name: "Home",
      component:Home,
    },
    {
      path: "/courses",
      name: "Courses",
      component:Courses,
    },

  ]
})
posted @ 2020-10-15 19:42  打酱油的阿超  阅读(93)  评论(0编辑  收藏  举报