JavaScript

1、什么是JavaScript

1.1、概述

JavaScript是一门世界上最流行的脚本语言,一个合格的后端人员,必须精通JavaScript

1.2、历史

https://blog.csdn.net/kese7952/article/details/79357868

ECMScript它可以理解为是一个JavaScript的标准,最新版本已到ES6版本~,但是大部分浏览器还只停留在支持ES5代码上!

开发环境—线上环境,版本不一致

2、快速入门

2.1、引入JavaScript

1、内部标签使用

1
2
3
<script>>
//......
</script>

2、外部引入

adc.js

1
//.......

test.html

1
<script src="abc.js"></script>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--script标签内,写JavaScript代码-->
<!--<script>-->
<!-- alert('hello,world');-->
<!--</script>-->
<!--外部引入-->
<!--注意:script必须成对出现-->
<script src="js/qj.js"></script>
<!--不用显示定义type,也默认就是JavaScript-->
<script type="text/javascript">

</script>
</head>
<body>
<!--这里也可以存放-->
</body>
</html>

2.2、基本语法入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--JavaScript严格区分大小写-->
<script>
// 1.定义变量 变量类型 变量名 = 变量值;
var score = 75;
// alert(num);
// 2.条件控制
if (score>60 && score<70){
alert("60~70");
}else if(score>70 && score<80){
alert("70~80");
}else {
alert("other");
}
// console.log() 在浏览器的控制台打印变量!System.out.println();
</script>
</head>
<body>

</body>
</html>

浏览器必备调试须知

image-20211027153207083

2.3、数据类型

数值、文本、图形、音频、视频。。。。

==变量==

1
var 王者荣耀 = "倔强青铜";

==number==js不区分小数和整数,number

1
2
3
4
5
6
123	   		//整数123
123.1 //浮点数123.1
1.123e3 //科学计数法
-99 //负数
NaN // not a number
Infinity //表示无限大

==字符串==

’abc‘、”abc“

==布尔值==

true、false

==逻辑运算==

1
2
3
&&		两个都为真,结果为真
|| 一个为真,结果为真
! 真即假,假即真

==比较运算符==

1
2
3
=
== 等于(类型不一样,值一样,也会判断为true)
=== 绝对等于(类型一样,值一样,结果为true)

这是一个JS的缺陷,坚持不要使用==比较

须知:

  • NaN===NaN,这个与所有的数值都不相等,包括自己
  • 只能通过isNaN(NaN)来判断这个数是否是NaN

浮点数问题

1
console.log((1/3) === (1-2/3))		//false

尽量避免使用浮点数进行运算,存在精度问题!!

1
Math.abs(1/3-(1-2/3))<0.0000001

==null和undefined==

  • null 空
  • undefined 未定义

==数组==

Java的数组必须是相同类型的对象~,JS中不需要这样!

1
2
3
//保证代码的可读性,尽量使用[]
var arr = [1,2,3,4,5,'hello',null,true]
new Array(1,5,6,13,'hello');

取数组下标:如果越界了,就会undefined

==对象==

对象是大括号,数组是中括号

每个属性之间使用逗号隔开,最后一个不需要添加

1
2
3
4
5
6
// Person person = new Person(1,2,3,4,5);
var person = {
name: "pomeloisland",
age: 3,
tags: ['js','java','web','...']
}

取对象的值

1
2
3
4
person.age
> 3
person.name
> 'pomeloisland'

2.4、严格检查模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
/*
前提:idea需要设置支持ES6语法,
'use strict';严格检查模式,预防JavaScript的随意性导致产生的一些问题,
必须写在JavaScript的第一行局部变量建议都使用let去定义
*/
// 全部变量
let i = 1;
// ES6 let
</script>
</head>
<body>

</body>
</html>

3、数据类型

3.1、字符串

1、正常字符串使用单引号或者双引号包裹

2、注意转义字符 \

1
2
3
4
5
\'
\t
\n
\u4e2d \u#### Unicode字符
\x41 Ascll字符

3、多行字符串编写

1
2
3
var msg = `hello
world
pomeloisland`

4、模板字符串

1
2
3
let name = "pomeloisland";
let age = 3;
let msg = `你好呀,${name}`

5、字符串长度

1
console.log(str.length)

6、字符串的可变性,不可变

image-20211028154230844

7、大小写转换

1
2
3
//注意,这里是方法,不是属性
console.log(student.toUpperCase())
console.log(student.toLowerCase())

8、student.indexOf(‘t’)

9、substring()

1
2
3
[)
student.substring(1) //从第一个字符串截取到最后一个字符串
student.substring(1,3)//[1,3)

3.2、数组

Array可以包含任意的数据类型

1
var arr = [1,2,3,4,5,6]

1、长度

1
arr.length

注意:假如给arr.length赋值,数组大小就会发生变化~,如果赋值过小,元素就会丢失

2、indexOf,通过元素获得下标索引

字符串的“1”和数字1是不同的

3、slice() 截取Array的一部分,返回一个新数组。类似于String中的substring

4、push、pop

  • push:压入到尾部

  • pop:弹出尾部的一个元素

5、unshift() shift() 头部

  • unshift:压入到头部
  • shift:弹出头部的一个元素

6、排序用sort()

7、元素反转reverse()

1
2
3
(3) ['A', 'B', 'C']
arr.reverse()
(3) ['C', 'B', 'A']

8、concat()

1
2
3
4
arr.concat([1,2,3])
(6) ['C', 'B', 'A', 1, 2, 3]
arr
(3) ['C', 'B', 'A']

注意:concat()并没有修改数组,只是会返回一个新的数组

9、连接符join

打印拼接数组,使用特定的字符串拼接

1
2
arr.join('-')
'C-B-A'

10、多维数组

1
2
3
arr = [[1,2],[3,4],["5","6"]]
arr[1][1]
4

数组:存储数据(如何存,如何取)

3.3、对象

若干个键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
var person = {
属性名: 属性值,
属性名: 属性值,
属性名: 属性值
}

//定义了一个person对象,它具有四个属性
var person = {
name: "pomeloisland",
age: 3,
email: "1005297142@qq.com",
score: 0
}

js中的对象,{…..}表示一个对象,键值对描述属性xxxx:xxxx,多个属性之间使用逗号隔开,最后一个属性不加逗号!

JavaScript中的所有的键都是字符串,值是任意对象!

1、对象赋值

1
2
3
4
person.name="柚屿"
'柚屿'
person.name
'柚屿'

2、使用一个不存在的对象属性,不会报错!

1
2
person.sss
undefined

3、动态删减属性,通过delete删除对象属性

1
2
3
delete person.name
true
persoon

4、动态的添加,直接给新的属性添加值即可

1
2
person.haha="haha"
'haha'

5、判断属性值是否在这个对象中! xxx in xxx!

1
2
3
4
5
'age' in person
true
//继承
'toString' in person
true

6、判断一个属性是否是这个对象自身拥有的hasOwnProperty()

1
2
3
4
person.hasOwnProperty('toStrinng')
false
person.hasOwnProperty('age')
true

3.4、流程控制

if判断

1
2
3
4
5
6
var age = 3;
if(age>3){ //第一个判断
alert("haha~");
}else { //否则
alert("kuwa~");
}

while循环,避免程序死循环

1
2
3
4
while(age < 100){
age = age + 1;
console.log(age)
}

for循环

1
2
3
for (let i = 0; i < 100; i++) {
console.log(i)
}

forEach循环

5.1引入的

1
2
3
4
5
var age = [12,5,7,87,6,79,413,468,748];
//函数
age.forEach(function (value){
console.log(value)
})

for…in

1
2
3
4
5
6
//for(var index in object){}
for(var num in age){
if(age.hasOwnProperty(num)){
console.log(age[num])
}
}

3.5、Map和Set

ES6的新特性~

​ Map:

1
2
3
4
5
6
7
//学生的成绩,学生的名字
// var names = ["tom","jack","pomeloisland"];
// var score = [100,90,80];
var map = new Map([['tom',100],['jack',90],['pomeloisland',80]]);
var name = map.get('tom'); //通过key获得value
map.set('admin',123456); //新增或修改
map.delete("tom"); //删除

Set:无序不重复的集合

1
2
3
4
var set = new Set([3,1,1,1,1,1]); //set可以去重
set.add(2); //添加
set.delete(1); //删除
console.log(set.has(3)) //是否包含某个元素

3.6、iterator 迭代器

ES6新特性

遍历数组

1
2
3
4
5
//通过for	of	/ for	in	下标
var arr = [3,4,5];
for(var x of arr){
console.log(x)
}

遍历map

1
2
3
4
var map = new Map([['tom',100],['jack',90],['pomeloisland',80]]);
for (let x of map){
console.log(x)
}

遍历set

1
2
3
4
var set = new Set([5,6,7]);
for(let x of set){
console.log(x)
}

4、函数

4.1、定义函数

定义方式一

绝对值函数

1
2
3
4
5
6
7
function abs(x){
if(x>=0){
return x;
}else{
return -x;
}
}

一旦执行到return代表函数结束,返回结果!

如果没有执行return,函数执行完也会返回结果,结果就是undefined

定义方式二

1
2
3
4
5
6
7
var abs = function(x){
if(x>=0){
return x;
}else{
return -x;
}
}

function(x){….}这是一个匿名类。但是可以把结果赋值给abs,通过abs就可以调用函数!

方式一和方式二等价!

调用函数

1
2
abs(10)		//10
abs(-10) //10

参数问题:JavaScript可以传任意个参数,也可以不传递参数~

1
2
3
4
5
6
7
8
9
10
11
var abs = function(x){
//手动抛出异常来判断
if(typeof x !== 'number'){
throw 'Not a Number';
}
if(x>=0){
return x;
}else{
return -x;
}
}

arguments

arguments是JS免费赠送的关键字;

代表,传递进来的所有的参数,是一个数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var abs = function(x){
console.log("x=>"+x);
for (var i = 0; i<arguments.length;i++){
console.log(arguments[i]);
}
if(arguments.length>1){
console.log(arguments[i]);
}
if(x>=0){
return x;
}else{
return -x;
}
}

问题:argumens包含所有的参数,有时候想使用多余的参数来进行附加操作。需要排除已有参数~

rest

以前:

1
2
3
4
5
if(arguments.length>2){
for(var i = 2; i<arguments.length;i++){
//....
}
}

ES6引入的新特性,获取除了已经定义的参乎上之外的所有参数~

1
2
3
4
5
function aaa(a,b,...rest){
console.log("a=>"+a);
console.log("b=>"+b);
console.log(rest);
}

rest参数只能写在后面,必须用…标识。

4.2、变量的作用域

1
2
3
4
5
function pi(){
var x = 1;
x = x + 1;
}
x = x + 2;//Uncaught ReferenceError: x is not defined

在JavaScript中,var定义的变量实际是有作用域的。

假设在函数体中声明,则在函数体外不可使用~(非要实现的话,可使用闭包

如果两个函数使用了相同的变量名,只要在函数内部,就不冲突

1
2
3
4
5
6
7
8
9
function pi(){
var x = 1;
x = x + 1;
}

function pi2(){
var x = 'A';
x = x + 1;
}
1
2
3
4
5
6
7
8
function pi(){
var x = 1;
//内部函数可以访问外部函数的成员,反之则不行
function pi2(){
var y = x + 1;//2
}
var z = y + 1;//Uncaught ReferenceError: z is not defined
}

假设,内部函数变量和外部 函数的变量重名!

1
2
3
4
5
6
7
8
9
10
11
function pi(){
var x = 1;
//内部函数可以访问外部函数的成员,反之则不行
function pi2(){
var x = 'A';
console.log('inner'+x);//outer1
}
console.log('outer'+x);//innerA
pi2()
}
pi()

假设在JavaScript中函数查找变量从自身函数开始~,由 ”内“ 向 ”外“查找。假设外部存在这个同名的函数变量,则内部函数会屏蔽外部函数的变量。

提升变量的作用域

1
2
3
4
5
function pi(){
var x = "x" + y;
console.log(x);
var y = 'y';
}

结果:xundefined

说明:JavaScript执行引擎,自动提升了y的声明,但不会给提升变量y的赋值;

1
2
3
4
5
6
function pi2(){
var y;
var x = "x" + y;
console.log(x);
y = 'y';
}

这个是在JavaScript建立之初就存在的特性。养成规范:所有的变量定义都放在函数的头部,不要乱放,便于代码的维护;

1
2
3
4
5
6
7
function pi2(){
var x = 1,
y = x + 1,
z,i,a;//undefined

//之后随意用
}

全局函数

1
2
3
4
5
6
7
//全局变量
var x = 1;
function f(){
console.log(x);
}
f();
console.log(x);

全局对象window

1
2
3
var x = 'xxx';
alert(x);
alert(window.x);// 默认所有的全局变量都会自动绑定在window对象下

alert()本身也是一个window的变量;

1
2
3
4
5
6
7
8
9
10
11
12
var x = 'xxx';
window.alert(x);
var old_alert = window.alert;
// old_alert(x);
window.alert = function (){

};
//发现alert()失效了
window.alert(123);
//恢复
window.alert = old_alert;
window.alert(456);

JavaScript实际上只有一个全局作用域,任何变量(函数也可以视为变量),假设没有在函数作用范围内找到,就会向外查找,如果在全局作用域都没有找到,报错RefrenceError

规范

由于我们所有的全局变量都会绑定到我们的window上,如果不同的js文件,使用了相同的全局变量,冲突~> 如果能够减少冲突?

1
2
3
4
5
6
7
//唯一全局变量
var Pomeloisland = {};
//定义全局变量
Pomeloisland.name = 'pomeloisland';
Pomeloisland.add = function (a,b){
return a + b;
}

把自己的代码全部放入自己定义的唯一空间名字中,降低全局命名冲突的问题~

局部作用域 let

1
2
3
4
5
6
function aaa(){
for (var i = 0; i < 100; i++) {
console.log(i);
}
console.log(i+1);//i 出了这个作用域还可以使用
}

ES6 let 关键字,解决局部作用域冲突问题!

1
2
3
4
5
6
function aaa(){
for (let i = 0; i < 100; i++) {
console.log(i);
}
console.log(i+1);//Uncaught ReferenceError: i is not defined
}

建议使用let去定义局部作用域的变量;

常量const

在ES6之前,怎么定义常量:只有用全部大写字母命名的变量就是常量;不要修改这样的值

1
2
3
4
var PI = '3.14';
console.log(PI);
PI = '278';//可以改变这个值
console.log(PI);

在ES6引入了常量关键字const

1
2
3
4
const PI = '3.14';//只读变量
console.log(PI);
PI = '278';//TypeError: Assignment to constant variable.
console.log(PI);

4.3、方法

定义方法

方法就是把函数放在对象的里面,对象里面只有两个东西:属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var pomeloisland = {
name:'柚屿',
birth:2000,
//方法
age: function (){
//今年 - 出生的年
var year = new Date().getFullYear();
return year - this.birth;
}
}
//属性
pomeloisland.name
//方法,一定要带()
pomeloisland.age()

this.代表什么:拆开上面的代码看看~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getAge(){
//今年 - 出生的年
var year = new Date().getFullYear();
return year - this.birth;
}
var pomeloisland = {
name:'柚屿',
birth:2000,
//方法
age: getAge
}

//pomeloisland.age() ok
//getAge() NaN window

this是无法指向的,是默认指向调用它的那个对象;

apply

在js中,可以控制this的指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getAge(){
//今年 - 出生的年
var year = new Date().getFullYear();
return year - this.birth;
}
var pomeloisland = {
name:'柚屿',
birth:2000,
//方法
age: getAge
}

var xiaoming = {
name:'小明',
birth:2000,
//方法
age: getAge
}
getAge.apply(pomeloisland,[]);//this,指向了pomeloisland,参数为空

5、内部对象

标准对象

1
2
3
4
5
6
7
8
typeof 123
'number'
typeof true
'boolean'
typeof NaN
'number'
typeof Math.abs
'function'

5.1、Date

基本使用

1
2
3
4
5
6
7
8
9
10
11
var now = new Date();//Tue Nov 23 2021 18:41:18 GMT+0800 (香港标准时间)
now.getFullYear();//年
now.getMonth();//月 0~11 代表月
now.getDate();//日
now.getDay();//星期几
now.getHours();//时
now.getMinutes();//分
now.getSeconds();//秒
now.getTime();//时间戳 全世界统一 1970 1.1 0:00 毫秒数

console.log(new Date(1637664346289))//时间戳转为时间

转换

1
2
3
4
5
new Date(1637664346289)
now.toLocaleString
ƒ toLocaleString() { [native code] }
now.toLocaleString()
'2021/11/23 下午6:46:47'

5.2、JSON

json是什么

早期,所有数据传输习惯使用xml文件!

  • JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

在JavaScript中一切皆为对象、任何js支持的类型都可以用json来表示;

格式:

  • 对象都用{}
  • 数组都用[]
  • 所有的键值对都使用key:value

JSON和JS对象的转化

1
2
3
4
5
6
7
8
9
var user = {
name: "pomeloisland",
age: 3,
sex: '男'
}
//对象转化为json字符串{"name":"pomeloisland","age":3,"sex":"男"}
var json = JSON.stringify(user);
//json字符串转化为对象 参数为json字符串
var obj = JSON.parse('{"name":"pomeloisland","age":3,"sex":"男"}');

JSON和JS的区别

1
2
var obj = {a:'hello',b:'hellob'};
var json = '{"a":"hello","b":"hellob"}';

6、面向对象编程

原型对象

  • 类:模板 原型对象

  • 对象:具体的实例

在JavaScript这个需要换一下思维方式!

原型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Student = {
name: "pomeloisland",
age: 3,
run: function (){
console.log(this.name + " run...")
}
}
var xiaoming = {
name: "xiaoming"
};
//原型对象
xiaoming.__proto__ = Student;

var Bird = {
fly: function (){
console.log(this.name + " fly...")
}
}
//小明的原型是Bird
xiaoming.__proto__ = Bird;
1
2
3
4
5
6
7
8
function Student(name){
this.name = name;
}

//给student新增一个方法
Student.prototype.hello = function (){
alert('hello')
}

class继承

class关键字是在ES6引入的

1、定义一个类,属性,方法

1
2
3
4
5
6
7
8
9
10
11
// 定义一个学生的类
class Student{
constructor(name) {
this.name = name;
}
hello(){
alert('hello');
}
}
var xiaoming = new Student("xiaoming");
xiaoming.hello()

2、继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义一个学生的类
class Student{
constructor(name) {
this.name = name;
}
hello(){
alert('hello');
}
}
class Xiaoxuesheng extends Student{
constructor(name,grade) {
super(name);
this.grade = grade;
}
myGrade(){
alert('我是一名小学生');
}
}
var xiaoming = new Student("xiaoming");
var xiaohong = new Xiaoxuesheng("xiaohong",1);

本质:查看对象原型

image-20211124151244371

原型链

__ proto __:

image-20211124151804658

7、操作Bom对象(重点)

浏览器介绍

JavaScript和浏览器关系

JavaScript诞生就是为了能够让他在浏览器中运行!

BOM:浏览器对象模型

  • IE 6~11
  • Chrome
  • Safari
  • FireFox
  • Opera

第三方浏览器

  • QQ浏览器
  • 360浏览器

window

window代表浏览器窗口

1
2
3
4
5
6
7
8
9
window.innerHeight
150
window.innerWidth
1280
window.outerHeight
724
window.outerWidth
1284
//调整浏览器窗口试试...

Navigator

Navigator封装了浏览器的信息

1
2
3
4
5
6
7
8
navigator.appName
'Netscape'
navigator.appVersion
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'
navigator.userActivation
UserActivation {hasBeenActive: true, isActive: true}
navigator.platform
'Win32'

大多时候不会使用navigator对象,因为会被人为修改!

不建议使用这些属性来判断和编写代码

screen

代表屏幕尺寸

1
2
3
4
screen.width
1280 px
screen.height
720 px

location(重要)

location代表当前页面的URL信息

1
2
3
4
5
6
host: "www.baidu.com"
href: "https://www.baidu.com/"
protocol: "https:"
reload: ƒ reload() //刷新网页
//设置新的地址
location.assign('https://v.qq.com/x/cover/y0jueuihog64xhb/e00369x5nb9.html')

document(内容)

document代表当前页面,HTML DOM文档树

1
2
3
4
document.title
'百度一下,你就知道'
document.title = '百度'
'百度'

获取具体的文档树节点、

1
2
3
4
5
6
7
8
<dl id="app">
<dt>Java</dt>
<dd>JavaSE</dd>
<dd>JavaEE</dd>
</dl>
<script>
var dl = document.getElementById('app');
</script>

获取cookie

1
2
document.cookie
''

劫持cookie原理

www.taobao.com

1
2
<script src="aa.js"></script>
<!--恶意人员:获取你的cookie上传到他的服务器-->

服务器端可以设置cookie为httpOnly

history(不建议使用)

history代表浏览器的历史记录

1
2
history.back()//后退
history.forward()//前进

8、操作DOM对象(重点)

核心

浏览器网页就是一个DOM树型结构!

  • 更新:更新DOM节点
  • 遍历dom节点:得到dom节点
  • 删除:删除一个dom节点
  • 添加:添加一个新的dom节点

要操作一个dom节点,必须要获得这个dom节点

获得DOM节点

1
2
3
4
5
6
7
8
//对应css的选择器
var h1 = document.getElementsByTagName('h1');
var p1 = document.getElementById('p1');
var p2 = document.getElementsByClassName('p2');
var father = document.getElementById('father');
var children = father.children;//获取父节点下的所有子节点
// father.firstChild
// father.lastChild

原生代码,之后使用jQuery();

更新节点

1
2
3
4
5
6
7
<div id="id1">

</div>

<script>
var id1 = document.getElementById('id1');
</script>

操作文本

  • id1.innerText='123' 修改文本的值
  • id1.innerHTML='<strong>123</strong>' 可以解析HTML文本标签

操作JS

1
2
3
id1.style.color = 'yellow'		//属性使用字符串包裹
id1.style.fontSize = '200px' //驼峰命名问题
id1.style.padding = '2em'

删除节点

删除节点的步骤:

  • 先获取父节点
  • 再通过父节点删除自己
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
var self = document.getElementById('p1');
var father = p1.parentElement;
father.removeChild(p1);
//删除是一个动态的过程
father.removeChild(father.children[0]);
father.removeChild(father.children[0]);
father.removeChild(father.children[0]);
</script>

注意:删除多个节点的时候,children是在时刻变化的,删除节点的时候一定要注意!

插入节点

获得了某个Dom节点,假设这个dom节点是空的,通过innerHTML就可以增加一个元素了,但是这个dom节点已经存在元素了,就不能这么干了!因为会产生覆盖

追加操作

1
2
3
4
5
6
7
8
9
10
11
<p id="js">JavaScript</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="me">JavaME</p>
</div>
<script>
var js = document.getElementById('js');
var list = document.getElementById('list');
list.appendChild(js);//追加到后面
</script>

效果:

image-20211211172334919

创建一个新的标签,实现插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
var js = document.getElementById('js'); //已存在的节点
var list = document.getElementById('list');
//通过js创建一个新的节点
var newP = document.createElement('p');//创建一个p标签
newP.id = 'newP';
newP.innerText = 'hello';
list.appendChild(newP);
//创建一个标签节点
var myScript = document.createElement('script');
myScript.setAttribute('type','text/javascript');
list.appendChild(myScript);
//可以创建一个style标签
var myStyle = document.createElement('style');//创建了一个空style标签
myStyle.setAttribute('type','text/css');
myStyle.innerHTML = 'body{background-color: chartreuse;}';//设置标签内容
document.getElementsByTagName('head')[0].appendChild(myStyle);
</script>

insertBefore

1
2
3
4
5
var ee = document.getElementById('ee');
var js = document.getElementById('js');
var list = document.getElementById('list');
//要包含的节点,insertBefore(newNode,targetNode)
list.insertBefore(js,ee);

9、操作表单

表单是什么 form DOM树

  • 文本框 text
  • 下拉框
  • 单选框 radio
  • 多选框 checkbox
  • 隐藏域 hidden
  • 密码框 password
  • 。。。。

表单的目的:提交信息

获得要提交的信息

1
2
3
4
5
6
7
8
9
10
11
12
<script>
var input_text = document.getElementById('username');
var boy_radio = document.getElementById('boy');
var girl_radio= document.getElementById('girl');
//得到输入框的值
input_text.value
//修改输入框的值
input_text.value = '123'
//对于单选框,多选框等等固定的值,girl_radio.value只能取到当前的值
girl_radio.checked;//查看返回的结果是否为true,如果为true,则被选中~
girl_radio.checked = true;//赋值
</script>

提交表单 md5加密密码,表单优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!--
表单绑定提交事件
onsubmit绑定一个提交检测的函数,true,false
将这个结果返回给表单,使用onsubmit接收!
onsubmit="return aaa()
-->
<form action="#" method="post" onsubmit="return aaa()">
<p>
<span>用户名:</span><input type="text" id="username" name="username">
</p>
<p>
<span>密码:</span><input type="password" id="input-password">
</p>
<input type="hidden" id="md5-password" name="password">
<!--绑定事件 onclick被点击-->
<button type="submit">提交</button>
</form>
<script>
function aaa(){
var uname = document.getElementById('username');
var pwd = document.getElementById('input-password');
var md5pwd = document.getElementById('md5-password');

md5pwd.value = md5(pwd.value);
//可以检验判断表单内容,true就是通过提交,false就是阻止提交
return true;
}
</script>

10、jQuery

JavaScript

jQuery库,里面存在大量的JavaScript函数

获取jQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
</head>
<body>
<!--
公式 :$(selector).action()
-->
<a href="" id="test-jquery">点我</a>
<script>
//选择器就是css的选择器
$('#test-jquery').click(function (){
alert('hello.jQuery')
})
</script>
</body>
</html>

选择器

1
2
3
4
5
6
7
8
9
10
11
12
//原生js,选择器少,麻烦不好记
//标签选择器
document.getElementsByTagName();
//id
document.getElementById();
//类
document.getElementsByClassName();

//jQuery css中的选择器它全部都能用
$('p').click() //标签选择器
$("#id1").click() //id选择器
$('.class1').click() //class选择器

文档工具站:https://jquery.cuishifeng.cn/

事件

  • 鼠标事件
  • 键盘事件
  • 其他事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="lib/jQuery.js"></script>
<style>
#divMove{
width: 500px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<!--要求:获取鼠标当前的一个坐标-->
mouse: <span id="move"></span>
<div id="divMove">
在这里移动鼠标试试
</div>
<script>
//当网页元素加载完毕之后,响应事件
$(function (){
$('#divMove').mousemove(function (e) {
$('#move').text('x:'+e.pageX + 'y:'+e.pageY)
})
});
</script>
</body>
</html>

操作DOM元素

节点文本操作

1
2
3
4
$('#test-ul li[name=python]').text();//获得值
$('#test-ul li[name=python]').text('123');//设置值
$('#test-ul').html();//获得值
$('#test-ul').html('<strong>123</strong>');//设置值

css的操作

1
$('#test-ul li[name=python]').css("color","red");

元素的显示和隐藏:本质 display:none;

1
2
$('#test-ul li[name=python]').show()//显示
$('#test-ul li[name=python]').hide()//隐藏

娱乐测试

1
2
3
$(window).width()
$(window).height()
$('#test-ul li[name=python]').toggle();

Vue

Soc:关注度分离原则

HTML+ CSS + JS:视图:给用户看,刷新后台给的数据

网络通信:axios

页面跳转:vue-router

状态管理:vuex

Vue-UI:ICE

虚拟DOM:利用内存

计算属性—–>Vue特色虚拟DOM

1、第一个Vue程序

1.1、什么是MVVM

MVVM(Model-View-ViewModel)是一种软件设计模式,由微软WPF(用于替代WinForm,以前就是用这个技术开发桌面应用程序的)和Silverlight(类似于Java Applet,简单点说就是在浏览器上运行WPF)的架构师Ken Cooper和Ted Peters开发,是一种简化用户界面的事件驱动编程方式。由John Gossman(同样也是WPF和Sliverlight的架构师)与2005年在他的博客上发表。

MVVM源自于经典的MVC(Model-View-Controller)模式。MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用。其作用如下:

  • 该层向上与视图层进行双向数据绑定
  • 向下与Model层通过接口请求进行数据交互

img

MVVM已经相当成熟了,主要运用但不仅仅在网络应用程序开发中。当下流行的MVVM框架有Vue.jsAnfular JS

1.2、为什么要使用MVVM

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处

低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
在这里插入图片描述

(1)View

View是视图层, 也就是用户界面。前端主要由HTH L和csS来构建, 为了更方便地展现vi eu to del或者Hodel层的数据, 已经产生了各种各样的前后端模板语言, 比如FreeMarker,Thyme leaf等等, 各大MV VM框架如Vue.js.Angular JS, EJS等也都有自己用来构建用户界面的内置模板语言。

(2)Model

Model是指数据模型, 泛指后端进行的各种业务逻辑处理和数据操控, 主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则

(3)ViewModel

ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层, 前端开发者对从后端获取的Model数据进行转换处理, 做二次封装, 以生成符合View层使用预期的视图数据模型。
  需要注意的是View Model所封装出来的数据模型包括视图的状态和行为两部分, 而Model层的数据模型是只包含状态的

  • 比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示)

  • 页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)

视图状态和行为都封装在了View Model里。这样的封装使得View Model可以完整地去描述View层。由于实现了双向绑定, View Model的内容会实时展现在View层, 这是激动人心的, 因为前端开发者再也不必低效又麻烦地通过操纵DOM去更新视图。
  MVVM框架已经把最脏最累的一块做好了, 我们开发者只需要处理和维护View Model, 更新数据视图就会自动得到相应更新,真正实现事件驱动编程
  View层展现的不是Model层的数据, 而是ViewModel的数据, 由ViewModel负责与Model层交互, 这就完全解耦了View层和Model层, 这个解耦是至关重要的, 它是前后端分离方案实施的重要一环

1.3、Vue

Vue(读音/vju/, 类似于view) 是一套用于构建用户界面的渐进式框架, 发布于2014年2月。与其它大型框架不同的是, Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层, 不仅易于上手, 还便于与==第三方库==(如:vue-router,vue-resource,vue x) 或既有项目整合。

(1)MVVM模式的实现者

  • Model:模型层, 在这里表示JavaScript对象

  • View:视图层, 在这里表示DOM(HTML操作的元素)

  • ViewModel:连接视图和数据的中间件, Vue.js就是MVVM中的View Model层的实现者

在MVVM架构中, 是不允许数据和视图直接通信的, 只能通过ViewModel来通信, 而View Model就是定义了一个Observer观察者

  • ViewModel能够观察到数据的变化, 并对视图对应的内容进行更新

  • ViewModel能够监听到视图的变化, 并能够通知数据发生改变

至此, 我们就明白了, Vue.js就是一个MV VM的实现者, 他的核心就是实现了DOM监听与数据绑定

(2)为什么要使用Vue.js

  • 轻量级, 体积小是一个重要指标。Vue.js压缩后有只有20多kb(Angular压缩后56kb+,React压缩后44kb+)
  • 移动优先。更适合移动端, 比如移动端的Touch事件
  • 易上手,学习曲线平稳,文档齐全
  • 吸取了Angular(模块化) 和React(虚拟DOM) 的长处, 并拥有自己独特的功能,如:计算属性
  • 开源,社区活跃度高

1.4、第一个Vue程序

【说明】IDEA可以安装Vue的插件!
  注意:Vue不支持IE 8及以下版本, 因为Vue使用了IE 8无法模拟的ECMAScript 5特性。但它支持所有兼容ECMAScript 5的浏览器。

(1)下载地址

  • 开发版本

    • 包含完整的警告和调试模式:https://yuejs.org/js/vue.js
    • 删除了警告, 30.96KB min+gzip:https://vuejs.org/js/vue.min.js
  • CDN

(2)代码编写

Vue.js的核心是实现了MVVM模式, 她扮演的角色就是View Model层, 那么所谓的第一个应用程序就是展示她的数据绑定功能,操作流程如下:

1、创建一个HTML文件

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

</body>
</html>

2、引入Vue.js

1
2
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>

3、创建一个Vue实例

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"hello,vue!"
}
});
</script>

说明:

  • el: '#vue':绑定元素的ID
  • data:{message:'Hello Vue!'}:数据对象中有一个名为message的属性,并设置了初始值 Hello Vue!

4、将数据绑定到页面元素

1
2
3
4
<!--view层,模板-->
<div id="app">
{{message}}
</div>

说明:只需要在绑定的元素中使用双花括号将Vue创建的名为message属性包裹起来, 即可实现数据绑定功能, 也就实现了View Model层所需的效果, 是不是和EL表达式非常像?

(3)完整的HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>

</head>
<body>

<!--view层,模板-->
<div id="app">
{{message}}
</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"hello,vue!"
}
});
</script>
</body>
</html>

(4)测试

为了能够更直观的体验Vue带来的数据绑定功能, 我们需要在浏览器测试一番, 操作流程如下:
  1、在浏览器上运行第一个Vue应用程序, 进入开发者工具
  2、在控制台输入vm.message=‘HelloWorld’, 然后回车, 你会发现浏览器中显示的内容会直接变成HelloWorld
  此时就可以在控制台直接输入vm.message来修改值, 中间是可以省略data的, 在这个操作中, 我并没有主动操作DOM, 就让页面的内容发生了变化, 这就是借助了Vue的数据绑定功能实现的; MV VM模式中要求View Model层就是使用观察者模式来实现数据的监听与绑定, 以做到数据与视图的快速响应。

2、基础语法指令

2.1、v-bind

我们已经成功创建了第一个Vue应用!看起来这跟渲染一个字符串模板非常类似, 但是Vue在背后做了大量工作。现在数据和DOM已经被建立了关联, 所有东西都是响应式的。我们在控制台操作对象属性,界面可以实时更新!
  我们还可以使用v-bind来绑定元素特性!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>

</head>
<body>

<!--view层,模板-->
<div id="app">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message: '页面加载于 ' + new Date().toLocaleString()
}
});
</script>
</body>
</html>

你看到的v-bind等被称为指令。指令带有前缀v以表示它们是Vue提供的特殊特性。可能你已经猜到了, 它们会在渲染的DOM上应用特殊的响应式行为在这里,该指令的意思是:“将这个元素节点的title特性和Vue实例的message属性保持一致”。
  如果你再次打开浏览器的JavaScript控制台, 输入app, message=‘新消息’,就会再一次看到这个绑定了title特性的HTML已经进行了更新。

2.2、v-if, v-else

  • v-if
  • v-else
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
type: true
}
});
</script>
</body>
</html>

测试:

  1. 在浏览器上运行,打开控制台!
  2. 在控制台输入vm.ok=false然后回车,你会发现浏览器中显示的内容会直接变成NO

注:使用v-*属性绑定数据是不需要双花括号包裹的

v-else-if

  • v-if
  • v-else-if
  • v-else

注:===三个等号在JS中表示绝对等于(就是数据与类型都要相等)上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<h1 v-if="type==='A'">A</h1>
<h1 v-else-if="type==='B'">B</h1>
<h1 v-else-if="type==='D'">D</h1>
<h1 v-else>C</h1>

</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
type: 'A'
}
});
</script>
</body>
</html>

2.3、v-for

格式说明

1
2
3
4
5
6
7
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}---{{index}}
</li>

</div>
123456

注:items是数组,item是数组元素迭代的别名。我们之后学习的Thymeleaf模板引擎的语法和这个十分的相似!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}---{{index}}
</li>

</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
items:[
{message:'狂神说Java'},
{message:'狂神说前端'},
{message:'狂神说运维'}
]
}
});
</script>
</body>
</html>

测试:在控制台输入vm.items.push({message:'狂神说运维'}),尝试追加一条数据,你会发现浏览器中显示的内容会增加一条狂神说运维.

2.4、v-on

v-on监听事件
 emsp;事件有Vue的事件、和前端页面本身的一些事件!我们这里的click是vue的事件, 可以绑定到Vue中的methods中的方法事件!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div id="app">
<button v-on:click="sayHi">点我</button>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:'Hello World'
},
methods:{
sayHi:function(event){
//'this'在方法里面指向当前Vue实例
alert(this.message);
}
}
});
</script>
</body>
</html>

点击测试
  Vue还有一些基本的使用方式, 大家有需要的可以再跟着官方文档看看, 因为这些基本的指令几乎我们都见过了,一通百通!掌握学习的方式!

3、表单双绑、组件

3.1、什么是双向数据绑定

Vue.js是一个MV VM框架, 即数据双向绑定, 即当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。
  值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vue x那么数据流也是单项的,这时就会和双向数据绑定有冲突。

(1)为什么要实现数据的双向绑定

Vue.js中,如果使用vuex, 实际上数据还是单向的, 之所以说是数据双向绑定,这是用的UI控件来说, 对于我们处理表单, Vue.js的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。

3.2、在表单中使用双向数据绑定

你可以用v-model指令在表单、及元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇, 但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
  注意:v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值!

(1)单行文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
输入的文本:<input type="text" v-model="message" value="hello">{{message}}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
message:""
}
});
</script>
</body>
</html>

(2)多行文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
多行文本:<textarea v-model="pan"></textarea>&nbsp;&nbsp;多行文本是:{{pan}}
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:"Hello hello!"
}
});
</script>
</body>
</html>

(3)单复选框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
性别:
<input type="radio" name="sex" value="男" v-model="pomeloisland">
<input type="radio" name="sex" value="女" v-model="pomeloisland">

<p>
选中了谁:{{pomeloisland}}
</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
pomeloisland: ''
}
});
</script>
</body>
</html>

4多复选框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div id="app">
多复选框:
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
&nbsp;&nbsp;
<label for="jack">Jack</label>
<input type="checkbox" id="join" value="Join" v-model="checkedNames">
&nbsp;&nbsp;
<label for="join">Jack</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
&nbsp;&nbsp;
<label for="mike">Mike</label>
<span>选中的值:{{checkedNames}}</span>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
checkedNames:[]
}
});
</script>
</body>
</html>

(5)单选按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div id="app">
单选框按钮
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<span>选中的值:{{picked}}</span>
</div>

<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
picked:'Two'
}
});
</script>
</body>
</html>

(6)下拉框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
下拉框:
<select v-model="pomeloisland">
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>value:{{pomeloisland}}</span>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
pomeloisland: ''
}
});
</script>
</body>
</html>

注意:v-model表达式的初始值未能匹配任何选项,元系将被渲染为“未选中”状态。 在iOS中, 这会使用户无法选择第一个选项,因为这样的情况下,iOS不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

3.3、什么是组件

组件是可复用的Vue实例, 说白了就是一组可以重复使用的模板, 跟JSTL的自定义标签、Thymelealth:fragment等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织:

img

在这里插入图片描述

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

(1)第一个Vue组件

注意:在实际开发中,我们并不会用以下方式开发组件,而是采用vue-cli创建,vue模板文件的方式开发,以下方法只是为了让大家理解什么是组件。

使用Vue.component()方法注册组件,格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app">
<pan></pan>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
//先注册组件
Vue.component("pan",{

template:'<li>Hello</li>'

});
//再实例化Vue
var vm = new Vue({
el:"#app",
});
</script>

说明:

  • Vue.component():注册组件
  • pan:自定义组件的名字
  • template:组件的模板

(2)使用props属性传递参数

像上面那样用组件没有任何意义,所以我们是需要传递参数到组件的,此时就需要使用props属性了!

注意:默认规则下props属性里的值不能为大写;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div id="app">
<!--组件:传递给组件中的值:props-->
<pan v-for="item in items" v-bind:panh="item"></pan>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
//定义组件
Vue.component("pan",{
props:['panh'],
template:'<li>{{panh}}</li>'

});
var vm = new Vue({
el:"#app",
data:{
items:["java","Linux","前端"]
}
});
</script>
</body>
</html>

说明:

  • v-for="item in items":遍历Vue实例中定义的名为items的数组,并创建同等数量的组件
  • v-bind:panh="item":将遍历的item项绑定到组件中props定义名为item属性上;= 号左边的panhprops定义的属性名,右边的为item in items 中遍历的item项的值

4、Axios异步通信

4.1、什么是Axios

Axios是一个开源的可以用在浏览器端和Node JS的异步通信框架, 她的主要作用就是实现AJAX异步通信,其功能特点如下:

  • 从浏览器中创建XMLHttpRequests
  • 从node.js创建http请求
  • 支持Promise API[JS中链式编程]
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防御XSRF(跨站请求伪造)

GitHub:https://github.com/axios/axios
中文文档:http://www.axios-js.com/

4.2、为什么要使用Axios

由于Vue.js是一个视图层框架并且作者(尤雨溪) 严格准守SoC(关注度分离原则)所以Vue.js并不包含AJAX的通信功能, 为了解决通信问题, 作者单独开发了一个名为vue-resource的插件, 不过在进入2.0版本以后停止了对该插件的维护并推荐了Axios框架。少用jQuery, 因为它操作Dom太频繁!

4.3、第一个Axios应用程序

咱们开发的接口大部分都是采用JSON格式, 可以先在项目里模拟一段JSON数据, 数据内容如下:创建一个名为data.json的文件并填入上面的内容, 放在项目的根目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en" xmlns:v-binf="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--v-cloak 解决闪烁问题-->
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="vue">
<div>地名:{{info.name}}</div>
<div>地址:{{info.address.country}}--{{info.address.city}}--{{info.address.street}}</div>
<div>链接:<a v-binf:href="info.url" target="_blank">{{info.url}}</a> </div>
</div>

<!--引入js文件-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#vue",
//data:属性:vm
data(){
return{
info:{
name:null,
address:{
country:null,
city:null,
street:null
},
url:null
}
}
},
mounted(){//钩子函数
axios
.get('data.json')
.then(response=>(this.info=response.data));
}
});
</script>
</body>
</html>

说明:

  1. 在这里使用了v-bind将a:href的属性值与Vue实例中的数据进行绑定
  2. 使用axios框架的get方法请求AJAX并自动将数据封装进了Vue实例的数据对象中
  3. 我们在data中的数据结构必须和Ajax响应回来的数据格式匹配!

4.4、Vue的生命周期

官方文档:https://cn.vuejs.org/v2/guide/instance.html#生命周期图示
  Vue实例有一个完整的生命周期,也就是从开始创建初女台化数据、编译模板、挂载DOM、渲染一更新一渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。
  在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue的实例。

img

5、计算属性、内容分发、自定义事件

5.1、什么是计算属性

计算属性的重点突出在属性两个字上(属性是名词),首先它是个属性其次这个属性有计算的能力(计算是动词),这里的计算就是个函数:简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<p>currentTime1:{{currentTime1()}}</p>
<p>currentTime2:{{currentTime2}}</p>
</div>

<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:"pan"
},
methods:{
currentTime1:function(){
return Date.now();//返回一个时间戳
}
},
computed:{
currentTime2:function(){//计算属性:methods,computed方法名不能重名,重名之后,只会调用methods的方法
this.message;
return Date.now();//返回一个时间戳
}
}
});
</script>
</body>
</html>

注意:methods和computed里的东西不能重名

说明:

  • methods:定义方法, 调用方法使用currentTime1(), 需要带括号
  • computed:定义计算属性, 调用属性使用currentTime2, 不需要带括号:this.message是为了能够让currentTime2观察到数据变化而变化
  • 如何在方法中的值发生了变化,则缓存就会刷新!可以在控制台使用vm.message=”q in jiang", 改变下数据的值,再次测试观察效果!

结论:

调用方法时,每次都需要讲行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;

5.2、内容分发

Vue.js中我们使用<slot>元素作为承载分发内容的出口,作者称其为插槽,可以应用在组合组件的场景中;

测试

比如准备制作一个待办事项组件(todo) , 该组件由待办标题(todo-title) 和待办内容(todo-items)组成,但这三个组件又是相互独立的,该如何操作呢?

第一步定义一个待办事项的组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<todo></todo>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
Vue.component('todo',{
template:'<div>\
<div>代办事项</div>\
<ul>\
<li>学习狂神说Java</li>\
</ul>\
</div>'
})
</script>

第二步 我们需要让,代办事项的标题和值实现动态绑定,怎么做呢?我们可以留一个插槽!

  1. 将上面的代码留出一个插槽,即slot
1
2
3
4
5
6
7
8
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
  1. 定义一个名为todo-title的待办标题组件 和 todo-items的待办内容组件
1
2
3
4
5
6
7
8
9
10
11
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});

12345
//这里的index,就是数组的下标,使用for循环遍历的时候,可以循环出来!
Vue.component("todo-items",{
props:["item","index"],
template:"<li>{{index+1}},{{item}}</li>"
});
  1. 实例化Vue并初始化数据
1
2
3
4
5
6
var vm = new Vue({
el:"#vue",
data:{
todoItems:['test1','test2','test3']
}
});
  1. 将这些值,通过插槽插入
1
2
3
4
5
6
7
8
<div id="vue">
<todo>
<todo-title slot="todo-title" title="秦老师系列课程"></todo-title>
<!--<todo-items slot="todo-items" v-for="{item,index} in todoItems" v-bind:item="item"></todo-items>-->
<!--如下为简写-->
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items
</todo>
</div>

说明:我们的todo-title和todo-items组件分别被分发到了todo组件的todo-title和todo-items插槽中
完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="vue">
<todo>
<todo-title slot="todo-title" title="title"></todo-title>
<!--<todo-items slot="todo-items" v-for="{item,index} in todoItems" v-bind:item="item"></todo-items>-->
<!--如下为简写-->
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items
</todo>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
//这里的index,就是数组的下标,使用for循环遍历的时候,可以循环出来!
Vue.component("todo-items",{
props:["item","index"],
template:"<li>{{index+1}},{{item}}</li>"
});

var vm = new Vue({
el:"#vue",
data:{
title:"秦老师系列课程",
todoItems:['test1','test2','test3']
}
});
</script>
</body>
</html>

5.3、自定义事件

通以上代码不难发现,数据项在Vue的实例中, 但删除操作要在组件中完成, 那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了, Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题; 使用this.$emit(‘自定义事件名’, 参数) , 操作过程如下:

  1. 在vue的实例中增加了methods对象并定义了一个名为removeTodoltems的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var vm = new Vue({
el:"#vue",
data:{
title_text:"秦老师系列课程",
todoItems:['test1','test2','test3']
},
methods:{
removeItems:function(index){
console.log("删除了"+this.todoItems[index]+"OK");
//splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目,其中index
this.todoItems.splice(index,1);
}
}
});
  1. 修改todo-items待办内容组件的代码,增加一个删除按钮,并且绑定事件!
1
2
3
4
5
6
7
8
9
10
11
Vue.component("todo-items",{
props:["item_p","index_p"],
template:"<li>{{index_p+1}},{{item_p}} <button @click='remove'>删除</button></li>",
methods:{
remove:function (index) {
//这里的remove是自定义事件名称,需要在HTML中使用v-on:remove的方式
//this.$emit 自定义事件分发
this.$emit('remove',index);
}
}
});
  1. 修改todo-items待办内容组件的HTML代码,增加一个自定义事件,比如叫remove,可以和组件的方法绑定,然后绑定到vue的方法!
1
2
3
<!--增加了v-on:remove="removeTodoItems(index)"自定义事件,该组件会调用Vue实例中定义的-->
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:item_p="item" :index_p="index" v-on:remove="removeItems(index)" :key="index"></todo-items>

对上一个代码进行修改,实现删除功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:item="item" :index="index" @remove="removeItems(index)" :key="index"></todo-items>
</todo>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//slot:插槽
Vue.component("todo",{
template:
'<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
Vue.component("todo-title",{
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items",{
props: ['item','index'],
template: '<li>{{index}}---{{item}} <button @click="remove">删除</button></li>',
methods: {
remove: function (index) {
this.$emit('remove',index)
}
}
});
var vm = new Vue({
el:"#app",
data: {
title: 'Java全栈',
todoItems: ['狂神说Java','狂神说前端','狂神说Linux']
},
methods: {
removeItems: function (index) {
console.log("删除了"+this.todoItems[index]+"OK");
this.todoItems.splice(index,1);//一次删除一个元素
}
}
});
</script>
</body>
</html>

逻辑理解

在这里插入图片描述

5.4、Vue入门小结

核心:数据驱动,组件化

优点:借鉴了AngularJS的模块化开发和React的虚拟Dom,虚拟Dom就是把Demo操作放到内存中执行;

常用的属性:

  • v-if
  • v-else-if
  • v-else
  • v-for
  • v-on绑定事件,简写@
  • v-model数据双向绑定
  • v-bind给组件绑定参数,简写

组件化:

  • 组合组件插槽slot
  • 组件内部绑定事件需要使用到this.$emit("事件名",参数);
  • 计算属性的特色,缓存计算机数据

遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含,比如Ajax之类的通信功能,为了解决通信问题,我们需要使用Axios框架做异步通信;

说明

Vue的开发都是要基于NodeJS,实际开发采用Vue-cli脚手架开发,vue-router路由,vuex做状态管理;Vue UI,界面我们一般使用ElementUI(饿了么出品),或者ICE(阿里巴巴出品)来快速搭建前端项目~~

官网:

6、第一个vue-cli项目

6.1、什么是vue-cli

vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板;
  预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个估计项目就是脚手架,我们的开发更加的快速;

项目功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

6.2、需要的环境

确认nodejs安装成功:

cmd下输入node -v,查看是否能够正确打印出版本号即可!
cmd下输入npm -v,查看是否能够正确打印出版本号即可!
这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多!
  安装Node.js淘宝镜像加速器(cnpm)
  这样的话,下载会快很多~

1
2
3
4
5
# -g 就是全局安装
npm install cnpm -g

# 或使用如下语句解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org

安装的过程可能有点慢~,耐心等待!虽然安装了cnpm,但是尽量少用!
安装的位置:C:\Users\administrator\AppData\Roaming\npm

安装vue-cli

1
2
3
cnpm install vue-cli-g
#测试是否安装成功#查看可以基于哪些模板创建vue应用程序,通常我们选择webpack
vue list

6.3、第一个vue-cli程序

  1. 创建一个Vue项目,新建一个目录
  2. 创建一个基于webpack模板的vue应用程序
1
2
3
#1、首先需要进入到对应的目录 cd D:\Project\vue-study
#2、这里的myvue是顶日名称,可以根据自己的需求起名
vue init webpack myvue

一路都选择no即可;

说明:

  • Project name:项目名称,默认回车即可
  • Project description:项目描述,默认回车即可
  • Author:项目作者,默认回车即可
  • Install vue-router:是否安装vue-router,选择n不安装(后期需要再手动添加)
  • Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
  • Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
  • Setupe2etests with Nightwatch:单元测试相关,选择n不安装(后期需要再手动添加)
  • Should we run npm install for you after the,project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果!

(1)初始化并运行

1
2
3
cd myvue
npm install
npm run dev

执行完成后,目录多了很多依赖

当出现问题时,可以查看提示进行处理

7、webpack使用

7.1、什么是webpack

本质上, webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler) 。当webpack处理应用程序时, 它会递归地构建一个依赖关系图(dependency graph) , 其中包含应用程序需要的每个模块, 然后将所有这些模块打包成一个或多个bundle.
  Webpack是当下最热门的前端资源模块化管理和打包工具, 它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换, 任何形式的资源都可以当做模块, 比如Commons JS、AMD、ES 6、CSS、JSON、Coffee Script、LESS等;
  伴随着移动互联网的大潮, 当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里, 使用HTML 5、CSS 3、ES 6等新的技术来开发丰富的功能, 网页已经不仅仅是完成浏览器的基本需求; WebApp通常是一个SPA(单页面应用) , 每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。
  前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

7.2、模块化的演进

Script标签

1
2
3
<script src = "module1.js"></script>
<script src = "module2.js"></script>
<script src = "module3.js"></script>

这是最原始的JavaScript文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window对象中,不同模块的调用都是一个作用域。
这种原始的加载方式暴露了一些显而易见的弊端:

  • 全局作用域下容易造成变量冲突
  • 文件只能按照<script>的书写顺序进行加载
  • 开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

CommonsJS

服务器端的NodeJS遵循CommonsJS规范,该规范核心思想是允许模块通过require方法来同步加载所需依赖的其它模块,然后通过exports或module.exports来导出需要暴露的接口。

1
2
3
4
5
require("module");
require("../module.js");
export.doStuff = function(){};
module.exports = someValue;
1234

优点:

  • 服务器端模块便于重用
  • NPM中已经有超过45万个可以使用的模块包
  • 简单易用

缺点:

  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

实现:

  • 服务端的NodeJS
  • Browserify,浏览器端的CommonsJS实现,可以使用NPM的模块,但是编译打包后的文件体积较大
  • modules-webmake,类似Browserify,但不如Browserify灵活
  • wreq,Browserify的前身

AMD

Asynchronous Module Definition规范其实主要一个主要接口define(id?,dependencies?,factory);它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行。

1
2
3
4
5
define("module",["dep1","dep2"],functian(d1,d2){
return someExportedValue;
});
require(["module","../file.js"],function(module,file){});
1234

优点

  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块

缺点

  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

实现

  • RequireJS
  • curl

CMD

Commons Module Definition规范和AMD很相似,尽保持简单,并与CommonsJS和NodeJS的Modules规范保持了很大的兼容性。

1
2
3
4
5
6
define(function(require,exports,module){
var $=require("jquery");
var Spinning = require("./spinning");
exports.doSomething = ...;
module.exports=...;
});

优点:

  • 依赖就近,延迟执行
  • 可以很容易在NodeJS中运行缺点
  • 依赖SPM打包,模块的加载逻辑偏重

实现

  • Sea.js
  • coolie

ES6模块

EcmaScript 6标准增加了JavaScript语言层面的模块体系定义。ES 6模块的设计思想, 是尽量静态化, 使编译时就能确定模块的依赖关系, 以及输入和输出的变量。Commons JS和AMD模块,都只能在运行时确定这些东西。

1
2
3
import "jquery"
export function doStuff(){}
module "localModule"{}

优点

  • 容易进行静态分析
  • 面向未来的Ecma Script标准

缺点

  • 原生浏览器端还没有实现该标准
  • 全新的命令,新版的Node JS才支持

实现

  • Babel

大家期望的模块
  系统可以兼容多种模块风格, 尽量可以利用已有的代码, 不仅仅只是JavaScript模块化, 还有CSS、图片、字体等资源也需要模块化。

7.3、安装webpack

WebPack是一款模块加载器兼打包工具, 它能把各种资源, 如JS、JSX、ES 6、SASS、LESS、图片等都作为模块来处理和使用。

安装:

1
2
npm install webpack -g
npm install webpack-cli -g

测试安装成功

  • webpack -v
  • webpack-cli -v

image-20211215155304465

配置
创建 webpack.config.js配置文件

  • entry:入口文件, 指定Web Pack用哪个文件作为项目的入口
  • output:输出, 指定WebPack把处理完成的文件放置到指定路径
  • module:模块, 用于处理各种类型的文件
  • plugins:插件, 如:热更新、代码重用等
  • resolve:设置路径指向
  • watch:监听, 用于设置文件改动后直接打包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
entry:"",
output:{
path:"",
filename:""
},
module:{
loaders:[
{test:/\.js$/,;\loade:""}
]
},
plugins:{},
resolve:{},
watch:true
}

直接运行webpack命令打包

7.4、使用webpack

  1. 创建项目
  2. 创建一个名为modules的目录,用于放置JS模块等资源文件
  3. 在modules下创建模块文件,如hello.js,用于编写JS模块相关代码
1
2
3
4
//暴露一个方法:sayHi
exports.sayHi = function(){
document.write("<div>Hello Webpack</div>");
}
  1. 在modules下创建一个名为main.js的入口文件,用于打包时设置entry属性
1
2
3
//require 导入一个模块,就可以调用这个模块中的方法了
var hello = require("./hello");
hello.sayHi();
  1. 在项目目录下创建webpack.config.js配置文件,使用webpack命令打包
1
2
3
4
5
6
7
module.exports = {
entry:"./modules/main.js",
output:{
filename:"./js/bundle.js"
}

}
  1. 在项目目录下创建HTML页面,如index.html,导入webpack打包后的JS文件
1
2
3
4
5
6
7
8
9
10
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>狂神说Java</title>
</head>
<body>
<script src="dist/js/bundle.js"></script>
</body>
</html>
  1. 在IDEA控制台中直接执行webpack;如果失败的话,就使用管理员权限运行即可!
  2. 运行HTML看效果

说明

1
2
# 参数--watch 用于监听变化
webpack --watch

8、vue-router路由

8.1、说明

学习的时候,尽量的打开官方的文档

Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成, 让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于Vue js过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的CSS class的链接
  • HTML5 历史模式或hash模式, 在IE 9中自动降级
  • 自定义的滚动行

8.2、安装

基于第一个vue-cli进行测试学习; 先查看node modules中是否存在vue-router
  vue-router是一个插件包, 所以我们还是需要用n pm/cn pm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。

1
npm install vue-router --save-dev

如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:

1
2
3
4
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter);

8.3、测试

1、先删除没有用的东西
2、components 目录下存放我们自己编写的组件
3、定义一个Content.vue 的组件

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<h1>内容页</h1>
</div>
</template>

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

Main.vue组件

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<h1>首页</h1>
</div>
</template>

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

4、安装路由,在src目录下,新建一个文件夹:router,专门存放路由,配置路由index.js,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Vue from'vue'
//导入路由插件
import Router from 'vue-router'
//导入上面定义的组件
import Content from '../components/Content'
import Main from '../components/Main'
//安装路由
Vue.use(Router) ;
//配置路由
export default new Router({
routes:[
{
//路由路径
path:'/content',
//路由名称
name:'content',
//跳转到组件
component:Content
},{
//路由路径
path:'/main',
//路由名称
name:'main',
//跳转到组件
component:Main
}
]
});

5、在main.js中配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Vue from 'vue'
import App from './App'

//导入上面创建的路由配置目录
import router from './router'//自动扫描里面的路由配置

//来关闭生产模式下给出的提示
Vue.config.productionTip = false;

new Vue({
el:"#app",
//配置路由
router,
components:{App},
template:'<App/>'
});

6、在App.vue中使用路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div id="app">
<!--
router-link:默认会被渲染成一个<a>标签,to属性为指定链接
router-view:用于渲染路由匹配到的组件
-->
<router-link to="/">首页</router-link>
<router-link to="/content">内容</router-link>
<router-view></router-view>
</div>
</template>

<script>
export default{
name:'App'
}
</script>
<style></style>

9、实战快速上手

我们采用实战教学模式并结合ElementUI组件库,将所需知识点应用到实际中,以最快速度带领大家掌握Vue的使用;

9.1、创建工程

注意:命令行都要使用管理员模式运行
1、创建一个名为hello-vue的工程vue init webpack hello-vue
2、安装依赖, 我们需要安装vue-router、element-ui、sass-loader和node-sass四个插件

1
2
3
4
5
6
7
8
9
10
11
12
#进入工程目录
cd hello-vue
#安装vue-routern
npm install vue-router --save-dev
#安装element-ui
npm i element-ui -S
#安装依赖
npm install
# 安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启功测试
npm run dev

3、Npm命令解释:

  • npm install moduleName:安装模块到项目目录下
  • npm install -g moduleName:-g的意思是将模块安装到全局,具体安装到磁盘哪个位置要看npm config prefix的位置
  • npm install -save moduleName:–save的意思是将模块安装到项目目录下, 并在package文件的dependencies节点写入依赖,-S为该命令的缩写
  • npm install -save-dev moduleName:–save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写

9.2、创建登录页面

把没有用的初始化东西删掉!
  在源码目录中创建如下结构:

  • assets:用于存放资源文件
  • components:用于存放Vue功能组件
  • views:用于存放Vue视图组件
  • router:用于存放vue-router配置

image-20211215172646866

创建首页视图,在views目录下创建一个名为Main.vue的视图组件:

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>首页</div>
</template>
<script>
export default {
name:"Main"
}
</script>
<style scoped>
</style>

创建登录页视图在views目录下创建名为Login.vue的视图组件,其中el-*的元素为ElementUI组件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>

<el-dialog title="温馨提示" :visible.sync="dialogVisiable" width="30%" :before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>

<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",trigger:"blur"}
]
},

//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>

<style lang="scss" scoped>
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align:center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>

创建路由,在router目录下创建一个名为index.js的vue-router路由配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//导入vue
import Vue from 'vue';
import VueRouter from 'vue-router';
//导入组件
import Main from "../views/Main";
import Login from "../views/Login";
//使用
Vue.use(VueRouter);
//导出
export default new VueRouter({
routes: [
{
//首页
path: '/main',
component: Main
},
//登录页
{
path: '/login',
component: Login
},
]

})

APP.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div id="app">
<router-view></router-view>
</div>
</template>

<script>


export default {
name: 'App',

}
</script>

<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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"

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(router)
Vue.use(ElementUI)

/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render:h=>h(App)
})

测试:在浏览器打开 http://localhost:8080/#/login
如果出现错误: 可能是因为sass-loader的版本过高导致的编译错误,当前最高版本是8.0.2,需要退回到7.3.1 ;
去package.json文件里面的 “sass-loader”的版本更换成7.3.1,然后重新cnpm install就可以了;

9.3、路由嵌套

嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
1、 创建用户信息组件,在 views/user 目录下创建一个名为 Profile.vue 的视图组件;
Profile.vue

1
2
3
4
5
6
7
8
9
10
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "UserProfile"
}
</script>
<style scoped>
</style>

2、在用户列表组件在 views/user 目录下创建一个名为 List.vue 的视图组件;
List.vue

1
2
3
4
5
6
7
8
9
10
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "UserList"
}
</script>
<style scoped>
</style>

3、 修改首页视图,我们修改 Main.vue 视图组件,此处使用了 ElementUI 布局容器组件,代码如下:
Main.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>

<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped lang="scss">
.el-header {
background-color: #056eee;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>

4、 配置嵌套路由修改 router 目录下的 index.js 路由配置文件,使用children放入main中写入子模块,代码如下
index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//导入vue
import Vue from 'vue';
import VueRouter from 'vue-router';
//导入组件
import Main from "../views/Main";
import Login from "../views/Login";
//导入子模块
import UserList from "../views/user/List";
import UserProfile from "../views/user/Profile";

//使用
Vue.use(VueRouter);
//导出
export default new VueRouter({
routes: [
{
//登录页
path: '/main',
component: Main,
// 写入子模块
children: [
{
path: '/user/profile',
component: UserProfile,
}, {
path: '/user/list',
component: UserList,
},
]
},
//首页
{
path: '/login',
component: Login

},
]
})

5、 路由嵌套实战效果图

image-20211215184620999

图 路由嵌套效果图

9.4、参数传递

这里演示如果请求带有参数该怎么传递

用的还是上述例子的代码 修改一些代码 这里不放重复的代码了
第一种取值方式

1、 修改路由配置, 主要是router下的index.js中的 path 属性中增加了 :id 这样的占位符

1
2
3
4
5
{
path: '/user/profile/:id',
name:'UserProfile',
component: UserProfile
}

2、传递参数
 此时我们在Main.vue中的route-link位置处 to 改为了 :to,是为了将这一属性当成对象使用,注意 router-link 中的 name 属性名称 一定要和 路由中的 name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;

1
2
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>

3、在要展示的组件Profile.vue中接收参数 使用 {undefined{$route.params.id}}来接收
Profile.vue 部分代码

1
2
3
4
5
6
7
<template>
<!-- 所有的元素必须在根节点下-->
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>

第二种取值方式 使用props 减少耦合
1、修改路由配置 , 主要在router下的index.js中的路由属性中增加了 props: true 属性

1
2
3
4
5
6
{
path: '/user/profile/:id',
name:'UserProfile',
component: UserProfile,
props: true
}

2、传递参数和之前一样 在Main.vue中修改route-link地址

1
2
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>

3、在Profile.vue接收参数为目标组件增加 props 属性
Profile.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>
个人信息
{{ id }}
</div>
</template>
<script>
export default {
props: ['id'],
name: "UserProfile"
}
</script>
<style scoped>
</style>

image-20211215190739404

图 传参效果图

9.5、组件重定向

重定向的意思大家都明白,但 Vue 中的重定向是作用在路径不同但组件相同的情况下,比如:
在router下面index.js的配置

1
2
3
4
5
6
7
8
9
{
path: '/main',
name: 'Main',
component: Main
},
{
path: '/goHome',
redirect: '/main'
}

说明:这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件;

使用的话,只需要在Main.vue设置对应路径即可;

1
2
3
<el-menu-item index="1-3">
<router-link to="/goHome">回到首页</router-link>
</el-menu-item>

9.6、路由模式与 404

路由模式有两种

修改路由配置,代码如下:

1
2
3
4
5
export default new Router({
mode: 'history',
routes: [
]
});

404 demo
1.创建一个NotFound.vue视图组件
NotFound.vue

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>
<h1>404,你的页面走丢了</h1>
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>

2.修改路由配置index.js

1
2
3
4
5
import NotFound from '../views/NotFound'
{
path: '*',
component: NotFound
}

3.效果图

image-20211215192409454

图 404效果图

路由钩子与异步请求

beforeRouteEnter:在进入路由前执行
beforeRouteLeave:在离开路由前执行

在Profile.vue中写

1
2
3
4
5
6
7
8
9
10
11
export default {
name: "UserProfile",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
next();
}
}

参数说明:
to:路由将要跳转的路径信息
from:路径跳转前的路径信息
next:路由的控制参数
next() 跳入下一个页面
next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
next(false) 返回原来的页面
next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例

在钩子函数中使用异步请求

1、安装 Axios

1
cnpm install --save vue-axios

2、main.js引用 Axios

1
2
3
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)

3、准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放入该目录下。
数据和之前用的json数据一样 需要的去上述axios例子里

1
2
// 静态数据存放的位置
static/mock/data.json

4.在 beforeRouteEnter 中进行异步请求
Profile.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
export default {
//第二种取值方式
// props:['id'],
name: "UserProfile",
//钩子函数 过滤器
beforeRouteEnter: (to, from, next) => {
//加载数据
console.log("进入路由之前")
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("离开路由之前")
next();
},
//axios
methods: {
getData: function () {
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function (response) {
console.log(response)
})
}
}
}

5.路由钩子和axios结合图

image-20211215200739611

图 效果图

gitee:

github: