-
用户列表 -
新增用户
Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同是,Vue被设计为可以自底向上逐层应用。Vue核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。目前最新版本是Vue3.x。
Vue3.x发布于2020年9月19日,在Vue2.x的基础上面进行了一些优化,对TypeScript有更好的支持。Vue3.x的语法和Vue2.x非常相似,如果已经会用Vue2.x,那么Vue3.x会非常简单。
Vue官网地址:https://cn.vuejs.org/
Vue3.x github地址:https://github.com/vuejs/vue-next
Vue3.x文档地址:https://v3.cn.vuejs.org/
注意:安装脚手架创建项目之前,必须安装Nodejs,推荐安装nodejs-16.X稳定版本
文档地址:https://v3.cn.vuejs.org/guide/installation.html
Vue-cli地址:https://cli.vuejs.org/
Vite地址:https://github.com/vitejs/vite
通过Vue-cli脚手架工具可以快速搭建Vue项目,目前Vue官方提供了2个脚手架,Vue-cli和Vite。
yarn global add @vue/cli
#OR
npm install -g @vue/cli
#OR
cnpm install -g @vue/cli
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm i -g yarn
vue create hello-vue
yarn serve
npm init vite-app
cd
npm install
npm run dev
yarn create vite-app
cd
yarn
yarn dev

import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
createApp(App).mount('#app')
{{msg}}
绑定对象:{{userinfo.username}}---{{userinfo.age}}
{{h2}}
绑定HTML:
![logo]()
![logo]()
鼠标放上去试试
鼠标放上去试试
鼠标放上去试试
...
注:attributeName将被动的评估为JavaScript表达式,且其评估值将用作参数的最终值。如果组件实例具有一个数据属性attributeName,其值为href,则此绑定将等效于v-bind:href。
循环遍历
- {{item}}
- {{item.title}}
- {{attr}}:{{value}}---{{index}}
-
{{item.cate}}
- {{i.title}}
{{msg}}
当v-bind与class一起使用时,Vue提供了特殊的增强功能style。除了字符串外,表达式可以求值为对象或数组。
{{msg}}
v-bind:class属性
v-bind:class属性
数组绑定Class
数组结合三目运算绑定Class
业务逻辑
data(){
return {
activeColor: "red",
fontSize: 30
}
}
template模板
业务逻辑
data(){
return {
styleObject: {
color: "red",
fontSize: "13px"
}
}
}
template模板
业务逻辑
data(){
return {
baseStyle: {
color: "Orange",
fontSize: "13px"
},
orangeStyle: {
width: "100px",
height: "100px",
background: "orange"
}
}
}
template模板
循环数据,第一个数据高亮显示
- {{item}}
{{msg}}
{{title}}
{{msg}}
有时需要在内联语句处理程序中访问原始DOM事件。可使用特殊$event变量将其传递给方法
在事件处理程序中使用逗号分隔多个事件处理程序
Vue中阻止冒泡阻止默认行为,可通过事件对象event.preventDefault()或event.stopPropagation()实现,还可以通过事件修饰符实现。
Vue提供了很多修饰符
.stop
.prevent
.capture
.self
.once
.passiv
stopPropagation
preventDefault
stopPropagation And preventDefault
监听键盘事件时,通常需要检查特定的键。Vue允许在监听事件时v-on或@在监听关键事件时添加按键修饰符
Vue为最常用的键提供别名
.enter
.tab
.delete(同时捕获"删除"和"退格"键)
.esc
.space
.up
.down
.left
.right
- 姓名:
- 年龄:
MVVM就是常说的双向数据绑定,Vue就是一个MVVM的框架。M表示model,V表示View。在MVVM的框架中model改变会影响视图view,view视图改变反过来影响model。注意:双向数据绑定主要用于表单中。
- 姓名:
- {{username}}
- 年龄:
- 姓名:
- 年龄:
- 性别:
- 城市:
- 性别:
- 姓名:
- 年龄:
- 性别:
- 城市:
- 爱好:
- 备注:
{{empinfo}}
- 姓名:
- 年龄:
- 性别:
- 城市:
- 爱好:
{{number + 1}}
{{flag ? 'YES' : 'NO'}}
{{msg.split('').reverse().join('')}}
this is true
this is true
this is false
注:v-else元素必须紧跟在带v-if或者v-else-if的元素后面,否则将不会被识别
大于0.5
小于0.5
注:与v-else相似,v-else-if元素必须紧跟v-if或v-else-if元素
A
B
C
v-if是一个指令,必须添加到一个元素上。若需切换多个元素呢?此时可把一个template元素当作不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含template元素。
这是div
这是span
用于根条件展示元素的选项是v-show指令。
v-if显示
v-show显示
v-if是dom操作,v-show只是css的显示隐藏,一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁的切换,则使用v-show较好。
在模板中表达式非常便利,但实际上只能用于简单操作。
模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是Vue.js将绑定表达式限制为一个表达式。若需要多于一个表达式的逻辑,应当使用计算属性。
{{reverseMsg}}
- {{item}}
Vue.js提供了一个方法watch,它用于观察Vue实例上的数据变动。当一些数据需要根据其它数据变化时,watch很诱人——特别是如果来自AngularJS。通常更好的办法时使用计算属性而不是命令式的watch回调。
{{firstName}} {{lastName}}
{{fullNameFn}}
npm install -D sass sass-loader node-sass
注:lang可以配置scss,scope表示这里写的css只有当前组件有效
todolist
实现完整toDoList(待办事项)以及类似京东App搜索缓存数据功能。
正在进行
-
{{item.title}}---
已经完成
-
{{item.title}}---
{{list}}
正在进行
-
{{item.title}}---
已经完成
-
{{item.title}}---
{{list}}
正在进行
{item.title}}---
-->
-
{{item.title}}---
已经完成
{item.title}}---
-->
-
{{item.title}}---
{{list}}
注:创建src/modules/storage.js
const storage = {
set(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
get(key){
return JSON.parse(localStorage.getItem(key));
},
remove(key){
localStorage.removeItem(key);
}
}
//暴露storage
export default storage;
正在进行
-
{{item.title}}---
已经完成
-
{{item.title}}---
{{list}}
组件可以拓展HTML标签,解决HTML标签构建应用的不足,Vue项目由一个一个的组件组成。
创建src/components/Home.vue
{{title}}
在src/App.vue中引入Home组件
创建src/components/Header.vue
头部组件
{{title}}
用户组件
- {{item}}
{{title}}
{{msg}}
{{count}}
{{user.name}}---{{user.age}}
Header.vue
{{title}}
{{msg}}
{{count}}
{{user.name}}---{{user.age}}
:home="this"表示将当前组件传递给子组件,子组件就可以获取父组件的数据和执行父组件的方法。
{{title}}
{{msg}}
{{count}}
{{user.name}}---{{user.age}}
{{home.list}}
可以为组件的props指定验证要求,如类型验证,若有需求没有被满足,则Vue会在浏览器控制台中警告。在开发一个会被别人用到的组件时尤其有帮助。
props: {
//基础的类型检查('null'和'undefined'会通过任何类型验证)
propA: Number,
//多个可能的类型
propB: [String,Number],
//必填的字符串
propC: {
type: String,
required: true
},
//带有默认值的数字
propD: {
type: Number,
default: 100
},
//带有默认值的对象
propE: {
type: Object,
//对象或数组默认值必须从一个工厂函数获取
default: function(){
return {message: 'hello'}
}
},
//自定义验证函数
propF: {
validator: function(value){
//这个值必须匹配下列字符串中的一个
return ['success','warning','danger'].indexOf(value) !== -1
}
},
//具有默认值的函数
propG: {
type: Function,
//与对象或数组默认值不同,这不是一个工厂函数--这是一个用作默认值的函数
default: function(){
return 'Default function'
}
}
}
所有的prop都使得其父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。
另外,每次父组件发生变更时,子组件中所有的prop都将会刷新为最新的值。这意味着不应该在一个子组件内部改变prop。
若需这样做,Vue会在浏览器控制台中发出警告。
#ref名称可自定义
this.$refs.header.属性
this.$refs.header.方法
Home组件
头部组件
this.$parent.数据
this.$parent.方法
Home组件
父组件msg:{{msg}}
头部组件
子组件msg:{{msg}}
注:Vue官方推荐始终使用kebab-case的事件名。
头部组件
Home组件
https://github.com/developit/mitt
Vue3.x以后从实例中移除了$on、$off和$once方法,仍然时现有API的一部分,只能实现子组件触发父组件的方法。
npm install --save mitt
// 引入mitt
import mitt from 'mitt'
// 创建mitt实例
const event = mitt();
// 暴露mitt
export default event;
头部组件
v-model主要用于表单的双向数据绑定,v-model实现自定义组件的双向数据绑定。
默认情况下,组件上的v-model使用modelValue作为prop和update:modelValue作为事件。可通过向v-model传递参数来修改这些名称
{{msg}}
子组件将需要一个foo prop并发出update:foo要同步的事件
{{msg}}
{{keyword}}
利用特定的prop和事件为目标的能力,v-model参数中,可以在单个组件实例上创建多个v-model绑定。
每个v-model将同步到不同的prop,而不需要在组件中添加额外的选项。
{{msg}}
{{keyword}}
UserName组件
{{firstName}}--{{lastName}}
Vue实现了一套内容分发的API,设计灵感来自于Web Components规范草案,将元素作为承载分发内容的出口。
{{msg}}
{{keyword}}
UserName组件
{{firstName}}--{{lastName}}
slot还允许在自定义组件里面传入任意的HTML标签或其它组件
提交
icon
搜索
slot还可以绑定父组件的数据
提交
icon搜索
{{msg}}
一个非prop的attribute是指传向一个组件,但该组件并没有相应props或emits定义的attribute。包括class、style和id属性。
取消
取消
不希望组件的根元素继承attribute,可在组件的选项中设置inheritAttrs:false。禁用attribute继承的情况是需要将attribute应用于根节点之外的其它元素。
通过将inheritAttes选项设置为false,可以在访问组件的$attris property,该property包括组件props和emits property中未包含的所有属性(如class、style、v-on监听器等)。
与单个根节点组件不同,具有多个根节点的组件不具有自动attribute回退行为。如果未显式绑定$attrs,将发出运行时警告。
//这将发出警告
app.component('custom-layout',{
template:`
`
})
//没有警告,$attrs被传递到元素
app.component('custom-layout',{
template:`
`
})

在实例化之后,数据观测(data observer)和event/watcher事件配置之前被调用。
在实例创建完成后被立即调用。在这一步,实例已完成以下配置:数据观测(data observer)property和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el property目前尚不可用。
在挂载开始之前被调用:相关的render函数首次被调用。
实例被挂载后调用,这时Vue.createApp({}).mount()被新创建的vm.
e
l
替
换
了
。
如
果
根
实
例
挂
载
到
了
一
个
文
档
内
的
元
素
上
,
当
m
o
u
n
t
e
d
被
调
用
时
v
m
.
el替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.
el替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.el也在文档内。
注意mounted不会保证所有的子组件都一起被挂载。如果希望等到整个视图都渲染完毕,可在mounted内部使用vm.$nextTick:
mounted(){
this.$nextTick(function(){
//仅在渲染整个视图后运行的代码
})
},
数据更新时调用,发生在虚拟DOM打补丁之前。这里合适在更新之前访问现有的DOM,如手动移除已添加的事件监听器。
由于事件更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。在大多数情况下,应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或侦听器取而代之。
注意:updated不会保证所有的子组件也都一起被重绘。希望等到整个视图重绘完毕,可以在updated里使用vm.$nextTick:
updated(){
this.$nextTick(function(){
//仅在渲染整个视图后运行的代码
})
},
被keep-alive缓存的组件停用时调用
被keep-alive缓存的组件停用时调用
在卸载组件实例之前调用,在这个阶段,实例仍然是完全正常的
卸载组件实例后调用,调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载
生命周期函数
{{msg}}
{{msg}}
当在这些组件之间切换时,有时会想保持这些组件的状态,以避免反复重复渲染导致的性能问题,此时候就用keep-alive。
在不同路由切换时想保持组件的状态可以使用keep-alive
生命周期函数
{{ msg }}
- {{ item.title }}
{content}} -->
{{msg}}
Vue可把获取DOM节点的代码放在mounted里面,如果要在渲染完成数据后获取DOM节点就需要用到this.$nextTick。
mounted(){
this.$nextTick(function(){
//仅在渲染整个视图后运行的代码
})
},
生命周期函数
{{ msg }}
- {{ item.title }}
{content}} -->
https://github.com/axios/axios
npm install axios --save
yarn add axios
- {{item.title}}--{{item.aid}}
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import Axios from "axios";
const app = createApp(App)
app.config.globalProperties.axios = Axios;
app.mount('#app')
- {{item.title}}--{{item.aid}}
新建src/modules/storage.js
const storage = {
set(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
get(key){
return JSON.parse(localStorage.getItem(key));
},
remove(key){
localStorage.removeItem(key);
}
}
//暴露storage
export default storage;
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
// 全局引入
import Axios from "axios";
import Storage from "./modules/storage"
const app = createApp(App)
// 全局绑定
app.config.globalProperties.axios = Axios; //this.Axios
app.config.globalProperties.Storage= Storage; //this.Storage
app.mount('#app')
- {{item.title}}--{{item.aid}}

axios不支持jsonp请求,可用jsonp来请求数据可以使用fetch-jsonp这个模块。
https://github.com/camsong/fetch-jsonp
npm install fetch-jsonp --save
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
// 全局引入
import Axios from "axios";
import Storage from "./modules/storage"
import fetchJsonp from 'fetch-jsonp';
const app = createApp(App)
// 全局绑定
app.config.globalProperties.axios = Axios; //this.Axios
app.config.globalProperties.Storage= Storage; //this.Storage
app.config.globalProperties.fetchJsonp=fetchJsonp; //this.fetchJsonp
app.mount('#app')
- {{item}}
- {{ item }}
- {{ item }}
混入(mixin)提供了一种非常灵活的方式,来分发Vue组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被"混入"进入该组件本身的选项。
新建src/mixin/baseMixin.js
const baseMixn = {
data(){
return{
apiUrl:"http://www.itying.con",
msg:"baseMixin",
}
},
methods:{
success(){
console.log("成功");
}
}
}
export default baseMixin;
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行合并。
首页模板--{{apiUrl}}
关于mixin的选项合并
{{msg}}
为Vue应用程序全局应用mixin
const baseMixin = {
data(){
return{
apiUrl:"http://www.itying.con",
msg:"baseMixin",
}
},
methods:{
success(){
console.log("成功");
}
}
}
export default baseMixin;
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
// 全局引入
import Axios from "axios";
import Storage from "./modules/storage"
import fetchJsonp from 'fetch-jsonp';
import baseMixin from "./mixin/baseMixin"
const app = createApp(App)
// 全局绑定
app.config.globalProperties.axios = Axios; //this.Axios
app.config.globalProperties.Storage= Storage; //this.Storage
app.config.globalProperties.fetchJsonp=fetchJsonp; //this.fetchJsonp
app.mixin(baseMixin);
app.mount('#app')
首页模板--{{apiUrl}}
关于mixin的选项合并
{{msg}}
Vue3.x中的组件模板属于该组件,需要把模板的内容移动到当前组件之外的DOM中,这时就可以使用Teleport。
表示teleport内包含的内容显示到body中
内容
内容
Composition API也叫组合式API,是Vue3.x的新特性。
通过创建Vue组件,可以将接口的可重复部分及其功能提却到可宠用的代码段中。仅此一项就可以使应用程序在可维护性和灵活性方面走的更远。然而,经验已证明,光靠这一点是不够的,尤其是当应用程序变得非常大时——几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要。
没有Composition API之前Vue相关业务代码需要配置到option的特定区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高。Vue3.x中的composition-api就是为了解决这个问题而生的。
composition-api提供了以下几个函数:
新的setup组件选项在创建组件之前执行,一旦props被解析,并充当合成API的入口。
提示
由于在执行setup时尚未创建组件实例,因此在setup选项中没有this。这意味着,除了props之外,将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
使用setup函数时,将接受两个参数
{{title}}
{{userinfo.username}}--{{userinfo.age}}
把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref,和响应式对象property一一对应。
{{title}}
{{userinfo.username}}--{{userinfo.age}}
{{desc}}---{{clicks}}
获取用户信息
{{ firstName }}---{{ lastName }}
{{ fullName }}
{{pass}}
{{msg}}
传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理。一个只读代理是"深层的",对象内部任何嵌套的属性也都是只读的。
获取用户信息
{{ firstName }}---{{ lastName }}
{{ fullName }}
{{pass}}
对比watchEffect,watch允许
在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
Search组件---{{num}}
{{msg}}
Search组件
{{keyword}}
可以通过在生命周期钩子前面加上"on"来访问组件的生命周期钩子。
下表包含如何在 setup () 内部调用生命周期钩子:
| 选项式 API | Hook inside setup |
|---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
{{msg}}
Search组件
当需要将数据从父组件传递到子组件时,使用props。有一些深嵌套的组件,只需要来自深嵌套子组件中父组件的某些内容。在这种情况下,仍需要将props传递到整个组件链中,这可能会很烦人。
对于这种情况,可以使用provide和inject对父组件可作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。这个特性有两个部分:父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这个数据。

在setup()中使用provide时,首先从的那个Vue显示导入provide方法。能够调用provide时来定义每个property。
provide函数允许通过两个参数定义property
使用自定义组件,提供的值可以按如下方式重构,provide、inject实现父子组件传值时,父子组件数据改变会相互影响。
{{msg}}
Location组件---{{title}}
{{userinfo.username}}---{{userinfo.age}}
{{msg}}
{{msg}}
{{msg}}
Location组件
{{title}}
{{userinfo.username}}---{{userinfo.age}}
https://v3.cn.vuejs.org/guide/typescript-support.html#项目创建
Vue CLI 可以生成使用 TypeScript 的新项目
# 1. Install Vue CLI, 如果尚未安装
npm install --global @vue/cli@next
# 2. 创建一个新项目, 选择 "Manually select features" 选项
vue create vue-demo
# 3. 如果已经有一个不存在TypeScript的 Vue CLI项目,请添加适当的 Vue CLI插件
vue add typescript

npm install -D sass-loader node-sass
Home组件---{{title}}
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
})
新闻组件---{{title}}
{{count}}
{{reverseTitle}}
User组件
{{ username }}
路由可以让应用程序根据用户输入的不同地址动态挂载不同的组件。
https://router.vuejs.org/
router-link
如何使用自定义组件来创建链接,而不是使用常规a标签。router-link这允许 Vue Router 在不重新加载页面的情况下更改 URL,处理 URL 生成及其编码。
router-view
router-view将显示与 url 对应的组件。可以把它放在任何地方以适应布局。
npm install vue-router@4
npm install vue-router@next --save
yarn add vue-router@4
Home组件、User组件、News组件
新建src/routes.ts配置路由
import { createRouter, createWebHashHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
// 配置路由
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: "/", component: Home },
{ path: "/news", component: News },
{ path: "/user", component: User },
]
})
export default router
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由
import route from "./routes"
const app = createApp(App)
// 挂载路由
app.use(route)
app.mount('#app')
http://localhost:8080/#/
http://localhost:8080/#/user
http://localhost:8080/#/news
App.vue
-
首页
-
用户
-
新闻
NewsContent组件
import { createRouter, createWebHashHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
// 配置路由
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: "/", component: Home },
{ path: "/news", component: News },
{ path: "/user", component: User },
// 配置动态路由
{path: "/newcontent/:aid", component: NewsContent},
]
})
// 暴露组件
export default router
News组件
-
{{ item }}
NewsContent.vue
NewsContent组件
import { createRouter, createWebHashHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
// 配置路由
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: "/", component: Home, alias: "/home" },
{ path: "/news", component: News },
{ path: "/user", component: User },
// 配置动态路由
{ path: "/newscontent", component: NewsContent },
]
})
// 暴露组件
export default router
News组件
-
{{ item }}
NewsContent组件
NewsContent组件
Home组件---{{title}}
src/routes.ts
import { createRouter, createWebHashHistory } from "vue-router";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
routes: [
...
]
})
import { createRouter, createWebHistory } from "vue-router";
// 配置路由
const router = createRouter({
// HTML History模式
history: createWebHistory(),
routes: [
...
]
})
注意:开启HTML5 History模式后,发布到服务器需要配置伪静态
https://router.vuejs.org/guide/essentials/history-mode.html
通过一个名称来标识一个路由显得方便一些,特别是在链接一个路由,或者是执行一些跳转时。可以创建Router实例时,在routes配置中给某个路由设置名称。
const router = new VueRouter({
routes: [{
path: '/user/:userId',
name: 'user',
component: User
}]
})
要链接到一个命名路由,可以给route-link的to属性传递一个对象
User
跟代码调用router.push()是一回事
this.$router.push({name:'user',params:{userId:123}})
这两种方式都会把路由导航到/user/123路径。
this.$router.push({name:'newscontent',query:{aid:567}})
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
// HTML History模式
// history: createWebHistory(),
routes: [
{ path: "/", component: Home, alias: "/home" },
{ path: "/news", component: News , name:"news"},
{ path: "/user", component: User },
// 配置动态路由
{ path: "/newscontent",name:"newscontent", component: NewsContent },
]
})
// 暴露组件
export default router
-
首页
-
用户
-
新闻
News组件
-
{ item }} -->
{{item}}
UserInfo组件
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
import UserInfo from "./components/UserInfo.vue";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
// HTML History模式
// history: createWebHistory(),
routes: [
{ path: "/", component: Home, alias: "/home" },
{ path: "/news", component: News, name: "news" },
{ path: "/user", component: User },
// 配置动态路由
{ path: "/newscontent", name: "newscontent", component: NewsContent },
{ path: "/userinfo/:id", name: "userinfo", component: UserInfo },
]
})
// 暴露组件
export default router
User组件
{{ username }}
跳转到用户详情
News组件
-
{ item }} -->
{{item}}
重定向在routes配置中完成,要从重定向/a到/b
const routes = [{path:'/home',redirect: {name:'homepage'} }]
重定向也可以针对命名路由
const routes = [{path:'/home',redirect:{name:'homepage'}}]
甚至使用函数进行动态重定向
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
import UserInfo from "./components/UserInfo.vue";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
// HTML History模式
// history: createWebHistory(),
routes: [
{path:'',redirect:"/home"}, //路由重定向
{ path: "/", component: Home, alias: "/home" },
{ path: "/news", component: News, name: "news" },
{ path: "/user", component: User },
// 配置动态路由
{ path: "/newscontent", name: "newscontent", component: NewsContent },
{ path: "/userinfo/:id", name: "userinfo", component: UserInfo },
]
})
// 暴露组件
export default router
http://localhost:8080
重定向是指用户访问时/home,URL将被替换/,然后与匹配/。
别名/as/home表示用户访问时/home,URL保持不变/home,但将被匹配,就像用户正在访问时一样。
以上内容可以在路由配置中表示为
const routes = [{path:'/',component:Homepage,alias:'/home'}]
别名可以自由地将UI结构映射到任意URL,而不受配置的嵌套结构约束。使别名以a开头,/以使路径在嵌套路由中使绝对的。甚至可以将两者结合起来,并为数组提供多个别名
const routes = [
{
path: '/users',
component: UsersLayout,
children: [
//- /users
//- /users/list
//- /people
{path:'',component:UsersList,alias:['/people','list']},
],
},
]
如果路径包含参数,请确保将其包含在任何绝对别名中
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import User from "./components/User.vue";
import NewsContent from "./components/NewsContent.vue";
import UserInfo from "./components/UserInfo.vue";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
// HTML History模式
// history: createWebHistory(),
routes: [
{ path: '', redirect: "/home" }, //路由重定向
{ path: "/", component: Home, alias: "/home" },
{ path: "/news", component: News, name: "news", alias: ["/n", "/c"] },
{ path: "/user", component: User, alias: "/people" },
// 配置动态路由
{ path: "/newscontent", name: "newscontent", component: NewsContent },
{ path: "/userinfo/:id", name: "userinfo", alias: "/u/:id", component: UserInfo },
]
})
// 暴露组件
export default router
http://localhost:8080/#/people
http://localhost:8080/#/n
http://localhost:8080/#/c
http://localhost:8080/#/u/123
-
用户列表
-
新增用户
src/component/User/UserList.vue
UserList组件
src/component/User/UserAdd.vue
UserAdd组件
-
新闻列表
-
新增新闻
src/component/News/NewsList.vue
NewsList组件
src/component/News/NewsAdd.vue
NewsAdd组件
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 引入组件
import Home from "./components/Home.vue";
import News from "./components/News.vue";
import NewsList from "./components/News/NewsList.vue";
import NewsAdd from "./components/News/NewsAdd.vue";
import User from "./components/User.vue";
import UserAdd from "./components/User/UserAdd.vue";
import UserList from "./components/User/UserList.vue";
// 配置路由
const router = createRouter({
// hash模式
history: createWebHashHistory(),
// HTML History模式
// history: createWebHistory(),
routes: [
{ path: '', redirect: "/home" },
{ path: '/home', component: Home },
{
path: '/news', component: News, name: "news",
children: [
{ path: '', redirect: "/news/newslist" },
{ path: 'newslist', component: NewsList },
{ path: 'newsadd', component: NewsAdd },
]
},
{
path: '/user', component: User,
children: [
{ path: '', redirect: "/user/userlist" },
{ path: 'userlist', component: UserList },
{ path: 'useradd', component: UserAdd }
]
},
]
})
// 暴露组件
export default router
-
首页
-
新闻
-
用户
Vuex是一个转为Vue.js应用程序开发的状态管理模式。
官网:https://vuex.vuejs.org/
主要功能:
Vuex的几个核心概念
npm install vuex@next --save
yarn add vuex@next --save
src目录下新建一个vuex的文件夹,vuex文件夹里面新建一个store.js
import { createStore } from "vuex";
const store = createStore({
state(){
return {
count:1,
}
}
})
export default store
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由
import route from "./routes"
// 引入状态
import store from "./vuex/store";
const app = createApp(App)
// 挂载路由
app.use(route)
// 挂载状态
app.use(store)
app.mount('#app')
用到组件里面引入的store,然后计算属性里面获取
computed: {
count(){
return store.state.count
}
}
由于全局配置了Vuex app.use(store),所以直接可以通过下面方法获取store里面的值
computed:{
count(){
return this.$store.state.count
}
}
computed:{
...mapState({
count:(state)=>state.count,
list:(state)=>state.title,
})
}
import { createStore } from "vuex";
const store = createStore({
// 定义数据
state() {
return {
count: 1,
title:['马总','李总','刘总'],
}
},
// 定义方法
mutations: {
incCount(state) {
state.count++;
},
setCount(state, num) {
state.count = num;
}
},
})
export default store
Home组件---{{ title }}
---{{ num }}
---{{ $store.state.count }}
- {{item}}---{{count}}
Getter会暴露为store.getters对象,可以以属性的形式访问这些值
store.getters.doneTodos
computed:{
doneTodosCount(){
return this.$store.getters.doneTodoCount
}
}
computed:{
//使用对象展开运算符讲getter混入computed对象中
...mapGetters([
'doneTodoCount',
'anotherGetter',
])
}
如果想将一个getter属性另取一个名字,使用对象形式
...mapGetters([ "reverseMsg","num"])
...mapGetters({
num:"num",
reverseMsg:"reverseMsg"
})
import { createStore } from "vuex";
const store = createStore({
// 定义数据
state() {
return {
count: 1,
title: ['马总', '李总', '刘总'],
msg: "你好vue",
}
},
// 定义方法
mutations: {
incCount(state) {
state.count++;
},
setCount(state, num) {
state.count = num;
},
setMsg(state, msg) {
state.msg = msg;
},
},
// 计算属性
getters: {
reverseMsg(state) {
return state.msg.split("").reverse().join("");
},
num(state) {
return state.count + 10;
}
}
})
export default store
Home组件---{{ title }}
---{{ num }}
---{{ $store.state.count }}
- {{ item }}---{{ count }}
获取Vuex中getters里面的数据---{{ revMsg }}
执行Vuex中getters里面的方法---{{ count }}
mapGetters---{{ num }}---{{ reverseMsg }}
更改Vuex的store中的状态的唯一方法是提交mutation。Vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)。这个回调函数实际进行状态更改的地方,并且会接受state作为第一个参数。
const store = createStore({
state:{
count:1
},
mutations:{
increment(state){
state.count++
}
}
})
触发mutations里面的方法
store.commit('increment')
Action类似于mutation,不同在于
import { createStore } from "vuex";
const store = createStore({
// 定义数据
state() {
return {
count: 1,
}
},
// 定义方法
mutations: {
incCount(state) {
state.count++;
},
},
//执行mutations里面的方法,异步操作放在actions
actions:{
addCount(context){
// 执行mutations的addCount方法
context.commit("incCount")
},
}
})
export default store
另一种写法
actions:{
addCount({commit}){
// 执行mutations的addCount方法
context.commit("incCount")
},
}
store.dispatch("addCount")
mutation必须同步执行,Action就不受约束,可以在actions内部执行异步操作
import { createStore } from "vuex";
const store = createStore({
// 定义数据
state() {
return {
count: 1,
title: ['马总', '李总', '刘总'],
msg: "你好vue",
}
},
// 定义方法
mutations: {
incCount(state) {
state.count++;
},
setCount(state, num) {
state.count = num;
},
setMsg(state, msg) {
state.msg = msg;
},
},
// 计算属性
getters: {
reverseMsg(state) {
return state.msg.split("").reverse().join("");
},
num(state) {
return state.count + 10;
}
},
//执行mutations里面的方法,异步操作放在actions
actions:{
addCount(context){
// 执行mutations的addCount方法
context.commit("incCount")
},
// setMsg(context,msg){
// setTimeout(() => {
// 执行mutations的setMsg方法
// context.commit("setMsg",mgs);
// }, 1000);
// }
// 结构解析
setMsg({commit},msg){
setTimeout(() => {
commit("setMsg",msg);
}, 1000);
}
}
})
export default store
Home组件---{{ title }}
---{{ num }}
---{{ $store.state.count }}
- {{ item }}---{{ count }}
获取Vuex中getters里面的数据---{{ revMsg }}
执行Vuex中getters里面的方法---{{ count }}
mapGetters---{{ num }}---{{ reverseMsg }}
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。
为了解决这个问题,Vuex允许将store分割成模块(module)。每个模块拥有自己的state、mutation、action、getter,甚至是嵌套子模块——从上至下进行同样方式的分割。
const moduleA={
state:()=>({...}),
mutations:{...},
actions:{...},
getters:{...}
}
const moduleB={
state:()=>({...}),
mutations:{...},
actions:{...},
}
const store=createStore({
modules:{
a:moduleA,
b:moduleB
}
})
store.state.a //->moduleA
store.state.b //->moduleB
let userStore = {
// 定义数据
state() {
return {
count: 1,
title: ['马总', '李总', '刘总'],
msg: "你好vue",
}
},
// 定义方法
mutations: {
incCount(state) {
state.count++;
},
setCount(state, num) {
state.count = num;
},
setMsg(state, msg) {
state.msg = msg;
},
},
// 计算属性
getters: {
reverseMsg(state) {
return state.msg.split("").reverse().join("");
},
num(state) {
return state.count + 10;
}
},
//执行mutations里面的方法,异步操作放在actions
actions:{
addCount(context){
// 执行mutations的addCount方法
context.commit("incCount")
},
// setMsg(context,msg){
// setTimeout(() => {
// 执行mutations的setMsg方法
// context.commit("setMsg",mgs);
// }, 1000);
// }
// 结构解析
setMsg({commit},msg){
setTimeout(() => {
commit("setMsg",msg);
}, 1000);
}
}
}
export default userStore
let newsStore={
// 定义数据
state() {
return {
list:["民生","娱乐"],
}
},
}
export default newsStore
import { createStore } from "vuex";
import newsStore from "./newsStore";
import userStore from "./userStore";
const store = createStore({
modules:{
"user":userStore,
"news":newsStore,
}
})
export default store
Home组件---{{ title }}
获取user模块里面的state:{{ $store.state.user.count }}
获取news模块里面的state:{{ $store.state.news.list }}
组合式api中没有this.$store,可以使用userStore来替代
export default {
setup(){
const store = useStore()
}
}
export default {
setup(){
const store = useStore()
return {
count: computed(()=>store.state.count),
double:computed(()=>store.getters.double)
}
}
}
export default{
setup(){
const store = useStore()
return {
addCount:()=>store.commit('increment'),
asyncIncrement:()=>store.dispatch('asyncIncrement')
}
}
}
Home组件
---{{ count }}---{{ num }}
---{{ count }}
---{{ msg }}
https://vuex.vuejs.org/guide/typescript-support.html
首先需要在Vue项目中集成typescript
vue add typescript
提示:如果配置完ts后调用this.$store有警告信息,重启vscode,或者安装Vue3的插件后重启vscode重试。
Vuex与Typescript一起使用时,必须声明自己的模块扩充
import { ComponentCustomProperties } from "vue";
import { Store, createStore } from 'vuex';
declare module '@vue/runtime-core' {
interface State {
count: number,
title: string[],
msg: string,
}
interface ComponentCustomProperties {
$store: Store
}
}
const store = createStore({
// 定义数据
state() {
return {
count: 1,
title: ['马总', '李总', '刘总'],
msg: "你好vue",
}
},
// 定义方法
mutations: {
incCount(state: any) {
state.count++;
},
setCount(state: any, num: number) {
state.count = num;
},
setMsg(state: any, msg: string) {
state.msg = msg;
},
},
// 计算属性
getters: {
reverseMsg(state: any) {
return state.msg.split("").reverse().join("");
},
num(state: any) {
return state.count + 10;
}
},
//执行mutations里面的方法,异步操作放在actions
actions: {
addCount(context) {
// 执行mutations的addCount方法
context.commit("incCount")
},
// setMsg(context,msg){
// setTimeout(() => {
// 执行mutations的setMsg方法
// context.commit("setMsg",mgs);
// }, 1000);
// }
// 结构解析
setMsg({ commit }, msg: string) {
setTimeout(() => {
commit("setMsg", msg);
}, 1000);
}
}
})
export default store
Home组件
---{{ count }}---{{ num }}
---{{ count }}
---{{ msg }}
Serverless又名无服务器,所谓无服务器并非是说不需要依赖和依靠服务器等资源,而是开发者再也不用过多考虑服务器的问题,可以更专注在产品代码上,狭义的Serverless是Fass和Bass组成。
无人点餐、无人收银系统是一个颠覆传统行业(中西餐厅、奶茶店、酒吧、火锅店、食堂、KTV)的智能点单系统。无人点单,手机支付,免费收银员,免点餐员,免硬件,免布线的一套智能管理系统。

Serverless是一种软件系统架构的思想和方法,它不是软件框架、类库或者工具。与传统框架的不同之处在于,完全由第三方管理,由事件触发,存在于无状态(Stateless)、暂存(可能只存在于一次调用的过程中)计算容器内。构建无服务应用程序意味着开发者可以专注在产品代码上,而无须管理和操作云端或本地的服务器或运行时(运行时通俗讲就是运行环境,如nodejs环境、java环境、php环境)。Serverless真正做到部署应用无需涉及基础设施的建设,自动构建、部署和启动服务。
通俗的讲:Serverless是构建和运行软件时不需要关心服务器的一种架构思想。老程序员都用过虚拟机,刚开始学Serverless时可以把它理解为虚拟机的升级版本。
虚拟主机已经是快被淘汰的上一代产物。云计算涌现出了很多改变传统IT架构和运维方式的新技术,比如虚拟机、容器、微服务,无论这些技术应用在哪些场景,降低成本、提升效率是云服务永恒的主题。Serverless的出现真正的解决了降低成本、提升效率的问题。它真正做到了弹性伸缩、高并发、按需收费、备份容灾、日志监控等。

ServerFul架构是n台Server通过网络通信的方式协作一起,可以说ServerFul架构是基于Server和网络通信(分布式计算)的软件实现架构,Server可以是虚拟机、物理机,以及基于硬件实现的云服务器。

Serverless的核心特点就是实现自动弹性伸缩和按量付费。

资源分配
在Serverless架构中,不用关心应用应用运行的资源(服务配置、磁盘大小)只提供一份代码就行。
计费方式
在Serverless架构中,计费方式按实际使用量计费(如函数调用次数、运行时长),不按传统的执行代码所需的资源计费(如固定CPU)。计费粒度也精确到了毫秒级,而不是传统的小时级。个别云厂商推出了每个月的免费额度,如腾讯云提供了每个月40万GBs的资源使用额度和100万次调用次数的免费额度。中小企业的网站访问量不是特别大的话完全可以免费使用。
弹性伸缩
Serverless架构的弹性伸缩更自动化、更精确,可以快速根据业务并发扩容更多的实例,甚至允许缩容到零实例状态来实现零费用,对用户来说是完全无感知的。而传统架构对服务器(虚拟机)进行扩容,虚拟机的启动速度比较慢,需要几分钟甚至更久。
计算能力
资源按需分配,无需申请资源;
Mwm:租户级别强隔离;
Docker:进程级别隔离;
Mwm+Docker轻量级资源毫秒级启动;
实时扩容,阶梯缩容;
按需收费;
系统运维能力
1、性能保障
整个链路耗时毫秒级内,并支持VPC内网访问
2、安全保障
资源对用户不可见,安全由腾讯云提供专业的保障
提供进程级和用户级安全隔离
访问控制管理
3、自动性扩缩容
根据CPU内容网络IO自动扩容底层资源
根据请求自动扩容函数实例,业务高峰期扩容,满足业务高并发需求,业务低峰期缩容,释放资源,降低成本
4、自愈能力
每一次请求都是一个健康的实例。
Serverless中云函数被第一次调用会执行冷启动,Serverless中云函数被多次连续调用会执行热启动。
冷启动是指在服务器中新开辟一块空间提供一个函数实例运行,这个过程有点像把这个函数放到虚拟机里去运行,每次运行前都需要先启动虚拟机加载这个函数,以前冷启动非常耗时,但是目前云厂商已经能做到毫秒级别的冷启动,这个过程不需要关心,但是需要注意的是使用Session时可能会导致Session丢失,所以建议将Session保存到数据库。
热启动则是说如果一个云函数被持续触发,那就先不释放这个云函数实例,下次请求让然由之前已经创建的云函数实例来运行,就好比打开虚拟机运行完这个函数之后没有关闭虚拟机,而是让它待机,等待下一次重新触发调用运行,这样的好处就是省去了开机的过程。
业务运维能力
亚马逊 AWS Lambda https://aws.amazon.com/cn/lambda/
谷歌 Google Cloud Functions https://cloud.goole.com/functions
微软Microsoft Azure https://www.azure.cn
阿里云函数计算 https://www.aliyun.com/product/fc
腾讯云 云函数 SCF(Serverless Cloud Function) https://cloud.tencent.com/product/scf
华为云 FunctionGraph https://www.huaweicloud.com/product/functiongraph.html
1、为什么不使用亚马逊、谷歌、微软、IBM的server less
在国内阿里云、腾讯云使用的更多一些。就像购买域名、服务器一样,首先想到的肯定不是国外的运营商,英语好的开源尝试一下。
2、阿里云、腾讯云、华为云为什么选择腾讯云
微信小程序的云开发就是基于腾讯云,选择腾云更方便和小程序对接
腾讯云在serverless方面相比其它厂商支持更好一些
腾讯云的技术在线客服非常棒
腾讯云和serverless合作在腾讯云中集成了serverless Framework可以用喜欢的框架开发serverless应用。可以快速部署老项目。
价格更便宜
3、会使用腾讯云的serverless后,其它服务商的serverless也会了吗
是的
通过Serverless Framework提供的云函数SCF组件快速创建与部署一个云函数项目。
Serverless Framework将项目快速部署到腾讯云Serverless平台,部署前,请确认已经注册腾讯云账号并完成实名认证。
npm install -g serverless
serverless -v
在项目的目录上面运行serverless
serverless
在serverless.yml文件所在的项目根目录,运行以下指令进行部署
serverless deploy
广义的Serverless更多是指一种技术理念:Serverless是构建和运行软件是不需要关心服务器的一种架构思想。
侠义的Serverless是指现阶段主流的技术实现:侠义的Serverless是Fass和Bass组成。

Serverless架构
Egg.js基础+Egg.js无人点餐后台管理系统以及API接口
Vue3.x基础+Vue无人点餐无人收银系统
项目部署到Serverless
count: computed(()=>store.state.count),
double:computed(()=>store.getters.double)
}
}
}
#### 15.8.2、组合式api中访问Mutations and Actions
export default{
setup(){
const store = useStore()
return {
addCount:()=>store.commit(‘increment’),
asyncIncrement:()=>store.dispatch(‘asyncIncrement’)
}
}
}
##### Home.vue
### 15.9、Typescript中使用Vuex
https://vuex.vuejs.org/guide/typescript-support.html
首先需要在Vue项目中集成typescript
vue add typescript
**提示**:如果配置完ts后调用this.$store有警告信息,重启vscode,或者安装Vue3的插件后重启vscode重试。
#### 15.9.1、修改store.js为store.ts
#### 15.9.2、配置store.ts中的代码
Vuex与Typescript一起使用时,必须声明自己的模块扩充
import { ComponentCustomProperties } from “vue”;
import { Store, createStore } from ‘vuex’;
declare module ‘@vue/runtime-core’ {
interface State {
count: number,
title: string[],
msg: string,
}
interface ComponentCustomProperties {
$store: Store
}
}
const store = createStore({
// 定义数据
state() {
return {
count: 1,
title: [‘马总’, ‘李总’, ‘刘总’],
msg: “你好vue”,
}
},
// 定义方法
mutations: {
incCount(state: any) {
state.count++;
},
setCount(state: any, num: number) {
state.count = num;
},
setMsg(state: any, msg: string) {
state.msg = msg;
},
},
// 计算属性
getters: {
reverseMsg(state: any) {
return state.msg.split(“”).reverse().join(“”);
},
num(state: any) {
return state.count + 10;
}
},
//执行mutations里面的方法,异步操作放在actions
actions: {
addCount(context) {
// 执行mutations的addCount方法
context.commit(“incCount”)
},
// setMsg(context,msg){
// setTimeout(() => {
// 执行mutations的setMsg方法
// context.commit(“setMsg”,mgs);
// }, 1000);
// }
// 结构解析
setMsg({ commit }, msg: string) {
setTimeout(() => {
commit("setMsg", msg);
}, 1000);
}
}
})
export default store
##### Home.vue
## 16、Serverless
### 16.1、Serverless架构介绍
Serverless又名无服务器,所谓无服务器并非是说不需要依赖和依靠服务器等资源,而是开发者再也不用过多考虑服务器的问题,可以更专注在产品代码上,狭义的Serverless是Fass和Bass组成。
无人点餐、无人收银系统是一个颠覆传统行业(中西餐厅、奶茶店、酒吧、火锅店、食堂、KTV)的智能点单系统。无人点单,手机支付,免费收银员,免点餐员,免硬件,免布线的一套智能管理系统。
#### 16.1.1、概念
Serverless是一种软件系统架构的思想和方法,它不是软件框架、类库或者工具。与传统框架的不同之处在于,完全由第三方管理,由事件触发,存在于无状态(Stateless)、暂存(可能只存在于一次调用的过程中)计算容器内。构建无服务应用程序意味着开发者可以专注在产品代码上,而无须管理和操作云端或本地的服务器或运行时(运行时通俗讲就是运行环境,如nodejs环境、java环境、php环境)。Serverless真正做到部署应用无需涉及基础设施的建设,自动构建、部署和启动服务。
通俗的讲:Serverless是构建和运行软件时不需要关心服务器的一种架构思想。老程序员都用过虚拟机,刚开始学Serverless时可以把它理解为虚拟机的升级版本。
虚拟主机已经是快被淘汰的上一代产物。云计算涌现出了很多改变传统IT架构和运维方式的新技术,比如虚拟机、容器、微服务,无论这些技术应用在哪些场景,降低成本、提升效率是云服务永恒的主题。Serverless的出现真正的解决了降低成本、提升效率的问题。它真正做到了弹性伸缩、高并发、按需收费、备份容灾、日志监控等。
#### 16.1.2、传统模式和Serverless模式下项目开发上线流程
#### 16.1.3、Serverless和ServerFul架构的区别
##### **传统的ServerFul架构模式**
ServerFul架构是n台Server通过网络通信的方式协作一起,可以说ServerFul架构是基于Server和网络通信(分布式计算)的软件实现架构,Server可以是虚拟机、物理机,以及基于硬件实现的云服务器。
[外链图片转存中...(img-KJ29ArV1-1668475744416)]
##### Serverless架构模式
Serverless的核心特点就是实现自动弹性伸缩和按量付费。
##### Serverless相比ServerFul的特点
- 资源分配
在Serverless架构中,不用关心应用应用运行的资源(服务配置、磁盘大小)只提供一份代码就行。
- 计费方式
在Serverless架构中,计费方式按实际使用量计费(如函数调用次数、运行时长),不按传统的执行代码所需的资源计费(如固定CPU)。计费粒度也精确到了毫秒级,而不是传统的小时级。个别云厂商推出了每个月的免费额度,如腾讯云提供了每个月40万GBs的资源使用额度和100万次调用次数的免费额度。中小企业的网站访问量不是特别大的话完全可以免费使用。
- 弹性伸缩
Serverless架构的弹性伸缩更自动化、更精确,可以快速根据业务并发扩容更多的实例,甚至允许缩容到零实例状态来实现零费用,对用户来说是完全无感知的。而传统架构对服务器(虚拟机)进行扩容,虚拟机的启动速度比较慢,需要几分钟甚至更久。
#### 16.1.4、Serverless的能力
- 计算能力
资源按需分配,无需申请资源;
Mwm:租户级别强隔离;
Docker:进程级别隔离;
Mwm+Docker轻量级资源毫秒级启动;
实时扩容,阶梯缩容;
按需收费;
- 系统运维能力
1、性能保障
整个链路耗时毫秒级内,并支持VPC内网访问
2、安全保障
资源对用户不可见,安全由腾讯云提供专业的保障
提供进程级和用户级安全隔离
访问控制管理
3、自动性扩缩容
根据CPU内容网络IO自动扩容底层资源
根据请求自动扩容函数实例,业务高峰期扩容,满足业务高并发需求,业务低峰期缩容,释放资源,降低成本
4、自愈能力
每一次请求都是一个健康的实例。
Serverless中云函数被第一次调用会执行冷启动,Serverless中云函数被多次连续调用会执行热启动。
**冷启动**是指在服务器中新开辟一块空间提供一个函数实例运行,这个过程有点像把这个函数放到虚拟机里去运行,每次运行前都需要先启动虚拟机加载这个函数,以前冷启动非常耗时,但是目前云厂商已经能做到毫秒级别的冷启动,这个过程不需要关心,但是需要注意的是使用Session时可能会导致Session丢失,所以建议将Session保存到数据库。
**热启动**则是说如果一个云函数被持续触发,那就先不释放这个云函数实例,下次请求让然由之前已经创建的云函数实例来运行,就好比打开虚拟机运行完这个函数之后没有关闭虚拟机,而是让它待机,等待下一次重新触发调用运行,这样的好处就是省去了开机的过程。
- 业务运维能力
#### 16.1.5、Serverless厂商
##### 云厂商
亚马逊 AWS Lambda https://aws.amazon.com/cn/lambda/
谷歌 Google Cloud Functions https://cloud.goole.com/functions
微软Microsoft Azure https://www.azure.cn
阿里云函数计算 https://www.aliyun.com/product/fc
腾讯云 云函数 SCF(Serverless Cloud Function) https://cloud.tencent.com/product/scf
华为云 FunctionGraph https://www.huaweicloud.com/product/functiongraph.html
##### 大家关心的问题
1、为什么不使用亚马逊、谷歌、微软、IBM的server less
在国内阿里云、腾讯云使用的更多一些。就像购买域名、服务器一样,首先想到的肯定不是国外的运营商,英语好的开源尝试一下。
2、阿里云、腾讯云、华为云为什么选择腾讯云
微信小程序的云开发就是基于腾讯云,选择腾云更方便和小程序对接
腾讯云在serverless方面相比其它厂商支持更好一些
腾讯云的技术在线客服非常棒
腾讯云和serverless合作在腾讯云中集成了serverless Framework可以用喜欢的框架开发serverless应用。可以快速部署老项目。
价格更便宜
3、会使用腾讯云的serverless后,其它服务商的serverless也会了吗
是的
#### 16.1.6、部署serverless应用
通过Serverless Framework提供的云函数SCF组件快速创建与部署一个云函数项目。
Serverless Framework将项目快速部署到腾讯云Serverless平台,部署前,请确认已经注册腾讯云账号并完成实名认证。
##### 安装serverless
npm install -g serverless
serverless -v
##### 创建项目
在项目的目录上面运行serverless
serverless
##### 部署
在serverless.yml文件所在的项目根目录,运行以下指令进行部署
serverless deploy
#### 16.1.7、Serverless组成
广义的Serverless更多是指一种技术理念:Serverless是构建和运行软件是不需要关心服务器的一种架构思想。
侠义的Serverless是指现阶段主流的技术实现:侠义的Serverless是Fass和Bass组成。
### 16.2、项目
Serverless架构
Egg.js基础+Egg.js无人点餐后台管理系统以及API接口
Vue3.x基础+Vue无人点餐无人收银系统
项目部署到Serverless