移动互联笔记二

Node

在这里插入图片描述
Node.js是一个JavaScript的运行时环境(JavaScript runtime ),构建于Chrome的V8 JavaScript引擎之上,可用于开发高速可伸缩的网络程序。Node.js采用的事件驱动、非阻塞I/O模型,使它既轻量又高效,并成为构建运行数据密集型实时程序的完美选择。
Node是JavaScript程序的平台,不要把它跟各种开发框架相混淆,以Java为例进行对比,Node.js可以看成是JRE。

应用场景:
Web应用程序:单页面应用(SPA)、REST服务、微服务
命令行工具:诸如npm、Gulp和Webpack之类都是命令行工具
后台程序:后台程序就是后台服务,比如PM2进程管理器。
桌面应用:桌面程序一般是用Electron框架写的软件,Electron用Node作为基于Web的桌面应用的后台。Atom和Visual Studio Code文本编辑器都属于这一类。

特点:Node.js应用本身是单线程的,但它提供了一个异步和事件驱动的编程模型实现非阻塞的I/O操作,这个编程模型的后面是由Node.js的许多底层核心模块(比如libuv)实现的。只要安装Node.js的最新版本,Node.js应用就可以使用最新的ECMAScript语法特性来开发,因为它是单一的产品,不像有多个厂家的浏览器,存在开发进度不一导致出现兼容性的问题(比如某浏览器能支持ES6的全部特性,而另一家仅支持80%的ES6特性),这就不得不让开发者放弃使用新特性以“迁就”支持特性最少的浏览器。
在这里插入图片描述
了解Node.js对ES新特性的支持情况:网址

node是 node.js 的主程序,用于运行 node 应用程序,
npm 是它的包管理器,用于安装、更新和删除各种包。

开发原生JavaScript代码
在这里插入图片描述
使用TypeScript开发
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

NPM

npm(Node Package Manager )是 Node.js 的模块管理器,使用它可以安装(或移除、更新) Node.js 应用所依赖的(第三方) 模块 。 npm无需单独安装,在安装 Node.js 时会随之一并安装。

初始化Node.js项目:
所有的Node.js 应用,都有一个名为package.json 的配置文件,其中保存了本应用的各种配置信息,其中,最重要的,就是项目的依赖信息。
这一配置文件无需手工创建,只需要在项目文件夹中运行npm init命令即可创建。

为Node.js安装第三方模块:
npm install 模块名
可选参数:
-g :安装为 全局 模块,在整个计算机范围内均可使用。
--save :将安装的模块版本信息放到 package.json 的 dependencies 部分
--save-dev :将安装的模块版本信息放到 package.json 的 develop-dependencies 部分
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Node模块

在这里插入图片描述
在这里插入图片描述
模块可以相互依赖
比如Node 的 http 模块在 net 模块基础之上实现了HTTP 协议。其他协议,比如 email 用的 SMTP 或传输文件用的 FTP ,也需要以 net 为基础实现。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Parent {

}
//导出父类型
module.exports = Parent;
const Parent = require('./parent');

class Child extends Parent {

}
module.exports = Child;

在这里插入图片描述

异步编程

回调callback

在这里插入图片描述
在这里插入图片描述
两种异步编程模式:
在Node的世界里流行两种异步编程模式 :回调和事件监听。
回调:通常用来定义一次性响应的逻辑。比如对于数据库查询,可以指定一个回调函数来 确定如何 处理查询结果。
事件监听器:本质上也是一个回调,不同的是,它跟一个概念实体(事件) 相 关联,可以被多次触发调用,并且我们可以为一个事件指定多个回调函数。

回调带来的最大问题就是“回调地狱”。
当数据处理流程比较复杂时,不可避免地会出现“多重嵌套”的回调代码。

在这里插入图片描述

const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
    if (req.url == '/') {
        fs.readFile('./titles.json', (err, data) => {
            if (err) {
                console.error(err);
                res.end('Server Error');
            } else {
                const titles = JSON.parse(data.toString());
                fs.readFile('./template.html', (err, data) => {
                    if (err) {
                        console.error(err);
                        res.end('Server Error');
                    } else {
                        const tmpl = data.toString();
                        const html = tmpl.replace('%', titles.join('</li><li>'));
                        res.writeHead(200, { 'Content-Type': 'text/html' });
                        res.end(html);
                    }
                });
            }
        });
    }
}).listen(3000, '127.0.0.1',()=>{
    console.log('Server is Ready, listening 3000')
});
[
    "Kazakhstan is a huge country... what goes on there?",
    "This weather is making me craaazy",
    "My neighbor sort of howls at night"
]
{
  "name": "NodeBlog",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

在这里插入图片描述

事件与事件发射器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述事件发射器可以“触发”特定的事件:
在Node.js 应用中, 可以 使用“ 事件 发射器( EventEmitter )”来 编写“事件驱动”的 代码 。 EventEmitter 由 Node.js 的 events 模块所提供。
以下代码触发了一个simpleEvent 事件:

//导入 events 模块
var events = require('events');
//创建事件发射器对象
var emitter = new events.EventEmitter();
//触发事件
emitter.emit('simpleEvent');

事件可以携带参数,在触发事件时,事件参数作为emit() 的第二个参数传入:

emit(eventName , [args])

事件发射器示例:
使用事件发射器的“on(事件名,回调函数)”或者“.addListener(事件名,回调函数 )”的 方式挂接事件响应代码。

var EventEmitter = require('events').EventEmitter;
var channel = new EventEmitter();
channel.on('join', (message) => {
    console.log('Welcome! ' + message);
});

channel.emit('join', '我来了!');

事件驱动的网络应用程序:
Node.js核心模块中的许多对象本质上都是一个 EventEmitter以下是一个 示例 ,使用 socket 对象监听 8888 端口,当有数据到来时, socket 会触发一个 data 事件。

const net = require('net');
//创建一个网络服务器
const server = net.createServer (socket => {
	//当有数据到来时,触发data事件
	socket.on('data', data=> {
		socket.write(data);
	});
});
//监听 8888 端口
server.listen(8888);

使用自定义事件发射器处理错误

var events = require('events');
var myEmitter = new events.EventEmitter();
//定义事件及事件响应函数
myEmitter.on('error', function(err) {
	console.log('ERROR: ' + err.message);
});
//触发事件
myEmitter.emit('error',new Error('Something is wrong.'));

使用 Promise 消除“回调地狱”

在这里插入图片描述
基本用法:

//Promise为ES6所直接支持
var promise = new Promise((resolve, reject) => {
    //resolve();  //导致then()方法被回调
    reject(); //导致catch()方法被回调
});
//Promise对象支持链式(级联)调用
promise.then(() => {
    console.log('promise.resolved')
}).catch(() => {
        console.log('promise.rejected');
    });

链式调用时值的传送:

new Promise((resolve, reject) => {
    resolve(1);  //1这个值将会被传给下一个回调函数
})
    .then(
        //argu参数的实际值是由前一个resolve方法传过来的
        argu => {
            argu += 1;
            return argu;  //用return语句继续往下传
        }
    ).then(result => {
        //输出最终的结果——result:2
        console.log('result:' + result);
    });

多级Promise示例:

new Promise((resolve,reject)=>{
    resolve("\n开始演示\n");
}).then(info=>{
    console.log(info);
    console.log('在第一级的回调函数中返回一个新的Promise对象');
    let promise2= new Promise((resolve,reject)=>{
        //试一试,切换以下两句的注释,看看后面输出的结果是什么样的
        //resolve('hello');
        reject('not hello');
    });
    return promise2;
}).then((msg)=>{
    console.log(`响应前一级生成的Promise对象的resolve方法,
    收到的参数值为:"${msg}"`);
},(err)=>{
    console.log(`响应前一级生成的Promise对象的reject方法,
    收到的参数值为:"${err}"`);
}).then(()=>{
    console.log('\n演示结束\n');
});

实例:读写文件,多层嵌套回调
当使用异步方式存取文件时,很容易写出嵌套多层的回调代三

var fs = require('fs');
//读入文件内容
fs.readFile('../data.txt', 'utf-8', (err, data) => {
    if (err) {
        console.log('文件未能成功读出');
    } else {
        //写入文件内容
        fs.writeFile('../data.bak.txt', data, err => {
            if (err) {
                console.log('文件未写入!');
            } else {
                console.log('文件成功写入')
            }
        });
    }
});

使用Promise清除回调需求:
所有进行“异步操作”的函数,全部返回一个 Promise 。

var fs = require('fs');

var myReadFile = function (fileName) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, 'utf-8', (err, data) => {
            if (err) {
                reject(err); //调用出错回调函数
            } else {
                resolve(data);
            }
        });
    });
}

var myWriteFile = function (fileName, content) {
    return new Promise((resolve, reject) => {
        //可以取消以下注释,主动地抛出一个异常
        //throw new Error('写入文件出错');
        fs.writeFile(fileName, content, err => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

//如果输入一个不存在的文件名,可以试验一下程序的输出结果
//通过级联调用.then()和.catch(),避免了出现多层嵌套的回调代码。
let readPromise = myReadFile('../data.txt');
readPromise.then((data) => {
    console.log('\n文件成功读出\n\n' + data);
    //返回一个写文件的Promise
    return myWriteFile('../data.bak.txt', data);
}, err => {
    throw new Error(`\n文件未能成功读出: ${err}\n`)
}).then(() => {
        console.log('\n文件成功写入\n')
    },(err) => {
            throw new Error(`文件未能写入: ${err}\n`)
}).then(() => {
        console.log('全部工作顺利完成\n');
}).catch((err) => {
        console.log(`\n工作未成功完成,期间出现错误:\n${err}`);
});

如果一个函数返回Promise ,那么它就是“可等待的(awaitable)”,另一个函数可以在代码中调用 await “异步等待”它的完成。
包容有await 语句的函数称为“异步函数”,需要使用 async进行声明。
使用async/await:

//这个函数返回一个Promise对象:
function doSomethingFinishedInFuture() {
	let promise = new Promise((resolve, reject) => {
        //延迟一定时间,再resolve或reject
		setTimeout(() => {
			let ran = Math.ceil(Math.random() * 10);
			if (ran % 2 == 0) {
				resolve("resolve:" + ran);
			} else {
				reject("rejected:" + ran);
			}
		}, 1000);
	});
	return promise;
}

//返回Promise 的函数可以 await await 语句需要放到 async 函数中:
//定义一个异步函数
const process = async () => {
	try {
		let result = await doSomethingFinishedInFuture();
		console.log(result);
	} catch (error) {
		console.error("in catch(): " + error);
	}};
//process方法不会被阻塞执行
process();
console.log("after process");

// doSomethingFinishedInFuture().then(res=>{
//         console.log(res);
//     }).catch(rej=>{
//         console.error(rej);
//     });

文件与流

文件内容的读与写

Node.js的 fs 模块,提供了与文件相关的功能。

var fs = require('fs');

打开文件:

fs.open(path, flags, [mode], callback)
fs.openSync(path, flags, [mode])

关闭文件:

fs.close(fd, callback)
fs.closeSync(fd)

典型代码:

fs.open(myFile ", 'w', function(err, fd){
	if (!err){
		fs.close(fd);
	}
});

在这里插入图片描述
写入文件:
有两种方式写入文件同步和异步:

//异步写入,需要指定回调方法
fs.writeFile(path , data, [options], callback)
//同步写入,要等到写入操作全部完成,才能执行下一句:
fs.writeFileSync(path, data, [option])

针对文件本身的操作

判断文件是否存在?

判断文件是否存在?
fs.exists(path, callback)
fs.existsSync(path)

fs.exists('filesystem.js', function (exists) {
	console.log(exists ? "Path Exists" : "Path Does Not Exist");
})

在这里插入图片描述
获取指定文件夹下的文件列表:

//异步方式
fs.readdir(path , callback)
//同步方式
fs.readdirSync(path)

删除文件

fs.unlink(path, callback)
fs.unlinkSync(path)

fs.unlink("new.txt", function(err){
	console.log(err ? "File Delete Failed" : "File Deleted");
});

截断文件:

fs.truncate(path, len, callback)
fs.truncateSync(path, len)

fs.truncate("new.txt", 1024, function(err){
	console.log(err ? "File Truncate Failed" : "File Truncated");
});

创建文件夹

fs.mkdir(path, [mode], callback)
fs.mkdirSync(path, [mode])

fs.mkdir("./data/folderA ", function(err){
	fs.mkdir("./data/folderA/folderB", function(err){
		fs.mkdir("./data/folderA/folderB/folderD ", function(err){
	});
});
fs.mkdir("./data/folderA/folderC", function(err){
	fs.mkdir("./data/folderA/folderC/folderE", function(err){
	});
})

删除文件夹

fs.rmdir(path, callback)
fs.rmdirSync(path)

fs.rmdir("./ folderA folderB folderC ", function(err){
	fs.rmdir("./ folderA folderB ", function(err){
		fs.rmdir("./ folderD ", function(err){
		});
	});
fs.rmdir("./data/folderA/folderC ", function(err){
	fs.rmdir("./data/folderE ", function(err){
		});
	});
});

文件或文件夹改名

fs.rename(oldPath ,newPath ,callback)
fs.renameSync(oldPath, newPath)

fs.rename("old.txt", "new.txt", function(err){
	console.log(err ? "Rename Failed" : "File Renamed");
});
fs.rename("testDir ", "renamedDir", function(err) {
	console.log(err ? "Rename Failed" : "Folder Renamed");
});

监控文件的改变:

fs.watchFile(path, [options], callback)

fs.watchFile("log.txt",{persistent:true, interval:5000}, function(curr , prev){
	console.log("log.txt modified at: " + curr.mtime);
	console.log("Previous modification was: " + prev.mtime);
});

流式数据处理:
流用于读取和写入文件
使用createReadStream 和 createWriteStream 创建读流和写流
在这里插入图片描述
之后,就可以使用它读取和写入数据到文件。
在这里插入图片描述
在这里插入图片描述
与传统的软件开发语言(Java C++ C# 等)相比, Node.js 大大地简化了文件的存取操作,只需要几行代码,就能从文件中读取或写入数据,并且支持管道,支持同步阻塞和异步非阻塞两种编程模式。

Node.js访问MongDB

在这里插入图片描述
在这里插入图片描述

使用MongoClient 直连 MongoDB

连接字符串:
mongodb://[username:password@]host[:port][/[database][?options]]

const client = require('mongodb').MongoClient;
const assert = require('assert');

const dburl = 'mongodb://localhost:27017';
const dbName = 'mydb';

测试:
client.connect(dburl, { useNewUrlParser: true },
    (err, client) => {
        assert.strictEqual(null, err);
        console.log('connected success to MongoDB Server');
        const db = client.db(dbName);
        //插入数据
        insertDocuments(db, () => {
            //查找数据
            findDocuments(db,() => {
                //关闭数据库连接
                client.close();
            })
        });

        // updateDocument(db,()=>{
        //     findDocuments2(db,{ 'a': 2 },()=>{
        //         client.close();
        //     });
        // });    

        // removeDocument(db, function() {
        //     findDocuments2(db,{ 'a': 3 },()=>{
        //         client.close();
        //     });
        //   });

    }
);
//插入数据
const insertDocuments = function (db, callback) {
    // 获取文档集合对象
    const collection = db.collection('documents');
    //生成一个随机数
    let ranvalue = parseInt(Math.random() * 100 + 1);
    // 插入4个文档
    collection.insertMany([{ a: 1 }, { a: 2 }, { a: 3 }, { a: ranvalue }],
        function (err, result) {
            assert.equal(err, null);
            assert.equal(4, result.result.n);
            assert.equal(4, result.ops.length);
            console.log("向文档集合中中插入了4个文档");
            //回调
            callback(result);
        });
};
//查找集合中的全部数据
const findDocuments = function (db, callback) {
    // Get the documents collection
    const collection = db.collection('documents');
    // Find some documents
    collection.find({}).toArray(function (err, docs) {
        assert.equal(err, null);
        console.log("Found the following records");
        console.log(docs)
        callback(docs);
    })
};
//查找满足条件的相应文档
const findDocuments2 = function (db, findWhat, callback) {
    // Get the documents collection
    const collection = db.collection('documents');
    // Find some documents
    collection.find(findWhat).toArray(function (err, docs) {
        assert.equal(err, null);
        console.log("Found the following records");
        console.log(docs);
        callback(docs);
    });
}
//更新文档
const updateDocument = function (db, callback) {
    // Get the documents collection
    const collection = db.collection('documents');
    // Update document where a is 2, set b equal to 1,b是新加的
    collection.updateOne({ a: 2 }, { $set: { b: 1 } },
         function (err, result) {
            assert.equal(err, null);
            assert.equal(1, result.result.n);
            console.log("Updated the document with the field a equal to 2");
            callback(result);
        });
};
//删除一个文档
const removeDocument = function (db, callback) {
    // Get the documents collection
    const collection = db.collection('documents');
    // Delete document where a is 3
    collection.deleteOne({ a: 3 }, function (err, result) {
        assert.equal(err, null);
        //如果没有找到满足条件的要删除的记录,则result.result.n=1
        assert.equal(1, result.result.n);
        console.log("Removed the document with the field a equal to 3");
        callback(result);
    });
};

列出所有数据库:

const client = require('mongodb').MongoClient;
let dburl = 'mongodb://localhost:27017/mydb';

client.connect(dburl,
    {
        useNewUrlParser: true
    },
    function (err, clientObj) {
        if (err) {
            console.log(err);
            return;
        }
        console.log('connected');
        var admin = clientObj.db().admin();
        admin.listDatabases().then(result => {
            console.log(result);
            clientObj.close();
        }).catch(err => {
            console.log(err);
            clientObj.close();
        });
    });

获取数据库服务器状态:

const client = require('mongodb').MongoClient;
let dburl = 'mongodb://127.0.0.1:27017/mydb';
client.connect(dburl,
    {
        useNewUrlParser: true
    },
    function (err, clientObj) {
        if(err){
            console.log(err);
            return;
        }
       console.log('connected');
       var admin=clientObj.db().admin();
        admin.serverInfo().then((status)=>{
            console.log(status);
            clientObj.close();
        }).catch(error=>{
            console.log(error);
            clientObj.close();
        });
    });

使用Mongoose存取

Mongoose是一个用于存取MongoDB的面向对象数据存取框架,它使用面向对象对象的思想封装了MongoDB的数据存取。
Mongoose提供了一组简单的对象模型,对应到底层的MongoDB数据存储结构,并且封装了一些CRUD方法(比如 find save 等),直接存取底层的MongoDB数据。
Mongoose的方法都是异步的,支持async/await和Promise两种异步编程模式。
官网
在这里插入图片描述
回调

// getting-start.js
var mongoose = require('mongoose');
//定义Schema
var kittySchema = new mongoose.Schema({
    name: String
});

// Schema可以添加方法
kittySchema.methods.speak = function () {
    var greeting = this.name
        ? "Meow name is " + this.name
        : "I don't have a name";
    console.log(greeting);
};

var Kitten = mongoose.model('Kitten', kittySchema);

mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
	//连接建立以后,就可以在这个地方编写CRUD代码
    console.log('connected to DB Server');

    var silence = new Kitten({ name: 'Silence' });
    console.log(silence.name); // 'Silence'
    silence.speak();
    var fluffy = new Kitten({ name: 'fluffy' });
    fluffy.save(function (err, fluffy) {
        if (err) return console.error(err);
        console.log('对象己成功保存~~~');
        fluffy.speak(); // "Meow name is fluffy"
        //从数据库中提取……
        Kitten.find({ name: /^fluff/ }, function (err, kittens) {
            if (err) return console.error(err);
            console.log(kittens);
            db.close();
        });
    });
});

异步
await只能用在异步函数中,因此,使用async()封装异步代码。
await不能直接用于顶层代码,所以,收尾工作使用Promise编程模式实现。

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

var Account = new Schema({
	username: { type: String },
	age: { type: Number, required: true, min: 0, max: 120 },
	date_created: { type: Date, default: Date.now },
	visits: { type: Number, default: 0 },
	active: { type: Boolean, default: false },
});

//添加静态方法
Account.statics.findByAgeRange = function (min, max) {
	return this.find({ age: { $gt: min, $lte: max } });
};

//异步代码放在这里,使用async/await编程模式
let demo = async () => {
	console.log("\n开始连接数据库...\n");
	try {
		await mongoose.connect("mongodb://localhost:27017/mydb", {
			useNewUrlParser: true,
			useUnifiedTopology: true,
		});
		console.log("Mongoose己成功连接到MongoDB");
	} catch (err) {
		console.log("出错:", err);
		return;
	}

	var AccountModel = mongoose.model("Account", Account);

	console.log("\n创建3个文档\n");

	for (let i = 1; i <= 3; i++) {
		var ranValue = parseInt(Math.random() * 100 + 1);
		var newUser = new AccountModel({
			username: "randomUser " + ranValue,
			age: ranValue,
		});
		//对数据进行有效性检测
		try {
			await newUser.validate();
			await newUser.save(); //保存到数据库中
			console.log(i + "已保存:", newUser);
		} catch (err) {
			console.log(err);
		}
	}

	//查找符合条件的记录
	let accounts = await AccountModel.findByAgeRange(18, 60);
	console.log(`\n找到${accounts.length}个文档\n`);
	accounts.forEach((element) => {
		console.log(element);
	});
};

//收尾工作,采用Promise编程模式
demo()
	.then(() => {
		mongoose.connection.close();
		console.log("断开与MongoDB的连接,演示结束");
	})
	.catch((err) => {
		mongoose.connection.close();
		console.log("出现错误,断开与MongoDB的连接,演示结束。", err);
	});

Express基础

Express是一个快(性能优越)、不固执己见(即对开发者有很多的限制和约定)、极简主义者(即仅提供最基础与必要的功能)的Web应用开发框架,运行于Node.js之上。
官网
虽然我们可以使用Node.js提供的http模块完成一个功能完备的WebServer,但同样的工作使用Express来完成要轻松得多,因为Express己经提供了许多现成的中间件,可以帮助开发者构建功能强大的运行于Node.js之上的Web应用。
在这里插入图片描述
在这里插入图片描述
Express的四个主要特性:
1.可以动态组合和连接的中间件
2.可以方便分割的路由机制功能
3.功能完备的request 和response 对象
4.支持加载外部视图

在这里插入图片描述
express():
express()是 express 模块所导出的顶级函数。
它的返回值是一个Express 应用程序对象,通常用名为 app 的变量引用它:

var express = require('express');
var app = express();

app其实就是一个函数,它是 Express 设计出来的一个 HTTP请求处理函数,它在内部维护一个由若干个中间件构建出来的 HTTP 请求处理管线。这条管线是使用 app.use 或app.METHOD 方法构建出来的。

支持“首尾相接”的Express中间件
Express中间件本质上就是一个有着固定参数的函数:

function myFunMiddleware (request, response, next){
	...
	next();
}

next()方法会导致下一个中间件被 调用 。具体 是哪个,由 app(即 Express 应用程序对象)决定,它其实是依据 app.use 和app.METHOD 来构建出 HTTP 请求处理管线的。
在这里插入图片描述

var express=require('express');
var http=require('http');
var app=express();

app.use((req,res,next)=>{
    res.write('middle ware 1');
    next();
});

app.use((req,res,next)=>{
    res.write(' \nmiddle ware 2');
    res.end();
});

http.createServer(app).listen(3000,()=>{
    console.log('Server is listening @ 3000');
});

在这里插入图片描述
路由示例:

const express = require('express')
const app = express()

//响应根请求
app.get('/',(req,res)=>{
    res.send('root path');
});

//此中间件将响应所有的请求
app.get('*', (req, res) => res.send(req.path));
//监听3000端口
app.listen(3000, () => console.log('Example app listening on port 3000!'));

Express路由与 HTTP 响应

路由(Routing )决定了一个 Web 应用如何响应 HTTP 请求。
简单地说,就是确定对应于特定的URI (或者 path 路径),调用哪个处理函数( handler function )去生成响应。
Express使用以下方式指定路由:
app.method(path, handler)
app 是 express 的一个实例
method 指 HTTP 方法,如 get 、 Post 等。
path 代表 URL 中的路径部分
handler 引用一个函数,由它生成响应。

app.get('/', function (req, res) {
	res.send('Hello World!)
})

在这里插入图片描述

const express = require('express')
const app = express()
//使用send方法向客户端生成响应
app.get('/', (req, res) => res.send('Hello World!'))
//监听3000端口
app.listen(3000, () => console.log('Example app listening on port 3000!'));

编写Express路由
如果想响应所有HTTP 方法

app.all ('/secret', function (req, res, next){
	console.log('Accessing the secret section ...')
	next() // pass control to the next handler
})

路径匹配:
支持正则表达式

//匹配 acd 和 abcd
app.get('/ab?cd', function (req, res){
	res.send('ab?cd')
})
//包容任何`a`的路径
app.get(/a/, function (req, res) {
	res.send('/a/')
})

路由参数-1:

const express = require('express')
const app = express()
//从路径中提取参数
app.get('/users/:userId/books/:bookId', function (req, res) {
    res.send(req.params);
})
//监听3000端口
app.listen(3000, () =>
    console.log('Example app listening on port 3000!'));

在这里插入图片描述
路由参数-2:
路由解析时“-”和“.”被当成普通文本,所以可以使用它们来完成一些特殊的参数提取功能:

Route path: /flights/:from-:to
Request URL: http://localhost:3000/flights/LAX-SFO
req.params: { "from": "LAX", "to": "SFO" }
Route path:/plantae/:genus.:species
Request URL:
http://localhost:3000/plantae/Prunus.persica
req.params: { "genus": "Prunus", "species":
"persica"}

提取查询字符串:
在这里插入图片描述
读取Header值
在这里插入图片描述
判断body类型:
在这里插入图片描述
路由处理函数(route handler):
至少两个参数:req 和 res ,可选第三个参数 next

可以从req 参数中提取 HTTP 请求的相关信息。
使用res 参数生成发回给客户端的响应 信息 。

如果希望转给下一个路由处理函数(或中间件)进行下一步处理,路由处理函数必须使用第三个参数,并且调用 next() 。

//一个URL,多个Handler
var express = require("express");
var app = express();

app.get("/one", (req, res, next) => {
	res.type("text/plain").write("Hello ");
	next(); //执行下一个请求处理中间件
});

app.get("/one", (req, res) => {
	res.write("World!");
	res.end(); //不加这句,会导致浏览器挂起,因为Server会一直不关闭连接,
});
//一次指定两个Handler,注意第2个handler不需要有next参数
app.get(
	"/two",
	(req, res, next) => {
		res.write("two!");
		next();
	},
	(req, res) => {
		res.write("end!");
		res.end();
	}
);

var server = app.listen(3000, () => {
	console.log("server is ready,Listening on 3000...");
});

可以使用数组来一次性地指定多个路由处理函数
在这里插入图片描述
在这里插入图片描述
一次性地定义多个路由处理函数:
在这里插入图片描述
使用这种方式,可以方便针对同一个 URL 不同 HTTP 方法编写响应处函数。

路由对象:
顶层的expres 对象提供了一个 Router() 方法可用于创建路由对象。路由对象可以当成普通的中间件(middleware )一样使用,也就是说,可以放在 app.use 中。
正式项目中,通常会将路由对象放到一个独立的模块文件中,在里面配置好各种 URL 和处理函数,之后使用 module.exports 导出它,然后在主程序中使用 app.use 挂接它

路由与子应用:
在这里插入图片描述
模块化的路由:
在这里插入图片描述
在这里插入图片描述2生成 HTTP 响应
在这里插入图片描述
生成“重定向”响应:

res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login');

设置Content Type:
在这里插入图片描述
设置location

res.location('/foo/bar');
res.location('http://example.com');
res.location('back');

设定HTTP Header
在这里插入图片描述
返回状态码:

res.sendStatus(200); // res.status(200 ).send('OK')
res.sendStatus(403); // res.status(403 ).send('Forbidden')
res.sendStatus(404); // res.status(404 ).send('Not Found')
res.sendStatus(500); // res.status(500 ).send('Internal Server Error')

提供文件下载
在这里插入图片描述
在这里插入图片描述
实现内容协商
在这里插入图片描述
返回Json

res.json(null);
res.json({ user:'tobi'));
res.status(500).json({ error: 'message'});

返回渲染的视图
在这里插入图片描述
返回特定类型的数据

res.send(new Buffer('whoop'));//MIME:application/octet-stream
res.send({ some: 'json' });//MIME:application/json
res.send('<p>some html</p>');
res.status(404).send('Sorry, we cannot find that!');
res.status(500).send({ error: 'something blew up'});

Express中间件

在这里插入图片描述
在这里插入图片描述

Vue

Vue.js是一个渐进式的JavaScript框架。
所谓“渐进式(Progressive)”,就是你可以一步一步、有阶段性地来使用Vue.js,不必一开始就使用所有的东西。
在这里插入图片描述
Vue 是很好的jQuery“继任者”
在这里插入图片描述
在这里插入图片描述

Vue实例

Vue的重要特性——Virtual DOM
在早期传统的Web应用中,程序员编写代码直接修改网页的DOM树,当要进行的数据处理和更新比较复杂时,这会导致Web前端应用代码量大,并且可能会影响到程序的性能。
为了解决这个问题,Vue在内部维护一个虚拟的DOM对象,程序员编写代码修改数据,虚拟DOM对象随之变化,Vue检测虚拟DOM对象与网页真实DOM对象之间的差异,然后更新那些真的需要更新的DOM。
Vue通过避免不必要的DOM操作,或者将多个小的操作采用批处理方式一次执行完毕,从而提升了性能,并把Web程序员从DOM操作中完全解放了出来。
在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<title>Hello,World</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="root">
			<div>{{message}}</div>
		</div>
		<script>
			Vue.createApp({
				data() {
					return {
						message: "Hello,World!",
					};
				},
			}).mount("#root");
		</script>
	</body>
</html>

在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>Vue生命周期</title>

		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">{{test}}</div>
		<script>
			Vue.createApp({
				data() {
					return {
						test: "Vue实例生命周期",
					};
				},
				beforeCreate: function () {
					console.info("beforeCreate");
				},
				created: function () {
					console.info("created");
				},
				beforeMount: function () {
					console.info("beforeMount");
				},
				mounted: function () {
					console.info("mounted");
				},
				beforeUnmount: function () {
					console.info("beforeUnmount");
				},
				unmounted: function () {
					console.info("unmounted");
				},
				beforeUpdate: function () {
					console.info("beforeUpdate");
				},
				updated: function () {
					console.info("updated");
				},
			}).mount("#app");
		</script>
	</body>
</html>

Vue实例的成员

计算属性“依赖”于其他的属性,当这些属性值有变化时,它会随之变化,通过计算得到一个新值。
计算属性的值会被缓存,直到它所依赖的属性值有变化,才会重新计算并重新缓存。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Hello,World</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="root">
			<div>{{name}}</div>
		</div>
		<script>
			Vue.createApp({
				data() {
					return {
						firstName: "John",
						lastName: "Mary",
					};
				},
				computed: {
					name() {
						return `${this.firstName} ${this.lastName}`;
					},
				},
			}).mount("#root");
		</script>
	</body>
</html>

Vue的计算属性不仅可以依赖其他计算属性,也可以依赖其他Vue实例的数据。
Vue的计算属性拥有“缓存”特性,当它所依赖的属性没有变化时,不会再次执行“计算”。

监听器(watch)函数:
当监听器函数所监视的属性变动时,它会被自动地调用。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Hello,World</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="root">
			<div>
				<input v-model="content" />
				<p>{{content}}</p>
			</div>
		</div>
		<script>
			Vue.createApp({
				data() {
					return {
						content: "Hello,World!",
					};
				},
				watch: {
					content(currentValue, prevValue) {
						console.log("CurrentValue:", currentValue);
						console.log("prevValue", prevValue);
					},
				},
			}).mount("#root");
		</script>
	</body>
</html>

模板与数据绑定

设计Vue.js模板
模板用于定义Vue组件最终生成的HTML代码。
Vue提供了非常方便的方法来定义模板,其语法称为“ Mustache ”。
开发者也可以不用模板,直接写渲染(render)函数 ,使用可选的JSX语法(另一个前端框架React中大量使用JSX)。
在这里插入图片描述
在这里插入图片描述加粗样式
One Time Bindings:使用v once 完成,绑定只发生一次,之后 data 对象中的相应属性值改变, UI 界面不会随之刷新。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>样式绑定</title>

		<script src="https://unpkg.com/vue@next"></script>

		<style>
			.activated {
				color: red;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div :style="[styleObj,{fontSize:'20px'}]" @click="handleDivClick">
				Hello world
			</div>
		</div>
		<script>
			Vue.createApp({
				el: "#app",
				data() {
					return {
						styleObj: {
							color: "black",
						},
					};
				},
				methods: {
					handleDivClick() {
						this.styleObj.color =
							this.styleObj.color === "black" ? "red" : "black";
					},
				},
			}).mount("#app");
		</script>
	</body>
</html>

表单绑定:
使用v-model 实现双向绑定
可以用于“Text fields ”、“Checkboxes ”、”Radio buttons“和“ Drop down lists ”。

<input v-model ="query" placeholder="Search...">
<textarea v-model="emailMessage">
</textarea>
<input type="checkbox" v-model="isPowerSyntaxEnabled">

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Vue原生指令应用示例:
实现“条件”渲染:
在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>v-if 和 v-show</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>

	<body>
		<div id="app">
			<div v-if="active">v-if:active={{active }}</div>
			<div v-else>v-else:active={{active}}</div>
			<div v-show="active">show</div>
		</div>
		<script>
			var app = Vue.createApp({
				data() {
					return {
						active: true,
					};
				},
			}).mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<div v-for="(message,index) in messages">{{index}}-{{ message }}</div>
		</div>
		<script>
			var app = Vue.createApp({
				data() {
					return {
						messages: ["a", "b", "c"],
					};
				},
			}).mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据绑定经典示例:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body class="container">
		<h2>Hello!</h2>
		<div id="app">
			<input
				v-model="inputValue"
				type="text"
				v-on:keyup.enter="handleBtnClick()"
			/>
			<button class="btn btn-primary" v-on:click="handleBtnClick()">
				Submit
			</button>
			<ul>
				<!-- 执行循环,每次循环有一个变量item,item值被传给组件的content属性 -->
				<todo-item
					v-bind:content="item"
					v-for="(item,index) in list"
					v-on:delete="handleItemDelete(index)"
				></todo-item>
			</ul>
		</div>
		<script>
			//定义一个组件,它有一个content属性,能从外部接收数据
			var TodoItem = {
				props: ["content"],
				template: "<li v-on:click='handleItemClick'>{{content}}</li>",
				methods: {
					handleItemClick() {
						//子组件触发事件
						this.$emit("delete");
					},
				},
			};

			var app = Vue.createApp({
				components: {
					TodoItem: TodoItem,
				},
				data() {
					return {
						list: [],
						inputValue: "",
					};
				},
				methods: {
					handleBtnClick() {
						this.list.push(this.inputValue);
						this.inputValue = "";
					},
					//用于响应子组件内发出的事件
					handleItemDelete(index) {
						this.list.splice(index, 1);
					},
				},
			}).mount("#app");
		</script>
	</body>
</html>

事件相应

响应鼠标事件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

响应按键事件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Vue组件基础

全局组件:
组件是可复用的Vue 实例,且带有一个名字。
使用app.component定义一个全局组件,然后页面中就可以使用它了。
注意组件要在在mount之前定义。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>

	<body>
		<div id="app">
			<!-- 使用自定义的Vue组件 -->
			<comp-one></comp-one>
		</div>
		<script>
			var app = Vue.createApp({});
			const component = {
				template: "<div>this is a component</div>",
			};
			//定义一个全局组件,注意一下命名方式
			app.component("CompOne", component);
			app.mount("#app");
		</script>
	</body>
</html>

局部组件:
在Vue实例中使用components属性可以定义仅用于本实例中的组件。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<!-- 使用自定义的Vue组件 -->
			<comp-one></comp-one>
		</div>
		<script>
			const component = {
				template: "<div>this is a local component</div>",
			};
			var app = Vue.createApp({
				//定义一个局部组件
				components: {
					CompOne: component,
				},
			}).mount("#app");
		</script>
	</body>
</html>

组件内部的数据绑定:
组件内部的data必须设计为一个函数,主要是为了避免多个组件之间的数据相互影响,因为这样一来,每个组件实体都返回一个独立的数据对象。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>

	<body>
		<div id="app">
			<!-- 使用自定义的Vue组件 -->
			<comp-one></comp-one>
		</div>
		<script>
			const component = {
				template: "<div>{{text}}</div>",
				data() {
					return {
						text: "hello",
					};
				},
			};
			var app = Vue.createApp({
				//定义一个局部组件
				components: {
					CompOne: component,
				},
			}).mount("#app");
		</script>
	</body>
</html>

给组件绑定DOM 原生事件:
只需要添加“native”修饰符即可:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>给组件绑定原生事件</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body class="container">
		<div id="app">
			<child @click.native="handleClick"></child>
		</div>
		<script>
			var app = Vue.createApp({
				methods: {
					handleClick(e) {
						console.log(e.target);
					},
				},
			});
			app.component("child", {
				template: "<div>Child</div>",
			});
			app.mount("#app");
		</script>
	</body>
</html>

组件间的数据交换
父组件使用props 向子组件部传值。
在定义组件时,可以使用props 属性定义一些需要从外界传入的值,这些值可用于组件内部模板的数据绑定。
组件不要更改props 属性所传入的值,如果真的要改,应该使用事件的方式通知外部去改,而不是自己动手直接改。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>

	<body>
		<div id="app">
			<comp-one :active="true" message="hello"></comp-one>
			<comp-one :active="false" message="world"></comp-one>
		</div>
		<script>
			const component = {
				props: {
					active: Boolean,
					message: String,
				},
				template: `
            <div>
                <p>{{message}}</p>
                <span v-show="active">see me is active</span>
            </div>
            `,
			};
			var app = Vue.createApp({
				//定义一个局部组件
				components: {
					CompOne: component,
				},
			}).mount("#app");
		</script>
	</body>
</html>

如果子组件需要修改父组件传来的值
子组件注意不要直接修改props 属性传入的值,而是应该克隆一份,然后修改这个克隆的值。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>Vue父子组件间信息交换</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body class="container">
		<div id="root">
			<!-- 父组件通过给子组件属性传值实现数据交换 -->
			<!-- “0”作为字符串传入子组件 -->
			<counter count="0"></counter>
			<!-- 加了冒号,字符串就是一个表达式,在这里,就是数字1 -->
			<counter :count="1"></counter>
		</div>
		<script>
			var counter = {
				//定义用于接收父组件传入的值的属性
				props: ["count"],
				//子组件克隆父组件传来的数据,以便修改
				data() {
					return {
						number: this.count,
					};
				},
				template: '<div @click="handleClick">{{number}}</div>',
				methods: {
					handleClick: function () {
						this.number++;
					},
				},
			};

			var vm = Vue.createApp({
				components: {
					counter: counter,
				},
			}).mount("#root");
		</script>
	</body>
</html>

子组件挂接父组件所提供的事件响应函数
子组件内包容一个按钮和一个标签,当用户点击按钮时,内外部标签同步更新。
外部标签由父组件负责更新。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>

	<body>
		<div id="app">
			<!--网页上的自定义组件 -->
			<p>子组件内按钮单击次数:<strong>{{buttonClickCount}}</strong></p>
			<div style="border: mediumblue solid 1px; padding: 10px">
				<!--将Vue实例所定义的方法引用传给子组件 -->
				<my-component :on-button-click="onChildComponentClick" />
			</div>
		</div>
		<script>
			const component = {
				props: {
					//定义一个可以挂接外部函数的属性
					//子组件使用props 属性接收外界传入的回调函数引用。
					onButtonClick: Function,
				},
				template: `
                <div>
                    <p>{{message}}</p>
                    <button @click="myClick">Click me!</button>
                </div>
            `,
            //子组件中的按钮点击时调用自身的myClick方法
            //同时刷新自身的message属性值。
				data() {
					return {
						message: "点击按钮增加计数值",
						counter: 0,
					};
				},
				methods: {
					//当按钮被点击时,调用此方法
					//在子组件的myClick函数中“串”起一切
					myClick() {
						//修改计数值
						this.counter++;
						this.message = `clicked ${this.counter} times`;
						//如果外部挂接了事件响应函数,则调用它
						if (this.onButtonClick) {
							this.onButtonClick(this.counter);
						}
					},
				},
			};

			//Vue实例中定义要了被回调的函数,并用数据绑定实时更新外部标签文本
			var app = Vue.createApp({
				//定义一个局部组件
				components: {
					MyComponent: component,
				},
				data() {
					return {
						//用于记录子组件内传出的计数值
						buttonClickCount: 0,
					};
				},
				methods: {
					//将被子组件所回调的函数
					onChildComponentClick(count) {
						this.buttonClickCount = count;
					},
				},
			}).mount("#app");
		</script>
	</body>
</html>

插槽:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>在Vue中使用插槽</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="root">
			<body-content>
				<template v-slot:header>
					<div>header</div>
				</template>
				<template v-slot:footer>
					<div>footer</div>
				</template>
			</body-content>
		</div>
		<script>
			var app = Vue.createApp({});
			app.component("body-content", {
				template: `<div>
                            <slot name='header'>default header</slot>
                            <h2>content</h2>
                            <slot name='footer'>default footer</slot>
                       </div>`,
			});
			app.mount("#root");
		</script>
	</body>
</html>

作用域插槽用于将子组件中的数据“上传”给父组件,从而父组件可以用它来定义HTML 内容。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>作用域插槽</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body class="container">
		<div id="app">
			<child>
				<!-- 以下是作用域模板的固定写法,其中props是随意命名的,它表示
              从子组件中传来的数据放在哪个对象中 -->
				<template v-slot="props">
					<!-- 这里是真正的模板内容,item是在子组件中定义的数据属性名 -->
					<li>{{props.item}}--list</li>
				</template>
			</child>
		</div>
		<script>
			var app = Vue.createApp({});
			app.component("child", {
				//注意一下slot的用法,其中定义了一个名叫item的属性,
				//父组件可以使用它
				template: `<div>
                        <ul>
                            <slot v-for="listItem of list"
                                  :item=listItem>
                            </slot>
                        </ul>
                    </div>`,
				data() {
					return {
						list: [1, 2, 3, 4],
					};
				},
				methods: {
					handleClick() {},
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

Vue-Router 使用基础

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>最简单的Vue路由示例</title>

		<script src="https://unpkg.com/vue@next"></script>
		<script src="https://unpkg.com/vue-router@4"></script>

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

	<body>
		<div id="app">
			<h2>最简单的Vue路由示例</h2>
			<!-- 显示跳转链接 -->
			<ul>
				<li>
					<router-link to="/foo">Go to Foo</router-link>
				</li>
				<li>
					<router-link to="/bar">Go to Bar</router-link>
				</li>
			</ul>
			<hr />
			<!-- 与相应URL所对应的组件,将显示在这里 -->
			<router-view></router-view>
		</div>

		<script>
			//1. 定义两个Vue组件
			const Foo = { template: "<div>foo</div>" };
			const Bar = { template: "<div>bar</div>" };

			//2. 设定路由选项
			const routes = [
				{ path: "/foo", component: Foo },
				{ path: "/bar", component: Bar },
			];

			//3. 创建Vue的路由对象
			const router = VueRouter.createRouter({
				history: VueRouter.createWebHashHistory(),
				routes: routes,
			});

			//4.给Vue实例挂接路由对象
			const app = Vue.createApp({});
			app.use(router);
			app.mount("#app");
		</script>
	</body>
</html>

动态路由:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>动态路由</title>
		<script src="https://unpkg.com/vue@next"></script>
		<script src="https://unpkg.com/vue-router@4"></script>
	</head>

	<body>
		<div id="app">
			<h2>动态路由</h2>
			<p>
				<router-link to="/user/1">Go to userId:1</router-link>
			</p>
			<p>
				<router-link to="/user/100">Go to userId:100</router-link>
			</p>
			<hr />
			<!-- 与相应URL所对应的组件,将显示在这里 -->
			<router-view></router-view>
		</div>
		<script>
			//使用$route.params可以取出路由参数值
			const User = {
				template: "<div>路径参数: {{ $route.params.id }}</div>",
			};
			const router = VueRouter.createRouter({
				history: VueRouter.createWebHashHistory(),
				routes: [
					//使用冒号定义路由参数
					{ path: "/user/:id", component: User },
				],
			});
			const app = Vue.createApp({});
			app.use(router);
			app.mount("#app");
		</script>
	</body>
</html>

嵌套路由:
在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>嵌套路由</title>
		<script src="https://unpkg.com/vue@next"></script>
		<script src="https://unpkg.com/vue-router@4"></script>
	</head>

	<body>
		<div id="app">
			<h2>嵌套路由</h2>
			<p>
				<router-link to="/user/1">Go to userId:1</router-link>
				<br />
				<router-link to="/user/1/profile"
					>Go to userId:1 's profile</router-link
				>
				<br />
				<router-link to="/user/1/posts">Go to userId:1 's posts</router-link>
			</p>
			<hr />
			<p>
				<router-link to="/user/100">Go to userId:100</router-link>
			</p>
			<hr />
			<router-view></router-view>
		</div>
		<script>
			//User组件的模板中,放置了一个<router-view>用于显示子视图
			const User = {
				template: `
                <div class="user">
                <h3>User {{ $route.params.id }}</h3>
                <router-view></router-view>
                </div>
            `,
			};
			//三个子视图模板
			const UserHome = {
				template: "<div>UserHome of User {{ $route.params.id }}</div>",
			};
			const UserProfile = {
				template: "<div>UserProfile of User {{ $route.params.id }}</div>",
			};
			const UserPosts = {
				template: "<div>UserPosts of User {{ $route.params.id }}</div>",
			};

			//使用children属性定义子路由
			const router = VueRouter.createRouter({
				history: VueRouter.createWebHashHistory(),
				routes: [
					{
						path: "/user/:id",
						component: User,
						children: [
							{ path: "", component: UserHome },
							{
								path: "profile",
								component: UserProfile,
							},
							{
								path: "posts",
								component: UserPosts,
							},
						],
					},
				],
			});

			const app = Vue.createApp({});
			app.use(router);
			app.mount("#app");
		</script>
	</body>
</html>

同一个URL定义多个视图:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>命名视图</title>
		<script src="https://unpkg.com/vue@next"></script>
		<script src="https://unpkg.com/vue-router@4"></script>
	</head>

	<body>
		<div id="app">
			<h1>命名视图</h1>
			<p>
				<router-link to="/test">测试同一URL多个命名视图</router-link>
			</p>
			<hr />
			<router-view class="view one"></router-view>
			<router-view class="view two" name="a"></router-view>
			<router-view class="view three" name="b"></router-view>
		</div>
		<script>
			const User = {
				template: `
                <div>
                <h2>User {{ $route.params.id }}</h2>
                <router-view></router-view>
                </div>
            `,
			};

			const Foo = {
				template: "<div>主视图</div>",
			};
			const Bar = {
				template: "<div>辅助视图一</div>",
			};
			const Baz = {
				template: "<div>辅助视图二</div>",
			};

			const router = VueRouter.createRouter({
				history: VueRouter.createWebHashHistory(),
				routes: [
					{
						path: "/test",
						components: {
							default: Foo,
							a: Bar,
							b: Baz,
						},
					},
				],
			});

			const app = Vue.createApp({});
			app.use(router);
			app.mount("#app");
		</script>
	</body>
</html>

响应事件:
如果需要响应路由事件,需要指定native修饰符:
<router-link to="/blog" @click.native="handleClick ">Blog</router-link>

Vuex状态管理

Vuex是 Vue 生态圈中用于实现组件状态管理的主要技术手段, 多用于规模较大的 Web 前端应用程序。
Web
前端应用中如果有多个组件之间需要共享数据 ,可以将这些需要共享的数据从组件内部独立出来单独存放和管理。这个单独的数据存放和管理的地方,在Vuex 中称为“ Store ”。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例</title>
		<script src="./vue3/vue.global.js""></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						this.$store.state.count++;
					},
					decrease() {
						this.$store.state.count--;
					},
				},
			};

			var app = Vue.createApp({
				components: { Counter, MyButton },
			});
			app.use(store);
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例——getters</title>

		<script src="./vue3/vue.global.js"></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<counter2></counter2>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
				//可以使用getters对原始状态值进行封装
				getters: {
					//将计数值翻倍后再传给外界
					doubleCounter: function (state) {
						return state.count * 2;
					},
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const Counter2 = {
				template: `<h3>Vuex中保存的翻倍后状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用Vuex的getters方法,获取其处理后的数据
						return this.$store.getters.doubleCounter;
					},
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						this.$store.state.count++;
					},
					decrease() {
						this.$store.state.count--;
					},
				},
			};

			var app = Vue.createApp({
				components: { Counter, Counter2, MyButton },
            });
            app.use(store);
            app.mount('#app');
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例——getters</title>

		<script src="./vue3/vue.global.js""></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<counter2></counter2>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
				//可以使用getters对原始状态值进行封装
				getters: {
					//将计数值翻倍后再传给外界
					doubleCounter: function (state) {
						return state.count * 2;
					},
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const mapGetters = Vuex.mapGetters;
			const Counter2 = {
				template: `<h3>Vuex中保存的翻倍后状态值:{{ count }}</h3>`,
				computed: {
					other: function () {
						return "其他的计算属性";
					},
					//使用对象展开运算符将 getter 混入 computed 对象中
					...mapGetters({
						count: "doubleCounter", //将Vuex中的getter,重定义为本组件的count属性
					}),
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						this.$store.state.count++;
					},
					decrease() {
						this.$store.state.count--;
					},
				},
			};

			var app = Vue.createApp({
				components: { Counter, Counter2, MyButton },
            });
            app.use(store);
            app.mount('#app');
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例——getters</title>

		<script src="./vue3/vue.global.js""></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<counter2></counter2>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
				//可以使用getters对原始状态值进行封装
				getters: {
					//将计数值翻倍后再传给外界
					doubleCounter (state) {
						return state.count * 2;
					},
				},
				//向外界提供修改状态的方法
				mutations: {
					increase (state) {
						return state.count++;
					},
					decrease (state) {
						return state.count--;
					},
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const mapGetters = Vuex.mapGetters;
			const Counter2 = {
				template: `<h3>Vuex中保存的翻倍后状态值:{{ count }}</h3>`,
				computed: {
					other: function () {
						return "其他的计算属性";
					},
					//使用对象展开运算符将 getter 混入 computed 对象中
					...mapGetters({
						count: "doubleCounter", //将Vuex中的getter,重定义为本组件的count属性
					}),
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						//不再直接访问Vuex中保存的原始数据,而是调用其mutations所定义函数。
						//this.$store.state.count++;
						this.$store.commit("increase");
					},
					decrease() {
						//this.$store.state.count--;
						this.$store.commit("decrease");
					},
				},
			};

			var app = Vue.createApp({
				components: { Counter, Counter2, MyButton },
            });
            app.use(store);
            app.mount('#app');
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例——getters</title>
		<script src="./vue3/vue.global.js""></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<counter2></counter2>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
				//可以使用getters对原始状态值进行封装
				getters: {
					//将计数值翻倍后再传给外界
					doubleCounter: function (state) {
						return state.count * 2;
					},
				},
				//向外界提供修改状态的方法
				mutations: {
					increase: function (state) {
						return state.count++;
					},
					decrease: function (state) {
						return state.count--;
					},
				},
				actions: {
					//支持异步的函数
					increaseAsync: function (context) {
						//这里使用setTimeout函数模拟费时操作,比如访问网络
						setTimeout(() => {
							//context是Vuex提供给我们的一个对象
							context.commit("increase");
						}, 1000);
					},
					decreaseAsync: function (context) {
						setTimeout(() => {
							context.commit("decrease");
						}, 1000);
					},
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const mapGetters = Vuex.mapGetters;
			const Counter2 = {
				template: `<h3>Vuex中保存的翻倍后状态值:{{ count }}</h3>`,
				computed: {
					other: function () {
						return "其他的计算属性";
					},
					//使用对象展开运算符将 getter 混入 computed 对象中
					...mapGetters({
						count: "doubleCounter", //将Vuex中的getter,重定义为本组件的count属性
					}),
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						//对于异步操作,需要使用dispatch方法
						this.$store.dispatch("increaseAsync");
					},
					decrease() {
						this.$store.dispatch("decreaseAsync");
					},
				},
			};

			var app = Vue.createApp({
				components: { Counter, Counter2, MyButton },
            });
            app.use(store);
            app.mount('#app');
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />

		<title>Vuex的简单示例——getters</title>
		<script src="./vue3/vue.global.js""></script>
		<script src="./vuex4/vuex.global.js"></script>
	</head>

	<body>
		<div id="app">
			<counter></counter>
			<counter2></counter2>
			<my-button></my-button>
		</div>
		<script>
			//Vuex负责保存数据
			const store = Vuex.createStore({
				state: {
					count: 0,
				},
				//可以使用getters对原始状态值进行封装
				getters: {
					//将计数值翻倍后再传给外界
					doubleCounter (state) {
						return state.count * 2;
					},
				},
				//向外界提供修改状态的方法
				mutations: {
					increase: function (state, step) {
						return (state.count += step);
					},
					decrease: function (state, step) {
						return (state.count -= step);
					},
				},
				actions: {
					//支持异步的函数(支持额外参数)
					increaseAsync (context, argu) {
						//这里使用setTimeout函数模拟费时操作,比如访问网络
						setTimeout(() => {
							//context是Vuex提供给我们的一个对象
							context.commit("increase", argu.step);
						}, argu.duration);
					},
					decreaseAsync (context, argu) {
						setTimeout(() => {
							context.commit("decrease", argu.step);
						}, argu.duration);
					},
				},
			});

			// 创建一个 Counter 组件
			const Counter = {
				template: `<h3>Vuex中保存的状态值:{{ count }}</h3>`,
				computed: {
					count() {
						//使用this.$store可以访问Vuex
						return this.$store.state.count;
					},
				},
			};

			const mapGetters = Vuex.mapGetters;
			const Counter2 = {
				template: `<h3>Vuex中保存的翻倍后状态值:{{ count }}</h3>`,
				computed: {
					other: function () {
						return "其他的计算属性";
					},
					//使用对象展开运算符将 getter 混入 computed 对象中
					...mapGetters({
						count: "doubleCounter", //将Vuex中的getter,重定义为本组件的count属性
					}),
				},
			};

			const MyButton = {
				template: `
            <div>
                <button @click='increase'>增加计数值</button>
                <button @click='decrease'>减少计数值</button>
            </div>
            `,
				methods: {
					increase() {
						//对于异步操作,需要使用dispatch方法
						this.$store.dispatch("increaseAsync", { step: 2, duration: 200 });
					},
					decrease() {
						this.$store.dispatch("decreaseAsync", { step: 10, duration: 1000 });
					},
				},
			};

			var app = Vue.createApp({
			components: { Counter, Counter2, MyButton },
            });
              app.use(store);
            app.mount('#app');
		</script>
	</body>
</html>

在这里插入图片描述

Axios

Vue用它来完成HTTP常规操作(GET、POST……)
Axios并不属于Vue,它只是Vue应用可以使用的一个库而己。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
axios是当前Web前端应用访问RESTful Service使用最广泛的库之一,用起来非常地简单方便。
在实际开发中,推荐使用Vue + axios构建Web前端应用,有两种方式,一种是前后端分离的独立的Web前端应用,可以使用Vue CLI创建项目,另一种是依托现有的Web后端开发框架(比如Spring Boot MVC和ASP.NET Core MVC),开发单页面应用(SPA: Single Page Application)

前后端分离的Vue

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
部署Vue:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Composition API

Vue 3.0中比较大的变化,就是引入了 Composition API ,从而对于开发中大规模的 Web 前端应用有更好的支持。
Composition API编程模型,全面引入了“ Reactive Programming编程风格。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<h3>{{name}}</h3>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					return {
						name: "hello world",
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<h4>{{counter}} <button @click="handleClick">Click!</button></h4>
			<div>
				<input v-model="user.name" />
				<p>{{user.name}}</p>
			</div>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					const { ref, reactive } = Vue;
					//适用于基本数据类型
					let counter = ref(1);
					//适用于对象
					let user = reactive({
						name: "",
					});
					return {
						counter: counter,
						user: user,
						handleClick: () => {
							counter.value += 1;
						},
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<h3>{{count}}--{{countAddFive}}</h3>
			<button @click="handleClick">Click!</button>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					const { ref, computed } = Vue;
					const count = ref(0);
					const handleClick = () => {
						count.value += 1;
					};
					//定义计算属性
					const countAddFive = computed(() => {
						return count.value + 5;
					});
					return {
						count,
						handleClick,
						countAddFive,
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<div><input v-model="user.name" /></div>
			<p>{{user.name}}</p>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					const { reactive, watch } = Vue;
					const user = reactive({ name: "张三" });
					//监听器
					watch(
						() => user.name,
						(currentValue, prevValue) => {
							console.log(currentValue, prevValue);
						}
					);
					return {
						user: user,
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<span>{{counter}}</span>
			<button @click="handleClick">Click</button>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					const {
						onBeforeMount,
						onMounted,
						onBeforeUpdate,
						onUpdated,
						ref,
					} = Vue;
					onBeforeMount(() => {
						console.log("onBeforeMount");
					});
					onMounted(() => {
						console.log("onMounted");
					});
					onBeforeUpdate(() => {
						console.log("onBeforeUpdate");
					});
					onUpdated(() => {
						console.log("onUpdated");
					});
					let counter = ref(1);
					return {
						counter,
						handleClick: () => {
							counter.value += 1;
						},
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<child>
				<template v-slot:header>
						<h5 >header</h5>
				</template>
			<template v-slot:footer>
					<h5 >footer</h4>
			</template>
			</child>
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					return {};
				},
			});
			app.component("child", {
				template: `
				<div>
					<slot name='header' />
				  <h2>Content</h2>
					<slot name='footer' />
				</div>`,
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<child message="Hello,message from parent." />
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					return {};
				},
			});
			app.component("child", {
				props: ["message"],
				template: "<div>{{message}}</div>",
				setup(props, context) {
					const { ref } = Vue;
					const message = ref(props.message);
					return {
						message: message,
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Composition API</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="app">
			<child />
		</div>
		<script>
			var app = Vue.createApp({
				setup(props, context) {
					const { provide } = Vue;
					//提供可供注入的对象
					provide("obj", { name: "张三", age: 34 });
					return {};
				},
			});
			app.component("child", {
				props: ["message"],
				template: "<div>{{parentObj.name}}:{{parentObj.age}}</div>",
				setup(props, context) {
					const { inject } = Vue;
					//接收注入的对象
					const parentObj = inject("obj");
					return {
						parentObj,
					};
				},
			});
			app.mount("#app");
		</script>
	</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>lesson 21</title>
		<script src="https://unpkg.com/vue@next"></script>
	</head>
	<body>
		<div id="root">
			<div>
				<input
					ref="userInput"
					:value="inputValue"
					@input="handleInputValueChange"
				/>
				<button @click="handleSubmit">提交</button>
			</div>
			<p>{{inputValue}}</p>
			<ul>
				<li v-for="item in list">{{item}}</li>
			</ul>
		</div>
	</body>
	<script>
		const listFunc = () => {
			const { reactive } = Vue;
			const list = reactive([]);
			const addItemToList = (item) => {
				list.push(item);
			};
			return { list, addItemToList };
		};

		const inputFunc = () => {
			const { ref, reactive } = Vue;
			const inputValue = ref("");
			const handleInputValueChange = (e) => {
				inputValue.value = e.target.value;
			};
			return { inputValue, handleInputValueChange };
		};

		const app = Vue.createApp({
			data() {
				return {
					userInput: {},
				};
			},
			mounted() {
				userInput = this.$refs.userInput;
			},
			setup(props, context) {
				const { list, addItemToList } = listFunc();
				const { inputValue, handleInputValueChange } = inputFunc();
				let userInput = null;
				const handleSubmit = () => {
					addItemToList(inputValue.value);
					inputValue.value = "";
					this.userInput.focus();
				};

				return {
					list,
					addItemToList,
					inputValue,
					handleInputValueChange,
					handleSubmit,
				};
			},
		});

		const vm = app.mount("#root");
	</script>
</html>

声明

文章中所有截图,图片,代码等均来自于北京理工大学金旭亮老师,本人仅作笔记用途。
如需使用本文中任何资料请与教师联系。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值