Vue铺垫知识 --- ES6模块化

Vue框架


Vue基础


Vue框架是当前前端最火的框架,这里为了后期配合springBoot框架完成一个完整项目的搭建

Vue

Vue是一套构建用户界面的渐进式JavaScript框架【渐进式: 可以自底向上逐层应用,简单的应用就只需要小巧的核心库,复杂的可以引入各式各样的Vue插件】

Vue的优点 :

  1. 采用组件化的模式,提高代码的复用率,让代码更好维护,比如一个图片的切换的部分就可以将其变成一个vue的文件
  2. 声明式编码,让编码人员无需注解操作DOM,提高开发的效率
//命令式编码
let htmlStr = '';
persons.forEach(p => {
    htmlStr += `<li>${p.id} - ${p.name} - ${p.age}</li>`;
});
//获取list元素
let list = document.getElementById('list');
//修改内容,手动操作DOM
list.innerHTML = htmlstr;

对应的,如果使用声明式编码

<ul id = 'list'>
	<li v-for="p in persons">
        {{p.id} - {p.name} - {p.age}}
    </li>
</ul>
  1. 虚拟DOM,采用Diff算法进行优化,提高复用 ---- 如果原来的数据生成一个列表,现在的列表多了一些数据,那么vue就采用的虚拟DOM,数据先转化为虚拟的DOM,再转化为真实的DOM

Vue前置内容

ES6模块化

模块化就是将js文件分解为各个功能不同的js文件,每一个js文件就是一个独立的模块,每一个模块就对应界面上的一个部分,但是模块化管理,很多的数据要进行共享,就需要遵守一个规范,现在的标准的规范就是ES6

在node.js中遵循的是CommonJS的模块化规范,其中:

  • 导入其他模块中使用require()方法

  • 模块对外共享成员使用module.exports对象

模块化的好处就是模块化和规范化,降低沟通的成本,方便各模块的相互调用; 比如像导航栏就可以单独弄成一个小的模块,这样就可以复用
在ES6模块化规范之前,还有其他的各种各样的模块,AMD和CMD等,但是这些小的规范都是局限的,而ES6模块化的出现就解决了杂乱,直接统一进行管理。

ES6模块化规范是浏览器端和服务器端 的通用的模块化开发规范。在其中定义了:

  1. 每一个js文件都是一个独立的模块
  2. 导入其他的模块的成员使用import关键字
  3. 向外共享模块成员使用export关键字

ES6模块化主要包含3中用法:

  1. 默认导出与默认导入
  2. 按需导出与按需导入
  3. 直接导入并执行模块中的代码

在项目中打开package.json,加入type:module

{
  "type": "module",
  "name": "vue01",
  "version": "1.0.0",
  "description": "first project",
  "author": "Cfeng",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  },
  "dependencies": {
    "vue": "^2.5.2",
    "vue-router": "^3.0.1"
  },

导入和导出操作的都是JSON对象,导入导出可以是默认的也就是找不到固定的export的JSON,就会直接导入默认的,所以导入的时候可以是任何的名称

默认导出【暴露】和默认导入

默认导出就是将js中的成员导出供其他的js文件的使用

export default 默认导出的成员  //默认导出的成员各种都可以,将他们放到{}中

要注意每一个模块只能允许使用唯一的一次的默认导出export default,否则就会报错

这里可以简单示范一下

//这里just作为ES6测试,ES6的出现是必然的趋势,模块化的管理方便js文件的复用,降低耦合度

//默认导出、暴露
let num1 = 2022;

function show(){
	alert("hello,es6");
}

export default { //向外共享的数据,这样其他的模块就可以接收到
	num1,
	show
}

export default {
  show
}

想要检查是否成功运行,就是用node的命令,node XX 就可以执行文件

PS D:\Vueprogramm\vue01> node Export.js
file:///D:/Vueprogramm/vue01/Export.js:15
export default {

SyntaxError: Identifier '.default' has already been declared
    at ESMLoader.moduleStrategy (node:internal/modules/esm/translators:115:18)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:289:14)
    at async link (node:internal/modules/esm/module_job:70:21)

这里就发现了只能使用一次默认导出,因为默认导入就是接收默认导出的,如果多个默认导出,就不知道具体要接收那个的数据【数据是可能出现不同的变量的值的】

这里编写一个简单的Export.js文件来进行模拟

//这里just作为ES6测试,ES6的出现是必然的趋势,模块化的管理方便js文件的复用,降低耦合度

//默认导出、暴露
let num1 = 10;

function show(){
	alert("hello,es6");
}

export default { //向外共享的数据,这样其他的模块就可以接收到
	num1,
	show
}

默认导入就是将已经共享了的数据成员给引入使用,默认导入的语法

import 接收名称 from '模块标识符'   //这里就是从执行的共享的文件中接收指定名称的方法或者成员

这里的模块名称是一个字符串,使用单引号。默认导入使用任意的名称都可以,只要合法即可;不能以数字开头

这里编写一个Imprort文件来进行模拟接收的一方

//Import文件模拟的是另外一个需要使用另外一个模块的数据

//从Export文件中接收数据,首先得到方法
import num1 from './Export.js'      //需要注意这里引入的时候这是前台的路径,写成Export.js会是识别为资源路径
console.log(num1);

这里直接使用node的命令来进行运行js文件; 这里的num1接收的是放入的JSON的数据,不只是其中某一个数据; 因为默认导出的时候没有名称,所以接收的时候可以为任意的合法的名称;同时因为默认导出没有指定名称,所以这里就只能有一个default,不能有多个默认的default

PS D:\Vueprogramm\vue01> node Import.js
{ num1: 2022, show: [Function: show] }

需要注意的是要使用ES6的模块化规范,就必须要修改JavaScript的配置,在根结点中要加入type: 'module'

按需导入和按需导出

按需导出的语法就是export 导出的成员

//这里就修改一下上面的导入导出的数据
//按需导出就是简单在声明变量的时候加上一个export关键字即可,导入的时候接收的就是导出的成员的JSON格式
export let num1 = 2022;

export function show(){   //这里就是将这两个变量进行了按需导出
	alert("hello,es6");
}

按需导入就是import {成员的名称} from '模块名称' //模块名称就是导入的位置 —>这里的位置为前台路径; 想要导入多个值也容易,就是在大括号中放入多个成员,成员之间使用,来进行分割【在HBuilderX中写好路径之后,前面的成员就可以识别

//Import文件模拟的是另外一个需要使用另外一个模块的数据

//从Export文件中接收数据,首先得到方法
import {num1,show} from './Export.js'

console.log(num1);//因为已经导入了成员,就可以直接进行使用

这里运行得到的就是一个具体的

PS D:\Vueprogramm\vue01> node Import.js
2022
  • 每一个js文件【模块】中可以使用多次按需导出,但是只能使用一次默认导出
  • 按需导入的成员的名称必须和按需导出的名称保持一致
  • 按需导入可以和默认导入一起使用
//默认导入的时候名称是任意的,指代的就是之前默认导出的那个JSON对象
import info,{num1 as year,show} from './Export.js'      //info就是默认导出的空的JSON对象


PS D:\Vueprogramm\vue01> node Import.js
{}
  • 按需导入的时候可以使用as进行重命名
import {num1 as year,show} from './Export.js'

console.log(year);//因为已经导入了成员,就可以直接进行使用

使用as重命名和之前的Mysql中取别名是类似的

直接导入并执行模块中的代码

如果只是像单纯地执行某个模块中的代码,但是不需要得到模块向外共享的成员,这个时候就可以直接导入并执行模块代码

import '模块路径'

这里可以简单演示一下

//被导入的模块的代码Export.js
//直接导入就是不需要得到被导入模块的数据,只是单纯执行模块的代码,就和之前在html中链入JavaScript代码类似
let year = 2022;
console.log(year + " 新年快乐呀");  //这里在HBuilderx中如果不小心按ins键,可能会改变光标为下横线,这个时候再按一次即可

//导入的模块Import.js
//直接导入就类似之前的html的链入,只是简单的执行代码,而不需要使用数据
console.log("今年是哪一年呢?");
import './Export.js';

执行的结果为

PS D:\Vueprogramm\vue01> node Import.js
2022 新年快乐呀
今年是哪一年呢?

这里可以看出来导入的模块就是最先执行,不管是在之前还是之后进行导入,最先执行import语句

安装node,vue

这里我就简单在HBuilderX中的节点中加入即可,否则就会报错 : Cannot use import statement outside a module,这里就引入外部的js文件的时候就不用写text/javaScrit

这里还是有问题,因为ES6模块化,这里就在HBuilerX中配置一下node方便直接运行js文件,而不需要再链入html中在下载node之前,这里解释几个名词:

webpack : 主要的用途就是通过CommonJS的语法把所有的浏览器需要发布的静态资源做相关的准备,比如对资源进行打包

vue-cil: 用户生成的Vue工程的模板,和IDEA中的archetype类似,就是帮助快速建立一个vue的项目,只需要npm install就可以安装;Vue3之后换成了@vue/cil

这里安装node就简单在官网上进行下载: Node.js (nodejs.org)

安装成功之后会显示: C:\Users\OMEY-PC>node -v
v16.14.0

这里的node其实和之前的maven有些类似,安装的时候可以参考maven的过程,也有本地仓库cache和镜像;maven是repository

可以使用npm config ls来查看npm的配置信息 --- ls 就是list的简写

//可以使用npm命令来进行修改
修改prefix的值:npm config set prefix 【全局仓库地址】
修改cache的值:npm config set cache【全局缓存地址】

//简单一点就是直接在文件中进行修改就是C盘用户的.npmrc
registry=http://registry.npm.taobao.org
prefix=D:\nodejs\node_global
cache=D:\nodejs\node_cache

在记事本中输入即可,第一个是配置镜像,和maven相同可以加快下载的速度

//使用npm list -global查看本地仓库的信息
C:\Users\OMEY-PC>npm list -global
D:\nodejs\node_global
`-- (empty)

//检查镜像站是否成功
C:\Users\OMEY-PC>npm config get registry
http://registry.npm.taobao.org/

//npm info vue 查看是否能够获得vue
S C:\Windows\system32> Npm info vue

vue@3.2.31 | MIT | deps: 5 | versions: 370
The progressive JavaScript framework for building modern web UI.
https://github.com/vuejs/core/tree/main/packages/vue#readme

dist
.tarball: https://registry.npmmirror.com/vue/-/vue-3.2.31.tgz
.shasum: e0c49924335e9f188352816788a4cca10f817ce6
.integrity: sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==
.unpackedSize: 2.5 MB

注意必须要以管理员身份运行cmd窗口

npm install xx -g 就是安装或者更新xx模块到设置的node_global中

这里就更新一下npm模块

PS C:\Windows\system32> npm list -global
D:\nodejs\node_global
`-- npm@8.5.0

这个时候modules的位置就发生了变化,所以需要配置环境变量NODE_PATH :

安装vue

  • 安装vue ,使用命令 npm install vue -g

  • 安装vue-router,使用命令npm install vue-router -g,安装之后仓库中的dist就是distribution就是发布的产品,也就是需要的文件

  • 安装vue-cli 使用命令npm install vue-cli -g 安装vue的脚手架

接下来要正常使用vue,需要配置环境变量,否则就要在当前目录打开cmd窗口【vue.cmd在node_global下面】,在path中加上路径即可

测试是否配置成功,输入命令vue -V

C:\Users\OMEY-PC>vue -V
2.9.6

脚手架vue-cil中的模板包括webpack和webpack-simple,后者简单一点

创建vue项目

vue create

跳转到项目放置的位置,然后vue create xxx

vue create vue01 //创建项目
cd vue01   //进入项目
npm run serve  运行,这个时候访问就localhost就可以运行项目
vue init webpack

这里使用vue的命令来初始化vue01

vue init webpack vue01  //初始化项目

cd vue01
npm install  //安装依赖

npm run dev //运行 --- 进入localst:8080/#/

或者
npm run  build  //直接打开dist文件夹下生成的index.html文件

注意创建项目的时候想要将项目放到哪里,就先将cmd命令跳转到哪里,必须要以管理员身份来运行

PS D:\Vueprogramm> vue init webpack vue01

'git' �����ڲ����ⲿ���Ҳ���ǿ����еij���
�������ļ�
? Project name vue01
? Project description first project
? Author Cfeng
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm

   vue-cli · Generated "vue01".


# Installing project dependencies ..

还可以使用可视化的vue的界面,需要3以上的版本

PS C:\Windows\system32> vue ui
🚀  Starting GUI...
🌠  Ready on http://localhost:8000

Vue的可视化界面-- 卸载老版本,安装新的版本 — vue-cil的3版本之后使用的新的,也就是@vue/cil

npm uninstall vue-cli -g 卸载老版本的vue-cli

npm install @vue/cli -g 安装新版本的@vue/cil

在HBuilderX中装载node.js插件

安装了node,之前都是在cmd管理员窗口中运行,在HbuilderX中在工具中配置node即可,引入外部下载的node的位置,下载终端即可运行

promise 解决回调地狱

所谓回调地狱就是指多层回调函数相互嵌套,就形成了回调地狱

这里可以以嵌套的延时器来进行举例,setTimeout就是设置延时器,延时Xms后执行前面的功能函数; 这里的功能函数使用的是Lambda表达式的写法,在java中是->,而JavaScript中为=> ;

//下面的实例代码就是回调地狱,这是一个嵌套的延时器,代码耦合度太高,难以维护;大量的冗余代码相互嵌套
setTimeout(() => {
  console.log('延时1秒后输出')

  setTimeout(() => {
    console.log('再延时2秒后输出')

    setTimeout(() =>{
      console.log('再延时3秒后输出')
    }, 3000)
  },2000)
}, 1000)


//运行的结果
PS D:\Vueprogramm\vue01> node Import.js
延时1秒后输出
再延时2秒后输出
再延时3秒后输出

为了解决回调地狱的问题,在ES6中新增了Promise的概念

Promise对象用于表示一个异步操作的最终是否完成以及其结果值,异步方法并不会立即返回最终的值,而是返回一个promise,以便未来某个时候交还给使用者 — 也就是异步方法返回的就是Promise,代表的是异步方法的处理结果,不管是否处理成功或者失败

  1. Promise是一个构造函数

    • 可以创建Promise类的实例, const p = new Promise();

    • new 出来的Promise的实例对象,代表的是一个异步的操作

  2. Promise.protoType上包含一个.then()的方法【prototype – 原型对象】

在这里插入图片描述

可以看到原型对象prototype中包含方法then、catch、finally等方法,所以创建的对象,都可以通过原型链的方式来调用相关的方法。

  1. then方法用来预先指定成功或者失败的回调函数; 这里的then和之前的ajax中的success和error的功能是相同的,就是可以指定成功或者失败会执行的回调函数;所谓的回调函数就是调用者不是programmer,而是条件发生时会自动进行调用

p.then(成功的回调函数,失败的回调函数) 也就是p.then(result=>{} , error => {}); 调用回调函数的时候,成功的回调函数是必选的,失败的回调函数是可选的【也就是失败的回调函数就是可以不用必须指定的】

回调函数的实例 – 顺序读取文件

使用fs模块 – 回调函数读取文件

这里的例子,一共建立了3个文本文件,first.txt;second.txt;third.txt;3个文本文件是有顺序的;读取文件采用的是node中的fs模块;Node.js 中的 fs 模块是文件操作的封装,它提供了文件读取、写入、更名、删除、遍历目录、链接等 POSIX 文件系统操作。与其它模块不同的是,fs 模块中所有的操作都提供了异步和同步的两个版本,具有 sync 后缀的方法为同步方法,不具有 sync 后缀的方法为异步方法

读取文件的方法 : fs.readFile(filename,[encoding],[callback(error,data)] callback就是回调函数,err代表是否有错误发生,boolean类型,而data就是文件的内容

这里使用ES6的语法导出的时候,需要注意的是,fs是默认导出,也就是export default; 所以导入的时候要使用默认导入,不能使用as这个按需导入中的词

//下面的例子就是使用回调回调函数按顺序读取文本文件的内容
//使用fs(file system)这个核心模块就可以操作文件   引入的时候,如果不加./就是根据名称引入核心模块,加上./就是相对的路径
import fs from 'fs'
fs.readFile('./testfile/first.txt','utf8',(err1,data1) => {
  if(err1) return console.log(err1)  //读取文件1失败
  console.log(data1)   //读取文件1成功
  //读取文件2
  fs.readFile('./testfile/second.txt','utf8',(err2,data2) => {
    if(err2) return console.log(err2) //读取文件2失败
    console.log(data2);
    fs.readFile('./testfile/third.txt','utf8',(err3,data3) => {
      if(err3) return console.log(err3)
      console.log(data3)
    })
  })
})

这里可以验证这种传统的嵌套回调函数,也就是回调地狱的结果

PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

今天是2022年的农历正月十五,值此佳节之际 -- 2

祝大家元宵节快乐!-- 3

执行的结果是正确的,但是发现上面的代码可读性和可维护性太差了,回调嵌套太多层了

再次优化,使用then-fs读取文件的内容

使用then-fs读取文件内容【promise】

由于node.js官方提供的fs模块只是支持使用回调函数的方式读取文件,不支持使用promise的方式;所以需要先安装相关的插件–then-fs这第三方的包,支持使用promise

npm install then-fs 【install 可以简写为i】,这里只是在这个项目中加入这个依赖,所以不需要-g;-g是下载到本地的仓库

接下来就可以使用promise的方式来优化回调地狱

但是下面的代码,不能保证文件读取的顺序,因为thenfs的操作时异步的

//使用promise的方式来进行文件的读取
//首先还是使用默认导入的方式导入then-fs模块,因为使用的是thenfs这个对象来进行文件操作
import thenfs from 'then-fs'  //导包就是从项目的node-modules中导入【不加相对路径的时候】,加上相对路径就可以导入其他的模块的内容
//之前提到过.then中失败的回调函数是可选的
//需要特别注意的是,这里的文件操作是异步的,也就是3者的执行顺序是没有关系的
thenfs.readFile("./testfile/first.txt",'utf8').then(data1 => console.log(data1),err1 => console.log(err1.message))
thenfs.readFile("./testfile/second.txt",'utf8').then(data2 => console.log(data2),err2 => console.log(err2.message))
thenfs.readFile("./testfile/third.txt",'utf8').then(data3 => console.log(data3),err3 => console.log(err3.message))

执行的结果为

PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

今天是2022年的农历正月十五,值此佳节之际 -- 2

祝大家元宵节快乐!-- 3

优化这里的执行顺序可以依赖.then方法的特性

如果上一个.then方法返回了一个新的Promise实例对象,则可以通过下一个.then()继续进行处理。通过.then()方法的链式调用,就解决了回调地狱的问题

thenfs的readFile方法的返回值就是一个Promise类型的对象

//Promise支持链式调用,解决回调地狱的问题,thenfs的返回值就是一个Promise类型的对象
//主要就是要将Promise对象进行返回,但是只要有一个失败了,后面的都会失败,下面就是假设都是成功
import thenfs from 'then-fs'
thenfs.readFile('./testfile/first.txt','utf8')//返回值就是一个promise类型的对象,所以可以使用then方法
  .then(data1 => {
    console.log(data1)
    return thenfs.readFile('./testfile/second.txt','utf8')  //将读取的第二个文件后的Promise对象返回
  }) //失败的回调函数可以省略
  .then(data2 => {
    console.log(data2)
    return thenfs.readFile('./testfile/third.txt','utf8')
  })
  .then(data3 => {
    console.log(data3)
  })
//这里就是将文件读取返回Promise对象放到了返回值之中

运行的结果和上面正确的普通的回调地狱的时类似的【思路也是类似的,就是读取成功之后读取下一个,但是少了很多冗余的代码;这里的链式的写法比之前的fs模块的回调地狱的嵌套的写法要好得多,也就是在前一个回调函数中读取下一个文件,但是这里只是使用了读取产生的Promise对象

通过.catch捕获错误,.finally进行强制执行

在Promise的链式操作中如果发生了错误,可以使用Promise.prototype.catch方法来进行捕获和处理

//比如这里的文件一换一个路径
thenfs.readFile('./testfile/first1.txt','utf8')

这个时候直接运行,什么都不会输出,因为没有指定错误时的callback函数
PS D:\Vueprogramm\vue01> node Import.js
PS D:\Vueprogramm\vue01>

这里我们就可以使用Promise的原型对象的.catch方法来进行错误的捕获处理

//这里链式操作,只要前面的错误了,后面的就都不会执行,因为只有执行成功之后才会返回一个Promise对象
import thenfs from 'then-fs'
thenfs.readFile('./testfile/first1.txt','utf8')//返回值就是一个promise类型的对象,所以可以使用then方法
  .then(data1 => {
    console.log(data1)
    return thenfs.readFile('./testfile/second.txt','utf8')  //将读取的第二个文件后的Promise对象返回
  }) //失败的回调函数可以省略
  .then(data2 => {
    console.log(data2)
    return thenfs.readFile('./testfile/third.txt','utf8')
  })
  .then(data3 => {
    console.log(data3)
  })
  .catch(err => {
     console.log(err.message)
   })
   .finally(() => {
     console.log("我是finally,和java类似,都会强制执行")
   })


----------运行的结果-----------
PS D:\Vueprogramm\vue01> node Import.js
ENOENT: no such file or directory, open 'D:\Vueprogramm\vue01\testfile\first1.txt'
我是finally,和java类似,都会强制执行

加了异常的捕获处理之后,就可以看到异常no such file or directory, open ,这里和java的异常处理类似,因为这里相当于域是上面的所有的代码,所以里面发生了异常,下面的2,3都不会执行,这里想要后面的执行,就哪里发生异常就在哪里进行捕获

//这里链式操作,只要前面的错误了,后面的就都不会执行,因为只有执行成功之后才会返回一个Promise对象
import thenfs from 'then-fs'
thenfs.readFile('./testfile/first1.txt','utf8')//返回值就是一个promise类型的对象,所以可以使用then方法
 .catch(err => {
    console.log(err.message)
  })
  .then(data1 => {
    console.log(data1)
    return thenfs.readFile('./testfile/second.txt','utf8')  //将读取的第二个文件后的Promise对象返回
  }) //失败的回调函数可以省略
  .then(data2 => {
    console.log(data2)
    return thenfs.readFile('./testfile/third.txt','utf8')
  })
  .then(data3 => {
    console.log(data3)
  })


------------------------------
PS D:\Vueprogramm\vue01> node Import.js
ENOENT: no such file or directory, open 'D:\Vueprogramm\vue01\testfile\first1.txt'
undefined  --->   //因为这里读取文件失败,所以打印的结果是undefined
今天是2022年的农历正月十五,值此佳节之际 -- 2  //后面的两次的执行的结果都是可以正常读取的

祝大家元宵节快乐!-- 3

PS D:\Vueprogramm\vue01>

后面的代码正常执行,和java的异常处理是类似的

Promise的静态方法 — Promise.all()

这个方法会发起并行的Promise的异步操作,等所有的异步操作全部执行结束之后才会执行下一步的.then方法,.all的参数就是Promise对象的集合 数组中Promise实例的顺序,就是最终结果的顺序, 因为执行的结果都存放好了,不是开始,而是结束

//这里链式操作,只要前面的错误了,后面的就都不会执行,因为只有执行成功之后才会返回一个Promise对象
import thenfs from 'then-fs'
//建立一个数组,来进行Promise结果的存储
const promiseArr = [
  thenfs.readFile('./testfile/first.txt','utf8'),
  thenfs.readFile('./testfile/second.txt','utf8'),
  thenfs.readFile('./testfile/third.txt','utf8')
]

Promise.all(promiseArr) //等待所有的异步操作全部执行完毕
  .then((data1,data2,data3) => {
    console.log(data1 + "~"  + data2 + "~" + data3)
  })
  .catch(err => {
    console.log(err.message)
  })
  .finally(() => {
    console.log("hello,你是否开心")
  })


-----------------------------
PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1
,今天是2022年的农历正月十五,值此佳节之际 -- 2
,祝大家元宵节快乐!-- 3
~undefined~undefined
hello,你是否开心

--------------直接输出的结果 就是一个结果的数组,将文件的读取结果放入了一个数组之中
[
  '今年是2022年,壬寅虎年 -- 1\n',
  '今天是2022年的农历正月十五,值此佳节之际 -- 2\n',
  '祝大家元宵节快乐!-- 3\n'
]

这里的data1就是上面的所有的异步对象的处理结果,后面两个参数并没有传入,所以是undefined

Promise的静态方法 — Promise.race()

这个方法和上面的方法不同,上面是all,需要所有的异步操作都执行完毕才会执行下一步的操作,但是race【赛跑】,就是发起的并行的异步操作,只要任何一个异步操作完成,就立即执行下一步的.then的操作(赛跑机制)

//这里链式操作,只要前面的错误了,后面的就都不会执行,因为只有执行成功之后才会返回一个Promise对象
import thenfs from 'then-fs'
//建立一个数组,来进行Promise结果的存储
const promiseArr = [
  thenfs.readFile('./testfile/first.txt','utf8'),
  thenfs.readFile('./testfile/second.txt','utf8'),
  thenfs.readFile('./testfile/third.txt','utf8')
]

Promise.race(promiseArr) //等待所有的异步操作全部执行完毕
  .then(data => {
    console.log(data)
  })
  .catch(err => {
    console.log(err.message)
  })

----这个时候打印的结果就是最先执行结束的异步操作的结果------------
PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

基于Promise封装读文件的方法

之前都是使用的then-fs包来进行文件的读取,其实可以不加入依赖,自己手动使用Promise来进行文件的读取方法的编写

但是方法封装也是有要求的

  • 方法的名称必须定义为getFile
  • 方法接收一个形参fpath,表示要读取文件的路径
  • 方法的返回值必须为Promise的实例对象 【 这里的普通的Promise创建的对象可能是读文件的异步操作,也可能是ajax的异步操作】

获取.then的两个实参 : 通过这个方法指定成功或者失败的回调函数,可以在function的形参中进行接收;可以调用resolve和reject来进行处理【这两个回调函数在Promise中

同时使用两个回调函数resolve和reject来进行处理,读取失败就调用reject,读取成功就调用resolve

所以这里可以简单写一个读取文件方法

//就是使用的还是原装的fs模块的内容,返回的就是一个读取文件的Promise对象

//自己封装读取文件的方法,方法名要求为getFile,方法有一个形参fpath代表路径
//必须有Promise类型的返回值
import fs from 'fs'
function getFile(fpath){
  //这里只是创建了一个形式上的异步操作,可能是ajax或者文件,要变成具体的异步操作,需要传递一个function
  //将具体的异步操作定义到function函数内部
  return new Promise(function(resolve,reject){ //两个回调函数对应的就是处理的结果
    //使用node本身的fs的文件操作模块表示为读文件的异步操作
    fs.readFile(fpath,'utf8',(err,data) => { //resolve和reject就是两个可以直接使用的函数
      if(err) return reject(err) //读取失败,就调用失败的回调函数
      return resolve(data)   //读取成功,就执行成功的回调函数resolve
    })
  })
}
//这里书写函数的时候不能使用XX = function(){}  而是使用 function XX(){} 这和原生的js是由区别的,不然下面就识别不到这个函数【根据前面的关键字进行识别】

需要注意的是,这里的reject和resolve都是代指的回调函数,这里不需要定义方法体,在Promise的then方法中可以对两个方法进行编写,这里可以使用promise的.then方法来执行失败或者成功的回调函数

//测试上面的自定义文件promise函数
getFile('./testfile/first.txt').then((data)=> {console.log(data)},(err)=> console.log(err.message))

PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

------------------修改为一个不存在的文件---------------------------
PS D:\Vueprogramm\vue01> node Import.js
ENOENT: no such file or directory, open 'D:\Vueprogramm\vue01\testfile\firs.txt'

说明自定义Promise读取文件函数是可以成功执行的;这里的关键就是引入fs模块,同时要返回一个文件操作的Promise对象;同时要注意创建的时候加入回调函数的参数

async/await简化promise

async和await就是ES8引入的新语法,用来简化Promise的异步操作,在语法出现之前,只能通过链式的.then来处理Promise的异步操作

await在方法的内部进行使用,并且在该方法的前面必须加上async修饰 ---- 要实现Promise的异步特点,当一个方法的返回值是一个Promise对象的时候,在前面加上await修饰,方法的返回值就不是Promise异步对象,而是完整的内容; 使用await,就需要在最前面使用async修饰【代表异步操作】

const r1 = await thenfs.readFile('./','utf8')   //这里返回的对象不是Promise实例对象,而是读取文件的内容

这里可以验证一下使用await前后的对象的类型 : 使用前是Promise对象的类型【Promise代表的是异步操作执行的结果】 — 因为不是专业的前端,所以这里就不深入了

function readAllFile(fpath1,fpath2,fpath3,fpath4,fpath5){
  const data1 =  thenfs.readFile(fpath1,'utf8')
  console.log(data1)  //这里可以验证一下修饰前后的区别
}

//调用验证
readAllFile('./testfile/first.txt')

---------------------首先就是不使用await关键字--------------
PS D:\Vueprogramm\vue01> node Import.js
Promise { _40: 0, _65: 0, _55: null, _72: null }


//之后改变一下,使用await进行修饰【 注意await和async一定要搭配使用】 --- async代表操作是异步的
async function  readAllFile(fpath1,fpath2,fpath3,fpath4,fpath5){
  const data1 = await thenfs.readFile(fpath1,'utf8')
  
------------------使用await关键字-----------
PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

这里一定要记得在包裹await的方法前面加上async关键字,这样Promise返回类型就变成了文件的内容了,然后直接打印输出即可,这里输出的顺序就是console的顺序; 这样就不用担心.then的链式操作的缺点了

//使用await和async来简化.then的链式操作
//定义一个方法 : readAllFile 【和之前的Promise的静态方法.all和.race那里区分开来】
import thenfs from 'then-fs'
async function  readAllFile(fpath1,fpath2,fpath3,fpath4,fpath5){
  const dataArr = [
    await thenfs.readFile(fpath1,'utf8'),
    await thenfs.readFile(fpath2,'utf8'),
    await thenfs.readFile(fpath3,'utf8'),
  ]

  for(var i = 0; i < dataArr.length; i++) {
    console.log(dataArr[i])
  }
}

//调用验证 【上面的函数的参数没用到的就自动废弃】
readAllFile('./testfile/first.txt','./testfile/second.txt','./testfile/third.txt')

----------------------------------执行结果----------------------
PS D:\Vueprogramm\vue01> node Import.js
今年是2022年,壬寅虎年 -- 1

今天是2022年的农历正月十五,值此佳节之际 -- 2

祝大家元宵节快乐!-- 3

使用async和await相比之前的Promise的链式.then操作和Promise的静态方法all要简化

  • 如果在function内部使用到await,并且一定要对function进行async进行修饰
  • 在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行 【 后面的都会异步执行,不管那一行是否有await关键字】
//使用await和async来简化.then的链式操作
//定义一个方法 : readAllFile 【和之前的Promise的静态方法.all和.race那里区分开来】
import thenfs from 'then-fs'

console.log("你好,echo")
async function  readAllFile(fpath1,fpath2,fpath3,fpath4,fpath5){   //这里相当于就是一个异步的线程
  console.log("第一个await之前相当于还是主线程的部分【同步】,await出现后就是异步线程了;在js中,存在主线程和异步线程时,永远是主线程先执行")
  const dataArr = [
    await thenfs.readFile(fpath1,'utf8'),
    await thenfs.readFile(fpath2,'utf8'),
    await thenfs.readFile(fpath3,'utf8'),
  ]

  for(var i = 0; i < dataArr.length; i++) {
    console.log(dataArr[i])
  }
}

//调用验证
readAllFile('./testfile/first.txt','./testfile/second.txt','./testfile/third.txt')
console.log("你好,我是Alice")

------------------------执行结果--------------------
PS D:\Vueprogramm\vue01> node Import.js
你好,echo
第一个await之前相当于还是主线程的部分【同步】,await出现后就是异步线程了;在js中,存在主线程和异步线程时,永远是主线程先执行
你好,我是Alice
今年是2022年,壬寅虎年 -- 1

今天是2022年的农历正月十五,值此佳节之际 -- 2

祝大家元宵节快乐!-- 3

在js中,当出现异步线程的时候,主线程先执行; 这里就是当识别到await关键字的时候,执行异步操作,在这之前都是同步的操作,会先跳出函数执行主线程的操作,也就是输出Alice

⚠ : JS是一门单线程执行的编程语言,并且,在JavaScript异步任务要等同步任务执行完成之后才会执行 — 这和java的多线程还是有点区别的

EventLoop

JavaScript是一门单线程执行的语言,和java的多线程语言是不相同的;同一时间只能做一件事【所以说JS中的异步和java的异步不同,js的异步是指的异步任务】

所以JavaScript中如果有多个任务,像上面的文件读取就是多个任务;这样就形成了一个任务的等待队列;但是JS单线程执行的问题: 如果前一个任务非常耗时间,后续的任务就不得不一直等待,导致程序假死

为了防止这个问题: JS把任务分为两类 : 同步任务synchronous和异步任务asynchronous

同步任务【JavaScript主线程执行】

  • 非耗时任务,指的是主线程上排队执行的任务;主线程的同步任务会优先异步任务执行
  • 只有前一个任务执行完毕,才能执行后一个任务【同步执行】,按照代码的先后顺序

异步任务 【宿主环境执行】

  • 耗时任务,比如上面的读取文件,异步任务是JavaScript委托给宿主环境进行执行的【js的执行环境 —浏览器、node等】
  • 当异步任务执行完成之后,会通知JavaScript主线程执行异步任务的回调函数 ; 比如上面的异步任务就是3个文件的读取,当任务完成之后,通知主线程执行readAllFile这个回调函数 【所以当时的顺序是……】

在这里插入图片描述

同步任务和异步任务的执行过程 :

JavaScript主线程执行栈执行完就会出栈;宿主环境主要负责执行异步任务, 第三个部分就是任务消息队列;当执行栈中的同步任务执行完之后,会主动去拉去下一条消息

JavaScript的主线程的同步任务先执行;在宿主环境中执行的异步任务,谁先执行完毕,就把对应的回调函数放到消息队列中等待被拉取【先进先出】,当主线程栈中的任务执行完毕后,会主动拉去消息队列的任务进行执行,不断重复,直到执行完毕 ----- 所以整个的这种运行机制称为EventLoop ,事件循环 清空之后拉去任务,请空之后再拉,一直重复

这里分析一个面试题目

import thenfs from 'then-fs'

console.log('A')
thenfs.readFile('./','utf8').then(data => {console.log('B')})
setTimeout(() => {console.log('C')},0)    //延时器
console.log('D')

这里分析可以发现,thenfs和setTimout为异步任务,同步任务先执行,所以就是AD,定时器任务先执行结束,所以输出C,最后输出B

宏任务与微任务

JavaScript把异步任务进行了进一步的划分,异步任务又分成了宏任务和微任务

宏任务 macrostack

异步Ajax请求,setTimeout,setInerval 延时器、定时器,文件操作等

微任务microstack

Promise.then, .catch, .finally ; process.nextTick 等

在这里插入图片描述

宏任务和微任务的执行顺序 : 每执行一个宏任务之后,会做出一个判断当前异步任务中是否又微任务,如果又微任务,就执行所有的微任务,执行完毕之后,执行下一个宏任务

每一个宏任务执行完毕之后,都会检查是否存在等待执行待执行的微任务;同步任务执行完毕之后也会检查执行微任务

  • 实例: 分析下面代码的执行顺序
setTimeout(function(){     //异步任务
    console.log('1')
})

new Promise(function (resolve) { //同步任务
    console.log('2')
    resolve()
}).then(function(){   //微任务
    console.log('3')
})

console.log('4')  //同步任务

首先new Promise对象属于同步任务,所以优先执行的是2,4;之后执行异步任务,先执行微任务队列的3,之后执行宏任务队列中的1

注意嵌套的时候,比如将new Promise放到延时器中,应该是整个宏任务的一部分,随着宏任务的执行而执行

API接口实例

基于MySQL数据库 + Express对外提供用户列表的接口服务 : 第三方包express和mysql8 ; ES6模块化 ; Promise; async/await

主要的实现步骤 :

  1. 搭建项目的基本结构
  2. 创建基本的服务器
  3. 创建db数据库操作模块
  4. 创建user_ctrl业务模块
  5. 创建user_router路由模块

所以首先就是要启用ES6的模块化支持 : package.json中加入module; 之后就是安装第三方的依赖包【npm i express@版本】 — 管理员身份

导入之后项目的node_modules中就有express和mysql了

创建服务器 app.js

这里使用的命令是nodemon 来进行运行; 需要先安装nodemon包到本机的全局地址,npm install -g nodemon ; 这样就可以正常运行到服务器

import express from 'express'  //这里app.js直接放在项目下,直接默认导入

const app = express(); //创建一个服务器实例

app.listen(8070, ()=>{
  console.log("server running at Cfeng.com")
})
//使用nodemon 命令运行
//listen EACCES: permission denied 0.0.0.0:80  允许被阻止,也就是80号端口被占用了,所以换一个端口号

------------------------执行结果--------------------
PS D:\Vueprogramm\vue01> nodemon app.js
[nodemon] 2.0.15
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
server running at Cfeng.com

创建db数据库操作模块

首先要使用第三包mysql,然后创建连接池,默认导出一个支持Promise API的pool

import mysql from 'mysql2'

//创建数据库连接池
const pool = mysql.createPool({  //和之前在spring中配置连接池类似
  host: '127.0.0.1',
  port:3306,
  database:"cfengbase",
  user: 'cfeng',
  password:'**********'
})

//默认导出一个支持Promise API的pool
export default pool.promise()

创建user_ctrl模块

这里就要使用上面的db操作模块的默认导出的对象,并且将方法getAllUser导出供外界使用

import db from './db.js'

//获取用户所有的列表的数据
export async function getAllUser(req,res) {  //在路由中使用,所以有req和res
  //db.query()函数的返回值为Promise的实例对象,可以使用async/await进行简化
  const [rows] = await db.query('SELECT stuno,stuname,stuclass FROM student')
  res.send({
    status: 0,
    message:'获取用户成功',
    data: rows,  //从查询结果中解构成为一个数据
  })
}

创建user_router模块

需要使用express模块

import express from 'express'
//从user_control模块中导入getAllUser函数
import {getAllUser} from './user_contrl.js'

//创建路由对象
const router = new express.Router()
//把路由挂载在服务器上 ,上面的路径就是/user为前缀
router.get('/user',getAllUser)

//使用Es6默认导出,共享数据router
export default router

使用try–catch捕获异常

上面的查询数据库会发生异常,所以需要进行处理,处理的方式和java相同

export async function getAllUser(req,res) {  //在路由中使用,所以有req和res
  //db.query()函数的返回值为Promise的实例对象,可以使用async/await进行简化
  try{
  const [rows] = await db.query('SELECT stuno,stuname,stuclass FROM student')
  res.send({
    status: 0,
    message:'获取用户成功',
    data: rows,  //从查询结果中解构成为一个数据
  })
  }catch(e){
      res.send({status:1,message:'获取用户列表失败',desc:e.message})
}
}

处理的方式相同就不再赘述

接下来就会正式进入Vue3.x的分享🎉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值