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

Vue框架

内容管理

  • Vue
  • Vue前置内容
    • ES6模块化
      • 默认导出【暴露】和默认导入
      • 按需导入和按需导出
      • 直接导入并执行模块中的代码
    • 安装node,vue
      • 安装vue
      • 创建vue项目
        • vue create
        • vue init webpack
    • promise 解决回调地狱
      • 回调函数的实例 -- 顺序读取文件
        • 使用fs模块 -- 回调函数读取文件
        • 使用then-fs读取文件内容【promise】
      • 通过.catch捕获错误,.finally进行强制执行
      • Promise的静态方法 --- Promise.all()
      • Promise的静态方法 --- Promise.race()
      • 基于Promise封装读文件的方法
    • async/await简化promise
    • EventLoop
      • 同步任务【JavaScript主线程执行】
      • 异步任务 【宿主环境执行】
    • 宏任务与微任务
      • 宏任务 macrostack
      • 微任务microstack
    • API接口实例
      • 创建服务器 app.js
      • 创建db数据库操作模块
      • 创建user_ctrl模块
      • 创建user_router模块
      • 使用try--catch捕获异常


Vue基础


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

Vue

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

Vue的优点 :

  1. 采用组件化的模式,提高代码的复用率,让代码更好维护,比如一个图片的切换的部分就可以将其变成一个vue的文件
  2. 声明式编码,让编码人员无需注解操作DOM,提高开发的效率
//命令式编码
let htmlStr = '';
persons.forEach(p => {
    htmlStr += `
  • ${p.id} - ${p.name} - ${p.age}
  • `
    ; }); //获取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
    
    [email protected] | 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
    `-- [email protected]
    

    这个时候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 – 原型对象】

    Vue铺垫知识 --- ES6模块化_第1张图片

    可以看到原型对象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这个回调函数 【所以当时的顺序是……】

    Vue铺垫知识 --- ES6模块化_第2张图片

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

    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 等

    Vue铺垫知识 --- ES6模块化_第3张图片

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

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

    • 实例: 分析下面代码的执行顺序
    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的分享

    你可能感兴趣的:(JAVAweb,vue.js,es6,javascript,前端)