Fork me on GitHub

Promise

# Promise

前言: Promise 是 异步编程 的一种解决方案,相比传统的解决方案 —> 回调函数和事件更加强大。由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象.

没有 Promise 之前,解决异步都是依赖回调,但执行多个具备前后顺序的异步操作时代码就会非常乱并且出现问题难调试.

Promise 的 本质 是要干什么的:就是单纯的为了解决 回调地狱问题,但是并不能帮我们减少代码量。

# "回调地狱" 是什么

” 回调地狱 “也叫” 回调金字塔 “,我们平时写代码的时候 js 如果异步 回调是不可避免的
例如 ajax 不断的进行异步请求数据 回调方法里还要对数据进行处理,继续回调… 形成回调地狱
这会使得我们的代码可读性变差,出现问题 不好调试 也会导致性能下降

而 nodejs 是一种单线程的事件驱动而且是非阻塞的 I/O 模型,而 I/O 模型,是异步的,这样 nodejs 在处理结果的时候 就需要在回调函数中执行,这样也就形成了回调地狱。

mark

# Promise 介绍

Promise 就是实现异步编程的一种解决方案,核心就是 Promise 对象。Promise 对象就像一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。它可以将异步操作以同步操作的流程表达出来,避免层层嵌套 (callback) 的毁掉函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 对象的两个特点

  1. 对象的状态不受外界影响。有三种状态:pending (进行中)、resolved (已成功)、rejected (已失败);

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 resolved 和从 pending 变为 rejected。

Promise 缺点:

  1. 无法取消 Promise,一旦新建它就会立即执行,无法中途取消;
  2. 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部;
  3. 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

# 基本概念:

  1. Promise 是一个构造函数,既然是构造函数,那么,我们就可以 new Promise () 得到一个 Promise 的实例

  2. 在 Promise 上,有两个函数,分别叫做 resolve(成功之后的回调函数) 和 reject(失败之后的回调函数)

  3. 在 Promise 构造函数的 Prototype 属性上,有一个 .then() 方法,也就说,只要是 Promise 构造函数创建的实例,都可以访问到 .then () 方法。

  4. Promise 表示一个 异步操作,每当我们 new 一个 Promise 的实例,这个实例,就表示一个具体的异步操作

  5. 既然 Promise 创建的实例,是一个异步操作,那么,这个 异步操作的结果,只能有两种状态:

​ 5.1 状态 1: 异步执行成功了,需要在内部调用成功的回调函数(resolve),把结果返回给调用者。

​ 5.2 状态 2: 异步执行失败了,需要在内部调用失败的回调函数(reject),把结果返回给调用者。

​ 5.3 由于 Promise 的实例,是一个异步操作,所以,内部拿到 操作结果后,无法使用 return 把操作的结果返回给调用者;这时候,只能使用回调函数的形式,来把 成功 或 失败的结果,返回给调用者。

  1. 我们可以在 new 出来的 Promise 实例上,调用 .then () 方法,【预先】为这个 Promise 异步操作指定 成功(resolve)和 失败(reject)回调函数

# 关于 Promise 要解决回调地狱问题的说明

实验需求:你要封装一个方法,我给你一个要读取文件的路径,你这个方法帮我读取文件,并把内容返回给我。

代码实际演示:

目录结构:

mark

# 第一步:初步实现一个读取文件的功能:

封装读取文件的方法.js

// 需求:你要封装一个方法,我给你一个要读取文件的路径,你这个方法帮我读取文件,并把内容返回给我

const fs = require("fs");
const path = require("path");

fs.readFile(path.join(__dirname, "./files/1.txt"), "utf-8", (err, dataStr) => {
  if (err) {
    throw err;
  }
  console.log(dataStr);
});

运行结果:

mark

# 第二步:封装一个读取文件的方法

封装读取文件的方法.js

const fs = require("fs");
const path = require("path");

//初衷 :给定文件路径 ,返回读取到的内容
function getFileByPath(fpath) {
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) {
      throw err;
    }
    return dataStr;
  });
}

let result = getFileByPath(path.join(__dirname, "./files/1.txt"));
console.log(result);

结果:

mark

# 解决方案:使用回调函数

封装读取文件的方法.js

const fs = require("fs");
const path = require("path");

//使用回调函数
function getFileByPath(fpath, callback) {
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) {
      return callback(err);
    }
    callback(dataStr);
  });
}

getFileByPath(path.join(__dirname, "./files/1.txt"), (dataStr) => {
  console.log(dataStr);
});

运行结果:

mark

# 封装读取文件的方法–改进:

# callback 中,设置两个参数(err, dataStr)

封装读取文件的方法.js

const fs = require("fs");
const path = require("path");

//使用回调函数
//我们可以规定一下,callback中,有两个参数,第一个参数是失败的结果,第二个参数是成功的结果
//同时,我们规定了:如果成功后,返回的结果,应该位于callback 参数的第二个位置,此时,第一个位置由于没有出错,所以放一个null,如果失败了则第一个位置放置 Error对象,第二个位置放置一个 undefined

function getFileByPath(fpath, callback) {
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) {
      return callback(err);
    }
    callback(null, dataStr);
  });
}

getFileByPath(path.join(__dirname, "./files/1.txt"), (err, dataStr) => {
  if (err) {
    return console.log(err.message);
  }
  console.log(dataStr);
});

mark

# 封装读取文件的方法 - 提高版

# 拆分成 两个 回调(getFileByPath (fpath, succCb, errCb))

封装读取文件的方法 - 提高版.js:

const fs = require("fs");
const path = require("path");

//使用两个回调---便于理解
function getFileByPath(fpath, succCb, errCb) {
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) {
      return errCb(err);
    }
    succCb(dataStr);
  });
}

getFileByPath(
  path.join(__dirname, "./files/1.txt"),
  (dataStr) => {
    console.log("成功的结果,使用成功的回调succCb: " + dataStr);
  },
  (err) => {
    console.log("失败的结果,使用失败的回调errCb: " + err.message);
  }
);

成功的结果:

mark

失败的结果:

mark

# 回调嵌套

# 需求:先读取文件 1,在读取文件 2,最后读取文件 3

# 初步实现:
//使用回调函数
function getFileByPath(fpath, callback) {
  fs.readFile(fpath, "utf-8", (err, dataStr) => {
    if (err) {
      return callback(err.message);
    }
    callback(dataStr);
  });
}

//回调地狱
getFileByPath(path.join(__dirname, "./files/1.txt"), (dataStr) => {
  console.log(dataStr);
  getFileByPath(path.join(__dirname, "./files/2.txt"), (dataStr) => {
    console.log(dataStr);
    getFileByPath(path.join(__dirname, "./files/3.txt"), (dataStr) => {
      console.log(dataStr);
    });
  });
});

运行结果:

mark

# 使用 **Promise **:

const fs = require("fs");
const path = require("path");

function getFileByPath(fpath) {
  return new Promise(function (resolve, reject) {
    fs.readFile(path.join(__dirname, fpath), "utf-8", (err, dataStr) => {
      if (err) {
        return reject(err);
      }
      resolve(dataStr);
    });
  });
}

getFileByPath("./files/1.txt")
  .then(function (data) {
    console.log(data);
    //读取文件2
    return getFileByPath("./files/2.txt");
  })
  .then(function (data) {
    console.log(data);
    //读取文件3
    return getFileByPath("./files/3.txt");
  })
  .then(function (data) {
    console.log(data);
  })
  .catch(function (err) {
    console.log(err.message);
  });

mark

# Ajax 使用 then 方法高逼格请求数据

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="./node_modules/jquery/dist/jquery.min.js"></script>
  </head>
  <body>
    <button id="btn">按钮</button>
    <script>
      $(function () {
        $("#btn").click(function () {
          $.ajax({
            url: "./data.json",
            type: "get",
            dataType: "json",
          }).then((data) => {
            console.log(data);
          });
        });
      });
    </script>
  </body>
</html>

运行结果:

mark

操作系统进程调度作业

# 操作系统进程调度作业

操作系统进程调度作业

锻炼下思维!

# 进程调度 1

问题描述

​ 要求输入 3 个进程,找出最先执行的那个进程的进程名。(如果遇到优先级一样,按照输入顺序执行。),本题中,优先数数值大的表示优先级比较高。

输入格式

​ 程序要求输入 3 行,以回车符号作为分隔,每行有 3 个数据,以空格作为分隔。首先输入一个字符串(长度小于等于 10),为进程名,第 2 个数据类型为整型,表示进程的优先数,第 3 个数据类型为整型,表示进程的运行时间。

输出格式

​ 输出一个字符串,为最先执行进程的进程名。

样例输入 1:

P1 1 1

P2 2 2

P3 3 3

样例输出 1:

P3

样例输入 2:

P1 130 10

P2 100 100

P3 100 100

样例输出 2:

P2

链表实现:

#include <stdio.h>
#include <string.h> //strcpy()
#include <stdlib.h>//malloc()
void insertQuestion();
void insertNode(char ProcessName[5],int Priority,int Time);
void view();
void arithmetic();
typedef struct process
{
	char ProcessName[5];
	int Priority;
	int Time;
	struct process *next;
}nodelist;

nodelist *pHead=NULL;//存放调度的首节点地址

int main(void){
    insertQuestion();
    arithmetic();
	system("pause");
    return 0;
}

//添加问题
void insertQuestion()
{
	void insertNode(char ProcessName[5],int Priority,int Time);
    char ProcessName[5];
	int Priority;
	int Time;
    int i=0;
	for (i = 0; i < 3; i++)
	{
	  scanf("%s",ProcessName);
	  scanf("%d",&Priority);
	  scanf("%d",&Time);
	  insertNode(ProcessName,Priority,Time);
	}

}

//将数据插入链表
void insertNode(char ProcessName[5],int Priority,int Time)
{
	//申请存储空间
	nodelist *pNew=(nodelist *)malloc(sizeof(nodelist));
	nodelist *p,*q;
	strcpy(pNew->ProcessName,ProcessName);
	pNew->Priority = Priority;
	pNew->Time = Time;
	pNew->next=NULL;
	// printf("5\n");
		if(pHead==NULL) //插入前链表为空,新插入的节点为头节点
		{
			pHead=pNew;
			//p1=pHead;
		}
		else
		{
			p=pHead;
			if(p->next!=0)
			{
				while(p->next!=0)
				{
				q=p->next;
			    p=q;
				}
				p->next=pNew;
				pNew->next=NULL;
			}
			else if (p->next==0)
			{
				p->next=pNew;
			    p=pNew;
				pNew->next=NULL;
			}

		}
}

//显示链表中的数据
void view()
{
	/* 显示所有的结果 */
	nodelist *p=pHead;
	if(pHead!=NULL)
	{
			while(p!=NULL)
			{
				printf("%s ",p->ProcessName);
				printf("%d ",p->Priority);
				printf("%d ",p->Time);
				p=p->next;
				printf("\n");
			}
	}
	else
	{
	  printf("链表中啥都没有!\n");
	}
}


void arithmetic()
{
	//相关算法实现
	nodelist *p=pHead;
	int max=p->Priority;
	int flag=0;
	char firstName[5];
	char ReturnProcessName[5];
	strcpy(firstName,p->ProcessName);
	if(pHead!=NULL)
	{
			while(p!=NULL)
			{
				if( max < (p->Priority)){
                    max= p->Priority;
                    strcpy(ReturnProcessName,p->ProcessName);
					flag=1;
				}
				p=p->next;
			}
	}
	if(flag==1){
		printf("%s\n",ReturnProcessName);
	}else{
		printf("%s\n",firstName);
	}

}

mark

mark

# 进程调度 2

问题描述

​ 要求输入 N 个进程(0<N<=100),找出最后执行的那个进程的进程名。(如果遇到优先级一样,按照输入顺序执行。),本题中,优先数数值较高的优先级也较高。

输入格式

​ 程序首先要求输入一个整型值 N,接下来输入为 N 行,以回车符号作为分隔,每行有 3 个数据,以空格作为分隔。首先输入一个字符串(长度小于等于 10),该字符串为进程名。第 2 个数据类型为整型,表示进程的优先数。第 3 个数据类型为整型,表示进程的运行时间。

输出格式:

​ 输出一个字符串,为最后执行进程的进程名。

样例输入 1:

3

P1 1 1

P2 2 2

P3 3 3

样例输出 1:

P3

样例输入 2:

2

P1 10 10

P2 100 100

样例输出 2:

P2

源代码:

#include <stdio.h>
#include <string.h> //strcpy()
#include <stdlib.h>//malloc()
void insertQuestion();
void insertNode(char ProcessName[5],int Priority,int Time);
void view();
void arithmetic();
typedef struct process
{
	char ProcessName[5];
	int Priority;
	int Time;
	struct process *next;
}nodelist;

nodelist *pHead=NULL;//存放调度的首节点地址

int main(){
    insertQuestion();
    arithmetic();
    // view();
    return 0;
}

//添加问题
void insertQuestion()
{
	void insertNode(char ProcessName[5],int Priority,int Time);
    char ProcessName[5];
	int Priority;
	int Time;
    int i=0;
    int n;
    scanf("%d",&n);
	for (i = 0; i < n; i++)
	{
	  scanf("%s %d %d",ProcessName,&Priority,&Time);
	  insertNode(ProcessName,Priority,Time);
	}
}

//将数据插入链表
void insertNode(char ProcessName[5],int Priority,int Time)
{
	//申请存储空间
	nodelist *pNew=(nodelist *)malloc(sizeof(nodelist));
	nodelist *p,*q;
	strcpy(pNew->ProcessName,ProcessName);
	pNew->Priority = Priority;
	pNew->Time = Time;
	pNew->next=NULL;
	// printf("5\n");
		if(pHead==NULL) //插入前链表为空,新插入的节点为头节点
		{
			pHead=pNew;
			//p1=pHead;
		}
		else
		{
			//将地址为pNew的节点插入到首地址为pHead的链表的尾部
			/*p1->next=pNew;
			p1=pNew;
			pNew->next=NULL;*/
			p=pHead;
			//q=(nodelist *)malloc(sizeof(nodelist));
			if(p->next!=0)     //文件不为空,即pHead后面有数据
			{
				while(p->next!=0)
				{
				q=p->next;
			    p=q;
				}
				p->next=pNew;
				pNew->next=NULL;
			}
			else if (p->next==0) //只有头结点,即pHead后面无数据
			{
				//将地址为pNew的节点插入到首地址为pHead的链表的尾部
				p->next=pNew;
			    p=pNew;
				pNew->next=NULL;
			}
		}
}

//显示链表中的数据
void view()
{
	/* 显示所有的结果 */
	nodelist *p=pHead;
	if(pHead!=NULL)
	{
			while(p!=NULL)
			{
				printf("%s ",p->ProcessName);
				printf("%d ",p->Priority);
				printf("%d ",p->Time);
				p=p->next;
				printf("\n");
			}
	}
	else
	{
	  printf("链表中啥都没有!\n");
	}
}


void arithmetic()
{
	//相关算法实现
	nodelist *p=pHead;
	nodelist *q=pHead;
	nodelist *m=pHead;
	int max=p->Priority;
	int flag=0;
	int i;
	int sum=0;
	char firstName[5];
	char ReturnProcessName[5];
	char FinallProcessName[5];
	strcpy(ReturnProcessName,p->ProcessName);

	if(pHead!=NULL)
	{

	while(m!=0){
		sum+=m->Time;
	    m=m->next;
	}

		for (i = 0; i < sum; ++i)
		{
			while(p!=NULL)
			{
				if( max < (p->Priority) && (p->Time>0)){
                    max= p->Priority;
                    strcpy(ReturnProcessName,p->ProcessName);
					flag=1;
				}
				p=p->next;

			}
				  	while(q!=NULL)
						{
							if(strcmp(q->ProcessName,ReturnProcessName) == 0){
                                strcpy(FinallProcessName,q->ProcessName);
								q->Priority--;
								q->Time--;
							}
							q=q->next;
						}
					p=pHead;
					q=pHead;
					max=p->Priority;
					strcpy(ReturnProcessName,p->ProcessName);

		}
			printf("%s\n",FinallProcessName);
	}
}

mark

mark

# 进程调度 3

问题描述

​ 要求输入 N 个进程(N 为正整型数,0<N<=25535),输出按照优先级从高到低执行的进程名字符串序列,直至结束。(如果遇到优先级一样,按照输入顺序先后执行。),本题中,优先数数值较高的进程,优先级也较高。

输入格式

​ 程序首先要求输入一个整型变量 N,接下来输入为 N 行,以回车符号作为分隔,每行有 3 个数据,以空格作为分隔。首先输入一个字符串(长度小于等于 10),该字符串为进程名。第 2 个数据类型为整型,表示进程的优先数。第 3 个数据类型为整型,表示进程的运行时间。

输出格式

​ 输出 1 行,M 个字符串,字符串之间用空格作为分隔。

样例输入 1:

3

P1 1 1

P2 2 2

P3 3 3

样例输出 1:

P3 P2 P3 P1 P2 P3

样例输入 2:

2

P1 3 3

P2 1 1

样例输出 2:

P1 P1 P1 P2

样例输入 3:

100

P0 0 1 P1 1 1 P2 2 1 P3 3 1 P4 4 1 P5 5 1 P6 6 1 P7 7 1 P8 8 1 P9 9 1 P10 10 1 P11 11 1 P12 12 1 P13 13 1 P14 14 1 P15 15 1 P16 16 1 P17 17 1 P18 18 1 P19 19 1 P20 20 1 P21 21 1 P22 22 1 P23 23 1 P24 24 1 P25 25 1 P26 26 1 P27 27 1 P28 28 1 P29 29 1 P30 30 1 P31 31 1 P32 32 1 P33 33 1 P34 34 1 P35 35 1 P36 36 1 P37 37 1 P38 38 1 P39 39 1 P40 40 1 P41 41 1 P42 42 1 P43 43 1 P44 44 1 P45 45 1 P46 46 1 P47 47 1 P48 48 1 P49 49 1 P50 50 1 P51 51 1 P52 52 1 P53 53 1 P54 54 1 P55 55 1 P56 56 1 P57 57 1 P58 58 1 P59 59 1 P60 60 1 P61 61 1 P62 62 1 P63 63 1 P64 64 1 P65 65 1 P66 66 1 P67 67 1 P68 68 1 P69 69 1 P70 70 1 P71 71 1 P72 72 1 P73 73 1 P74 74 1 P75 75 1 P76 76 1 P77 77 1 P78 78 1 P79 79 1 P80 80 1 P81 81 1 P82 82 1 P83 83 1 P84 84 1 P85 85 1 P86 86 1 P87 87 1 P88 88 1 P89 89 1 P90 90 1 P91 91 1 P92 92 1 P93 93 1 P94 94 1 P95 95 1 P96 96 1 P97 97 1 P98 98 1 P99 99 1

样例输出 3:

P100 P99 P98 P97 P96 P95 P94 P93 P92 P91 P90 P89 P88 P87 P86 P85 P84 P83 P82 P81 P80 P79 P78 P77 P76 P75 P74 P73 P72 P71 P70 P69 P68 P67 P66 P65 P64 P63 P62 P61 P60 P59 P58 P57 P56 P55 P54 P53 P52 P51 P50 P49 P48 P47 P46 P45 P44 P43 P42 P41 P40 P39 P38 P37 P36 P35 P34 P33 P32 P31 P30 P29 P28 P27 P26 P25 P24 P23 P22 P21 P20 P19 P18 P17 P16 P15 P14 P13 P12 P11 P10 P9 P8 P7 P6 P5 P4 P3 P2 P1

源代码:

#include <stdio.h>
#include <string.h> //strcpy()
#include<stdlib.h>//malloc()
void insertQuestion();
void insertNode(char ProcessName[5],int Priority,int Time);
void view();
void arithmetic();
typedef struct process
{
	char ProcessName[5];
	int Priority;
	int Time;
	struct process *next;
}nodelist;

nodelist *pHead=NULL;//存放调度的首节点地址

int main(){
    insertQuestion();
    arithmetic();
    // view();
    return 0;
}

//添加问题
void insertQuestion()
{
	void insertNode(char ProcessName[5],int Priority,int Time);
    char ProcessName[5];
	int Priority;
	int Time;
    int i=0;
    int n;
    scanf("%d",&n);
	for (i = 0; i < n; i++)
	{
	  scanf("%s %d %d",ProcessName,&Priority,&Time);
	  insertNode(ProcessName,Priority,Time);
	}
}

//将数据插入链表
void insertNode(char ProcessName[5],int Priority,int Time)
{
	//申请存储空间
	nodelist *pNew=(nodelist *)malloc(sizeof(nodelist));
	nodelist *p,*q;
	strcpy(pNew->ProcessName,ProcessName);
	pNew->Priority = Priority;
	pNew->Time = Time;
	pNew->next=NULL;
	// printf("5\n");
		if(pHead==NULL) //插入前链表为空,新插入的节点为头节点
		{
			pHead=pNew;
			//p1=pHead;
		}
		else
		{
			//将地址为pNew的节点插入到首地址为pHead的链表的尾部
			/*p1->next=pNew;
			p1=pNew;
			pNew->next=NULL;*/
			p=pHead;
			//q=(nodelist *)malloc(sizeof(nodelist));
			if(p->next!=0)     //文件不为空,即pHead后面有数据
			{
				while(p->next!=0)
				{
				q=p->next;
			    p=q;
				}
				p->next=pNew;
				pNew->next=NULL;
			}
			else if (p->next==0) //只有头结点,即pHead后面无数据
			{
				//将地址为pNew的节点插入到首地址为pHead的链表的尾部
				p->next=pNew;
			    p=pNew;
				pNew->next=NULL;
			}

		}
}

//显示链表中的数据
void view()
{
	/* 显示所有的结果 */
	nodelist *p=pHead;
	if(pHead!=NULL)
	{
			while(p!=NULL)
			{
				printf("%s ",p->ProcessName);
				printf("%d ",p->Priority);
				printf("%d ",p->Time);
				p=p->next;
				printf("\n");
			}
	}
	else
	{
	  printf("链表中啥都没有!\n");
	}
}


void arithmetic()
{
	//相关算法实现
	nodelist *p=pHead;
	nodelist *q=pHead;
	nodelist *m=pHead;
	int max=p->Priority;
	int flag=0;
	int i;
	int sum=0;
	char firstName[5];
	char ReturnProcessName[5];
	strcpy(ReturnProcessName,p->ProcessName);

	if(pHead!=NULL)
	{

	while(m!=0){
		sum+=m->Time;
	    m=m->next;
	}
    // printf("1\n");
    // printf("%d\n",sum);
    // printf("2\n");


		for (i = 0; i < sum; ++i)
		{
			while(p!=NULL)
			{
				if( max < (p->Priority) && (p->Time>0)){
                    max= p->Priority;
                    strcpy(ReturnProcessName,p->ProcessName);
					flag=1;
				}
				p=p->next;

			}
					// printf("ReturnProcessName:%s\n", ReturnProcessName);
                    //printf("q->ProcessName:%s\n",q->ProcessName);
                    //printf("\n");
				  	while(q!=NULL)
						{
							 // printf("ReturnProcessName:%s\n", ReturnProcessName);
      						 // printf("q->ProcessName:%s\n",q->ProcessName);
							if(strcmp(q->ProcessName,ReturnProcessName) == 0){
								printf("%s ",q->ProcessName);
								q->Priority--;
								q->Time--;
							}
							q=q->next;
						}
					p=pHead;
					q=pHead;
					max=p->Priority;
					// printf("\n");
					// view();
					// printf("\n");

					strcpy(ReturnProcessName,p->ProcessName);
		}
	}
}

mark

mark

# 进程调度 4:时间片轮转

问题描述

​ 要求输入 N 个进程(0<N<=100),输入时间片 M(0<M〈=5),按照进程输入的顺序以时间片轮转的方法输出指定的第 K 轮(K>0)执行的那个进程的进程名。

输入格式

​ 程序首先输入一个正整数 M(0<M〈=5)作为时间片,下一行输入一个正整数 N(0<N<=100),接下来输入为 N 行,以回车符号作为分隔,每行有 2 个数据,以空格作为分隔。第一个数据是字符串(长度小于等于 10),该字符串为进程名,第 2 个数据类型为整型,表示该进程需要的运行时间。最后输入一个正整数 K,作为时间片轮转的次数(次数从 1 开始计数)。

输出格式

​ 输出一个字符串,为最后执行进程的进程名;若无进程运行,则输出 “over”(不含双引号,所有字母皆为小写)。

样例输入 1:

1

3

P1 1

P2 2

P3 3

3

样例输出 1:P3

样例输入 2:

1

3

P1 1

P2 2

P3 3

10

样例输出 2:over

样例输入 3:

2

3

P1 1

P2 2

P3 3

4

样例输出 3:P3

代码展示:

#include <stdio.h>
#include <string.h> //strcpy()
#include<stdlib.h>//malloc()
void insertQuestion();
 void insertNode(char ProcessName[5],int Time);
void view();
void arithmetic(int M,int n,int count);
typedef struct process
{
    char ProcessName[5];
    int Priority;
    int Time;
    struct process *next;
}nodelist;

nodelist *pHead=NULL;//存放调度的首节点地址

int main(){
    insertQuestion();
    // view();
    return 0;
}

//添加问题
void insertQuestion()
{
    void insertNode(char ProcessName[5],int Time);
	void arithmetic(int M,int n,int count);
    char ProcessName[5];
    int Priority;
    int Time;
    int i=0;
    int n;
	int M;
	int count;
	scanf("%d",&M);
    scanf("%d",&n);
    for (i = 0; i < n; i++)
    {
      scanf("%s %d",ProcessName,&Time);
      insertNode(ProcessName,Time);
    }
	scanf("%d",&count);
	arithmetic(M,n,count);
}

//将数据插入链表
void insertNode(char ProcessName[5],int Time)
{
    //申请存储空间
    nodelist *pNew=(nodelist *)malloc(sizeof(nodelist));
    nodelist *p,*q;
    strcpy(pNew->ProcessName,ProcessName);
    pNew->Time = Time;
    pNew->next=NULL;
    // printf("5\n");
        if(pHead==NULL) //插入前链表为空,新插入的节点为头节点
        {
            pHead=pNew;
            //p1=pHead;
        }
        else
        {
            //将地址为pNew的节点插入到首地址为pHead的链表的尾部
            /*p1->next=pNew;
            p1=pNew;
            pNew->next=NULL;*/
            p=pHead;
            //q=(nodelist *)malloc(sizeof(nodelist));
            if(p->next!=0)     //文件不为空,即pHead后面有数据
            {
                while(p->next!=0)
                {
                q=p->next;
                p=q;
                }
                p->next=pNew;
                pNew->next=NULL;
            }
            else if (p->next==0) //只有头结点,即pHead后面无数据
            {
                //将地址为pNew的节点插入到首地址为pHead的链表的尾部
                p->next=pNew;
                p=pNew;
                pNew->next=NULL;
            }

        }
}

//显示链表中的数据
void view()
{
    /* 显示所有的结果 */
    nodelist *p=pHead;
    if(pHead!=NULL)
    {
            while(p!=NULL)
            {
                printf("%s ",p->ProcessName);
                printf("%d ",p->Time);
                p=p->next;
                printf("\n");
            }
    }
    else
    {
      printf("链表中啥都没有!\n");
    }
}


void arithmetic(int M,int n,int count)
{
    //相关算法实现
    nodelist *p=pHead;
    int index=0;
    int i=0;
    int forcount=0;
    int Numcount=0;
    int sum=0;
    int flag=0;
    int finallyNum=0;
	 // printf("%d %d %d\n",M,n,count);
     // printf("%d\n",count/n);

    if(count%n==0)
    {
        forcount=count/n;
    }else{
        forcount=count/n+1;
    }
     // printf("forcount:%d\n", forcount);
     for (i = 0; i < forcount; i++)
         {
                    while(p!=NULL)
                    {

                        if(p->Time>0){

                            p->Time=p->Time - M;
                            // printf("%s\n",p->ProcessName);
                            // view();
                            // printf("\n");
                            Numcount++;
                            // printf("%d\n",Numcount);
                            finallyNum=Numcount;
                            if (Numcount==count)
                            {
                              printf("%s\n",p->ProcessName);
                            }
                        }

                         p=p->next;
                    }
                    p=pHead;
         }
if(finallyNum < count){
   printf("over\n");
}
}

mark

mark

mark


# 存储管理 1

问题描述

​ 现有一个 8*8 的存储器,要对其空间进行分配。(下标从 0 开始,最后一个内存块下标为 63)。现已有块号为 1、7、13、23、47、59 的几个内存块被占用。现操作系统要求申请 N 块内存空间(0<N<=64),当输入的块数 N 超出其剩余空闲块数的时候,输出为 “false”,当输入为合理范围的时候,就输出其以行主序分配的最后一个内存空间的下标。

输入格式:

​ 程序要求输入一个整型数 N,表示要申请分配空间的大小。

输出格式

​ 输出为一个整型数,表示最后一个被分配空间的下标。

样例输入 1:

3

样例输出 1:

3

样例输入 2:

100

样例输出 2:

false

样例输入 3:

50

样例输出 3:

54

源代码

#include <stdio.h>
int main()
{
    int arr[64]={0};
    int i=0;
    int n;
    int count=0;
    arr[1]=1;
    arr[7]=1;
    arr[13]=1;
    arr[23]=1;
    arr[47]=1;
    arr[59]=1;
    scanf("%d",&n);
    if (n>58)
    {
    	printf("false\n");
    }else{
    	 for (i = 0; i < n+1; i++)
		    {
		    	if (arr[i]==1)
		    	{
		    		count++;
		   		}
		 	}
 	printf("%d\n", count+n-1);
    }
	return 0;
}

mark

mark

mark

# 存储管理 2

问题描述

​ 现有一个 8*8 的存储器,要对其空间进行分配。(下标从 0 开始,最后一个内存块下标为 63)。现已有块号为 2、7、13、23、37、47、59、61 的几个内存块被占用。要求输入需分配的进程数 M(0<M<=56),接下来输入为 M 个整型数,每个数为各个进程需占用的内存块数。当分配到某进程时,其剩余空闲块数可以分配,就输出当前进程分配的最后一个内存空间的下标。当分配到某进程时,其进程块数超出剩余空闲块数无法分配,输出为 “false”(不含双引号,且为全小写)。输出的多个下标(或 "false")之间用空格隔开。

输入格式

​ 程序输入分为两行,第一行要求输入一个整型数 M,表示要所需分配空间的进程数,接下来的第二行输入 M 个整型数,每个数之间用空格隔开,表示 M 个进程每个进程占用的内存空间大小。

输出格式:

​ 输出为 M 组整型数(或 "false"),每个整型数表示该进程最后一个被分配的内存空间的下标(或 "false"),下标(或 "false")之间用空格隔开。

样例输入 1:

3

3 3 3

样例输出 1:

3 6 10

样例输入 2:

4

3 3 64 3

样例输出 2:

3 6 false 10

源代码:

#include <stdio.h>
#include <string.h>
int main()
{
  void funsave(int n,int arr[64],int residue);
    int arr[64]={0};
    int num[200]={0};
    int inputnum;
    int i=0;
    int x[64]={0};
    int sumsheng=56;
    char thirdName[10];
    char str[10];
    int getInTStr;
    arr[2]=1;
    arr[7]=1;
    arr[13]=1;
    arr[23]=1;
    arr[37]=1;
    arr[41]=1;
    arr[47]=1;
    arr[59]=1;
    arr[61]=1;
    scanf("%d",&inputnum);
    for (i = 0; i < inputnum; i++)
    {
        scanf("%d",&num[i]);
        if (sumsheng>num[i])
        {
           sumsheng=sumsheng-num[i];
        }
      x[i]=sumsheng;

    }

     for (i = 0; i < inputnum; i++)
     {
      funsave(num[i],arr,x[i]);
     }
  return 0;
}

void funsave(int n,int arr[64],int residue){
    int i=0;
    int count=0;
    int indexI=0;
    int flag=0;
    int arr1[20];
    int arr2[20];
    int finallyIndex=0;
    int arrindex=0;
    int arrindex2=0;
   // printf("start n:%d\n",n);
    // printf("getInTStr:%d\n",getInTStr);
  if (n>residue)
    {
      printf("false ");
    }else{
       if (arr[0]==0)
       {
         for (i = 0; i < n; i++)
        {
          if (arr[i]==1)
          {
            count++;
          }
      }
       for (i = 0; i < count+n; i++)
         {
            if (arr[i]==0)
            {
                arr2[arrindex2]=i;
                arrindex2++;
            }
            arr[i]=1;
         }
         printf("%d ", n+count-1);

       }else{
        for (i = 0; i < 64; i++)
        {
          if (arr[i]==0)
          {
            indexI=i;
            goto LOOP;
          }
        }

       LOOP:for(i=indexI;i<indexI+n;i++){
          if (arr[i]==1)
          {
            count++;
          }
        }
        for (i = indexI; i < indexI+count+n; i++)
         {
          if (arr[i]==0)
          {
            finallyIndex=i;
            arr1[arrindex]=i;
          arrindex=arrindex+1;

          }
            arr[i]=1;
         }
         printf("%d ", finallyIndex);

       }


    }
}

mark

mark

# 储存管理 3

问题描述:

​ 现有一个 8*8 的存储器,要对其已分配的空间进行分配及回收。(下标从 0 开始,最后一个内存块下标为 63)。现已有块号为 2、7、13、23、37、41、47、59、61 的几个内存块被占用。要求输入需分配的进程数 M(0<M<=55),接下来输入为 M 个整型数,每个数为各个进程需占用的内存块数。当分配到某进程时,其剩余空闲块数可以分配,就输出当前进程分配的最后一个内存空间的下标。当分配到某进程时,其进程块数超出剩余空闲块数无法分配,输出为 “false”(不含双引号,且为全小写)。输出的多个下标(或 "false")之间用空格隔开。以上进程不管是否分配成功,按照输入顺序依次命名为 p1、p2、p3………pM。回收的时候输入进程名 pN,则返回进程名为 pN 的所有占用内存块号下标,如果该进程名不存在或输入的数值为不合理范围,则返回 “false”。

输入格式:

​ 程序输入分为三行,第一行是一个整型数 M,表示要所需分配空间的进程数,第二行为 M 个整型数,每个数之间用空格隔开,表示 M 个进程每个进程占用的内存空间大小。第三行为需要回收的进程名 pN,p 为小写字母,N 为正整型数。

输出格式:

​ 输出为两行,第一行为一组整型数,每个整型数表示该进程最后一个被分配的内存空间的下标,下标之间用空格隔开。第二行为一组整型数,表示被回收的进程的内存块下标,多个下标之间用空格隔开。

样例输入 1:

3

3 3 3

p3

样例输出 1:

3 6 10

8 9 10

样例输入 2:

4

3 3 64 3

p3

样例输出 2:

3 6 false 10

false

源代码:

#include <stdio.h>
#include <string.h>
int main()
{
	int *funsave(int n,int arr[64],int residue,int getInTStr,int indexflag,int num[64]);
    int arr[64]={0};
    int num[200]={0};
    int inputnum;
    int i=0,j=0;
    int x[64]={0};
    int sumsheng=56;
    char thirdName[10];
    char str[10];
    int getInTStr;
     int *p;
     int indexflag=0;
    arr[2]=1;
    arr[7]=1;
    arr[13]=1;
    arr[23]=1;
    arr[37]=1;
    arr[41]=1;
    arr[47]=1;
    arr[59]=1;
    arr[61]=1;
    scanf("%d",&inputnum);
    for (i = 0; i < inputnum; i++)
    {
        scanf("%d",&num[i]);
        if (sumsheng>num[i])
        {
        	 sumsheng=sumsheng-num[i];
        }
 			x[i]=sumsheng;

    }
    scanf("%s",thirdName);

    strncpy(str, thirdName+1, 3);

	getInTStr=atoi(str);

     for (i = 0; i < inputnum; i++)
     {
     	p=funsave(num[i],arr,x[i],getInTStr,indexflag,num);

     		indexflag=indexflag+1;
     }
     // printf("sumsheng:%d\n", sumsheng);
     // for(i=0;i<inputnum;i++){
     //       printf("%d ",num[i]);
     //   }
       // printf("\n");
       for(i=0;i<inputnum;i++){
           if(num[i]>sumsheng&&getInTStr==i+1){
           		printf("false\n");
           }
       }
       printf("\n");
       if (getInTStr==1)
       {
	       for ( i = 0; i < num[0]; i++ )
		   {
		       printf("%d ", *(p + i));
		   }
       }



	return 0;
}

int *funsave(int n,int arr[64],int residue,int getInTStr,int indexflag,int num[64]){
    int i=0,j=0;
    int count=0;
    int indexI=0;
    int flag=0;
    static  int arr1[64][64];
    static  int arr2[20];
    int finallyIndex=0;
    int arrindex=0;
    int arrindex2=0;
   // printf("start n:%d\n",n);
    // printf("getInTStr:%d\n",getInTStr);
    //
    // for(i=0;i<10;i++){
    // 	printf("%d ", num[i]);
    // }
    // printf("\n");
	if (n>residue)
    {
    	printf("false ");
    }else{
    	 if (arr[0]==0)
    	 {
    	 	 for (i = 0; i < n; i++)
		    {
		    	if (arr[i]==1)
		    	{
		    		count++;
		   		}
		 	}
		 	 for (i = 0; i < count+n; i++)
			   {
			   	  if (arr[i]==0)
			   	  {
			   	      arr2[arrindex2]=i;
			   	      arrindex2++;
			   	  }
			   	  arr[i]=1;
			   }
			   printf("%d ", n+count-1);

    	 }else{
    	 	for (i = 0; i < 64; i++)
    	 	{
    	 		if (arr[i]==0)
    	 		{
    	 			indexI=i;
    	 			goto LOOP;
    	 		}
    	 	}

    	 LOOP:for(i=indexI;i<indexI+n;i++){
    	 		if (arr[i]==1)
		    	{
		    		count++;
		   		}
    	 	}
    	 	// printf("indexflag:%d\n", indexflag);
    	 	for (i = indexI; i < indexI+count+n; i++)
			   {
			   	if (arr[i]==0)
			   	{
			   		finallyIndex=i;
			   		arr1[indexflag-1][arrindex]=i;
					arrindex=arrindex+1;
			   	}
			   	  arr[i]=1;
			   }
			    printf("%d ", finallyIndex);
    			}
    	 }



    	 if (getInTStr==1)
    	 {
    	 	return arr2;
    	 }else{

    	 	// printf("getInTStr:%d\n", getInTStr);
       //       printf("n:%d\n",n );
       //       printf("indexflag:%d\n",indexflag );
    	 	// for(i=0;i<10;i++)
			    // {
			    //     for(j=0;j< 10;j++)
			    //     {
			    //         printf("%d\t",arr1[i][j]);//访问i行j列的二维数组元素
			    //     }
			    //     printf("\n");
			    // }

             if (n<=residue){


             	 if (indexflag+1>=n)
				 {
				 		printf("\n");

				        for(j=0;j<num[getInTStr-1];j++)
				        {
				        	if(arr1[getInTStr-2][j]!=0){
 									printf("%d ",arr1[getInTStr-2][j]);//访问i行j列的二维数组元素
				        	}

				        }
				 }

			}



    	 }

   }

mark

mark

mark

mark

React 学习第三天

# React 第三天学习

在 react 项目中 添加样式

加载外部 css 样式文件

css 样式作用域的问题(modules,localIdentName, local 和 global)

在 React 项目中 使用 bootsrtap( 使用 sass )

React 中 绑定事件

React 中双向绑定事件(e.target.value 和 this.refs.txt.value)

React 组件的生命周期

Dream what you want to dream; go where you want to go; be what you want to be, because you have only one life and one chance to do all the things you want to do!

做你想做的梦吧,去你想去的地方吧,成为你想成为的人吧,因为你只有一次生命,一个机会去做所有那些你想做的事。

# 在 react 项目中 添加样式

# 第一种方式:

<h1 style={{ color: "red", fontSize: "50px" }}>这是评论列表组件</h1>

# 第一层封装:

将样式对象抽离出来

const cmtStyle={color:'red',fontSize:'30px'}

<h1 style={cmtStyle}>这是评论列表组件</h1>

# 第二层封装:

合并成一个大的样式对象

import React from "react";

const styles = {
  Item: { border: "1px dashed #ccc", margin: "10px 10px" },
  user: { color: "#333" },
  content: { color: "green", fontSize: "22px" },
};

export default function CmtItem(props) {
  return (
    <div style={styles.Item}>
      <h3 style={styles.user}>评论人:{props.user}</h3>
      <p style={styles.content}>评论内容:{props.content}</p>
    </div>
  );
}

mark

# 第三层封装

抽离为单独的 样式表模块

mark

CmtItem.jsx

import React from 'react
import styles from '@/components/styles'

export default function CmtItem(props) {
    return <div style={styles.Item}>
        <h3 style={styles.user}>评论人:{props.user}</h3>
        <p style={styles.content}>评论内容:{props.content}</p>
    </div>
}

styles.js:

export default {
  Item: { border: "1px dashed #ccc", margin: "10px 10px" },
  user: { color: "#333" },
  content: { color: "green", fontSize: "22px" },
};

# 加载外部 css 样式文件

index.js:

import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

import CmtList from "@/components/CmtList";

//使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <CmtList></CmtList>
  </div>,
  document.getElementById("app")
);

CmtList.jsx

import React from "react";
import CmtItem from '@/components/CmtItem'

const cmtStyle = {color: 'red', fontSize: '30px', textAlign: 'center'}
import cssobj from cmtList.scss


export default class CmtList extends React.Component {
    constructor() {
        super()
        this.state = {
            CommentList: [
                {id: 1, user: '张三', content: '哈哈哈哈哈'},
                {id: 2, user: '李四', content: '打游戏'},
                {id: 3, user: '王五', content: '唱歌,喝酒'},
                {id: 4, user: '王麦子', content: '到处happy'},
                {id: 5, user: '周琛', content: '帅的不要不要了!'}
            ]
        }
    }

    render() {
        return <div>
            <h1>这是评论列表组件</h1>
            {this.state.CommentList.map(item =>
                <CmtItem {...item} key={item.id}></CmtItem>
            )}

        </div>
    }
}

CmtItem.jsx

import React from "react";

import styles from "@/components/styles";

export default function CmtItem(props) {
  return (
    <div style={styles.Item}>
      <h1 style={styles.user}>评论人:{props.user}</h1>
      <p>评论内容:{props.content}</p>
    </div>
  );
}

webpack.config.js:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const htmlplugin = new HtmlWebpackPlugin({
  template: path.join(__dirname, "src/index.html"),
  filename: "index.html",
});

module.exports = {
  mode: "production",
  devServer: {
    open: true,
    port: 3000,
    contentBase: path.join(__dirname, "src"),
  },
  plugins: [htmlplugin],
  module: {
    rules: [
      {
        test: /\.m?js|jsx$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              "@babel/preset-env",
              [
                "@babel/preset-react",
                {
                  pragmaFrag: "DomFrag", // default is React.Fragment
                  throwIfNamespace: false, // defaults to true
                },
              ],
            ],
            plugins: ["@babel/plugin-proposal-class-properties"],
          },
        },
      },
      { test: /\.css$/, use: ["style-loader,css-loader"] },
    ],
  },
  resolve: {
    extensions: [".js", ".jsx", ".json"],
    alias: {
      "@": path.join(__dirname, "./src"), //这样,@ 就表示 项目根目录中 的src 这一层路径
    },
  },
};

# 配置好相应的文件后,但是却意外的发生了错误!

mark

查阅资料:参考 [Error: Cannot resolve module ‘style-loader’](https://stackoverflow.com/questions/35171288/error-cannot-resolve-module-style-loader)

但是不知咋地,我瞎弄了一下,突然就好了!~

结果

mark

# 引出 css 样式作用域的问题

# 给普通样式表加模块化

CSS MODULE 是一种 css in javascript 的方式,当我们把一个 css 文件 import 到一个 js 模块时,这个 css 文件会暴露一个对象,这个对象映射所有的本地和全局 css 类名

你真的知道 css-loader 怎么用吗?

# modules

操作

  • 配置 webpack.config.js: 追加参数 modules:表示为 普通的 css 样式表,启用模块化。
{test:/\.css$/,use:['style-loader','css-loader?modules']}

注意

  • css 模块化,只针对 类选择器Id 选择器生效。
  • css 模块化,不会将 标签选择器 模块化。

使用

import cssobj from cmtList.scss

<h1 className={cssobj.title}>这是评论列表组件</h1>

mark

# localIdentName

通常 modules 参数还要通过 localIdentName 的配合来设置 css 的类名。在上文中我们看到没有设置 localIdentName 的 css 编译后是一串随机字符串,可读性很差,因此我们还需要对它的类名进行处理,这就用到了 localIdentName。

mark

使用 localIdentName 设置 css-modules 模式下 local 类名的命名。

例如

【path】:

{test:/\.css$/,use:['style-loader','css-loader?modules&localIdentName=[path]']}

mark

【path】【name】:

{test:/\.css$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]']}

mark

【path】【name】【local】:

{test:/\.css$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]']}

mark

【path】【name】【local】【hash】:

{test:/\.css$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash]']}

mark

# 通过 local 和 global 设置类名是否被模块化

:local() 包裹起来的类名,会被模块化,默认情况下,所有的类名和 ID 都被模块化了。

被 :global () 包裹起来的类名,不会被模块化,而会全局生效。

需求

mark

解决方案: 可以参考

:global(.test) {
  margin: 10px 10px;
}

结果

  • test 类名 未被模块化 ,而是 全局生效

mark

# 在 React 项目中 使用 bootsrtap

  1. 安装 bootstrap 我一般使用 3.3.7 版本
npm i bootstrap@3.3.7 -S
  1. 安装 相关 路径 图标 加载依赖
npm i url-loader file-loader -D
  1. 配置 webpack-config-js 文件
{test:/\.ttf|woff|woff2|eot|svg$/,use:'url-loader'}
  1. 导入 bootstrap 包 并且 使用 bootstrap 类
import bootcss from "bootstrap/dist/css/bootstrap.min.css";

<button className={[bootcss.btn, bootcss["btn-primary"]].join(" ")}>
  点我
</button>;

mark

# 升级:

问题:className={[bootcss.btn,bootcss [‘btn-primary’]].join (’ ')} 这样调用太麻烦了

自己规定

第三方的 样式表:(例如: bootsrtap 等) 都是以 .css 结尾,这样,我们不要为普通的 .css 启用模块化。

自己定义样式表:都要以 .scss 或 .less 结尾,只为 .scss 或 .less 结尾的启用模块化。

# 使用 sass

Sass/Scss、Less 是什么?

  1. 安装:
npm i sass-loader node-sass -D
  1. 配置 webpack.comfig.js :
{test:/\.css$/,use:['style-loader','css-loader']},

{test:/\.scss/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']}
  1. 导入 bootstrap 包 并且 使用 bootstrap 类只用导入第三方路径即可
import "bootstrap/dist/css/bootstrap.min.css";

<button className="btn btn-danger">点我</button>;

mark

# React 中 绑定事件

# 基本使用

BindEvent.jsx:

import React from "react";

export default class BindEvent extends React.Component {
  constructor() {
    super();
    this.state = {};
  }
  render() {
    return (
      <div>
        BindEvent组件
        <hr />
        <button
          onClick={function () {
            console.log("ok");
          }}
        >
          按钮
        </button>
      </div>
    );
  }
}

mark

分离

import React from "react";

export default class BindEvent extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  render() {
    return (
      <div>
        <h1>BindEvent组件!</h1>
        <hr />
        <button
          onClick={() => {
            this.myClickHandler();
          }}
        >
          你敢点我吗?
        </button>
      </div>
    );
  }

  myClickHandler = () => {
    alert("这是myClickHandler方法!");
  };
}

mark

传递参数

import React from "react";

export default class BindEvent extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  render() {
    return (
      <div>
        <h1>BindEvent组件!</h1>
        <hr />
        <button
          onClick={() => {
            this.myClickHandler("哈哈哈哈哈哈!");
          }}
        >
          你敢点我吗?
        </button>
      </div>
    );
  }

  myClickHandler = (arg1) => {
    alert("这是myClickHandler方法!" + arg1);
  };
}

mark

# 需求:点击按钮,改变 this.state 里面的值

this.setState 方法的使用

注意:this.setState 方法的执行是 异步

mark

import React from "react";

export default class BindEvent extends React.Component {
  constructor() {
    super();
    this.state = {
      msg: "周琛!",
    };
  }

  render() {
    return (
      <div>
        <h1>BindEvent组件!</h1>
        <hr />
        <button
          onClick={() => {
            this.myClickHandler("哈哈哈哈哈哈!", "大傻瓜!");
          }}
        >
          你敢点我吗?
        </button>
        <h3>{this.state.msg}</h3>
      </div>
    );
  }

  myClickHandler = (arg1, arg2) => {
    // alert('这是myClickHandler方法!'+arg1+arg2)
    this.setState({
      msg: "张三",
    });
  };
}

mark

注意:在执行 this.setState 后,又想立即拿到最新的 state 值,需要使用 this.setState ({},callback)

myClickHandler = (arg1, arg2) => {
  // alert('这是myClickHandler方法!'+arg1+arg2)
  this.setState(
    {
      msg: "张三" + arg1 + arg2,
    },
    function () {
      console.log(this.state.msg);
    }
  );
};

# React 中绑定文本框与 State 中的值

类似于 Vue 的双向绑定

# 第一种双向绑定实现方式

<input
  type="text"
  style={{ width: "100%" }}
  value={this.state.msg}
  onChange={(e) => this.txtChanged(e)}
/>;

txtChanged = (e) => {
  this.setState({
    msg: e.target.value,
  });
};

mark

# 使用 ref 获取 DOM 元素引用

Vue 中,Vue 为页面上的元素提供了 ref 的属性,获取元素引用,则需要使用 this.$refs 引用名称

React 中 获取元素的引用 :this.refs

第二种双向绑定实现方式

<input
  type="text"
  style={{ width: "100%" }}
  value={this.state.msg}
  onChange={(e) => this.txtChanged(e)}
  ref="txt"
/>;

txtChanged = (e) => {
  this.setState({
    msg: this.refs.txt.value,
  });
};

# React 组件的生命周期

可以参考

mark

如图,React 生命周期主要包括三个阶段:初始化阶段、运行中阶段和销毁阶段,在 React 不同的生命周期里,会依次触发不同的钩子函数,下面我们就来详细介绍一下 React 的生命周期函数

React 学习第二天

# React 学习第二天

React 学习第二天

创建组件的方式( function 和 class)

ES6 扩展运算符使用

抽离 jsx 组件

如何省略 jsx 后缀名?

使用 @别名表示路径

两种创建组件方式的对比

评论列表案例

没有收拾残局的能力,就别放纵善变的情绪!!!

# 创建组件

# 第一种创建组件的方式

//第一步:导入包
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

function Hello() {
  return <div>这是一个组件</div>;
}

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Hello></Hello>
  </div>,
  document.getElementById("app")
);

mark

# 接受外部传值

function Hello(props) {
  console.log(props);
  return (
    <div>
      这是一个组件---{props.name}---{props.age}
    </div>
  );
}

const dog = {
  name: "大黄",
  age: "18",
};

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Hello name={dog.name} age={dog.age}></Hello>
  </div>,
  document.getElementById("app")
);

mark

# 改进:使用 ES6 扩展运算符

function Hello(props) {
  console.log(props);
  return (
    <div>
      这是一个组件---{props.name}---{props.age}
    </div>
  );
}

const dog = {
  name: "大黄",
  age: "18",
};

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Hello {...dog}></Hello>
  </div>,
  document.getElementById("app")
);

# 抽离 jsx 组件

//第一步:导入包
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

import Hello from "./components/Hello.jsx";

const dog = {
  name: "大黄",
  age: "18",
};

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Hello {...dog}></Hello>
  </div>,
  document.getElementById("app")
);

mark

Hello.jsx:

import React from "react";

//创建并导出
export default function Hello(props) {
  console.log(props);
  return (
    <div>
      这是一个组件---{props.name}---{props.age}
    </div>
  );
}

# 如何省略 jsx 后缀名?

mark

配置 webpack.config.js 文件:

resolve: {
  extensions: [".js", ".jsx", ".json"];
}

# 使用 @别名表示路径

mark

配置 webpack.config.js 文件:

resolve:{
    extensions:['.js','.jsx','.json'],
        alias:{
            '@':path.join(__dirname,'./src') //这样,@ 就表示 项目根目录中的src 这一层路径
        }
}

# 第二种创建组件的方式

使用 class 关键字来创建自建

ES6 中 class 关键字,是实现面向对象编程的新形式(语法糖)

# 了解 class

  1. 创建一个类并提供参数
class Animate {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

let p1 = new Animate("大黄", 3);
console.log(p1);

mark

# 挂载原型对象的实例方法( function 和 class 方法对比)

function f(name, age) {
  this.name = name;
  this.age = age;
}

f.prototype.show = function () {
  console.log("这是实例方法!");
};

let p = new f("大黄", 3);
console.log(p);

console.log("--------------------------------");

class Animate {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  show() {
    console.log("这是实例方法!");
  }
}

let p1 = new Animate("大黄", 3);
console.log(p1);

mark

# 静态方法

//使用 function创建对象
function f() {}

//静态方法
f.show = function () {
  console.log("这是function创建的 f 的静态 show 方法");
};
f.show();

console.log("--------------分割线------------------");

//使用class 创建对象
class Animate {
  //静态方法
  static info = "这是class方法创建的静态方法!";
}
console.log(Animate.info);

mark

# extends 继承

//这是父类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class American extends Person {}

const a1 = new American("Jack", 22);
console.log(a1);

class Chinese extends Person {}

const c1 = new Chinese("张三", 22);
console.log(c1);

mark

# super 调用父类 constructor 可以参考

//这是父类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log("大家好!");
  }
}

class American extends Person {
  constructor(name, age) {
    super(name, age);
  }
}

const a1 = new American("Jack", 22);
console.log(a1);
console.log(a1.sayHello());

class Chinese extends Person {
  constructor(name, age, IDNumber) {
    super(name, age);
    this.IDNumber = IDNumber;
  }
}

const c1 = new Chinese("张三", 22, "1213123");
console.log(c1);

mark

# 子类没有定义 constructor,会默认添加

class Chinese extends Person {}

// 等同于
class Chinese extends Person {
  constructor(...args) {
    super(...args);
  }
}

# 使用 class 关键字创建组件

# 基本结构

import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

//如果使用class 定义组件,必须让自己的组件,继承自 React.Component
class Movie extends React.Component {
  //在组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM 结构
  render() {
    //render 函数中,必须 返回合法的 JSX 虚拟DOM 结构
    return <div>这是class创建组件的方式</div>;
  }
}

//使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Movie></Movie>
  </div>,
  document.getElementById("app")
);

mark

# 传递参数

import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

class Movie extends React.Component {
  //使用class关键字创建的组件中,如果想使用外界传递过来的 props 参数,不需接受,直接通过 this.props.*** 来访问!
  render() {
    return (
      <div>
        这是class创建组件的方式--{this.props.name}--{this.props.age}---
        {this.props.hobby}
      </div>
    );
  }
}

const dog = {
  name: "小黄",
  age: "18",
  hobby: "play games",
};

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Movie {...dog}></Movie>
  </div>,
  document.getElementById("app")
);

mark

# 这两种创建组件方式的对比

注意:使用 function 创建的组件只有 props,没有自己的私有数据 和生命周期 函数

注意:使用 **class 关键字 ** 创建的组件,有自己的私有数据;

  1. 构造函数 创建出来的组件:叫做 “ 无状态组件
  2. class 关键字 创建出来的组件:叫做 “ 有状态组件

有状态组件无状态组件 之间的本质区别就是:有无 state 属性

可以参考

import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

class Movie extends React.Component {
  constructor() {
    super();
    this.state = {
      msg: "大家好啊!我是class私有数据!",
    }; //相当于 Vue 里面的 data(){ return {}}函数
  }

  //使用class关键字创建的组件中,如果想使用外界传递过来的 props 参数,不需接受,直接通过 this.props.*** 来访问!
  render() {
    return (
      <div>
        这是class创建组件的方式--{this.props.name}
        <p>{this.state.msg}</p>
      </div>
    );
  }
}

const dog = {
  name: "小黄",
  age: "18",
  hobby: "play games",
};
//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <Movie {...dog}></Movie>
  </div>,
  document.getElementById("app")
);

mark

# 评论列表案例

# 初步实现:
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

//使用 function 定义 普通的无状态组件
function CmtItem(props) {
  return (
    <div>
      <h3>评论人:{props.user}</h3>
      <p>评论内容:{props.content}</p>
    </div>
  );
}

class CmtList extends React.Component {
  constructor() {
    super();
    this.state = {
      CommentList: [
        { id: 1, user: "张三", content: "哈哈哈哈哈" },
        { id: 2, user: "李四", content: "打游戏" },
        { id: 3, user: "王五", content: "唱歌,喝酒" },
        { id: 4, user: "王麦子", content: "到处happy" },
        { id: 5, user: "周琛", content: "帅的不要不要了!" },
      ],
    };
  }

  render() {
    return (
      <div>
        <h1>这是评论列表组件</h1>
        {this.state.CommentList.map((item) => (
          <CmtItem {...item} key={item.id}></CmtItem>
        ))}
      </div>
    );
  }
}

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div>
    <CmtList></CmtList>
  </div>,
  document.getElementById("app")
);

mark

# 抽离各模块

便于减少一个文件的代码量,并且符合一种模块化思想

mark

index.js

import React from 'react' //创建组件,虚拟DOM元素,生命周期
import ReactDOM from 'react-dom' //把创建好的 组件 和 虚拟DOM 放到页面上展示的
·
import CmtList from '@/components/CmtList'

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(<div>
    <CmtList></CmtList>
</div>, document.getElementById('app'))

CmtList.jsx:

import React from "react";
import CmtItem from "@/components/CmtItem";

export default class CmtList extends React.Component {
  constructor() {
    super();
    this.state = {
      CommentList: [
        { id: 1, user: "张三", content: "哈哈哈哈哈" },
        { id: 2, user: "李四", content: "打游戏" },
        { id: 3, user: "王五", content: "唱歌,喝酒" },
        { id: 4, user: "王麦子", content: "到处happy" },
        { id: 5, user: "周琛", content: "帅的不要不要了!" },
      ],
    };
  }

  render() {
    return (
      <div>
        <h1>这是评论列表组件</h1>
        {this.state.CommentList.map((item) => (
          <CmtItem {...item} key={item.id}></CmtItem>
        ))}
      </div>
    );
  }
}

CmtItem.jsx:

import React from "react";
//使用 function 定义 普通的无状态组件
export default function CmtItem(props) {
  return (
    <div>
      <h3>评论人:{props.user}</h3>
      <p>评论内容:{props.content}</p>
    </div>
  );
}

mark

React 学习第一天

# React 学习第一天

ReactJS 简介

ReactJS 的背景和原理

三大主流前端框架 React、Vue、Angular

Vue 与 React 两个框架的区别和优势对比

React(虚拟 DOM,DIff 算法)

创建基本的 webpack4.x 项目( 并且解决了之前的一个 bug )

在项目中使用 react

把圈子变小 把语言变干净 把成绩往上提 把故事往心里收 现在想要的三年后都会有 !o ( ̄ ▽  ̄) ブ

# 1、ReactJS 简介

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在 2013 年 5 月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。

# 2、ReactJS 的背景和原理

在 Web 开发中,我们总需要将变化的数据实时反应到 UI 上,这时就需要对 DOM 进行操作。而复杂或频繁的 DOM 操作通常是性能瓶颈产生的原因(如何进行高性能的复杂 DOM 操作通常是衡量一个前端开发人员技能的重要指标)。

React 为此引入了虚拟 DOM(Virtual DOM)的机制:在浏览器端用 Javascript 实现了一套 DOM API。基于 React 进行开发时所有的 DOM 构造都是通过虚拟 DOM 进行,每当数据变化时,React 都会重新构建整个 DOM 树,然后 React 将当前整个 DOM 树和上一次的 DOM 树进行对比,得到 DOM 结构的区别,然后仅仅将需要变化的部分进行实际的浏览器 DOM 更新。而且 React 能够批处理虚拟 DOM 的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从 A 变成 B,然后又从 B 变成 A,React 会认为 UI 不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟 DOM 树,但是因为虚拟 DOM 是内存数据,性能是极高的,而对实际 DOM 进行操作的仅仅是 Diff 部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的 DOM 元素,而只需要关心在任意一个数据状态下,整个界面是如何 Render 的。

如果你像在 90 年代那样写过服务器端 Render 的纯 Web 页面那么应该知道,服务器端所要做的就是根据数据 Render 出 HTML 送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段 HTML 发生了变化,而只需要根据数据刷新整个页面。换句话说,任何 UI 的变化都是通过整体刷新来完成的。而 React 将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是 React 框架要完成的事情。

借用 Facebook 介绍 React 的视频中聊天应用的例子,当一条新的消息过来时,你的开发过程需要知道哪条数据过来了,如何将新的 DOM 结点添加到当前 DOM 树上;而基于 React 的开发思路,你永远只需要关心数据整体,两次数据之间的 UI 如何变化,则完全交给框架去做。可以看到,使用 React 大大降低了逻辑复杂性,意味着开发难度降低,可能产生 Bug 的机会也更少。


# 清楚理解两个概念:

  1. ibrary (库):小而巧的库,只提供特定的 API;优点就是 船小好调头。可以很方便的从一个库切换到另外的库;但是代码几乎不会改变。

  2. Framework (框架):大而全的是框架;框架提供了一整套的解决方案;所以,如果在项目中间,想切换到另外的框架,是比较困难的。

# 三大主流前端框架 React、Vue、Angular

mark

React( 最流行 )起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013 年 5 月开源。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。它有以下的特性

  1. 声明式设计:React 采用声明范式,可以轻松描述应用。

  2. 高效:React 通过对 DOM 的模拟,最大限度地减少与 DOM 的交互。

  3. 灵活:React 可以与已知的库或框架很好地配合。

1. 速度快:在UI渲染过程中,React通过在虚拟DOM中的微操作来实现对实际DOM的局部更新。
2. 跨浏览器兼容:虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
3. 模块化:为你程序编写独立的模块化UI组件,这样当某个或某些组件出现问题是,可以方便地进行隔离。
4. 单向数据流:Flux是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。
5. 同构、纯粹的javascript:因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。
6.兼容性好:比如使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。
缺点:
React本身只是一个V而已,并不是一个完整的框架,所以如果是大型项目想要一套完整的框架的话,基本都需要加上ReactRouter和Flux才能写大型应用。

mark

Vue ( 目前市场上的主流 ) 是尤雨溪编写的一个构建数据驱动的 Web 界面的库,准确来说不是一个框架,它聚焦在 V(view)视图层。

它有以下的特性

  1. 轻量级的框架

  2. 双向数据绑定

  3. 指令

  4. 插件化

优点:
1. 简单:官方文档很清晰,比 Angular 简单易学。
2. 快速:异步批处理方式更新 DOM。
3. 组合:用解耦的、可复用的组件组合你的应用程序。
4. 紧凑:~18kb min+gzip,且无依赖。
5. 强大:表达式 无需声明依赖的可推导属性 (computed properties)。
6. 对模块友好:可以通过 NPM、Bower 或 Duo 安装,不强迫你所有的代码都遵循 Angular 的各种规定,使用场景更加灵活。
缺点:
1. 新生儿:Vue.js是一个新的项目,没有angular那么成熟。
2. 影响度不是很大:google了一下,有关于Vue.js多样性或者说丰富性少于其他一些有名的库。
3. 不支持IE8

mark

Angular( 最早 )是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。

它有以下的特性

  1. 良好的应用程序结构

  2. 双向数据绑定

  3. 指令

  4. HTML 模板

  5. 可嵌入、注入和测试

优点:
1. 模板功能强大丰富,自带了极其丰富的angular指令。
2. 是一个比较完善的前端框架,包含服务,模板,数据双向绑定,模块化,路由,过滤器,依赖注入等所有功能;
3. 自定义指令,自定义指令后可以在项目中多次使用。
4. ng模块化比较大胆的引入了Java的一些东西(依赖注入),能够很容易的写出可复用的代码,对于敏捷开发的团队来说非常有帮助。
5. angularjs是互联网巨人谷歌开发,这也意味着他有一个坚实的基础和社区支持。
缺点:
1. angular 入门很容易 但深入后概念很多, 学习中较难理解.
2. 文档例子非常少, 官方的文档基本只写了api, 一个例子都没有, 很多时候具体怎么用都是google来的, 或直接问misko,angular的作者.
3. 对IE6/7 兼容不算特别好, 就是可以用jQuery自己手写代码解决一些.
4. 指令的应用的最佳实践教程少, angular其实很灵活, 如果不看一些作者的使用原则,很容易写出 四不像的代码, 例如js中还是像jQuery的思想有很多dom操作.
5. DI 依赖注入 如果代码压缩需要显示声明.

# Vue 与 React 两个框架的区别和优势对比

Vue 与 React 的区别: 可以参考

如果你喜欢用模板搭建应用(或者有这个想法),请选择 Vue

Vue 应用的默认选项是把 markup 放在 HTML 文件中。数据绑定表达式采用的是和 Angular 相似的 mustache 语法,而指令(特殊的 HTML 属性)用来向模板添加功能。

相比之下,React 应用不使用模板,它要求开发者借助 JSX 在 JavaScript 中创建 DOM。

对于来自标准 Web 开发方式的新开发者,模板更容易理解。但是一些资深开发者也喜欢模板,因为模板可以更好的把布局和功能分割开来,还可以使用 Pug 之类的模板引擎。

但是使用模板的代价是不得不学习所有的 HTML 扩展语法,而渲染函数只需要会标准的 HTML 和 JavaScript。而且比起模板,渲染函数更加容易调试和测试。当然你不应该因为这方面的原因错过 Vue,因为在 Vue2.0 中提供了使用模板或者渲染函数的选项。

如果你喜欢简单和 “能用就行” 的东西,请选择 Vue

一个简单的 Vue 项目可以不需要转译直接运行在浏览器中,所以使用 Vue 可以像使用 jQuery 一样简单。当然这对于 React 来说在技术上也是可行的,但是典型的 React 代码是重度依赖于 JSX 和诸如 class 之类的 ES6 特性的。

Vue 的简单在程序设计的时候体现更深,让我们来比较一下两个框架是怎样处理应用数据的(也就是 state)。

React 中是通过比较当前 state 和前一个 state 来决定何时在 DOM 中进行重渲染以及渲染的内容,因此需要不可变(immutable)的 state。

Vue 中的数据是可变(mutated)的,所以同样的操作看起来更加简洁。

让我们来看看 Vue 中是如何进行状态管理的。当向 state 添加一个新对象的时候,Vue 将遍历其中的所有属性并且转换为 getter,setter 方法,现在 Vue 的响应系统开始保持对 state 的跟踪了,当 state 中的内容发生变化的时候就会自动重新渲染 DOM。令人称道的是,Vue 中改变 state 的状态的操作不仅更加简洁,而且它的重新渲染系统也比 React 的更快更有效率。

Vue 的响应系统还有有些坑的,例如:它不能检测属性的添加和删除和某些数组更改。这时候就要用到 Vue API 中的类似于 React 的 set 方法来解决。

如果你想要你的应用尽可能的小和快,请选择 Vue

当应用程序的状态改变时,React 和 Vue 都将构建一个虚拟 DOM 并同步到真实 DOM 中。 两者都有各自的方法优化这个过程。

Vue 核心开发者提供了一个 benchmark 测试,可以看出 Vue 的渲染系统比 React 的更快。测试方法是 10000 个项目的列表渲染 100 次,结果如下图。从实用的观点来看,这种 benchmark 只和边缘情况有关,大部分应用程序中不会经常进行这种操作,所以这不应该被视为一个重要的比较点。但是,页面大小是与所有项目有关的,这方面 Vue 再次领先,它目前的版本压缩后只有 25.6KB。React 要实现同样的功能,你需要 React DOM(37.4KB)和 React with Addon 库(11.4KB),共计 44.8KB,几乎是 Vue 的两倍大。双倍的体积并不能带来双倍的功能。

如果你打算构建一个大型应用程序,请选择 React

像文章开头那种同时用 Vue 和 React 实现的简单应用程序,可能会让一个开发者潜意识中更加倾向于 Vue。这是因为基于模板的应用程序第一眼看上去更加好理解,而且能很快跑起来。但是这些好处引入的技术债会阻碍应用扩展到更大的规模。模板容易出现很难注意到的运行时错误,同时也很难去测试,重构和分解。

相比之下,Javascript 模板可以组织成具有很好的分解性和干(DRY)代码的组件,干代码的可重用性和可测试性更好。Vue 也有组件系统和渲染函数,但是 React 的渲染系统可配置性更强,还有诸如浅(shallow)渲染的特性,和 React 的测试工具结合起来使用,使代码的可测试性和可维护性更好。

与此同时,React 的 immutable 应用状态可能写起来不够简洁,但它在大型应用中意义非凡,因为透明度和可测试性在大型项目中变得至关重要。

如果你想要一个同时适用于 Web 端和原生 APP 的框架,请选择 React

React Native 是一个使用 Javascript 构建移动端原生应用程序(iOSAndroid)的库。 它与 React.js 相同,只是不使用 Web 组件,而是使用原生组件。 如果你学过 React.js,很快就能上手 React Native,反之亦然。

它的意义在于,开发者只需要一套知识和工具就能开发 Web 应用和移动端原生应用。如果你想同时做 Web 端开发和移动端开发,React 为你准备了一份大礼。

阿里的 Weex 也是一个跨平台 UI 项目,目前它以 Vue 为灵感,使用了许多相同的语法,同时计划在未来完全集成 Vue,然而集成的时间和细节还不清楚。因为 Vue 将 HTML 模板作为它设计的核心部分,并且现有特性不支持自定义渲染,因此很难看出目前的 Vue.js 的跨平台能力能像 React 和 React Native 一样强大。

如果你想要最大的生态系统,请选择 React

毫无疑问,React 是目前最受欢迎的前端框架。它在 NPM 上每个月的下载量超过了 250 万次,相比之下,Vue 是 22.5 万次。人气不仅仅是一个肤浅的数字,这意味着更多的文章,教程和更多 Stack Overflow 的解答,还意味有着更多的工具和插件可以在项目中使用,让开发者不再孤立无援。

这两个框架都是开源的,但是 React 诞生于 Facebook,有 Facebook 背书,它的开发者和 Facebook 都承诺会持续维护 React。相比之下,Vue 是独立开发者尤雨溪的作品。尤雨溪目前在全职维护 Vue,也有一些公司资助 Vue,但是规模和 Facebook 和 Google 没得比。不过请对 Vue 的团队放心,它的小规模和独立性并没有成为劣势,Vue 有着固定的发布周期,甚至更令人称道的是,Github 上 Vue 只有 54 个 open issue,3456 个 closed issue,作为对比,React 有多达 530 个 open issue,3447 个 closed issue。

# 总结一下,我们发现的,Vue 的优势是:

模板和渲染函数的弹性选择

简单的语法和项目配置

更快的渲染速度和更小的体积

# React 的优势是:

更适合大型应用和更好的可测试性

Web 端和移动端原生 APP 通吃

更大的生态系统,更多的支持和好用的工具

然而,React 和 Vue 都是很优秀的框架,它们之间的相似之处多过不同之处,并且大部分的优秀功能是相通的:

用虚拟 DOM 实现快速渲染

轻量级

响应式组件

服务端渲染

集成路由工具,打包工具,状态管理工具的难度低

优秀的支持和社区

# React

# 1. 虚拟 DOM(Virtual Document Object Model)

  • DOM 的本质什么:浏览器中的概念,用 JS 对象来表示 页面上的 元素,并提供了操作 DOM 对象的 API。
  • 什么是 React 中的虚拟 DOM:是框架中的概念,是 程序员 用 JS 对象来模拟 页面上的 DOM 和 DOM 嵌套。

# DOM

先直接说一下,DOM 意思是文档对象模型(Dcoument Object Model),它是一个结构化文本的抽象。对于 Web 开发者,这个文本是一段 HTML 代码,DOM 也就被叫做 HTML DOM。HTML 的元素在 DOM 中变成了节点。

所以,HTML 是一段文本,DOM 就是这段文本在内存中的表示。

可以对比一个程序的一个进程实例。对于一个程序,可以存在多个进程,就像一段同样的 HTML 可以有多个 DOM 一样。(例:同一个页面被多个 tab 加载)。

在 HTML DOM 中提供了遍历和修改节点的接口(API)。像 getElementById 或者 removeChild 这样的方法。我们一般使用 JavaScript 来操作 DOM,这是因为…… 好吧,天知道为什么。😃

所以,只要我们想要动态修改网页的内容的时候,我们就修改 DOM。

var item = document.getElementById("myLI");
item.parentNode.removeChild(item);

所谓 document 就是根节点的抽象,而 getElementByIdparentNode emoveChild 则是 HTML DOM API 中的方法。

# 虚拟 DOM

首先 - 虚拟 DOM 不是 React 发明的,但是 React 用了它且免费提供。

虚拟 DOM 是 HTML DOM 的抽象。它是轻量的,是从浏览器特定(Browser-specific,这里意指特定的浏览器需要特定的实现)实现细节中提取出来的。由于 DOM 本身就已经是一个抽象了,所以虚拟 DOM,实际上,是一个抽象的抽象。

也许把虚拟 DOM 当做 React 的本地和简化版的 HTML DOM 更好。它允许 React 跳过既慢又限于特定浏览器的真实 DOM 操作,以在这个抽象世界中做自己的计算。

常规 DOM 和虚拟 DOM 二者并没有什么大的不同。这也是为什么 React 代码的 JSX 部分可以看起来几乎跟纯 HTML 很像的原因。

var CommentBox = React.createClass({
  render: function () {
    return (
      <div className="commentBox">
        <div>Hello, world! I am a CommentBox.</div>
      </div>
    );
  },
});

在大多数情况下,当你有一段 HTML 代码且想要将其写成一个 React 组件时,你只需要做这个。

  1. 在 render 方法中返回 HTML 代码;
  2. 将 class 属性替换成 className 属性,因为 class 在 JavaScript 中是一个保留关键字。

二者之间还有一些,相当细微的区别。

  • 虚拟 DOM 的这些属性不在真实的 DOM 中出现 ——key,ref 和 dangerouslySetInnerHTML。查看更多
  • React 范的 DOM 引入了一些限制

# 为什么需要虚拟 DOM?

先介绍浏览器加载一个 HTML 文件需要做哪些事,帮助我们理解为什么我们需要虚拟 DOM。webkit 引擎的处理流程,一图胜千言:

mark

所有浏览器的引擎工作流程都差不多,如上图大致分 5 步:创建 DOM tree –> 创建 Style Rules -> 构建 Render tree -> 布局 Layout –> 绘制 Painting

  • 第一步,用 HTML 分析器,分析 HTML 元素,构建一颗 DOM 树。

  • 第二步:用 CSS 分析器,分析 CSS 文件和元素上的 inline 样式,生成页面的样式表。

  • 第三步:将上面的 DOM 树和样式表,关联起来,构建一颗 Render 树。这一过程又称为 Attachment。每个 DOM 节点都有 attach 方法,接受样式信息,返回一个 render 对象(又名 renderer)。这些 render 对象最终会被构建成一颗 Render 树。

  • 第四步:有了 Render 树后,浏览器开始布局,会为每个 Render 树上的节点确定一个在显示屏上出现的精确坐标值。

  • 第五步:Render 数有了,节点显示的位置坐标也有了,最后就是调用每个节点的 paint 方法,让它们显示出来。

当你用传统的源生 api 或 jQuery 去操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。比如当你在一次操作时,需要更新 10 个 DOM 节点,理想状态是一次性构建完 DOM 树,再执行后续操作。但浏览器没这么智能,收到第一个更新 DOM 请求后,并不知道后续还有 9 次更新操作,因此会马上执行流程,最终执行 10 次流程。显然例如计算 DOM 节点的坐标值等都是白白浪费性能,可能这次计算完,紧接着的下一个 DOM 更新请求,这个节点的坐标值就变了,前面的一次计算是无用功。

即使计算机硬件一直在更新迭代,操作 DOM 的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验。真实的 DOM 节点,哪怕一个最简单的 div 也包含着很多属性,可以打印出来直观感受一下:

虚拟 DOM 就是为了解决这个浏览器性能问题而被设计出来的。例如前面的例子,假如一次操作中有 10 次更新 DOM 的动作,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地的一个 js 对象中,最终将这个 js 对象一次性 attach 到 DOM 树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量。

# Virtual DOM 算法

可以用新渲染的对象树去和旧的树进行对比,记录这两棵树差异。记录下来的不同就是我们需要对页面真正的 DOM 操作,然后把它们应用在真正的 DOM 树上,页面就变更了。这样就可以做到:视图的结构确实是整个全新渲染了,但是最后操作 DOM 的时候确实只变更有不同的地方。

这就是所谓的 Virtual DOM 算法。包括几个步骤:

  1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  3. 把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

# DIff 算法

比较两棵 DOM 树的差异是 Virtual DOM 算法最核心的部分。简单的说就是新旧虚拟 dom 的比较,如果有差异就以新的为准,然后再插入的真实的 dom 中,重新渲染。、 借网络一张图片说明:

mark

比较只会在同层级进行,不会跨层级比较。
比较后会出现四种情况:
1、此节点是否被移除 -> 添加新的节点
2、属性是否被改变 -> 旧属性改为新属性
3、文本内容被改变 -> 旧内容改为新内容
4、节点要被整个替换 -> 结构完全不相同 移除整个替换

# 创建基本的 webpack4.x 项目

  1. 运行 npm init -y 快速初始化下项目
  2. 在项目根目录创建 src 源代码目录 和 dist 产品目录
  3. 在 src 目录下创建 index.html
  4. 使用 npm 安装 webpack,运行 npm i webpack webpack-cli -D
  5. 注意:webpack4.x 提供了 约定大于配置的概念;目的是为了尽量减少 配置文件的体积。
    • 默认约定了
      • 打包的入口是 src -> index.js
      • 打包的输出文件是 dist -> main.js
      • 4.x 中新增的 mode 项:developmentproduction

mark

mode 介绍

mode 参数:(developmentproduction ):决定了 配置代码 (压缩代码体积)是否被压缩。

mark

# 配置 webpack-dev-server (自动打包)

webpack-dev-server 能够用于快速开发应用程序

  1. 安装:运行: npm i webpack-dev-server -D
  2. 配置 package.json
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server"
}
  • 可以在webpack.config.js 配置相应的 devServer(自动打开窗口,配置端口号,首页路径展示情况)

mark

# 配置 html-webpack-plugin

HtmlWebpackPlugin 简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。 你可以让插件为你生成一个 HTML 文件,使用 lodash 模板提供你自己的模板,或使用你自己的 loader

  1. 安装:运行: npm i html-webpack-plugin -D
  2. 配置 package.json

mark

# 配置 Babel

Babel 是一个广泛使用的转码器,可以将 ES6 代码转为 ES5 代码

webpack 中使用 Babel 官方文档

  1. 安装 npm install -D babel-loader @babel/core @babel/preset-env webpack

  2. 在 webpack 配置对象中,需要将 babel-loader 添加到 module 列表中,就像下面这样

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
        },
      },
    },
  ];
}

# 解决之前遇到的一个 BUG

mark

1556355771552

解决方案

  1. 安装: npm i @babel/plugin-proposal-class-properties -D
  2. 配置 webpack.config.js

mark

运行结果

mark

# 在项目中使用 react

  1. 运行 npm i react react-dom -s 安装包
    • react:专门用于创建组件和虚拟 DOM 的,同时组件的生命周期都在这个包中
    • react-dom:专门进行 DOM 操作的,最主要的应用场景,就是 ReactDOM.render ()

尝试写一个 react 项目

index.js:

//第一步:导入包
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

//第二步:创建虚拟DOM元素
//参数1:创建的元素的类型,字符串,表示元素的名称
//参数2:是一个对象或 null,表示 当前这个DOM 元素的属性
//参数3:子节点(包括 其它 虚拟DOM 获取 文本子节点)
//参数n:其它子节点
const myh1 = React.createElement(
  "h1",
  { id: "myh1", title: "this id h1" },
  "这是一个大大H1!"
);

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
//参数一:要渲染的那个虚拟DOM元素
//参数二:指定页面上的DOM元素,当作容器
ReactDOM.render(myh1, document.getElementById("app"));

index.html:

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

mark

# 创建 DOM 结构

# 方式一:(~ 基本不用)

index.js:

//第一步:导入包
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

//第二步:创建虚拟DOM元素
const myh1 = React.createElement(
  "h1",
  { id: "myh1", title: "this id h1" },
  "这是一个大大H1!"
);
const mydiv = React.createElement("div", null, "这是一个div元素", myh1);

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(mydiv, document.getElementById("app"));

# 方式二:JSX 语法 (~ important)

JSX 语法:看起来可能有些奇怪的标签语法既不是字符串也不是 HTML。

JSX 语法的本质,还是在运行的时候,被转换成了 React.createElement 形式来执行的

使用 babel 将 JSX 语法转换为 React.createElement 形式 JSX 官方详细介绍

关键包:运行 npm install --save-dev @babel/preset-react 可以参考 babel 官方文档

mark

原因:少写了中括号。(粗心大意了)不该!!~~~

配置 webpack.config.js

//配置 @babel/preset-react
module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: "babel-loader",
        options: {
          presets: [
            "@babel/preset-env",
            [
              "@babel/preset-react",
              {
                pragmaFrag: "DomFrag", // default is React.Fragment
                throwIfNamespace: false, // defaults to true
              },
            ],
          ],
          plugins: ["@babel/plugin-proposal-class-properties"],
        },
      },
    },
  ];
}

# 在 JSX 中使用表达式 JSX 中使用表达式
//第一步:导入包
import React from "react"; //创建组件,虚拟DOM元素,生命周期
import ReactDOM from "react-dom"; //把创建好的 组件 和 虚拟DOM 放到页面上展示的

let a = 10;
let str = "zc";
let arr = [<h1>这是h1</h1>, <h2>这是h2</h2>];
let arr1 = ["周琛", "张三", "李四", "王五"];

//第三步:使用 ReactDOM 把虚拟 DOM 渲染到页面上
ReactDOM.render(
  <div title={str}>
    {a + 11}
    <hr />
    {arr}
    <hr />
    {arr1.map((item) => {
      return <h3>{item}</h3>;
    })}
  </div>,
  document.getElementById("app")
);

mark

Mint-UI、MUI

# Vue 第七天学习

Mint-UI 学习

MUI

# Mint-UI

# 基于 Vue.js 的移动端组件库

特性介绍:Mint-UI 官网

  1. Mint UI 包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要。通过它,可以快速构建出风格统一的页面,提升开发效率。
  2. 真正意义上的按需加载组件。可以只加载声明过的组件及其样式文件,无需再纠结文件体积过大。
  3. 考虑到移动端的性能门槛,Mint UI 采用 CSS3 处理各种动效,避免浏览器进行不必要的重绘和重排,从而使用户获得流畅顺滑的体验。
  4. 依托 Vue.js 高效的组件化方案,Mint UI 做到了轻量化。即使全部引入,压缩后的文件体积也仅有~30kb (JS + CSS) gzip。

安装:

// 安装
# Vue 1.x
npm install mint-ui@1 -S
# Vue 2.0
npm install mint-ui -S

来自官网

// 引入全部组件
import Vue from "vue";
import Mint from "mint-ui";
Vue.use(Mint);
// 按需引入部分组件
import { Cell, Checklist } from "minu-ui";
Vue.component(Cell.name, Cell);
Vue.component(Checklist.name, Checklist);

# 项目演示:

项目目录:

mark

main.js:

import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);

//导入所以的 MintUI 组件
//导入 mint-ui
import MintUI from "mint-ui"; //把所有的组件都导入进来
//这里可以 省略 node_modules 目录
import "mint-ui/lib/style.css";

Vue.use(MintUI); //把所有的组件,注册为全局的组件

//导入app组件
import app from "./App.vue";
//导入 自定义路由模块
import router from "./router.js";

let vm = new Vue({
  el: "#app",
  data: {
    msg: "123",
  },
  render: (c) => c(app),
  router,
});

App.vue:

<template>
  <div>
    <h1>这是app组件</h1>
    <mt-button type="primary" size="large">default</mt-button>
    <router-link to="/account">account</router-link>
    <router-link to="/goodslist">goodslist</router-link>
    <router-view></router-view>
  </div>
</template>

<script></script>

<style></style>

mark

# JS components 使用

# 例如:使用 Toast 组件

App.vue:

<template>
  <div>
    <h1>这是app组件</h1>
    <mt-button type="primary" size="large" @click="show">default</mt-button>
    <router-link to="/account">account</router-link>
    <router-link to="/goodslist">goodslist</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
import { Toast } from "mint-ui";

export default {
  data() {
    return {};
  },
  methods: {
    show() {
      Toast({
        message: "Upload Complete",
        position: "middle",
        duration: 5000,
      });
    },
  },
};
</script>

<style></style>

mark

# 使用 bootstrap 图标 配合 **Toast 组件 ** 使用

  1. 安装 bootstrap 包
npm i bootstrap@3.3.7 -S
  1. 在 main.js 文件
import "bootstrap/dist/css/bootstrap.min.css";
  1. 在 App.vue 文件里

mark

mark

# 自定义图标颜色

mark

mark

# 模拟 完成接收数据 Toast 组件消失需求

# 需求: 1s 后 Toast 组件消失需求

App.vue:

<template>
  <div>
    <h1>这是app组件</h1>
    <mt-button type="primary" size="large" @click="show">default</mt-button>
    <router-link to="/account">account</router-link>
    <router-link to="/goodslist">goodslist</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
import { Toast } from "mint-ui";

export default {
  data() {
    return {
      instance: null,
    };
  },
  created() {
    this.getList();
  },
  methods: {
    getList() {
      this.show();
      setTimeout(() => {
        this.instance.close();
      }, 1000);
    },
    show() {
      this.instance = Toast({
        message: "Upload Complete",
        duration: -1,
        position: "middle",
        duration: 5000,
        iconClass: "glyphicon glyphicon-heart",
        className: "mytoast",
      });
    },
  },
};
</script>

<style></style>

mark

# babel-plugin-component 插件

作用:为了项目整体内容不过于庞大,按需加载是许多第三方的库和插件必不可少的,于是使用了官方提供的按需加载插件 babel-plugin-component

# 按需导入:

  1. 1. 安装:
npm install babel-plugin-component -D
  1. 2. 修改 .babelrc 里面的 plugins
{
  "presets": ["env", "stage-0"],
  "plugins": [
    "transform-runtime",
    [
      "component",
      [
        {
          "libraryName": "mint-ui",
          "style": true
        }
      ]
    ]
  ]
}

# 项目改造

# 按需导入

mian.js:

import "mint-ui/lib/style.css";

//按需导入
import { Button } from "mint-ui";
Vue.component("mybtn", Button); //导入自定义的 mybtn 按钮组件

App.vue:

<template>
    <div>
        <h1>这是app组件</h1>
        <mybtn type="primary">mybtn</mybtn>
        <router-link to="/account">account</router-link>
        <router-link to="/goodslist">goodslist</router-link>
        <router-view></router-view>
    </div>
</templat

mark

# MUI

注意: MUI 不同于 Mint-UI,MUI 只是开发出来的一套好用的代码片段,里面提供了配套的样式、配套的 HTML 代码段,类似于 Bootstrap; 而 Mint-UI,是真正的组件库,是使用 Vue 技术封装出来的 成套的组件,可以无缝的和 VUE 项目进行集成开发;
因此,从体验上来说, Mint-UI 体验更好,因为这是别人帮我们开发好的现成的 Vue 组件;
从体验上来说, MUI 和 Bootstrap 类似;
理论上,任何项目都可以使用 MUI 或 Bootstrap,但是,MInt-UI 只适用于 Vue 项目;

**mui 框架:** 性能和体验的差距,一直是 mobile app 开发者放弃 HTML5 的首要原因。 浏览器天生的切页白屏、不忍直视的转页动画、浮动元素的抖动、无法流畅下拉刷新等问题,这些都让 HTML5 开发者倍感挫败,尤其拿到 Android 低端机运行,摔手机的心都有; 另一方面,浏览器默认控件样式又少又丑,制作一个漂亮的控件非常麻烦,也有一些制作简单的 ui 框架但性能低下。

mui 框架有效的解决了这些问题,这是一个可以方便开发出高性能 App 的框架,也是目前最接近原生 App 效果的框架。

注意: MUI 并不能使用 npm 去下载,需要自己手动从 github 上,下载现成的包,自己解压出来,然后手动拷贝到项目中使用;

  1. 下载 MUI 包 从 github 上

# 使用:

在 main.js 导入 MUI 包:

import "./lib/dist/css/mui.min.css";

在 App.vue 中使用(例如一个 按钮):

<button type="button" class="mui-btn mui-btn-royal">
  Badge button <span class="mui-badge mui-badge-royal">999</span>
</button>

mark

webpack、webpack 后续问题

# Vue 第六天学习

file-loader(解决 webpack 打包图片路径问题,字体路径问题)

webpack 中 babel 的配置(处理高级的 es6 语法或者 es7 语法)

Vue 中的 render 函数

在 webpack 构建的项目中,使用 Vue 进行开发

export default 和 export 的使用方式

结合 webpack 使用 vue-router

组件中 style 标签 lang 属性和 scoped 属性的介绍

抽离路由模块

一个人如果不想输,就要不断学好眼前的东西,它们将来都会大有用处…

# webpack 后续问题

# 问题:Webpack 打包图片路径问题

mark

mark

# 在页面中引入图片有两种方式

  • img 标签引入
  • css 引入

# 解决方案:Webpack 使用 file-loader 处理图片

# 安装:file-loader

  1. 运行 npm i url-loader file-loader -D 来安装 file-loader

mark

  1. 配置 webpack.config.js 文件

mark

  1. 运行 npm run dev

mark

# file-loader 参数

  • limit 给定的值,是图片的大小,单位是 byte,如果我们引用的 图片,大于 给定的值,则会被转为 base64 格式 的字符串,如果,图片 ** 小于或等于 ** 给定的 limit 值,则不会被转为 base64 的 字符串
    mark

    mark

    mark

  • name 属性 使图片路径 url 不变

mark

mark

# 处理字体文件 路径问题

使用 file-loader

问题描述:引入 bootstrap 字体图标库,但报错!

mark

解决方案

  1. 配置 webpack.config.js
{test:/\.(ttf|eot|svg|woff|woff2)$/,use:'url-loader'},//这是 处理 字体文件的 loader

mark

  1. 运行 npm run dev

# webpack 中 babel 的配置

babel 介绍

问题描述

  1. webpack 中默认只能处理一部分 es6 语法,一些更高级的 es6 语法或者 es7 语法 webpack 处理不了,这时候就需要借助第三方 loader 帮助 webpack 处理这些语法
  2. class 是 es6 中提供的语法,是用来实现 es6 中面向对象编程的方式,class 和 static 与 Java 中类似

mark

解决方案

  1. 通过 babel 可以将高级语法转化为低级语法
  2. 安装,运行两个命令,安装两套包,去安装 babel 相关的功能
  • 第一套包npm i babel-core babel-loader babel-plugin-transform-runtime -D
  • 第二套包npm i babel-preset-env babel-preset-stage-0 -D
  1. 打开 webpack 配置文件,在 module 节点写的 rules 数组中添加一个新的匹配规则
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/}

mark

排除 node_module 目录的原因:

  • 如果排除 node_module,babel 会把 node_module 目录下的所有第三方 js 文件都打包编译,这会非常消耗 CPU,同时打包速度非常慢
  • babel 把 node_module 中的 js 文件转化完毕项目也无法正常运行
  1. 在项目的根目录中新建一个叫做 .babelrc 的 babel 配置文件,这个配置文件属于 json 格式
{
    "presets":["env","stage-0"],
    "plugins":["transform-runtime"]
}

mark

报错:

mark

** 解决方案:** 执行 npm i babel-loader@7

mark

报错:

mark

啊啊啊啊~~崩溃!!

# 修改之后还是跑不起来 ---- 醉了!!

mark

mark

报错:

mark

# 升级总结(来自百度

# -babel 升级 7.X 踩坑记录

  1. babel 包名改变,以前安装是 npm i babel-* 现在安装 babel 系列需要写成 npm i @babel/*
  2. .babelrc 文件写法改变, preset plugins 等全部写成 @babel/* 的形式
npm:
- babel-preset-env
+ @babel/preset-env
- babel-preset-react
+ @babel/preset-react
- babel-preset-stage-0

.babelrc:
- "presets": ["react", "env", "stage-0", "mobx"]
+ "presets": ["@babel/preset-react", "@babel/preset-env", "mobx"]

除了上述的 preset ,我还用了 babel-preset-mobx
但是没找到 @babel/preset-mobx ,从 babel-preset-mobx git 提交日志上看,作者已经支持了最新的 babel 。在之后的测试中,发现 mobx 的功能也能正常使用。
另外,stage-* 已弃用

# 使用 Vue 实例的 render 方法

Vue 推荐使用在绝大多数情况下使用 template 来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接近编译器。

* 基本使用 **

<div id="app"></div>
<script>
  let login = {
    template: "<h1>login 组件</h1>",
  };

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {},
    render(createElement) {
      return createElement(login);
    },
  });
</script>

mark

# 在 webpack 构建的项目中,使用 Vue 进行开发

  • 在普通网页中如何使用 Vue
    • 1. 使用 script 标签,引入 Vue 的包
    • 2. 在 index 页面中,创建 一个 id 为 app div 容器
    • 3. 通过 new Vue 得到一个 vm 的实例

# 重点:在 webpack 中 尝试 使用 Vue

  1. 直接导入 Vue 包

mark

结果报错

mark

mark

# - 回顾 :包的 查找 规则

  1. 找 项目根目录中有没有 node_modules 的文件夹
  2. 在 node_modules 中,根据包名,找对应的 vue 文件夹
  3. 在 vue 文件夹中,找 一个叫 package.json 的包配置文件
  4. 在 package.json 文件中,查找 一个 main 属性【mian 属性指定了这个包在被加载的时候的入口文件】

解决方案 1

mark

解决方案 2

mark

解决方案 3(更优雅):

mark

# 定义 文件形式 vue 组件 加载到页面上

例如

mark

报错:

mark

原因:

  • 默认,webpack 无法打包 .vue 文件,需要安装 * 相关的 loader

解决方案:

  1. 安装:执行 npm i vue-loader vue-template-compiler -D 命令

  2. 配置 webpack.config.js 文件

{test:/\.vue$/,use:'vue-loader'} 	//处理 .vue后缀名的 loader

mark

结果:还是报错

mark

# 原因:Vue-loader 在 15.* 之后的版本都是 vue-loader 的使用都是需要伴生 VueLoaderPlugin 的.

解决:在 webpack.config.js 中加入

const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
  devtool: "sourcemap",
  entry: "./js/entry.js",
  output: {
    filename: "bundle.js",
  },
  plugins: [new VueLoaderPlugin()],
  module: {},
};

mark

使用 render 函数 :

markmark

# 总结梳理:

# 总结梳理: webpack 中如何使用 vue

  1. 安装 vue 的包: cnpm i vue -S

  2. 由于 在 webpack 中,推荐使用 .vue 这个组件模板文件定义组件,所以,需要安装 能解析这种文件的 loader ,执行命令: cnpm i vue-loader vue-template-complier -D

  3. 在 main.js 中,导入 vue 模块 import Vue from ‘vue’

  4. 定义一个 .vue 结尾的组件,其中,组件有三部分组成: template script style

  5. 使用 import login from ‘./login.vue’ 导入这个组件

  6. 创建 vm 的实例 var vm = new Vue ({el: ‘#app’, render: c => c (login) })

  7. 在页面中创建一个 id 为 app 的 div 元素,作为我们 vm 实例要控制的区域;

# export default 和 export 的使用方式

基本使用:

<template>
  <div>
    <h3>这是登录组件,vue 文件定义出来的--{{ msg }}</h3>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "哈哈哈",
    };
  },
  methods: {
    show() {
      console.log("调用了 login.vue 中的show 方法!");
    },
  },
};
</script>

<style></style>

mark

# export,import ,export default 是什么?

ES6 模块主要有两个功能:export 和 import
export 用于对外输出本模块(一个文件可以理解为一个模块)变量的接口
import 用于在一个模块中加载另一个含有 export 接口的模块。
也就是说使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加载这个模块(文件)。这几个都是 ES6 的语法。

  • 注意:export default 向外暴露的成员,可以使用任意的变量来接受
  • 注意:在一个模块中,export default 只允许向外暴露一次
  • 注意:在一个模块中,可以同时使用 export 和 export default 向外暴露成员
在 Node中,使用 var 名称= require('模块标识符')

module.exports和 exports 来暴露成员

# export 与 export default

上面讲的是 export 和 import,但是 exportexport default 有什么区别呢?如下:

  1. export 与 export default 均可用于导出常量、函数、文件、模块等
  2. 你可以在其它文件或模块中通过 import+(常量 | 函数 | 文件 | 模块) 名的方式,将其导入,以便能够对其进行使用
  3. 在一个文件或模块中,export、import 可以有多个,export default 仅有一个,export default 只能导出一个默认模块,这个模块可以匿名( 引入的时候可以给这个模块取任意名字,例如 “obj”,且不需要用大括号括起来。)

export :

//demo1.js
export const str = "hello world";

export function f(a) {
  return a + 1;
}

对应的引入方式:

//demo2.js
import { str, f } from "demo1";

export default

//demo1.js
export default {
  a: "hello",
  b: "world",
};

对应的引入方式:

//demo2.js
import obj from "demo1";
  1. 通过 export 方式导出,在导入时要加 { },export default 则 不需要

这样来说其实很多时候 export 与 export default 可以实现同样的目的,只是用法有些区别。注意第四条,通过 export 方式导出,在导入时要加 { },export default 则不需要。使用 export default 命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。

例如:

var name="李四";
export { name }
//import { name } from "/.a.js"
可以写成:
var name="李四";
export default name
//import name from "/.a.js" 这里name不需要大括号

# 说明与比较:new Vue () 和 export default {}?

在生成、导出、导入、使用 Vue 组件的时候,像我这种新手就会常常被位于不同文件的 new Vue()export default{} 搞得晕头转向。它们含义到底是什么,又有什么异同呢?

首先,Vue 是什么? po 主的理解是 Vue 就是一个构造函数,生成的实例是一个巨大的对象,可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。

所以渲染的时候,可以使用构造 Vue 实例的方式来渲染相应的 html 页面:

new Vue({
    el: '#app'
    ...
})

那么 export default {} 又是来干嘛的?

这是在复用组件的时候用到的。假设我们写了一个单页面组件 A 文件,而在另一个文件 B 里面需要用到它,那么就要用 ES6 的 import/export 语法 ,在文件 A 中定义输出接口 export ** ,在文件 B 中引入 import ** ,然后再生成一个 Vue 实例 new Vue (**) ,把引入的组件用起来,这样就可以复用组件 A 去配合文件 B 生成 html 页面了。

# 结合 webpack 使用 vue-router

  1. 安装 npm i vue-router -S

例:

目录结构:

mark

main.js:

//在 webpack 构建的项目中,使用Vue 进行开发
import Vue from "../node_modules/vue/dist/vue.js";
//1. 导入 vue-router 包
import VueRouter from "vue-router";

//2. 手动安装
Vue.use(VueRouter);

//导入app组件
import app from "./App.vue";
//导入 Account 组件
import account from "./main/Account.vue";
//导入 GoodsList 组件
import goodslist from "./main/GoodsList.vue";

//3. 创建路由对象
let router = new VueRouter({
  routes: [
    { path: "/account", component: account },
    { path: "/goodslist", component: goodslist },
  ],
});

let vm = new Vue({
  el: "#app",
  data: {
    msg: "12322",
  },
  render: (c) => c(app),
  router,
});
//注意:App 这个组件,是通过 VM 实例的 render 函数,渲染出来的,render 函数如果要渲染 组件渲染出来的组件,只能是 el :'#app' 所指定的 元素中

//Account 和 GoodsList 组件,是通过 路由匹配监听到的,所以,这两个组件,只能展示到 属于 路由的 <router-view></router-view> 中去

App.vue:

<template>
  <div>
    <h1>这是app组件</h1>
    <router-link to="/account">account</router-link>
    <router-link to="/goodslist">goodslist</router-link>
    <router-view></router-view>
  </div>
</template>

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

mark

# webpack - 路由嵌套

目录结构:

mark

main.js:

//在 webpack 构建的项目中,使用Vue 进行开发
import Vue from "../node_modules/vue/dist/vue.js";
//1. 导入 vue-router 包
import VueRouter from "vue-router";

//2. 手动安装
Vue.use(VueRouter);

//导入app组件
import app from "./App.vue";
//导入 Account 组件
import account from "./main/Account.vue";
//导入 GoodsList 组件
import goodslist from "./main/GoodsList.vue";

import login from "./son/login.vue";
import register from "./son/register.vue";

//3. 创建路由对象
let router = new VueRouter({
  routes: [
    {
      path: "/account",
      component: account,
      children: [
        { path: "login", component: login },
        { path: "register", component: register },
      ],
    },
    { path: "/goodslist", component: goodslist },
  ],
});

let vm = new Vue({
  el: "#app",
  data: {
    msg: "12322",
  },
  render: (c) => c(app),
  router,
});
//注意:App 这个组件,是通过 VM 实例的 render 函数,渲染出来的,render 函数如果要渲染 组件渲染出来的组件,只能是 el :'#app' 所指定的 元素中

//Account 和 GoodsList 组件,是通过 路由匹配监听到的,所以,这两个组件,只能展示到 属于 路由的 <router-view></router-view> 中去

App.vue:

<template>
  <div>
    <h1>这是app组件</h1>
    <router-link to="/account">account</router-link>
    <router-link to="/goodslist">goodslist</router-link>
    <router-view></router-view>
  </div>
</template>

<script></script>

<style></style>

Account:

<template>
  <div>
    <h1>这是Account组件</h1>

    <router-link to="/account/login">登录</router-link>
    <router-link to="/account/register">注册</router-link>
    <router-view></router-view>
  </div>
</template>

<script></script>

<style></style>

mark

# 组件中 style 标签 lang 属性和 scoped 属性的介绍

**scoped:** 在子组件中设置 style 属性,如果不加 scoped 属性,如果是单页面程序,样式就会作用到全局中去,加了 scoped 属性以后,表示限制了该样式作用域只在该组件中。

<template>
  <div>
    <h1>这是goods组件</h1>
  </div>
</template>

<script></script>

<style scoped>
div {
  color: red;
}
</style>

**lang 属性:** 普通的 style 标签只支持普通的样式,如果想启用 scss 或 less, 需要为 style 设置 lang 属性

<template>
  <div>
    <h1>这是account组件</h1>
  </div>
</template>

<script></script>

<style lang="scss" scoped>
body {
  div {
    font-style: italic;
  }
}
</style>

# 抽离路由模块

目录结构:

mark

main.js:

//在 webpack 构建的项目中,使用Vue 进行开发
import Vue from "../node_modules/vue/dist/vue.js";
//1. 导入 vue-router 包
import VueRouter from "vue-router";

//2. 手动安装
Vue.use(VueRouter);

//导入app组件
import app from "./App.vue";
//导入 自定义路由模块
import router from "./router.js";

let vm = new Vue({
  el: "#app",
  data: {
    msg: "12322",
  },
  render: (c) => c(app),
  router,
});
//注意:App 这个组件,是通过 VM 实例的 render 函数,渲染出来的,render 函数如果要渲染 组件渲染出来的组件,只能是 el :'#app' 所指定的 元素中

//Account 和 GoodsList 组件,是通过 路由匹配监听到的,所以,这两个组件,只能展示到 属于 路由的 <router-view></router-view> 中去

router.js:

import VueRouter from "vue-router";

//导入 Account 组件
import account from "./main/Account.vue";
//导入 GoodsList 组件
import goodslist from "./main/GoodsList.vue";

import login from "./son/login.vue";
import register from "./son/register.vue";
//3. 创建路由对象
let router = new VueRouter({
  routes: [
    {
      path: "/account",
      component: account,
      children: [
        { path: "login", component: login },
        { path: "register", component: register },
      ],
    },
    { path: "/goodslist", component: goodslist },
  ],
});

export default router;

监听事件、nrm、webpack

# Vue 第五天

监听事件(keyup ,watch ,computed )

nrm (简单介绍了下)

webpack (很重要!很关键!很实用!重点中的重点!知识点有点多,得慢慢品味!)

# 监听事件

# 需求:实现名称监听案例

# 1.keyup 事件监听

<div id="app">
  <input type="text" v-model="firstName" @keyup="getFullName" />
  <input type="text" v-model="lastName" @keyup="getFullName" />
  <input type="text" v-model="fullName" />
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      firstName: "",
      lastName: "",
      fullName: "",
    },
    methods: {
      getFullName() {
        this.fullName = this.firstName + this.lastName;
      },
    },
  });
</script>

# 2. 使用 watch 监听 ( 更常用 – 用途更广 )

<div id="app">
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
  <input type="text" v-model="fullName" />
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      firstName: "",
      lastName: "",
      fullName: "",
    },
    //使用这个 属性,可以监视 data 中指定数据的变化,然后触发这个 watch 中对应的 function 处理函数
    watch: {
      firstName(newVal, oldVal) {
        // this.fullName = this.firstName + this.lastName
        this.fullName = newVal + this.lastName;
      },
      lastName(newVal, oldVal) {
        // this.fullName = this.firstName + this.lastName
        this.fullName = this.firstName + newVal;
      },
    },
  });
</script>

mark

  • # watch 监听 路由 改变
<div id="app">
  <router-link to="/login">登录</router-link>
  <router-link to="/register">注册</router-link>
  <router-view></router-view>
</div>

<script>
  let login = {
    template: "<h3>这是登录子组件</h3>",
  };
  let register = {
    template: "<h3>这是注册子组件</h3>",
  };

  let router = new VueRouter({
    routes: [
      {
        path: "/login",
        component: login,
      },
      {
        path: "/register",
        component: register,
      },
    ],
    linkActiveClass: "myactive",
  });

  let vm = new Vue({
    el: "#app",
    data: {},
    router,
    watch: {
      "$route.path"(newVal, oldVal) {
        if (newVal == "/login") {
          console.log("欢迎来到登录页面");
        } else if (newVal == "/register") {
          console.log("欢迎来到注册页面");
        }
      },
    },
  });
</script>

mark

# 3. computed 监听

<div id="app">
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
  <input type="text" v-model="fullName" />
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      firstName: "",
      lastName: "",
    },
    //计算属性:在引用的时候,一定不要加() 去调用,直接把它当作 普通属性去使用就好了
    //只要 计算属性,这个方法内部,所用到的 任何 data 中的数据发生了变化,就会立即重新 计算 这个属性的值
    //计算属性的求值结果,会被缓存起来,方便下次直接使用;如果 计算属性中的数据 都没有发生变化,则不会 重新对 计算属性求值
    computed: {
      fullName() {
        return this.firstName + this.lastName;
      },
    },
  });
</script>

# methods,watch,computed 的区别

  1. computed 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  2. methods 方法表示一个具体的操作,主要书写业务逻辑;
  3. watch 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是 computed 和 methods 的结合体;

# nrm

  • 什么是 nrm?

    nrm 是一个 npm 源管理器,允许你快速地在 npm 源间切换

  • 安装 nrm

    在命令行执行命令, npm install -g nrm ,全局安装 nrm。

mark

  • 切换

    如果要切换到 taobao 源,执行命令 nrm use taobao

mark

# webpack

# 1. 在网页中会引用哪些常见的静态资源?

  • JS
    • .js .jsx .coffee .ts(TypeScript 类 C# 语言)
  • CSS
    • .css .less .sass .scss
  • Images
    • .jpg .png .gif .bmp .svg
  • 字体文件(Fonts)
    • .svg .ttf .eot .woff .woff2
  • 模板文件
    • .ejs .jade .vue【这是在 webpack 中定义组件的方式,推荐这么用】

# 2. 网页中引入的静态资源多了以后有什么问题???

  1. 网页加载速度慢, 因为 我们要发起很多的二次请求;
  2. 要处理错综复杂的依赖关系

# 3. 如何解决上述两个问题

  1. 合并、压缩、精灵图、图片的 Base64 编码
  2. 可以使用之前学过的 requireJS、也可以使用 webpack 可以解决各个包之间的复杂依赖关系;

# 4. 什么是 webpack?

webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;

# 5. 如何完美实现上述的 2 种解决方案

  1. 使用 Gulp, 是基于 task 任务的;
  2. 使用 Webpack, 是基于整个项目进行构建的;
    • 借助于 webpack 这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。
    • 根据官网的图片介绍 webpack 打包的过程
    • webpack 官网

# 6.webpack 安装的两种方式

  1. 运行 npm i webpack -g 全局安装 webpack,这样就能在全局使用 webpack 的命令
  2. 在项目根目录中运行 npm i webpack --save-dev 安装到项目依赖中

# webpack 小案例

最终实现效果图:

mark

# 第一阶段:用 webpack 打包 main.js 文件生成 bundle.js 文件

目录结构

mark

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="../dist/bundle.js"></script>
  </head>
  <body>
    <ul>
      <li>这是第1个li</li>
      <li>这是第2个li</li>
      <li>这是第3个li</li>
      <li>这是第4个li</li>
      <li>这是第5个li</li>
      <li>这是第6个li</li>
      <li>这是第7个li</li>
      <li>这是第8个li</li>
      <li>这是第9个li</li>
      <li>这是第10个li</li>
    </ul>
  </body>
</html>

main.js:

// main.js 是我们项目的JS的入口文件

//1.导入 Jquery
// import *** from *** 是 ES6 中导入模块的方式
//由于 ES6 语法太高级了,浏览器解析不了,所以,执行会报错
import $ from "jquery";

$(function () {
  $("li:odd").css({
    background: "red",
  });
  $("li:even").css({
    background: function () {
      return "#" + "D97634";
    },
  });
});

1. 安装完相关包之后,在根目录下运行 webpack .\src\main.js -o .\dist\bundle.js

mark

运行结果:

mark

# 第二阶段:配置 webpack.config.js

Webpack 在执行的时候,除了在 命令行传入参数,还可以通过指定的 配置文件 来执行。默认情况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者通过 --config 选项来指定配置文件。

# 当我们在 控制台,直接输入 webpack 命令执行的时候,webpack 做了以下几步:
  1. 首先,webpack 发现,我们并没有通过命令的形式,给它指定入口和出口
  2. webpack 就会去 项目的 根目录中,查找一个叫做 webpack.config.js 的配置文件
  3. 当找到配置文件后,webpack 会去解析执行这个 配置文件,当解析执行完配置文件后,就得到了 配置文件中,导出的配置对象
  4. 当 webpack 拿到 配置对象后,就拿到了 配置对象中,指定的 入口 和 出口,然后进行打包构建;

webpack.config.js

const path = require("path");

module.exports = {
  entry: path.join(__dirname, "./src/main.js"), // 入口,表示,要使用 webpack 打包哪个文件
  output: {
    // 输出文件相关的配置
    path: path.join(__dirname, "./dist"), // 指定 打包好的文件,输出到哪个目录中去
    filename: "bundle.js", // 这是指定 输出的文件的名称
  },
};
  • 然后再根目录下运行 webpack

mark

# 使用 webpack-dev-server 这个工具,来实现自动打包编译的功能

  1. 运行 npm i webpack-dev-server -D 把这个工具安装到项目的本地开发依赖 npm install --save 和 npm install -d 的区别
  2. 安装完毕后,这个 工具的用法, 和 webpack 命令的用法,完全一样
  3. 由于,我们是在项目中,本地安装的 webpack-dev-server , 所以,无法把它当作 脚本命令,在 powershell 终端中直接运行;(只有那些 安装到 全局 -g 的工具,才能在 终端中正常执行)
  4. 注意: webpack-dev-server 这个工具,如果想要正常运行,要求,在本地项目中,必须安装 webpack
  5. webpack-dev-server 帮我们打包生成的 bundle.js 文件,并没有存放到 实际的 物理磁盘上;而是,直接托管到了 电脑的内存中,所以,我们在 项目根目录中,根本找不到 这个打包好的 bundle.js;
  6. 我们可以认为, webpack-dev-server 把打包好的 文件,以一种虚拟的形式,托管到了 咱们项目的 根目录中,虽然我们看不到它,但是,可以认为, 和 dist src node_modules 平级,有一个看不见的文件,叫做 bundle.js
  • 再 package.json 文件中 配置 webpack-dev-server

mark

  • 在根目录下运行命令: npm run dev

mark

# 自动编译(保存代码即自动刷新浏览器)

mark

# 运行 npm run dev 后 自动 打开对应端口号的浏览器窗口 改端口号 显示内容

# 方式一:配置 package.json 文件

优点:------- 推荐,简单,直接。在开发中更常用。

package.json:

mark

# 方式二:配置 webpack.config.js 文件

  • 相比之下 麻烦一点

webpack.config.js:

const path = require("path");
// 启用热更新的 第2步
const webpack = require("webpack");

module.exports = {
  entry: path.join(__dirname, "./src/main.js"), // 入口,表示,要使用 webpack 打包哪个文件
  output: {
    // 输出文件相关的配置
    path: path.join(__dirname, "./dist"), // 指定 打包好的文件,输出到哪个目录中去
    filename: "bundle.js", // 这是指定 输出的文件的名称
  },
  devServer: {
    //这是配置  dev-server 命令参数的第二种形式,相对来说,麻烦一点。
    open: true, //自动打开浏览器
    port: 3000, //设置启动时候的运行端口
    contentBase: "src",
    hot: true, //启用热更新 第1步
  },
  plugins: [
    //配置插件的节点
    new webpack.HotModuleReplacementPlugin(), // new 一个热更新的 模块对象, 这是 启用热更新的第 3 步
  ],
};

# 使用 html-webpack-plugin 插件

配合 webpack-dev-server 工具使用

作用:为 html 文件中引入的外部资源

这个插件的两个作用:

  • 为 html 文件中引入的外部资源如 scriptlink 动态添加每次 compile 后的 hash,防止引用缓存的外部文件问题
  • 可以生成创建 html 入口文件,比如单页面可以生成一个 html 文件入口,配置 Nhtml-webpack-plugin 可以生成 N 个页面入口

实例作用(简单来说)

  1. 自动在内存中根据指定页面生成一个内存的页面

  2. 自动,把打包好的 bundle.js 追加到页面中去

    mark

# 安装使用如下:

一、首先安装 html-webpack-plugin 插件

​ 在 cmd 中打开项目,输入 npm i html-webpack-plugin -D

二、在 webpack-config.js 的 plugins 里面添加 信息,如下图

mark

然后在 cmd 中输入 npm run dev ,即可以在项目文件夹下自动生成 index.html。如果报错,则表示,未安装 html-webpack-plugin 插件。

注:不配置任何选项的 html-webpack-plugin 插件,他会默认将 webpack 中的 entry 配置所有入口 thunk 和 extract-text-webpack-plugin 抽取的 css 样式都插入到文件指定的位置

# webpack 处理第三方文件类型的过程

# 需求:打包处理 css 文件

注意:

  • webpack, 默认只能打包处理 JS 类型的文件,无法处理 其它的非 JS 类型的文件; 如果要处理 非 JS 类型的文件,我们需要手动安装一些 合适 第三方 loader 加载器;

  • 1. 如果想要打包处理 css 文件,需要安装 npm i style-loader css-loader -D

    mark

  • 2. 打开 webpack.config.js 这个配置文件,在 里面,新增一个 配置节点,叫做 module, 它是一个对象;在 这个 module 对象身上,有个 rules 属性,这个 rules 属性是个 数组;这个数组中,存放了,所有第三方文件的 匹配和 处理规则;

    mark

webpack 处理第三方文件类型的过程:

  1. 发现这个 要处理的文件不是 JS 文件,然后就去 配置文件中,查找有没有对应的第三方 loader 规则
  2. 如果能找到对应的规则, 就会调用 对应的 loader 处理 这种文件类型;
  3. 在调用 loader 的时候,是从后往前调用的;
  4. 当最后的一个 loader 调用完毕,会把 处理的结果,直接交给 webpack 进行 打包合并,最终输出到 bundle.js 中去

# 打包 less 文件

第一步:安装包:

  1. 安装 npm i less-loader -D

    mark

  2. less-loader 内部依赖 less 包,所以安装 npm i less -D

mark

第二步:配置 webpack.config.js 文件

mark

第三步:根目录下 运行 npm run dev

mark

父子组件间通信、路由

# Vue 第四天

父子组件间通信

ref 获取 DOM 元素 和 组件

路由

# 父组件向子组件传值

# 1. 子组件使用父组件中的数据
  • 初步尝试 —(错误分析)

mark

  • 正确使用方法
<div id="app">
  <!--父组件,可以在引用子组件的时候,通过 属性绑定(v-bind:) 的形式,把需要 传递给子组件 的数据,以属性绑定的形式,传递到子组件内部,供子组件使用-->
  <com1 v-bind:parentmsg="msg"></com1>
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      msg: "123 父组件中的数据",
    },
    methods: {},
    components: {
      com1: {
        //props中的数据,都是只读的,无法重新赋值。
        props: ["parentmsg"], //把父组件传递过来的 parentmsg 属性,先在 props 数组中,先定义一下,这样才能使用这个数据
        template: "<h1>这是子组件---{{parentmsg}}</h1>",
      },
    },
  });
</script>

mark

# 2. 子组件使用父组件中的方法
<div id="app">
  <com1 @func="show"></com1>
</div>

<template id="tmp1">
  <div>
    <h1>这是子组件</h1>
    <button @click="myclick">这是子组件的中的按钮</button>
  </div>
</template>
<script>
  let com2 = {
    template: "#tmp1",
    methods: {
      myclick() {
        //  emit 英文原意:是触发,调用,发射的意思
        this.$emit("func");
      },
    },
  };

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {
      show() {
        console.log("调用父组件身上的show方法");
      },
    },
    components: {
      com1: com2,
    },
  });
</script>

mark

# 子组件向父组件传值

  • 设置参数( 供子组件传递参数 )

mark

mark

  • 列举 list 数据对象 说明
<div id="app">
  <com1 @func="show"></com1>
</div>

<template id="tmp1">
  <div>
    <h1>这是子组件</h1>
    <button @click="myclick">这是子组件的中的按钮</button>
  </div>
</template>

<script>
  let com2 = {
    template: "#tmp1",
    data() {
      return {
        list: [{ id: "1", age: "18" }],
      };
    },
    methods: {
      myclick() {
        // emit 英文原意:是触发,调用,发射的意思
        this.$emit("func", this.list);
      },
    },
  };

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {
      show(data) {
        console.log(data);
      },
    },
    components: {
      com1: com2,
    },
  });
</script>

mark

# 组件案例 - 发表评论

发表评论案例

所需知识点: 父子组件传值,localStorage 本地储存

代码:

<div id="app">
    <comment-box @func="loadlocalStorage"></comment-box> <!--评论组件调用-->
    <ul class="list-group">
        <li class="list-group-item" v-for="item in list" :key="item.id">
            <span class="badge">{{item.user}}</span>
            {{item.content}}
        </li>
    </ul>
</div>

<!--评论组件模板-->
<template id="tmp1">
    <div>
        <div class="form-group">
            <label for="exampleInputEmail1">user:</label>
            <input type="search" class="form-control" 	             		                        id="exampleInputEmail1" placeholder="user" v-model="user">
        </div>
        <div class="form-group">
            <label for="exampleInputEmail2">content:</label>
            <textarea class="form-control" id="exampleInputEmail2" v-model="content"                placeholder="发表留言"></textarea>
        </div>
        <button type="button" class="btn btn-primary" style="margin-bottom: 15px"                       @click="Postcomment">发表
   </div>
</template>

    <script>
        let commentBox = {
            template: '#tmp1',
            data() {
                return {
                    user: '',
                    content: ''
                }
            },
            methods: {
                Postcomment() {
                    //分析:发表评论的业务逻辑
                    //1.评论数据存到哪里去? 存放到 localStorage 中 	 		            	                       localStorage.setItem('cmts','')
                    //2.先组织出一个最新的评论数据对象
                    //3.想办法,把 第二步中,得到的评论对象。保存到 localStorage 中
                    //   3.1 localStorage 只支持存放字符串数据,要先调用 	  JSON.stringify
                    //   3.2 在保存最新的 评论数据之前,要先从 localStorage 获取之前的评论数据  (string),转换为一个数组对象然后把最新的评论,push到这个数组
                    //   3.3 如果获取到的localStorage中 的评论字符串,为空不存在,则 可以 返回一个'[]' ,让JSON.parse 去转换
                    //   3.4 把 最新的 评论列表数组,再次调用 JSON.stringify 转换为 数组字符串,然后调用 localStorage.setItem()

                    let comment = {id: Date.now(), user: this.user, content: this.content}

                    //从 localStorage 获取所有的评论
                    let list = JSON.parse(localStorage.getItem('cmts') || '[]')

                    list.unshift(comment)
                    //重新保存 最新的评论数据
                    localStorage.setItem('cmts', JSON.stringify(list))
                    this.user = this.content = ''
                    this.$emit('func')
                }
            }
        }

        let vm = new Vue({
            el: '#app',
            data: {
                list: [{id: Date.now(), user: '李白', content: '天生我材必有用'},
                       {id: Date.now(), user: '张三', content: '锄禾日当午'},
                       {id: Date.now(), user: '李四', content: '白日依山尽'}]
            },
            created() {
                //从本地的 localStorage 中加载评论列表
                this.list = JSON.parse(localStorage.getItem('cmts') || '[]')
            },
            methods: {
                loadlocalStorage() {
                    this.list = JSON.parse(localStorage.getItem('cmts') || '[]')
                }
            },
            components: {
                commentBox: commentBox
            }
    </script>

mark

# ref 获取 DOM 元素和组件

# ref 获取 DOM 元素

  • 基本使用
<div id="app">
  <button @click="getElement">获取元素</button>
  <h3 id="myh3" ref="myh3">今天阳光明媚</h3>
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {
      getElement() {
        // console.log(document.getElementById('myh3').innerText)
        console.log(this.$refs.myh3.innerText);
      },
    },
  });
</script>

mark

# ref 获取组件

<div id="app">
  <button @click="getElement">获取元素</button>
  <h3 id="myh3" ref="myh3">今天阳光明媚</h3>
  <hr />
  <login ref="mylogin"></login>
</div>
<script>
  let login = {
    template: "<h1>这是组件</h1>",
    data() {
      return {
        msg: "这是子组件的数据",
      };
    },
    methods: {
      show() {
        console.log("这是子组件的show方法");
      },
    },
  };

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {
      getElement() {
        // console.log(document.getElementById('myh3').innerText)
        // console.log(this.$refs.myh3.innerText)
        console.log(this.$refs.mylogin.msg);
      },
    },
    components: {
      login,
    },
  });
</script>

mark

# 路由

# 什么是路由?

可以参考

后端路由:对于普通的网站,所有的超链接都是 URL 地址,所有的 URL 地址都对应服务器上对应的资源;

前端路由:对于单页面应用程序来说,主要通过 URL 中的 hash (# 号) 来实现不同页面之间的切换,同时,hash 有一个特点:HTTP 请求中不会包含 hash 相关的内容;所以,单页面程序中的页面跳转主要用 hash 实现;

在单页面应用程序中,这种通过 hash 改变来切换页面的方式,称作前端路由(区别于后端路由);URL 中的 hash(井号)

Vue Router

实用小文档

  • 路由的基本使用
  • 登陆和注册路由切换小案例:
<script src="../../vue.min.js"></script>
<!--1.安装 vue-router 路由模块-->
<script src="../vue-router.js"></script>
<div id="app">
  <!--在后面 a标签 会被 router-link 所替代-->
  <a href="#/login">登陆</a>
  <a href="#/register">注册</a>
  <!--这是 vue-router 提供的元素,专门用来 当作占位符的,将来,路由规则,匹配到的组件,就会展示到这个 router-view 中去-->
  <!--所以:我们可以把 router-view 认为是一个 占位符-->
  <router-view></router-view>
</div>
<script>
  //组件模板对象
  let login = {
    template: "<h1>登陆组件</h1>",
  };

  let register = {
    template: "<h1>注册组件</h1>",
  };

  //2. 创建 一个路由对象,当 导入 vue-router 包之后,在window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter
  // 在new 路由对象的时候,可以为 构造函数,传递一个配置对象
  let routerobj = new VueRouter({
    // routes :这个配置对象中的 route 表示 【路由匹配规则】 的意思
    routes: [
      //路由匹配规则
      //每个路由规则,都是一个对象,这个规则对象身上,有两个必须的属性
      //属性1 是 path ,表示哪个路由链接地址;
      //属性2 是 component ,表示,如果 路由是前面匹配到的 path,则展示 component 属性对应的那个组件
      { path: "/login", component: login },
      { path: "/register", component: register },
    ],
  });

  let vm = new Vue({
    el: "#app",
    data: {
      msg: "sadsad",
    },
    router: routerobj, //将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
  });
</script>

mark

  • router-link 的使用(替代 a 标签):

mark

  • 首页重定向(根路径重定向)

mark

  • 路由高亮效果

mark

mark

mark

  • 在路由中使用 动画

mark

mark

# 路由规则中定义参数

  • 传参方式一:
<div id="app">
  <router-link to="/login?id=10">登陆</router-link>
  <router-link to="/register">注册</router-link>
  <router-view></router-view>
</div>
<script>
  //组件模板对象
  let login = {
    template: "<h1>登陆组件</h1>",
    created() {
      console.log(this.$route);
    },
  };

  let register = {
    template: "<h1>注册组件</h1>",
  };

  let routerobj = new VueRouter({
    routes: [
      //路由匹配规则
      { path: "/", redirect: "/login" }, //这里的 redirect 和 Node 中 的redirect 完全是两回事
      { path: "/login", component: login },
      { path: "/register", component: register },
    ],
  });

  let vm = new Vue({
    el: "#app",
    data: {
      msg: "sadsad",
    },
    router: routerobj, //将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
  });
</script>

mark

  • 拿到参数渲染到组件上

mark

mark

或者:==> 更简洁,直观!

mark

  • 传值方式二:

mark

mark

# 路由的嵌套

  • children 的使用
<div id="app">
  <router-link to="/account">account</router-link>
  <router-view></router-view>
</div>

<template id="tmp1">
  <div>
    <h1>这是account组件</h1>
    <router-link to="/account/login">登录</router-link>
    <router-link to="/account/register">注册</router-link>
    <router-view></router-view>
  </div>
</template>
<script>
  let account = {
    template: "#tmp1",
  };

  let login = {
    template: "<h3>login</h3>",
  };

  let register = {
    template: "<h3>register</h3>",
  };

  let router = new VueRouter({
    routes: [
      {
        path: "/account",
        component: account,
        /*使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,
否则永远以根路径开始请求,这样不方便我们用户去理解URL地址*/
        children: [
          { path: "login", component: login },
          { path: "register", component: register },
        ],
      },
    ],
  });

  let vm = new Vue({
    el: "#app",
    data: {
      msg: "哈哈",
    },
    router,
  });
</script>

mark

# 路由 - 命名视图实现经典布局

  • 命名视图实现经典布局
<style>
  * {
    margin: 0;
    padding: 0;
  }
  .header {
    height: 80px;
    background: orange;
  }
  .container {
    display: flex;
    height: 400px;
  }
  .left {
    background: #2b542c;
    flex: 2;
  }
  .main {
    background: red;
    flex: 8;
  }
</style>

<div id="app">
  <router-view></router-view>
  <div class="container">
    <router-view name="left"></router-view>
    <router-view name="main"></router-view>
  </div>
</div>

<script>
  let header = {
    template: '<h1 class="header">Header头部区域</h1>',
  };
  let leftBox = {
    template: '<h1 class="left">leftBox区域</h1>',
  };
  let mainBox = {
    template: '<h1 class="main">mainBox区域</h1>',
  };

  let router = new VueRouter({
    routes: [
      // {path:'/',component:header},
      // {path:'/left',component:leftBox},
      // {path:'/main',component:mainBox}
      {
        path: "/",
        components: {
          default: header,
          left: leftBox,
          main: mainBox,
        },
      },
    ],
  });

  let vm = new Vue({
    el: "#app",
    data: {
      msg: "哈啊哈",
    },
    router,
  });
</script>

mark

过渡效果、组件

# Vue 第三天

过渡效果

组件

# 过渡效果

# 概述

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。
包括以下工具:

  1. 在 CSS 过渡和动画中自动应用 class

  2. 可以配合使用第三方 CSS 动画库,如 Animate.css

  3. 在过渡钩子函数中使用 JavaScript 直接操作 DOM

  4. 可以配合使用第三方 JavaScript 动画库,如 Velocity.js

# 1. 使用过度类名实现动画

写个简单例子说明:

<!--自定义两组样式,来控制 transition 内部的元素实现动画-->
<style>
  /* v-enter :  【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入
       v-leave-to:【这是一个时间点】 是动画离开之后,离开的终止状态,此时, 元素动画已经结束
    */
  .v-enter,
  .v-leave-to {
    opacity: 0;
    transform: translateX(80px);
  }
  /*
      v-enter-active: 入场动画的时间段
      v-leave-active:离场动画的时间段
    */
  .v-enter-active,
  .v-leave-active {
    transition: all 2s;
  }
</style>

<div id="app">
  <button @click="flag=!flag">点击</button>
  <!--1. transition 元素,是Vue 官方提供的-->
  <transition>
    <h3 v-if="flag">这是一个h3</h3>
  </transition>
</div>

<script>
  let vm = new Vue({
    el: "#app",
    data: {
      flag: true,
    },
  });
</script>

mark

修改 v - 前缀 (自定义前缀)

<style>
  .my-enter,
  .my-leave-to {
    opacity: 0;
    transform: translateY(-80px);
  }
  .my-enter-active,
  .my-leave-active {
    transition: all 2s;
  }
</style>
<body>
  <button @click="flag1=!flag1">点击</button>
  <!--1. transition 元素,是Vue 官方提供的-->
  <transition name="my">
    <h6 v-if="flag1">这是一个h6</h6>
  </transition>
</body>

# 2. 使用第三方类实现动画

animate.css 官网

在线 cdn

<link rel="stylesheet" href="../../animate.css" />
<div id="app">
  <button @click="flag=!flag">点击</button>
  <!--使用 transition 元素 包裹起来-->
  <transition
    enter-active-class="bounceIn"
    leave-active-class="bounceOut"
    :duration="{enter:200,leave:400}"
  >
    <h3 v-if="flag">这是一个h3</h3>
  </transition>
</div>

直接使用在元素身上

<transition>
  <h3 class="animated infinite bounce delay-2s">这是一个h3</h3>
</transition>

# 3.JavaScript 钩子

html:

<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"
  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  <!-- ... -->
</transition>

js

methods: {
  // 进入中
  beforeEnter: function (el) {
    // ...
  },
  // 此回调函数是可选项的设置
  // 与 CSS 结合时使用
  enter: function (el, done) {
      done()
  },
  afterEnter: function (el) {
  },
  enterCancelled: function (el) {
  },

  // 离开时
  beforeLeave: function (el) {
    // ...
  },
  // 此回调函数是可选项的设置
  // 与 CSS 结合时使用
  leave: function (el, done) {
    done()
  },
  afterLeave: function (el) {
    // ...
  },
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) {
    // ...
  }
}
# 案例:小球动画
<div id="app">
  <button @click="flag = !flag">加入购物车</button>
  <!--使用 transition 元素把 小球包裹起来-->
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:after-enter="afterEnter"
  >
    <div v-show="flag" class="ball"></div>
  </transition>
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      flag: false,
    },
    methods: {
      //注意:动画钩子函数的第一个参数 el ,表示 要执行动画的那个 DOM 元素,是个原生的JS 对象
      beforeEnter(el) {
        //beforeEnter : 表示动画入场之前,此时,动画尚未开始,可以在 beforeEnter中,设置元素开始动画之前的起始样式
        el.style.transform = "translate(0,0)";
      },
      enter(el, done) {
        el.offsetWidth;
        //这句话,没有实际的作用,但是,如果不写,出不来动画效果
        //可以认为  el.offsetWidth 会强制动画刷新
        // enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的,结束状态
        el.style.transform = "translate(150px,150px)";
        el.style.transition = "all 2s";

        //这里的done, 起始就是 afterEnter 这个函数。也就是说:done 是afterEnter函数的引用
        done();
      },
      afterEnter(el) {
        //动画完成之后,会调用 afterEnter()
        this.flag = !this.flag;
      },
    },
  });
</script>

mark

# 列表过渡

目前为止,关于过渡我们已经讲到

  • 单个节点
  • 一次渲染多个节点

那么怎么同时渲染整个列表,比如使用 v-for ?在这种场景中,使用 <transition-group> 组件。在我们深入例子之前,先了解关于这个组件的几个特点:

  • 不同于 <transition> , 它会以一个真实元素呈现:默认为一个 <span> 。你也可以通过 tag 特性更换为其他元素。
  • 元素 一定需要 指定唯一的 **key 特性值 **

举例说明:

# 列表添加动画

<style>
  .v-enter,
  .v-leave-to {
    opacity: 0;
    transform: translateY(80px);
  }

  .v-enter-active,
  .v-leave-active {
    transition: all 2s;
  }
</style>

<div id="app">
  <ul>
    <!--在实现列表过度的时候,如果需要过度的元素,是通过 v-for 循环渲染出来的,不能使用transition 包裹,需要使用transitionGroup-->
    <!--如果要为 v-for 循环创建的元素设置动画,必须为每一个元素 设置 :key 属性-->
    <transition-group>
      <li v-for="item in list" :key="item.id">{{item.id}}--- {{item.name}}</li>
    </transition-group>
  </ul>
  <label for="id">id:</label>
  <input type="text" v-model="id" id="id" />
  <label for="name">name:</label>
  <input type="text" v-model="name" id="name" />
  <button @click="add">添加</button>
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {
      list: [
        {
          id: 1,
          name: "张三",
        },
        {
          id: 2,
          name: "李四",
        },
        {
          id: 3,
          name: "王五",
        },
        {
          id: 4,
          name: "赵二",
        },
        {
          id: 5,
          name: "王八",
        },
      ],
      id: "",
      name: "",
    },
    methods: {
      add() {
        this.list.push({ id: this.id, name: this.name });
        (this.id = ""), (this.name = "");
      },
    },
  });
</script>

mark

# 删除动画

<style>
  .v-enter,
  .v-leave-to {
    opacity: 0;
    transform: translateY(80px);
  }
  .v-enter-active,
  .v-leave-active {
    transition: all 2s;
  }

  /*下面的 .v-move和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐飘上来的效果*/
  .v-move {
    transition: all 3s ease;
  }
  .v-leave-active {
    position: absolute;
  }
</style>

<ul>
  <transition-group>
    <li v-for="(item,i) in list" :key="item.id" @click="del(i)">
      {{item.id}}---{{item.name}}
    </li>
  </transition-group>
</ul>

mark

# 实现入场时候的效果( appear 属性)

<ul>
  <!--给 transition-group 添加 appear 属性,实现入场时候的效果-->
  <transition-group appear>
    <li v-for="(item,i) in list" :key="item.id" @click="del(i)">
      {{item.id}}---{{item.name}}
    </li>
  </transition-group>
</ul>

mark

# 解决 ul 标签下 span 包裹问题

mark

# 解决方案

去掉 ul 标签,并给 transition-group 标签加 tag=“ul” 。

<transition-group appear tag="ul">
  <li v-for="(item,i) in list" :key="item.id" @click="del(i)">
    {{item.id}}---{{item.name}}
  </li>
</transition-group>

# 组件

# 什么是组件?

组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。

组件的出现,就是为了拆分 Vue 实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可

# 组件化和模块化的不同:

# 组件化

​ 就是 **“基础库 " 或者 “基础组件”,** 意思是把代码重复的部分提炼出一个个组件供给功能使用。

​ 使用:Dialog,各种自定义的 UI 控件、能在项目或者不同项目重复应用的代码等等。

​ 目的:复用,解耦。

​ 依赖:组件之间低依赖,比较独立。

​ 架构定位:纵向分层(位于架构底层,被其他层所依赖)。

# 模块化

​ 就是 **“业务框架 " 或者 “业务模块”**,也可以理解为 “框架”,意思是把功能进行划分,将同一类型的代码整合在一起,所以模块的功能相对复杂,但都同属于一个业务。

​ 使用:按照项目功能需求划分成不同类型的业务框架(例如:注册、登录、外卖、直播…)

​ 目的:隔离 / 封装 (高内聚)。

​ 依赖:模块之间有依赖的关系,可通过路由器进行模块之间的耦合问题。

​ 架构定位:横向分块(位于架构业务框架层)。

# 总结

​ 其实组件相当于,把一些能在项目里或者不同类型项目中可复用的代码进行工具性的封装。

​ 而模块相应于业务逻辑模块,把同一类型项目里的功能逻辑进行进行需求性的封装。

# 创建组件实例:

注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容必须有且只能有唯 一的一个根元素

# 全局组件

# 创建组件的方式 1

实例:

<div id="app">
  <my-com1></my-com1>
</div>
<script>
  //1.1 使用 Vue.extend 来创建全局的Vue组件
  let com1 = Vue.extend({
    template: "<h3>这是使用 Vue.extend 创建的组件</h3>",
  });

  //1.2 使用  Vue.component('组件的名称',创建出来的组件模板对象)
  //如果 使用  Vue.component定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母,两个单词之间,使用 - 链接
  //如果不使用驼峰,则直接拿名称来使用
  Vue.component("myCom1", com1);

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {},
  });
</script>

mark

# 简化:

Vue.component(
  "myCom1",
  Vue.extend({
    template: "<h3>这是使用 Vue.extend 创建的组件</h3>",
  })
);

# 创建组件的方式 2

实例

<div id="app">
  <my-com2></my-com2>
</div>
<script>
  Vue.component("myCom2", {
    //注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容必须有且只能有唯一的一个根元素
    template: "<h3>这是直接使用 Vue.component 创建出来的组件</h3>",
  });
</script>

mark

# 创建组件的方式 3

<div id="app">
  <my-com3></my-com3>
</div>
<!--在 被控制的 #app 外面,使用 template 元素,定义组件的 HTML 模板结构-->
<template id="tmp1">
  <div>
    <h3>
      这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的智能提示和
      高亮
    </h3>
    <h4>好用不错哦!</h4>
  </div>
</template>

<script>
  Vue.component("myCom3", {
    template: "#tmp1",
  });

  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {},
  });
</script>

mark

# 私有组件

自定义一个私有组件

<div id="app">
  <login></login>
</div>
<script>
  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {},
    components: {
      //定义实例内部私有组件
      login: {
        template: "<h2>这是定义实例内部私有组件</h2>",
      },
    },
  });
</script>

mark 同理而言:或者这样处理

<div id="app">
  <login></login>
</div>
<!--在 被控制的 #app 外面,使用 template 元素,定义组件的 HTML 模板结构-->
<template id="tmp2">
  <div>
    <h2>这是定义实例内部私有组件</h2>
  </div>
</template>
<script>
  let vm = new Vue({
    el: "#app",
    data: {},
    methods: {},
    components: {
      //定义实例内部私有组件
      login: {
        template: "#tmp2",
      },
    },
  });
</script>

# 组件中的 data 和 methods

  1. 组件可以有自己的 data 数据

  2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但组件的 data 必须为一个方法

  3. 组件中的 data 除了为一个方法之外,这个方法内部,还必须返回一个对象才行

Vue.component("myCom1", {
  template: "<h3>{{msg}}</h3>",
  data: function () {
    return {
      msg: "这是组件中定义的数据",
    };
  },
});

mark

# 组件切换

# v-if 和 v-else ( 2 个组件之间切换 )

<div id="app">
  <a href="" @click.prevent="flag=true">登陆</a>
  <a href="" @click.prevent="flag=false">注册</a>
  <login v-if="flag"></login>
  <register v-else="flag"></register>
</div>

mark

# component :is 实现多个组件之间的切换

<div id="app">
  <a href="" @click.prevent="comName='login'">登陆</a>
  <a href="" @click.prevent="comName='register'">注册</a>
  <!--Vue 提供了 component ,来展示对应名称的组件-->
  <!--component 是一个占位符,:is 属性,可以用来指定展示的组件的名称-->
  <component :is="comName"></component>
</div>
<script>
  Vue.component("login", {
    template: "<h3>{{msg}}</h3>",
    data: function () {
      return {
        msg: "这是登陆",
      };
    },
  });

  Vue.component("register", {
    template: "<h3>{{msg}}</h3>",
    data: function () {
      return {
        msg: "这是注册",
      };
    },
  });

  let vm = new Vue({
    el: "#app",
    data: {
      comName: "login",
    },
    methods: {},
  });
</script>

mark

# 多个组件切换动画

<style>
  .v-enter,
  .v-leave-to {
    opacity: 0;
    transform: translateX(80px);
  }

  .v-enter-active,
  .v-leave-active {
    transition: all 2s;
  }
</style>

<div id="app">
  <a href="" @click.prevent="comName='login'">登陆</a>
  <a href="" @click.prevent="comName='register'">注册</a>

  <!--通过 mode 属性,设置组件切换时候的 模式-->
  <transition mode="out-in">
    <component :is="comName"></component>
  </transition>
</div>

mark

  • Copyrights © 2015-2021 zhou chen
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信